Version 1.0
Copyright © 2022 Lowell D. Thomas
Python APG
 … an ABNF Parser Generator
generator.py
Go to the documentation of this file.
1 ''' @file apg_py.py
2 @brief This is the APG command line parser grammar generator.
3 It takes an input SABNF grammar syntax and generates a
4 grammar object suitable for use with a Python APG parser.
5 <pre>
6 options:
7  -h, --help show this help message and exit
8  -i INPUT, --input INPUT
9  the input SABNF source file name (see note 1)
10  -o OUTPUT, --output OUTPUT
11  the output grammar file name (see note 2)
12  -v, --version display the version and copyright information
13  -s, --strict allow only strict ABNF (RFC5234 & RFC7405) syntax
14  --dry-run inhibits output file generation
15  -dr, --display-rules display the grammar rule and UDT(if any) names
16  -dd, --display-rule-dependencies
17  for each rule, display the rules it references and all rules referring to it
18  -da, --display-rule-attributes
19  for each rule, display the rule attributes (left-recursive, etc.)
20  --index if specified, rules are displayed by index(the order they appear in the grammar), otherwise alphabetically
21 
22 NOTES: 1) Multiple input files can be specified with by comma-separated names. The files will be concatenated in the order in which they appear. 2) The output file name is
23 optional. If no output name is specified the first input name will be stripped of its extension, if any, and the extension ".py" added.
24 </pre>
25 '''
26 import os
27 import sys
28 import argparse
29 sys.path.append(os.getcwd())
30 # add the current working directory to the path
31 # DO NOT MOVE THE FOLLOWING STATEMENT
32 # if using autopep8 formatter, for example, set argument '--ignore=E402'
33 from apg_py.api.api import Api
34 
35 
36 def get_source(file_names):
37  '''Get the list of input file names, open them and concatenate the files.
38  Construct the default output file name from the first input name.'''
39  # get the list of file names
40  names = file_names.split(',')
41  name_range = range(len(names))
42  for i in name_range:
43  names[i] = names[i].replace(' ', '')
44  source = ''
45  for name in names:
46  fd = open(name, 'r')
47  source += fd.read()
48  fd.close()
49 
50  # strip extension, if any, from first file name
51  dir_name = os.path.dirname(names[0])
52  base_name = os.path.basename(names[0])
53  exts = base_name.split('.')
54  len_exts = len(exts)
55  if(len_exts > 1):
56  if(len(exts) == 2):
57  base_name = exts[0]
58  else:
59  base_name = exts[0]
60  for i in range(1, len_exts - 1):
61  base_name += '.' + exts[i]
62  default_output = dir_name + os.path.sep + base_name + '.py'
63  return {'source': source, 'default_output': default_output}
64 
65 
66 desc = '''
67 This is Python APG - an ABNF Parser Generator.
68 It generates a grammar object from a superset of ABNF(SABNF)
69 suitable for use with the APG parser.
70 '''
71 ep = '''NOTES: 1) Multiple input files can be specified with by
72 comma-separated names.
73 The files will be concatenated in the order in which they appear.
74 2) The output file name is optional. If no output name is specified
75 the first input name will be stripped of its extension, if any,
76 and the extension ".py" added.
77 '''
78 
79 
80 def main():
81  parser = argparse.ArgumentParser(
82  prog='apg-py',
83  description=desc,
84  epilog=ep
85  )
86  parser.add_argument(
87  '-i',
88  '--input',
89  help='the input SABNF source file name (see note 1)',
90  dest='input',
91  required=False,
92  action='store')
93  parser.add_argument('-o', '--output',
94  help='the output grammar file name (see note 2)',
95  dest='output',
96  action='store')
97  parser.add_argument('-v', '--version',
98  help='display the version and copyright information',
99  dest='version',
100  action='store_true')
101  parser.add_argument('-s', '--strict',
102  help='allow only strict ABNF (RFC5234 & RFC7405) syntax',
103  dest='strict',
104  action='store_true')
105  parser.add_argument('--dry-run',
106  help='inhibits output file generation',
107  dest='dry_run',
108  action='store_true')
109  parser.add_argument(
110  '-dr',
111  '--display-rules',
112  help='display the grammar rule and UDT(if any) names',
113  dest='display_rules',
114  action='store_true')
115  hlp = 'for each rule, display the rules it references '
116  hlp += 'and all rules referring to it'
117  parser.add_argument(
118  '-dd',
119  '--display-rule-dependencies',
120  help=hlp,
121  dest='display_dependencies',
122  action='store_true')
123  parser.add_argument(
124  '-da',
125  '--display-rule-attributes',
126  help='for each rule, display the rule attributes (left-recursive, etc.)',
127  dest='display_attributes',
128  action='store_true')
129  hlp = 'if specified, rules are displayed by index'
130  hlp += '(the order they appear in the grammar), '
131  hlp += 'otherwise alphabetically'
132  parser.add_argument(
133  '--index',
134  help=hlp,
135  dest='index',
136  action='store_true')
137  args = parser.parse_args()
138 
139  if(args.version):
140  # handle the version request
141  text = 'Python APG version 1.0'
142  text += '\nCopyright (c) 2022 Lowell D. Thomas'
143  print(text)
144  exit()
145 
146  if(not args.input):
147  # input SABNF file name required
148  print('--input argument required.', end=' ')
149  print('Use --help for options and descriptions.')
150  exit()
151 
152  # get the SABNF source
153  result = get_source(args.input)
154 
155  # generate the grammar object
156  api = Api()
157  api.generate(result['source'], args.strict)
158  if(api.errors):
159  print('\nGRAMMAR ERRORS')
160  print(api.display_errors())
161  print()
162  print('ANNOTATED GRAMMAR')
163  print(api.display_grammar())
164  exit()
165 
166  if(args.display_rules):
167  sort_type = 'alpha'
168  if(args.index):
169  sort_type = 'index'
170  print('\nGRAMMAR RULES')
171  print(api.display_rules(sort_type))
172 
173  if(args.display_dependencies):
174  sort_type = 'alpha'
175  if(args.index):
176  sort_type = 'index'
177  print('\nRULE DEPENDENCIES')
178  print(api.display_rule_dependencies(sort_type))
179 
180  if(args.display_attributes):
181  sort_type = 'alpha'
182  if(args.index):
183  sort_type = 'index'
184  print('\nRULE ATTRIBUTES')
185  print(api.display_rule_attributes(sort_type))
186 
187  if(not args.dry_run):
188  outname = args.output if(args.output) else result['default_output']
189  api.write_grammar(outname)
190  print('grammar object written to file ' + outname)
The API class.
Definition: api.py:61
def get_source(file_names)
Get the list of input file names, open them and concatenate the files.
Definition: generator.py:36
Python APG, Version 1.0, is licensed under the 2-Clause BSD License,
an Open Source Initiative Approved License.