Version 1.0
Copyright © 2022 Lowell D. Thomas
Python APG
 … an ABNF Parser Generator
rule_dependencies.py
Go to the documentation of this file.
1 ''' @file apg_py/api/rule_dependencies.py
2 @brief Determine which rules each rule depends on and vice versa.'''
3 from apg_py.lib import identifiers as id
4 from pprint import pprint
5 
6 
7 def rule_dependencies(rules, udts, rule_names, udt_names):
8  '''Determine the rule dependencies and
9  recursive types for each rule.
10  @param rules The rules from the syntax & semantic phases.
11  @param udts The UDTs from the syntax & semantic phases.
12  @param rule_names The NameList object (see class Api)for looking up
13  rule name indexes.
14  @param udt_names The NameList object for
15  looking up UDT name indexes.
16  @returns The rule dependencies object.
17  '''
18 
19  rule_range = range(len(rules))
20 
21  def scan(index, is_scanned):
22  rdi = rule_deps[index]
23  is_scanned[index] = True
24  for op in rules[index]['opcodes']:
25  if(op['type'] == id.RNM):
26  rdop = rule_deps[op['index']]
27  rdi['refers_to'][op['index']] = True
28  if(is_scanned[op['index']] is False):
29  scan(op['index'], is_scanned)
30  for j in rule_range:
31  if(rdop['refers_to'][j]):
32  rdi['refers_to'][j] = True
33  for j in range(udt_count):
34  if(rdop['refers_to_udt'][j]):
35  rdi['refers_to_udt'][j] = True
36  elif(op['type'] == id.UDT):
37  rdi['refers_to_udt'][op['index']] = True
38  elif(op['type'] == id.BKR):
39  lookup = rule_names.get(op['lower'])
40  if(lookup == -1):
41  lookup = udt_names.get(op['lower'])
42  if(lookup == -1):
43  raise Exception(
44  'back referenced name is not a rule or UDT')
45  rdi['refers_to_udt'][lookup['index']] = True
46  else:
47  rdi['refers_to'][lookup['index']] = True
48 
49  # initialize all rule dependencies
50  rule_count = len(rules)
51  udt_count = len(udts)
52  rule_deps = []
53  for i in rule_range:
54  rule_deps.append({
55  'refers_to': [False] * rule_count,
56  'is_referenced_by': [False] * rule_count,
57  'refers_to_udt': [False] * udt_count,
58  'recursive_type': id.ATTR_N,
59  'group_no': -1
60  })
61 
62  # discover which rules each rule refers to
63  for i in rule_range:
64  is_scanned = [False] * rule_count
65  scan(i, is_scanned)
66 
67  # discover all rules which each rule is referenced by
68  for i in rule_range:
69  for j in rule_range:
70  if(rule_deps[j]['refers_to'][i]):
71  rule_deps[i]['is_referenced_by'][j] = True
72 
73  # find the recursive rules
74  for i in rule_range:
75  if(rule_deps[i]['refers_to'][i]):
76  rule_deps[i]['recursive_type'] = id.ATTR_R
77 
78  # find the mutually-recursive groups, if any
79  group_count = -1
80  for i in rule_range:
81  rdi = rule_deps[i]
82  if(rdi['recursive_type'] == id.ATTR_R):
83  new_group = True
84  for j in rule_range:
85  if(j != i):
86  rdj = rule_deps[j]
87  if(rdj['recursive_type'] == id.ATTR_R):
88  if(rdi['refers_to'][j] and rdj['refers_to'][i]):
89  if(new_group):
90  group_count += 1
91  new_group = False
92  rdi['recursive_type'] = id.ATTR_MR
93  rdi['group_no'] = group_count
94  rdj['recursive_type'] = id.ATTR_MR
95  rdj['group_no'] = group_count
96  return rule_deps
97 
98 
99 def display_deps(rule_deps, rules, udts, alpha=True):
100  '''Display the rule dependencies.
101  @param rule_deps The rule dependencies.
102  The returned object from the @ref rule_dependencies() function.
103  @param rules The rules from the syntax & semantic phases.
104  @param udts The UDTs from the syntax & semantic phases.
105  @param alpha If True (default), rules are listed alphabetically.
106  Otherwise, they are listed in the order in which they appear
107  in the grammar syntax.
108  @returns The display string.
109  '''
110  RULES_LINE = 8
111 
112  def alpha_rules(val):
113  return rules[val]['lower']
114 
115  def alpha_udts(val):
116  return udts[val]['lower']
117 
118  def type(val):
119  return rule_deps[val]['recursive_type']
120 
121  def group(val):
122  return rule_deps[val]['group_no']
123 
124  rule_count = len(rules)
125  udt_count = len(udts)
126  rule_range = range(rule_count)
127  udt_range = range(udt_count)
128  rule_alphas = [0] * rule_count
129  udt_alphas = [0] * udt_count
130  for i in rule_range:
131  rule_alphas[i] = rules[i]['index']
132  for i in range(udt_count):
133  udt_alphas[i] = udts[i]['index']
134  if(alpha):
135  rule_alphas.sort(key=alpha_rules)
136  udt_alphas.sort(key=alpha_udts)
137  show = ''
138  # compute max name length
139  length = 0
140  for i in rule_range:
141  rlen = len(rules[i]['name'])
142  if(rlen > length):
143  length = rlen
144  fmt = '%' + str(length) + 's: '
145  show += (fmt + 'legend\n') % ('')
146  show += (fmt + '=> (rule refers to)\n') % ('rule')
147  show += (fmt + '<= (rule is referenced by)\n\n') % ('rule')
148  for ii in rule_range:
149  i = rule_alphas[ii]
150  # show += (fmt + '%s\n') % (rules[i]['name'],
151  # id.dict.get(rule_deps[i]['recursive_type']))
152  counti = 0
153  show += (fmt + '=> ') % (rules[i]['name'])
154  for kk in rule_range:
155  if(rule_deps[i]['refers_to'][kk]):
156  counti += 1
157  for kk in udt_range:
158  if(rule_deps[i]['refers_to_udt'][kk]):
159  counti += 1
160  if(counti):
161  count = 0
162  for jj in rule_range:
163  j = rule_alphas[jj]
164  k = rule_deps[i]['refers_to'][j]
165  if(k):
166  if(count % RULES_LINE == 0 and count > 1):
167  show += ('\n' + fmt + '=> ') % ('')
168  show += rules[j]['name']
169  if(count < counti - 1):
170  show += ', '
171  count += 1
172  for jj in udt_range:
173  j = udt_alphas[jj]
174  k = rule_deps[i]['refers_to_udt'][j]
175  if(k):
176  if(count % RULES_LINE == 0 and count > 1):
177  show += ('\n' + fmt + '=> ') % ('')
178  show += udts[j]['name']
179  if(count < counti - 1):
180  show += ', '
181  count += 1
182  show += '\n'
183  show += (fmt + '<= ') % ('')
184  counti = 0
185  for kk in rule_range:
186  if(rule_deps[i]['is_referenced_by'][kk]):
187  counti += 1
188  if(counti):
189  count = 0
190  for jj in rule_range:
191  j = rule_alphas[jj]
192  k = rule_deps[i]['is_referenced_by'][j]
193  if(k):
194  if(count % RULES_LINE == 0 and count > 1):
195  show += ('\n' + fmt + '<= ') % ('')
196  show += rules[j]['name']
197  if(count < counti - 1):
198  show += ', '
199  count += 1
200  show += '\n'
201  return show
def rule_dependencies(rules, udts, rule_names, udt_names)
Determine the rule dependencies and recursive types for each rule.
def display_deps(rule_deps, rules, udts, alpha=True)
Display the rule dependencies.
Python APG, Version 1.0, is licensed under the 2-Clause BSD License,
an Open Source Initiative Approved License.