Version 1.0
Copyright © 2022 Lowell D. Thomas
Python APG
 … an ABNF Parser Generator
stats.py
Go to the documentation of this file.
1 ''' @file apg_py/lib/stats.py @brief Collects parser's node statistics.'''
2 from apg_py.lib import identifiers as id
3 
4 
5 class Stats():
6  def __init__(self, parser):
7  '''Class constructor.
8  @param parser the parser object to attach this Stats object to'''
9  self.parserparser = parser
10  parser.stats = self
11  self.statsstats = {}
12  self.rule_statsrule_stats = {}
13  self.namesnames = {}
14  for rule in self.parserparser.rules:
15  self.namesnames[rule['lower']] = rule['name']
16  for udt in self.parserparser.udts:
17  self.namesnames[udt['lower']] = udt['name']
18  self.clearclear()
19 
20  def clear(self):
21  '''For internal use.
22  Called here and by the parser to initialize the hit counts.'''
23  s = {id.EMPTY: 0, id.MATCH: 0, id.NOMATCH: 0}
24  self.statsstats[id.ALT] = s.copy()
25  self.statsstats[id.CAT] = s.copy()
26  self.statsstats[id.REP] = s.copy()
27  self.statsstats[id.RNM] = s.copy()
28  self.statsstats[id.TLS] = s.copy()
29  self.statsstats[id.TBS] = s.copy()
30  self.statsstats[id.TRG] = s.copy()
31  self.statsstats[id.UDT] = s.copy()
32  self.statsstats[id.AND] = s.copy()
33  self.statsstats[id.NOT] = s.copy()
34  self.statsstats[id.BKR] = s.copy()
35  self.statsstats[id.BKA] = s.copy()
36  self.statsstats[id.BKN] = s.copy()
37  self.statsstats[id.ABG] = s.copy()
38  self.statsstats[id.AEN] = s.copy()
39  for name in self.namesnames:
40  self.rule_statsrule_stats[name] = s.copy()
41 
42  def total(self, stat):
43  '''For internal use. Computes the total number of hits.'''
44  return stat[id.EMPTY] + stat[id.MATCH] + stat[id.NOMATCH]
45 
46  def collect(self, op):
47  '''Called by the parser for each node to collect the hit count.'''
48  state = self.parserparser.state
49  self.statsstats[op['type']][state] += 1
50  if(op['type'] == id.RNM):
51  rule = self.parserparser.rules[op['index']]
52  self.rule_statsrule_stats[rule['lower']][state] += 1
53  if(op['type'] == id.UDT):
54  udt = self.parserparser.udts[op['index']]
55  self.rule_statsrule_stats[udt['lower']][state] += 1
56 
57  def display(self):
58  '''Display the parse tree node hit statistics.
59  It will first display the node statistics for the various
60  node operators.
61  It then displays the rule name and UDT name statistics.
62  Operators and rule/UDT names for which the hit count is 0 are
63  not displayed.'''
64  mTotal = 0
65  eTotal = 0
66  nTotal = 0
67  tTotal = 0
68  # display operator stats
69  print(' OPERATOR NODE HIT STATISTICS')
70  print(
71  '%5s %7s %7s %7s %7s' %
72  ('', 'MATCH', 'EMPTY', 'NOMATCH', 'TOTAL'))
73  for stat_id in self.statsstats:
74  stat = self.statsstats[stat_id]
75  total = self.totaltotal(stat)
76  if(total):
77  # ignore operators with no hits
78  print('%5s' % (id.dict.get(stat_id)) + ' ', end='')
79  mTotal += stat[id.MATCH]
80  eTotal += stat[id.EMPTY]
81  nTotal += stat[id.NOMATCH]
82  tTotal += total
83  p = '%7d %7d %7d %7d' % (
84  stat[id.MATCH], stat[id.EMPTY], stat[id.NOMATCH], total)
85  print(p)
86  print('%5s ' % ('TOTAL'), end='')
87  p = '%7d %7d %7d %7d' % (
88  mTotal, eTotal, nTotal, tTotal)
89  print(p)
90  print()
91  print(' RULE NAME (RNM/UDT) NODE HIT STATISTICS')
92  print(
93  '%7s %7s %7s %7s %s' %
94  ('MATCH', 'EMPTY', 'NOMATCH', 'TOTAL', 'RULE/UDT NAME'))
95 
96  def by_name(val):
97  return val[0]
98 
99  def by_count(val):
100  return val[1]
101 
102  # display rule stats
103  # order rules alphabetically and then by number of hits,
104  # ignoring those with no hits
105  ll = []
106  for name in self.rule_statsrule_stats:
107  stat = self.rule_statsrule_stats[name]
108  ll.append((name, self.totaltotal(stat)))
109 
110  ll.sort(key=by_name)
111  ll.sort(key=by_count, reverse=True)
112  for item in ll:
113  count = item[1]
114  if(count):
115  name = item[0]
116  stat = self.rule_statsrule_stats[name]
117  p = '%7d %7d %7d %7d %s' % (
118  stat[id.MATCH], stat[id.EMPTY], stat[id.NOMATCH],
119  count, self.namesnames[name])
120  print(p)
def collect(self, op)
Called by the parser for each node to collect the hit count.
Definition: stats.py:46
def total(self, stat)
For internal use.
Definition: stats.py:42
def clear(self)
For internal use.
Definition: stats.py:20
def __init__(self, parser)
Class constructor.
Definition: stats.py:6
def display(self)
Display the parse tree node hit statistics.
Definition: stats.py:57
Python APG, Version 1.0, is licensed under the 2-Clause BSD License,
an Open Source Initiative Approved License.