Version 1.0
Copyright © 2022 Lowell D. Thomas
Python APG
 … an ABNF Parser Generator
ast_translate.py
Go to the documentation of this file.
1 ''' @file examples/exp/ast_translate.py
2 @brief Demonstrates using the AST for translation
3  of the pattern matched results.
4  Also demonstrates how to use the AST in the replace() function
5  (see @ref apg_py/exp/exp.py.)
6 '''
7 import sys
8 import os
9 # add the current working directory to the path
10 # DO NOT MOVE THE FOLLOWING STATEMENT
11 # if using autopep8 formatter, for example, set argument '--ignore=E402'
12 sys.path.append(os.getcwd())
13 from apg_py.exp.exp import ApgExp
14 from apg_py.lib import identifiers as id
15 from apg_py.lib import utilities as utils
16 
17 title = '''This example will demonstrate how to use the AST
18 for matched result translations. ApgExp will be used to match
19 floating point numbers and the AST will be used to put the
20 numbers in a "normalized" form.
21 A second example will use the same AST to create a replacement
22 function to do replacements with the translated matches.
23 See also the examples/exp/ast.py and examples/exp/replace.py examples.
24 '''
25 print()
26 print(title)
27 
28 # AST callback functions
29 
30 
31 def float(state, input, index, length, data):
32  ret = id.SEM_OK
33  if(state == id.SEM_PRE):
34  data['sign'] = ''
35  data['integer'] = '0'
36  data['fraction'] = '0'
37  data['esign'] = '+'
38  data['exp'] = 0
39  return ret
40  if(state == id.SEM_POST):
41  exponent = '' if(data['exp'] == 0) else 'e' + \
42  data['esign'] + str(data['exp'])
43  data['normal'] = data['sign'] + \
44  data['integer'] + '.' + data['fraction'] + exponent
45  return ret
46 
47 
48 def sign(state, input, index, length, data):
49  ret = id.SEM_OK
50  if(state == id.SEM_PRE):
51  if(input[index] == 45):
52  data['sign'] = '-'
53  return ret
54 
55 
56 def integer(state, input, index, length, data):
57  ret = id.SEM_OK
58  if(state == id.SEM_PRE):
59  if(length > 0):
60  data['integer'] = utils.tuple_to_string(
61  input[index:index + length])
62  return ret
63 
64 
65 def fraction(state, input, index, length, data):
66  ret = id.SEM_OK
67  if(state == id.SEM_PRE):
68  if(length > 0):
69  data['fraction'] = utils.tuple_to_string(
70  input[index:index + length])
71  return ret
72 
73 
74 def esign(state, input, index, length, data):
75  ret = id.SEM_OK
76  if(state == id.SEM_PRE):
77  if(input[index] == 45):
78  data['esign'] = '-'
79  return ret
80 
81 
82 def exponent(state, input, index, length, data):
83  ret = id.SEM_OK
84  if(state == id.SEM_PRE):
85  exp = utils.tuple_to_string(input[index:index + length])
86  data['exp'] = int(exp)
87  return ret
88 
89 
90 pattern = '''float = sign decimal exponent
91 sign = ["+" / "-"]
92 decimal = integer [dot fraction]
93  / dot fraction
94 integer = 1*%d48-57
95 dot = "."
96 fraction = *%d48-57
97 exponent = ["e" esign exp]
98 esign = ["+" / "-"]
99 exp = 1*%d48-57
100 '''
101 print()
102 print('PATTERN')
103 print(pattern)
104 input = '[ 123 ]'
105 input += '[ 123. ]'
106 input += '[ .123 ]'
107 input += '[ +.123 ]'
108 input += '[ -1.23 ]'
109 input += '[ 123.e2 ]'
110 input += '[ .123E+1 ]'
111 input += '[ -1234.56789E-10 ]'
112 input += '[ 123e0 ]'
113 input += '[ +123e-0 ]'
114 input += '[ -.123e-001 ]'
115 input += '[ 123e+000 ]'
116 header = 'RESULT'
117 testno = 0
118 
119 # use the global flag to find all matches
120 exp = ApgExp(pattern, 'g')
121 exp.include(['float', 'sign', 'integer', 'fraction', 'esign', 'exp'])
122 result = exp.exec(input)
123 testno += 1
124 print('\n' + str(testno) + ') Normalize all matched floating point numbers.')
125 print('input string: ' + input)
126 print(header)
127 while(result):
128  result.ast.add_callback('float', float)
129  result.ast.add_callback('sign', sign)
130  result.ast.add_callback('integer', integer)
131  result.ast.add_callback('fraction', fraction)
132  result.ast.add_callback('esign', esign)
133  result.ast.add_callback('exp', exponent)
134  data = {}
135  result.ast.translate(data)
136  print(result.match, end=' => ')
137  print(data['normal'])
138  result = exp.exec(input)
139 
140 
141 def fn(input, result):
142  result.ast.add_callback('float', float)
143  result.ast.add_callback('sign', sign)
144  result.ast.add_callback('integer', integer)
145  result.ast.add_callback('fraction', fraction)
146  result.ast.add_callback('esign', esign)
147  result.ast.add_callback('exp', exponent)
148  data = {}
149  result.ast.translate(data)
150  return data['normal']
151 
152 
153 # replace all matches with the normalized form
154 testno += 1
155 print(
156  '\n' +
157  str(testno) +
158  ') Use AST translation function to replace all matches with normalized form.')
159 print('input string: ' + input)
160 print(header)
161 result = exp.replace(input, fn)
162 print(result)
The ApgExp class provides a pattern-matching engine similar to JavaScript's RegExp
Definition: exp.py:79
def sign(state, input, index, length, data)
def fn(input, result)
def float(state, input, index, length, data)
def exponent(state, input, index, length, data)
def integer(state, input, index, length, data)
def esign(state, input, index, length, data)
def fraction(state, input, index, length, data)
Python APG, Version 1.0, is licensed under the 2-Clause BSD License,
an Open Source Initiative Approved License.