Version 1.0
Copyright © 2022 Lowell D. Thomas
Python APG
 … an ABNF Parser Generator
semantic_callbacks.py
Go to the documentation of this file.
1 ''' @file apg_py/api/semantic_callbacks.py
2 @brief All the semantic AST translation callback functions.
3 '''
4 import sys
5 from apg_py.lib import identifiers as id
6 from apg_py.lib import utilities as utils
7 
8 
10  ast.add_callback('file', semantic_file)
11  ast.add_callback('rule', semantic_rule)
12  ast.add_callback('rule-lookup', semantic_rule_lookup)
13  ast.add_callback('rule-name', semantic_rule_name)
14  ast.add_callback('defined', semantic_defined)
15  ast.add_callback('inc-alt', semantic_inc_alt)
16  ast.add_callback('alternation', semantic_alternation)
17  ast.add_callback('concatenation', semantic_concatenation)
18  ast.add_callback('repetition', semantic_repetition)
19  ast.add_callback('option-open', semantic_option_open)
20  ast.add_callback('rep-op', semantic_rep_op)
21  ast.add_callback('rep-min', semantic_rep_min)
22  ast.add_callback('rep-max', semantic_rep_max)
23  ast.add_callback('rep-min-max', semantic_rep_min_max)
24  ast.add_callback('and-op', semantic_and_op)
25  ast.add_callback('not-op', semantic_not_op)
26  ast.add_callback('rnm-op', semantic_rnm_op)
27  ast.add_callback('abg-op', semantic_abg_op)
28  ast.add_callback('aen-op', semantic_aen_op)
29  ast.add_callback('bka-op', semantic_bka_op)
30  ast.add_callback('bkn-op', semantic_bkn_op)
31  ast.add_callback('ci', semantic_ci)
32  ast.add_callback('cs', semantic_cs)
33  ast.add_callback('um', semantic_um)
34  ast.add_callback('rm', semantic_rm)
35  ast.add_callback('bkr-name', semantic_bkr_name)
36  ast.add_callback('bkr-op', semantic_bkr_op)
37  ast.add_callback('udt-empty', semantic_udt_empty)
38  ast.add_callback('udt-non-empty', semantic_udt_non_empty)
39  ast.add_callback('tls-op', semantic_tls_op)
40  ast.add_callback('tls-string', semantic_tls_string)
41  ast.add_callback('cls-string', semantic_cls_string)
42  ast.add_callback('tbs-op', semantic_tbs_op)
43  ast.add_callback('d-string', semantic_d_string)
44  ast.add_callback('b-string', semantic_b_string)
45  ast.add_callback('x-string', semantic_x_string)
46  ast.add_callback('trg-op', semantic_trg_op)
47  ast.add_callback('dmin', semantic_dmin)
48  ast.add_callback('bmin', semantic_bmin)
49  ast.add_callback('xmin', semantic_xmin)
50  ast.add_callback('xmax', semantic_xmax)
51  ast.add_callback('bmax', semantic_bmax)
52  ast.add_callback('dmax', semantic_dmax)
53 
54 
55 def decnum(chars, beg, len):
56  num = 0
57  for i in range(beg, beg + len):
58  num = 10 * num + chars[i] - 48
59  return num
60 
61 
62 def binnum(chars, beg, len):
63  num = 0
64  for i in range(beg, beg + len):
65  num = 2 * num + chars[i] - 48
66  return num
67 
68 
69 def hexnum(chars, beg, len):
70  num = 0
71  for i in range(beg, beg + len):
72  digit = chars[i]
73  if(digit >= 48 and digit <= 57):
74  digit -= 48
75  elif(digit >= 65 and digit <= 70):
76  digit -= 55
77  elif(digit >= 97 and digit <= 102):
78  digit -= 87
79  else:
80  raise Exception(
81  'semantic-callbacks: hexnum: hex digit out of range', digit)
82  num = 16 * num + digit
83  return num
84 
85 
86 def semantic_file(state, input, phrase_index, phrase_length, data):
87  if(state == id.SEM_PRE):
88  # data['errors'].append({"line": 0, 'index': 0, 'msg': 'file: test'})
89  data['alt_stack'] = []
90  data['top_alt'] = None
91  data['top_rule'] = None
92  data['definedas'] = None
93  data['rule_name'] = None
94  data['min'] = None
95  data['max'] = None
96  data['top_rep'] = None
97  data['ci'] = None
98  data['cs'] = None
99  data['um'] = None
100  data['ur'] = None
101  data['bkr_name'] = None
102  data['tbs_str'] = None
103  else:
104  for rule in data['rules']:
105  for op in rule['opcodes']:
106  # validate RNM rule names and reset the opcode rule index
107  if(op['type'] == id.RNM):
108  name = op['index']['name']
109  ph = op['index']['phrase_index']
110  lookup = data['rule_names'].get(name)
111  if(lookup == -1):
112  op['index'] = -1
113  msg = 'rule name \'' + name + '\' used but not defined'
114  data['errors'].append({
115  'line': data['find_line'](ph),
116  'index': ph,
117  'msg': msg})
118  else:
119  op['index'] = lookup['index']
120  if(op['type'] == id.BKR):
121  name = op['name']['name']
122  um = op['bkr_mode'] == id.BKR_MODE_UM
123  ph = op['name']['phrase_index']
124  op['name'] = name
125  op['is_udt'] = False
126  op['empty'] = None
127  lookup = data['rule_names'].get(name)
128  if(lookup == -1):
129  # not a rule name, try UDTs
130  lookup = data['udt_names'].get(name)
131  if(lookup == -1):
132  # not a rule or UDT name
133  op['name'] = ''
134  msg = 'back referenced name \'' + name + \
135  '\' not a rule or UDT name'
136  data['errors'].append({
137  'line': data['find_line'](ph),
138  'index': ph,
139  'msg': msg})
140  else:
141  # BKR references a UDT name
142  udt = data['udts'][lookup['index']]
143  op['is_udt'] = True
144  op['empty'] = udt['empty']
145  op['index'] = udt['index']
146  if(um):
147  udt['is_bkru'] = True
148  else:
149  udt['is_bkrr'] = True
150  else:
151  ru = data['rules'][lookup['index']]
152  op['index'] = lookup['index']
153  if(um):
154  ru['is_bkru'] = True
155  else:
156  ru['is_bkrr'] = True
157  return id.SEM_OK
158 
159 
160 def semantic_rule_lookup(state, input, phrase_index, phrase_length, data):
161  if(state == id.SEM_POST):
162  if(data['definedas'] == '='):
163  rule_name = data['rule_names'].add(data['rule_name'])
164  if(rule_name == -1):
165  # rule previously defined
166  msg = 'Rule name \'' + \
167  data['rule_name'] + '\' previously defined.'
168  data['errors'].append({'line': data['find_line'](phrase_index),
169  'index': phrase_index,
170  'msg': msg})
171  else:
172  # start a new rule
173  data['top_rule'] = {'name': rule_name['name'],
174  'lower': rule_name['lower'],
175  'index': rule_name['index'],
176  'line': data['find_line'](phrase_index),
177  'is_bkru': False,
178  'is_bkrr': False,
179  'has_bkrr': False,
180  'opcodes': []}
181  data['rules'].append(data['top_rule'])
182  else:
183  rule_name = data['rule_names'].get(data['rule_name'])
184  if(rule_name == -1):
185  # rule not previously defined
186  msg = 'Rule name \'' + \
187  data['rule_name'] + \
188  '\' for incremental alternative not previously defined.'
189  data['errors'].append({'line': data['find_line'](phrase_index),
190  'index': phrase_index,
191  'msg': msg})
192  else:
193  data['top_rule'] = data['rules'][rule_name['index']]
194  return id.SEM_OK
195 
196 
197 def semantic_rule(state, input, phrase_index, phrase_length, data):
198  if(state == id.SEM_PRE):
199  data['alt_stack'].clear()
200  data['top_stack'] = None
201  return id.SEM_OK
202 
203 
204 def semantic_rule_name(state, input, phrase_index, phrase_length, data):
205  if(state == id.SEM_PRE):
206  data['rule_name'] = utils.tuple_to_string(
207  input[phrase_index:phrase_index + phrase_length])
208  return id.SEM_OK
209 
210 
211 def semantic_defined(state, input, phrase_index, phrase_length, data):
212  if(state == id.SEM_PRE):
213  data['definedas'] = '='
214  return id.SEM_OK
215 
216 
217 def semantic_inc_alt(state, input, phrase_index, phrase_length, data):
218  if(state == id.SEM_PRE):
219  data['definedas'] = '=/'
220  return id.SEM_OK
221 
222 
223 def semantic_alternation(state, input, phrase_index, phrase_length, data):
224  if(state == id.SEM_PRE):
225  if(data['top_stack'] is None):
226  if(data['definedas'] == '='):
227  # first ALT of new rule
228  data['top_alt'] = {
229  'alt': {
230  'type': id.ALT,
231  'children': []},
232  'cat': None}
233  data['alt_stack'].append(data['top_alt'])
234  data['top_rule']['opcodes'].append(data['top_alt']['alt'])
235  else:
236  # reset the rule we are adding the ALT op to
237  assert(data['definedas'] == '=/'), 'invalid defined-as'
238  data['top_alt'] = {
239  'alt': data['top_rule']['opcodes'][0], 'cat': None}
240  data['alt_stack'].append(data['top_alt'])
241  else:
242  # add another ALT op to the current rule
243  data['top_alt'] = {
244  'alt': {
245  'type': id.ALT,
246  'children': []},
247  'cat': None}
248  data['alt_stack'].append(data['top_alt'])
249  data['top_rule']['opcodes'].append(data['top_alt']['alt'])
250  else:
251  assert(state == id.SEM_POST), 'invalid AST translation state'
252  # pop the ALT stack
253  data['alt_stack'].pop()
254  if(len(data['alt_stack']) > 0):
255  data['top_alt'] = data['alt_stack'][-1]
256  else:
257  data['top_alt'] = None
258  return id.SEM_OK
259 
260 
261 def semantic_concatenation(state, input, phrase_index, phrase_length, data):
262  if(state == id.SEM_PRE):
263  data['top_alt']['alt']['children'].append(
264  len(data['top_rule']['opcodes']))
265  data['top_alt']['cat'] = {'type': id.CAT, 'children': []}
266  data['top_rule']['opcodes'].append(data['top_alt']['cat'])
267  else:
268  assert(state == id.SEM_POST), 'invalid AST translation state'
269  data['top_alt']['cat'] = None
270  return id.SEM_OK
271 
272 
273 def semantic_repetition(state, input, phrase_index, phrase_length, data):
274  if(state == id.SEM_PRE):
275  data['top_alt']['cat']['children'].append(
276  len(data['top_rule']['opcodes']))
277  return id.SEM_OK
278 
279 
280 def semantic_option_open(state, input, phrase_index, phrase_length, data):
281  if(state == id.SEM_PRE):
282  data['top_rule']['opcodes'].append(
283  {'type': id.REP, 'min': 0, 'max': 1})
284  return id.SEM_OK
285 
286 
287 def semantic_rep_op(state, input, phrase_index, phrase_length, data):
288  if(state == id.SEM_PRE):
289  data['min'] = 0
290  data['max'] = sys.maxsize
291  data['top_rep'] = {'type': id.REP, 'min': 0, 'max': sys.maxsize}
292  data['top_rule']['opcodes'].append(data['top_rep'])
293  else:
294  assert(state == id.SEM_POST), 'invalid AST translation state'
295  if(data['min'] > data['max']):
296  min = str(data['min'])
297  max = str(data['max'])
298  msg = 'repetition min(' + min + ') > max(' + max + ')'
299  data['errors'].append({'line': data['find_line'](phrase_index),
300  'index': phrase_index, 'msg': msg})
301  if(data['min'] == 0 and data['max'] == 0):
302  msg = 'repetition 0*0 not allowed - '
303  msg += 'for explicit empty string use ""'
304  data['errors'].append({'line': data['find_line'](phrase_index),
305  'index': phrase_index, 'msg': msg})
306  data['top_rep']['min'] = data['min']
307  data['top_rep']['max'] = data['max']
308  return id.SEM_OK
309 
310 
311 def semantic_rep_min(state, input, phrase_index, phrase_length, data):
312  if(state == id.SEM_POST):
313  data['min'] = decnum(input, phrase_index, phrase_length)
314  return id.SEM_OK
315 
316 
317 def semantic_rep_max(state, input, phrase_index, phrase_length, data):
318  if(state == id.SEM_POST):
319  data['max'] = decnum(input, phrase_index, phrase_length)
320  return id.SEM_OK
321 
322 
323 def semantic_rep_min_max(state, input, phrase_index, phrase_length, data):
324  if(state == id.SEM_POST):
325  data['max'] = decnum(input, phrase_index, phrase_length)
326  data['min'] = data['max']
327  return id.SEM_OK
328 
329 
330 def semantic_and_op(state, input, phrase_index, phrase_length, data):
331  if(state == id.SEM_POST):
332  data['top_rule']['opcodes'].append({'type': id.AND, })
333  return id.SEM_OK
334 
335 
336 def semantic_not_op(state, input, phrase_index, phrase_length, data):
337  if(state == id.SEM_POST):
338  data['top_rule']['opcodes'].append({'type': id.NOT, })
339  return id.SEM_OK
340 
341 
342 def semantic_rnm_op(state, input, phrase_index, phrase_length, data):
343  if(state == id.SEM_POST):
344  # note: saving rule name in 'index',
345  # will be converted to integer index later
346  name = utils.tuple_to_string(
347  input[phrase_index:phrase_index + phrase_length])
348  data['top_rule']['opcodes'].append({
349  'type': id.RNM,
350  'index': {'name': name, 'phrase_index': phrase_index}})
351  return id.SEM_OK
352 
353 
354 def semantic_abg_op(state, input, phrase_index, phrase_length, data):
355  if(state == id.SEM_POST):
356  data['top_rule']['opcodes'].append({'type': id.ABG, })
357  return id.SEM_OK
358 
359 
360 def semantic_aen_op(state, input, phrase_index, phrase_length, data):
361  if(state == id.SEM_POST):
362  data['top_rule']['opcodes'].append({'type': id.AEN, })
363  return id.SEM_OK
364 
365 
366 def semantic_bka_op(state, input, phrase_index, phrase_length, data):
367  if(state == id.SEM_POST):
368  data['top_rule']['opcodes'].append({'type': id.BKA, })
369  return id.SEM_OK
370 
371 
372 def semantic_bkn_op(state, input, phrase_index, phrase_length, data):
373  if(state == id.SEM_POST):
374  data['top_rule']['opcodes'].append({'type': id.BKN, })
375  return id.SEM_OK
376 
377 
378 def semantic_ci(state, input, phrase_index, phrase_length, data):
379  if(state == id.SEM_POST):
380  data['ci'] = True
381  data['cs'] = False
382  return id.SEM_OK
383 
384 
385 def semantic_cs(state, input, phrase_index, phrase_length, data):
386  if(state == id.SEM_POST):
387  data['cs'] = True
388  data['ci'] = False
389  return id.SEM_OK
390 
391 
392 def semantic_um(state, input, phrase_index, phrase_length, data):
393  if(state == id.SEM_POST):
394  data['um'] = True
395  return id.SEM_OK
396 
397 
398 def semantic_rm(state, input, phrase_index, phrase_length, data):
399  if(state == id.SEM_POST):
400  data['ur'] = True
401  return id.SEM_OK
402 
403 
404 def semantic_bkr_name(state, input, phrase_index, phrase_length, data):
405  if(state == id.SEM_POST):
406  # phrase_index will be used for error reporting, then deleted
407  name = utils.tuple_to_string(
408  input[phrase_index:phrase_index + phrase_length])
409  data['bkr_name'] = {'name': name, 'phrase_index': phrase_index}
410  return id.SEM_OK
411 
412 
413 def semantic_bkr_op(state, input, phrase_index, phrase_length, data):
414  if(state == id.SEM_PRE):
415  # set defaults
416  data['ci'] = True
417  data['cs'] = False
418  data['um'] = True
419  data['ur'] = False
420  else:
421  assert(state == id.SEM_POST), 'invalid AST translation state'
422  case = id.BKR_MODE_CS if(data['cs']) else id.BKR_MODE_CI
423  mode = id.BKR_MODE_RM if(data['ur']) else id.BKR_MODE_UM
424  data['top_rule']['opcodes'].append({
425  'type': id.BKR,
426  'name': data['bkr_name'],
427  'lower': data['bkr_name']['name'].lower(),
428  'bkr_case': case,
429  'bkr_mode': mode
430  })
431  return id.SEM_OK
432 
433 
434 def generic_udt(input, phrase_index, phrase_length, data, empty=False):
435  name = utils.tuple_to_string(
436  input[phrase_index:phrase_index + phrase_length])
437  udt_name = data['udt_names'].add(name)
438  if(udt_name == -1):
439  # name already exists
440  udt_name = data['udt_names'].get(name)
441  assert(udt_name != -1), 'UDT name list look up error'
442  else:
443  # add the new UDT to the UDT list
444  data['udts'].append({
445  'name': udt_name['name'],
446  'lower': udt_name['lower'],
447  'index': udt_name['index'],
448  'is_bkru': False,
449  'is_bkrr': False,
450  'empty': empty
451  })
452  data['top_rule']['opcodes'].append({
453  'type': id.UDT,
454  'empty': empty,
455  'index': udt_name['index']
456  })
457 
458 
459 def semantic_udt_empty(state, input, phrase_index, phrase_length, data):
460  if(state == id.SEM_POST):
461  generic_udt(
462  input,
463  phrase_index,
464  phrase_length,
465  data,
466  empty=True)
467  return id.SEM_OK
468 
469 
470 def semantic_udt_non_empty(state, input, phrase_index, phrase_length, data):
471  if(state == id.SEM_POST):
472  generic_udt(
473  input,
474  phrase_index,
475  phrase_length,
476  data,
477  empty=False)
478  return id.SEM_OK
479 
480 
481 def semantic_tls_op(state, input, phrase_index, phrase_length, data):
482  if(state == id.SEM_PRE):
483  data['ci'] = True
484  data['cs'] = False
485  return id.SEM_OK
486 
487 
488 def semantic_tls_string(state, input, phrase_index, phrase_length, data):
489  if(state == id.SEM_POST):
490  tup = input[phrase_index:phrase_index + phrase_length]
491  if(data['ci']):
492  # case insensitive (TLS)
493  string = utils.tuple_to_string(tup)
494  tup = utils.string_to_tuple(string.lower())
495  data['top_rule']['opcodes'].append({
496  'type': id.TLS,
497  'string': tup
498  })
499  else:
500  # case sensitive (TBS)
501  data['top_rule']['opcodes'].append({
502  'type': id.TBS,
503  'string': tup
504  })
505  return id.SEM_OK
506 
507 
508 def semantic_cls_string(state, input, phrase_index, phrase_length, data):
509  if(state == id.SEM_POST):
510  tup = input[phrase_index:phrase_index + phrase_length]
511  if(len(tup) == 0):
512  # only TLS allowed to be empty
513  data['top_rule']['opcodes'].append({
514  'type': id.TLS,
515  'string': ''
516  })
517  else:
518  # convert string to tuple of character codes
519  data['top_rule']['opcodes'].append({
520  'type': id.TBS,
521  'string': tup
522  })
523  return id.SEM_OK
524 
525 
526 def semantic_tbs_op(state, input, phrase_index, phrase_length, data):
527  if(state == id.SEM_PRE):
528  data['tbs_str'] = []
529  else:
530  data['top_rule']['opcodes'].append({
531  'type': id.TBS,
532  'string': tuple(data['tbs_str'])
533  })
534  return id.SEM_OK
535 
536 
537 def semantic_d_string(state, input, phrase_index, phrase_length, data):
538  if(state == id.SEM_POST):
539  data['tbs_str'].append(decnum(input, phrase_index, phrase_length))
540  return id.SEM_OK
541 
542 
543 def semantic_b_string(state, input, phrase_index, phrase_length, data):
544  if(state == id.SEM_POST):
545  data['tbs_str'].append(binnum(input, phrase_index, phrase_length))
546  return id.SEM_OK
547 
548 
549 def semantic_x_string(state, input, phrase_index, phrase_length, data):
550  if(state == id.SEM_POST):
551  data['tbs_str'].append(hexnum(input, phrase_index, phrase_length))
552  return id.SEM_OK
553 
554 
555 def semantic_trg_op(state, input, phrase_index, phrase_length, data):
556  if(state == id.SEM_PRE):
557  data['min'] = 0
558  data['max'] = 0
559  else:
560  if(data['min'] > data['max']):
561  min = str(data['min'])
562  max = str(data['max'])
563  msg = 'TRG, terminal range, min(' + min + ') > max(' + max + ')'
564  data['errors'].append({'line': data['find_line'](phrase_index),
565  'index': phrase_index, 'msg': msg})
566  else:
567  data['top_rule']['opcodes'].append({
568  'type': id.TRG,
569  'min': data['min'],
570  'max': data['max'],
571  })
572  return id.SEM_OK
573 
574 
575 def semantic_dmin(state, input, phrase_index, phrase_length, data):
576  if(state == id.SEM_POST):
577  data['min'] = decnum(input, phrase_index, phrase_length)
578  return id.SEM_OK
579 
580 
581 def semantic_dmax(state, input, phrase_index, phrase_length, data):
582  if(state == id.SEM_POST):
583  data['max'] = decnum(input, phrase_index, phrase_length)
584  return id.SEM_OK
585 
586 
587 def semantic_bmin(state, input, phrase_index, phrase_length, data):
588  if(state == id.SEM_POST):
589  data['min'] = binnum(input, phrase_index, phrase_length)
590  return id.SEM_OK
591 
592 
593 def semantic_bmax(state, input, phrase_index, phrase_length, data):
594  if(state == id.SEM_POST):
595  data['max'] = binnum(input, phrase_index, phrase_length)
596  return id.SEM_OK
597 
598 
599 def semantic_xmin(state, input, phrase_index, phrase_length, data):
600  if(state == id.SEM_POST):
601  data['min'] = hexnum(input, phrase_index, phrase_length)
602  return id.SEM_OK
603 
604 
605 def semantic_xmax(state, input, phrase_index, phrase_length, data):
606  if(state == id.SEM_POST):
607  data['max'] = hexnum(input, phrase_index, phrase_length)
608  return id.SEM_OK
def semantic_file(state, input, phrase_index, phrase_length, data)
def semantic_repetition(state, input, phrase_index, phrase_length, data)
def semantic_and_op(state, input, phrase_index, phrase_length, data)
def semantic_tls_string(state, input, phrase_index, phrase_length, data)
def semantic_bkr_name(state, input, phrase_index, phrase_length, data)
def semantic_bmax(state, input, phrase_index, phrase_length, data)
def semantic_trg_op(state, input, phrase_index, phrase_length, data)
def semantic_rnm_op(state, input, phrase_index, phrase_length, data)
def semantic_bkn_op(state, input, phrase_index, phrase_length, data)
def semantic_bmin(state, input, phrase_index, phrase_length, data)
def semantic_udt_non_empty(state, input, phrase_index, phrase_length, data)
def semantic_rep_min_max(state, input, phrase_index, phrase_length, data)
def semantic_rep_max(state, input, phrase_index, phrase_length, data)
def semantic_inc_alt(state, input, phrase_index, phrase_length, data)
def semantic_rep_op(state, input, phrase_index, phrase_length, data)
def semantic_option_open(state, input, phrase_index, phrase_length, data)
def semantic_rm(state, input, phrase_index, phrase_length, data)
def semantic_cs(state, input, phrase_index, phrase_length, data)
def semantic_d_string(state, input, phrase_index, phrase_length, data)
def semantic_b_string(state, input, phrase_index, phrase_length, data)
def generic_udt(input, phrase_index, phrase_length, data, empty=False)
def semantic_rule_name(state, input, phrase_index, phrase_length, data)
def semantic_bka_op(state, input, phrase_index, phrase_length, data)
def semantic_ci(state, input, phrase_index, phrase_length, data)
def semantic_defined(state, input, phrase_index, phrase_length, data)
def semantic_rule_lookup(state, input, phrase_index, phrase_length, data)
def semantic_tls_op(state, input, phrase_index, phrase_length, data)
def semantic_um(state, input, phrase_index, phrase_length, data)
def semantic_x_string(state, input, phrase_index, phrase_length, data)
def semantic_concatenation(state, input, phrase_index, phrase_length, data)
def semantic_xmax(state, input, phrase_index, phrase_length, data)
def semantic_not_op(state, input, phrase_index, phrase_length, data)
def semantic_bkr_op(state, input, phrase_index, phrase_length, data)
def semantic_dmax(state, input, phrase_index, phrase_length, data)
def semantic_rep_min(state, input, phrase_index, phrase_length, data)
def semantic_udt_empty(state, input, phrase_index, phrase_length, data)
def semantic_tbs_op(state, input, phrase_index, phrase_length, data)
def semantic_xmin(state, input, phrase_index, phrase_length, data)
def semantic_aen_op(state, input, phrase_index, phrase_length, data)
def semantic_dmin(state, input, phrase_index, phrase_length, data)
def semantic_rule(state, input, phrase_index, phrase_length, data)
def semantic_alternation(state, input, phrase_index, phrase_length, data)
def semantic_cls_string(state, input, phrase_index, phrase_length, data)
def semantic_abg_op(state, input, phrase_index, phrase_length, data)
Python APG, Version 1.0, is licensed under the 2-Clause BSD License,
an Open Source Initiative Approved License.