01123e733cb36538b28b69186eaaeecf7ab7a5c1
[asn2quickder] / asn1ate / asn2quickder.py
1 #!/usr/bin/python
2 #
3 # asn2quickder -- Generate header files for C for use with Quick `n' Easy DER
4 #
5 # This program owes a lot to asn1ate, which was built to generate pyasn1
6 # classes, but which was so well-written that it could be extended with a
7 # code generator for Quick DER.
8 #
9 # Much of the code below is diagonally inspired on the pyasn1 backend, so
10 # a very big thank you to Schneider Electric Buildings AB for helping to
11 # make this program possible!
12 #
13 # From: Rick van Rein <rick@openfortress.nl>
14
15
16 import sys
17 import os.path
18
19 from asn1ate import parser
20 from asn1ate.sema import * 
21
22
23 def toCsym (name):
24         """Replace unsupported characters in ASN.1 symbol names"""
25         return str (name).replace (' ', '').replace ('-', '_')
26
27
28 class QuickDERgen ():
29         """Generate the C header files for Quick DER, a.k.a. Quick and Easy DER.
30
31            There are two things that are generated for each of the ASN.1 syntax
32            declaration symbol of a unit:
33
34            #define DER_PACK_unit_SyntaxDeclSym \
35                            DER_PACK_ENTER | ..., \
36                            ... \
37                            DER_PACK_LEAVE, \
38                            DER_PACK_END
39
40            this is a walking path for the der_pack() and der_unpack() instructions.
41            In addition, there will be a struct for each of the symbols:
42
43            struct unit_SyntaxDeclSym_ovly {
44                    dercursor field1;
45                    dercursor field2;
46                    struct unit_EmbeddedSym_ovly field3;
47                    dercursor field4;
48            };
49
50            The unit prefix will be set to the filename of the module, usually
51            something like rfc5280 when the parsed file is rfc5280.asn1 and the
52            output is then written to rfc5280.h for easy inclusion by the C code.
53         """
54
55         def __init__ (ik, semamod, outfn, refmods):
56                 ik.semamod = semamod
57                 ik.refmods = refmods
58                 if outfn [-2:] == '.h':
59                         raise Exception ('File cannot overwrite itself -- use another extension than .h for input files')
60                 ik.unit = toCsym (outfn.rsplit ('.', 1) [0])
61                 ik.outfile = open (ik.unit + '.h', 'w')
62                 ik.wout = ik.outfile.write
63                 # Setup function maps
64                 ik.ovly_funmap = {
65                         DefinedType: ik.ovlyDefinedType,
66                         ValueAssignment: ik.ignore_node,
67                         TypeAssignment: ik.ovlyTypeAssignment,
68                         TaggedType: ik.ovlyTaggedType,
69                         SimpleType: ik.ovlySimpleType,
70                         BitStringType: ik.ovlySimpleType,
71                         ValueListType: ik.ovlySimpleType,
72                         SequenceType: ik.ovlyConstructedType,
73                         SetType: ik.ovlyConstructedType,
74                         ChoiceType: ik.ovlyConstructedType,
75                         SequenceOfType: ik.ovlySimpleType,      # var sized
76                         SetOfType: ik.ovlySimpleType,           # var sized
77                         ComponentType: ik.ovlySimpleType,  #TODO#
78                 }
79                 ik.pack_funmap = {
80                         DefinedType: ik.packDefinedType,
81                         ValueAssignment: ik.ignore_node,
82                         TypeAssignment: ik.packTypeAssignment,
83                         TaggedType: ik.packTaggedType,
84                         SimpleType: ik.packSimpleType,
85                         BitStringType: ik.packSimpleType,
86                         ValueListType: ik.ovlySimpleType,
87                         SequenceType: ik.packSequenceType,
88                         SetType: ik.packSetType,
89                         ChoiceType: ik.packChoiceType,
90                         SequenceOfType: ik.packSequenceOfType,
91                         SetOfType: ik.packSetOfType,
92                         ComponentType: ik.packSimpleType,  #TODO#
93                 }
94
95         def newcomma (ik, comma, firstcomma=''):
96                 ik.comma0 = firstcomma
97                 ik.comma1 = comma
98
99         def comma (ik):
100                 ik.wout (ik.comma0)
101                 ik.comma0 = ik.comma1
102
103         def close (ik):
104                 ik.outfile.close ()
105
106         def generate_head (ik):
107                 ik.wout ('/*\n * asn2quickder output for ' + ik.semamod.name + ' -- automatically generated\n *\n * For information on Quick `n\' Easy DER, see https://github.com/vanrein/quick-der\n *\n * For information on the code generator, see https://github.com/vanrein/asn2quickder\n *\n */\n\n\n#include <quick-der/api.h>\n\n\n')
108                 # ik.wout ('/* This module ' + toCsym (ik.semamod.name) + ' depends on:\n')
109                 # for rm in ik.refmods:
110                 #       ik.wout (' *   ' + toCsym (rm.name) + '\n')
111                 # if len (ik.refmods) == 0:
112                 #       ik.wout (' *   (no other modules)\n')
113                 # ik.wout (' */\n\n')
114                 closer = ''
115                 for rm in ik.semamod.imports.module2symbols.keys ():
116                         rmfn = toCsym (rm.rsplit ('.', 1) [0]).lower ()
117                         ik.wout ('#include <quick-der/' + rmfn + '.h>\n')
118                         closer = '\n\n'
119                 ik.wout (closer)
120                 closer = ''
121                 for rm in ik.semamod.imports.module2symbols.keys ():
122                         rmfn = toCsym (rm.rsplit ('.', 1) [0]).lower ()
123                         for sym in ik.semamod.imports.module2symbols [rm]:
124                                 ik.wout ('typedef DER_OVLY_' + toCsym (rmfn) + '_' + toCsym (sym) + ' DER_OVLY_' + toCsym (ik.unit) + '_' + toCsym (sym) + ';\n')
125                                 closer = '\n\n'
126                 ik.wout (closer)
127                 closer = ''
128                 for rm in ik.semamod.imports.module2symbols.keys ():
129                         rmfn = toCsym (rm.rsplit ('.', 1) [0]).lower ()
130                         for sym in ik.semamod.imports.module2symbols [rm]:
131                                 ik.wout ('#define DER_PACK_' + toCsym (ik.unit) + '_' + toCsym (sym) + ' DER_PACK_' + toCsym (rmfn) + '_' + toCsym (sym) + '\n')
132                                 closer = '\n\n'
133                 ik.wout (closer)
134
135         def generate_tail (ik):
136                 ik.wout ('\n\n/* asn2quickder output for ' + ik.semamod.name + ' ends here */\n')
137
138         def generate_ovly (ik):
139                 ik.wout ('\n\n/* Overlay structures with ASN.1 derived nesting and labelling */\n\n')
140                 for assigncompos in dependency_sort (ik.semamod.assignments):
141                         for assign in assigncompos:
142                                 ik.generate_ovly_node (assign)
143
144         def generate_pack (ik):
145                 ik.wout ('\n\n/* Parser definitions in terms of ASN.1 derived bytecode instructions */\n\n')
146                 for assigncompos in dependency_sort (ik.semamod.assignments):
147                         for assign in assigncompos:
148                                 tnm = type (assign)
149                                 if tnm in ik.pack_funmap:
150                                         ik.pack_funmap [tnm] (assign)
151                                 else:
152                                         print 'No pack generator for ' + str (tnm)
153
154         def generate_ovly_node (ik, node):
155                 tnm = type (node)
156                 if tnm in ik.ovly_funmap:
157                         ik.ovly_funmap [tnm] (node)
158                 else:
159                         print 'No overlay generator for ' + str (tnm)
160                         raise Exception ('RAISED WHERE?')
161
162         def generate_pack_node (ik, node):
163                 tnm = type (node)
164                 if tnm in ik.pack_funmap:
165                         ik.pack_funmap [tnm] (node)
166                 else:
167                         print 'No pack generator for ' + str (tnm)
168
169         def ignore_node (ik, node):
170                 pass
171
172         def ovlyTypeAssignment (ik, node):
173                 ik.wout ('typedef ')
174                 ik.generate_ovly_node (node.type_decl)
175                 ik.wout (' DER_OVLY_' + ik.unit + '_' + toCsym (node.type_name) + ';\n\n')
176
177         def packTypeAssignment (ik, node):
178                 ik.wout ('#define DER_PACK_' + ik.unit + '_' + toCsym (node.type_name))
179                 ik.newcomma (', \\\\\n\t', ' \\\\\n\t')
180                 ik.generate_pack_node (node.type_decl)
181                 ik.wout ('\n\n')
182
183         def ovlyDefinedType (ik, node):
184                 mod = node.module_name or ik.unit
185                 ik.wout ('DER_OVLY_' + toCsym (mod) + '_' + toCsym (node.type_name))
186
187         def packDefinedType (ik, node):
188                 mod = node.module_name or ik.unit
189                 ik.comma ()
190                 ik.wout ('DER_PACK_' + toCsym (mod) + '_' + toCsym (node.type_name))
191
192         def ovlySimpleType (ik, node):
193                 ik.wout ('dercursor')
194
195         def packSimpleType (ik, node):
196                 ik.comma ()
197                 ik.wout ('DER_PACK_STORE | DER_TAG_' + node.type_name.replace (' ', '').upper ())
198
199         def ovlyTaggedType (ik, node):
200                 # tag = str (node) 
201                 # tag = tag [:tag.find (']')] + ']'
202                 # ik.wout ('/* ' + tag + ' */ ')
203                 # if node.implicity == TagImplicity.IMPLICIT:
204                 #       tag = tag + ' IMPLICIT'
205                 # elif node.implicity == TagImplicity.IMPLICIT:
206                 #       tag = tag + ' EXPLICIT'
207                 ik.generate_ovly_node (node.type_decl)
208
209         def packTaggedType (ik, node):
210                 #TODO# Need to push down node.implicity == TagImplicity.IMPLICIT
211                 #TODO# Need to process tag class
212                 ik.comma ()
213                 ik.wout ('DER_PACK_ENTER | DER_' + (node.class_name or 'CONTEXT') + '_TAG(' + node.class_number + ')')
214                 ik.generate_pack_node (node.type_decl)
215                 ik.comma ()
216                 ik.wout ('DER_PACK_LEAVE')
217
218         # Sequence, Set, Choice
219         def ovlyConstructedType (ik, node):
220                 ik.wout ('struct {\n');
221                 for comp in node.components:
222                         if isinstance (comp, ExtensionMarker):
223                                 ik.wout ('\t/* ...extensions... */\n')
224                                 continue
225                         if isinstance (comp, ComponentType) and comp.components_of_type is not None:
226                                 ik.wout ('\t/* TODO: COMPONENTS OF TYPE ' + str (comp.components_of_type) + ' */\n')
227                                 continue
228                         ik.wout ('\t')
229                         ik.generate_ovly_node (comp.type_decl)
230                         ik.wout (' ' + toCsym (comp.identifier) + '; // ' + str (comp.type_decl) + '\n')
231                 ik.wout ('}')
232
233         def packSequenceType (ik, node):
234                 ik.comma ()
235                 ik.wout ('DER_PACK_ENTER | DER_TAG_SEQUENCE')
236                 for comp in node.components:
237                         if isinstance (comp, ExtensionMarker):
238                                 ik.comma ()
239                                 ik.wout ('/* ...ASN.1 extensions... */')
240                                 continue
241                         if comp.optional:
242                                 ik.comma ()
243                                 ik.wout ('DER_PACK_OPTIONAL')
244                         if comp.type_decl is not None:
245                                 # TODO: None would be due to components_of_type
246                                 ik.generate_pack_node (comp.type_decl)
247                 ik.comma ()
248                 ik.wout ('DER_PACK_LEAVE')
249
250         def packSetType (ik, node):
251                 ik.comma ()
252                 ik.wout ('DER_PACK_ENTER | DER_TAG_SET')
253                 for comp in node.components:
254                         if isinstance (comp, ExtensionMarker):
255                                 ik.comma ()
256                                 ik.wout ('/* ...extensions... */')
257                                 continue
258                         if comp.optional:
259                                 ik.comma ()
260                                 ik.wout ('DER_PACK_OPTIONAL')
261                         if comp.type_decl is not None:
262                                 # TODO: None would be due to components_of_type
263                                 ik.generate_pack_node (comp.type_decl)
264                 ik.comma ()
265                 ik.wout ('DER_PACK_LEAVE')
266
267         def packChoiceType (ik, node):
268                 ik.comma ()
269                 ik.wout ('DER_PACK_CHOICE_BEGIN')
270                 for comp in node.components:
271                         if isinstance (comp, ExtensionMarker):
272                                 ik.comma ()
273                                 ik.wout ('/* ...extensions... */')
274                                 continue
275                         if comp.type_decl is not None:
276                                 # TODO: None would be due to components_of_type
277                                 ik.generate_pack_node (comp.type_decl)
278                 ik.comma ()
279                 ik.wout ('DER_PACK_CHOICE_END')
280
281         def packSequenceOfType (ik, node):
282                 ik.comma ()
283                 ik.wout ('DER_PACK_STORE | DER_TAG_SEQUENCE')
284
285         def packSetOfType (ik, node):
286                 ik.comma ()
287                 ik.wout ('DER_PACK_STORE | DER_TAG_SEQUENCE')
288
289
290 """The main program asn2quickder is called with one or more .asn1 files,
291    the first of which is mapped to a C header file and the rest is
292    loaded to fulfil dependencies.
293 """
294
295 if len (sys.argv) < 2:
296         sys.stderr.write ('Usage: %s main[.asn1] dependency[.asn1]...\n'
297                 % sys.argv [0])
298         sys.exit (1)
299
300 mods = []
301 for file in sys.argv [1:]:
302         print 'Parsing', file
303         asn1fh = open (file, 'r')
304         asn1txt = asn1fh.read ()
305         asn1fh.close ()
306         asn1tree = parser.parse_asn1 (asn1txt)
307         print 'Building semantic model for', file
308         asn1sem = build_semantic_model (asn1tree)
309         mods.insert (0, asn1sem [0])
310         print 'Realised semantic model for', file
311
312 cogen = QuickDERgen (mods [-1], os.path.basename (sys.argv [1]), mods [1:])
313
314 cogen.generate_head ()
315 cogen.generate_ovly ()
316 cogen.generate_pack ()
317 cogen.generate_tail ()
318
319 cogen.close ()
320
321