Fix issue 11: respect module tag defaults.
[asn2quickder] / asn1ate / pyasn1gen.py
1 # Copyright (c) 2013, Schneider Electric Buildings AB
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are met:
6 #     * Redistributions of source code must retain the above copyright
7 #       notice, this list of conditions and the following disclaimer.
8 #     * Redistributions in binary form must reproduce the above copyright
9 #       notice, this list of conditions and the following disclaimer in the
10 #       documentation and/or other materials provided with the distribution.
11 #     * Neither the name of Schneider Electric Buildings AB nor the
12 #       names of contributors may be used to endorse or promote products
13 #       derived from this software without specific prior written permission.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
19 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26 from __future__ import print_function  # Python 2 compatibility
27
28 import sys
29 import keyword
30 from asn1ate import parser
31 from asn1ate.support import pygen
32 from asn1ate.sema import *
33
34
35 class Pyasn1Backend(object):
36     """ Backend to generate pyasn1 declarations from semantic tree.
37
38     Pyasn1 represents type assignments as class derivation, e.g.
39
40         # Foo ::= INTEGER
41         class Foo(univ.Integer):
42             pass
43
44     For constructed types, the component types are instantiated inline, e.g.
45
46         # Seq ::= SEQUENCE {
47         #     foo INTEGER
48         # }
49         class Seq(univ.Sequence):
50              componentType = namedtype.NamedTypes(
51                 namedtype.NamedType('foo', univ.Integer())
52              )
53
54     (univ.Integer is not a base class here, but a value.)
55
56     To cope with circular dependencies, we define types in two passes so we'll
57     generate the above as:
58
59         class Seq(univ.Sequence):
60             pass
61
62         Seq.componentType = namedtype.NamedTypes(
63             namedtype.NamedType('foo', univ.Integer())
64         )
65
66     This is nice, because we separate the introduction of a name (``Seq``) from
67     the details of what it contains, so we can build recursive definitions
68     without getting into trouble with Python's name lookup.
69
70     We call the empty class a *declaration*, and the population of its members
71     *definition*. The instantiation of univ.Integer is called an
72     *inline definition*.
73
74     The translation from ASN.1 constructs to Pyasn1 come in different flavors,
75     depending on whether they're declarations, definitions or inline
76     definitions.
77
78     Only type and value assignments generate declarations. For type assignments
79     we generate a definition once all dependent declarations are created. If the
80     type assignment involves a constructed type, it is filled with inline
81     definitions.
82     """
83     def __init__(self, sema_module, out_stream):
84         self.sema_module = sema_module
85         self.writer = pygen.PythonWriter(out_stream)
86
87         self.decl_generators = {
88             TypeAssignment: self.decl_type_assignment,
89             ValueAssignment: self.decl_value_assignment
90         }
91
92         self.defn_generators = {
93             ChoiceType: self.defn_constructed_type,
94             SequenceType: self.defn_constructed_type,
95             SetType: self.defn_constructed_type,
96             SequenceOfType: self.defn_collection_type,
97             SetOfType: self.defn_collection_type,
98             TaggedType: self.defn_tagged_type,
99             SelectionType: self.defn_selection_type,
100             SimpleType: self.defn_simple_type,
101             DefinedType: self.defn_defined_type,
102             ValueListType: self.defn_value_list_type,
103             BitStringType: self.defn_bitstring_type,
104         }
105
106         self.inline_generators = {
107             TaggedType: self.inline_tagged_type,
108             SelectionType: self.inline_selection_type,
109             SimpleType: self.inline_simple_type,
110             DefinedType: self.inline_defined_type,
111             ComponentType: self.inline_component_type,
112             NamedType: self.inline_named_type,
113             SequenceOfType: self.inline_sequenceof_type,
114             SetOfType: self.inline_setof_type,
115             ValueListType: self.inline_value_list_type,
116             ChoiceType: self.inline_constructed_type,
117             SequenceType: self.inline_constructed_type,
118             SetType: self.inline_constructed_type,
119         }
120
121     def generate_code(self):
122         self.writer.write_line('from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful')
123         self.writer.write_blanks(2)
124
125         # Generate _OID if sema_module contains any object identifier values.
126         oids = [n for n in self.sema_module.descendants() if isinstance(n, ObjectIdentifierValue)]
127         if oids:
128             self.writer.write_block(self.generate_OID())
129             self.writer.write_blanks(2)
130
131         assignment_components = dependency_sort(self.sema_module.assignments)
132         for component in assignment_components:
133             for assignment in component:
134                 self.writer.write_block(self.generate_decl(assignment))
135                 self.writer.write_blanks(2)
136
137             for assignment in component:
138                 details = self.generate_definition(assignment)
139                 if details:
140                     self.writer.write_block(details)
141                     self.writer.write_blanks(2)
142
143     def generate_definition(self, assignment):
144         assert isinstance(assignment, (ValueAssignment, TypeAssignment))
145
146         if isinstance(assignment, ValueAssignment):
147             return None  # Nothing to do here.
148
149         assigned_type, type_decl = assignment.type_name, assignment.type_decl
150         assigned_type = _translate_type(assigned_type)
151         return self.generate_defn(assigned_type, type_decl)
152
153     def generate_decl(self, t):
154         generator = self.decl_generators[type(t)]
155         return generator(t)
156
157     def generate_expr(self, t):
158         generator = self.inline_generators[type(t)]
159         return generator(t)
160
161     def generate_defn(self, class_name, t):
162         generator = self.defn_generators[type(t)]
163         return generator(class_name, t)
164
165     def decl_type_assignment(self, assignment):
166         fragment = self.writer.get_fragment()
167
168         assigned_type, type_decl = assignment.type_name, assignment.type_decl
169
170         if isinstance(type_decl, SelectionType):
171             type_decl = self.sema_module.resolve_selection_type(type_decl)
172
173         assigned_type = _translate_type(assigned_type)
174         base_type = _translate_type(type_decl.type_name)
175         fragment.write_line('class %s(%s):' % (assigned_type, base_type))
176         fragment.push_indent()
177         fragment.write_line('pass')
178         fragment.pop_indent()
179
180         return str(fragment)
181
182     def decl_value_assignment(self, assignment):
183         assigned_value, type_decl, value = assignment.value_name, assignment.type_decl, assignment.value
184         assigned_value = _sanitize_identifier(assigned_value)
185         construct_expr = self.build_value_construct_expr(type_decl, value)
186         return '%s = %s' % (assigned_value, construct_expr)
187
188     def defn_simple_type(self, class_name, t):
189         if t.constraint:
190             return '%s.subtypeSpec = %s' % (class_name, self.build_constraint_expr(t.constraint))
191
192         return None
193
194     def defn_defined_type(self, class_name, t):
195         return None
196
197     def defn_constructed_type(self, class_name, t):
198         fragment = self.writer.get_fragment()
199
200         fragment.write_line('%s.componentType = namedtype.NamedTypes(' % class_name)
201         fragment.push_indent()
202         fragment.write_block(self.inline_component_types(t.components))
203         fragment.pop_indent()
204         fragment.write_line(')')
205
206         return str(fragment)
207
208     def defn_tagged_type(self, class_name, t):
209         fragment = self.writer.get_fragment()
210
211         implicity = self.sema_module.resolve_tag_implicity(t.implicity, t.type_decl)
212         if implicity == TagImplicity.IMPLICIT:
213             tag_implicity = 'tagImplicitly'
214         elif implicity == TagImplicity.EXPLICIT:
215             tag_implicity = 'tagExplicitly'
216         else:
217             assert False, "Unexpected implicity: %s" % implicity
218
219         base_type = _translate_type(t.type_decl.type_name)
220
221         fragment.write_line('%s.tagSet = %s.tagSet.%s(%s)' % (class_name, base_type, tag_implicity, self.build_tag_expr(t)))
222         nested_dfn = self.generate_defn(class_name, t.type_decl)
223         if nested_dfn:
224             fragment.write_line(nested_dfn)
225
226         return str(fragment)
227
228     def defn_selection_type(self, class_name, t):
229         return None
230
231     def defn_value_list_type(self, class_name, t):
232         fragment = self.writer.get_fragment()
233
234         if t.named_values:
235             fragment.write_line('%s.namedValues = namedval.NamedValues(' % class_name)
236             fragment.push_indent()
237
238             named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
239             fragment.write_enumeration(named_values)
240
241             fragment.pop_indent()
242             fragment.write_line(')')
243
244         if t.constraint:
245             fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.constraint)))
246
247         return str(fragment)
248
249     def defn_bitstring_type(self, class_name, t):
250         fragment = self.writer.get_fragment()
251
252         if t.named_bits:
253             fragment.write_line('%s.namedValues = namedval.NamedValues(' % class_name)
254             fragment.push_indent()
255             named_bits = ['(\'%s\', %s)' % (b.identifier, b.value) for b in t.named_bits]
256             fragment.write_enumeration(named_bits)
257             fragment.pop_indent()
258             fragment.write_line(')')
259
260         if t.constraint:
261             fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.constraint)))
262
263         return str(fragment)
264
265     def defn_collection_type(self, class_name, t):
266         fragment = self.writer.get_fragment()
267         fragment.write_line('%s.componentType = %s' % (class_name, self.generate_expr(t.type_decl)))
268
269         if t.size_constraint:
270             fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.size_constraint)))
271
272         return str(fragment)
273
274     def inline_simple_type(self, t):
275         type_expr = _translate_type(t.type_name) + '()'
276         if t.constraint:
277             type_expr += '.subtype(subtypeSpec=%s)' % self.build_constraint_expr(t.constraint)
278
279         return type_expr
280
281     def inline_defined_type(self, t):
282         return _translate_type(t.type_name) + '()'
283
284     def inline_constructed_type(self, t):
285         fragment = self.writer.get_fragment()
286
287         class_name = _translate_type(t.type_name)
288
289         fragment.write_line('%s(componentType=namedtype.NamedTypes(' % class_name)
290
291         fragment.push_indent()
292         fragment.write_block(self.inline_component_types(t.components))
293         fragment.pop_indent()
294
295         fragment.write_line('))')
296
297         return str(fragment)
298
299     def inline_component_types(self, components):
300         fragment = self.writer.get_fragment()
301
302         component_exprs = []
303         for c in components:
304             if not isinstance(c, ExtensionMarker):
305                 component_exprs.append(self.generate_expr(c))
306
307         fragment.write_enumeration(component_exprs)
308
309         return str(fragment)
310
311     def inline_tagged_type(self, t):
312         implicity = self.sema_module.resolve_tag_implicity(t.implicity, t.type_decl)
313         if implicity == TagImplicity.IMPLICIT:
314             tag_implicity = 'implicitTag'
315         elif implicity == TagImplicity.EXPLICIT:
316             tag_implicity = 'explicitTag'
317         else:
318             assert False, "Unexpected implicity: %s" % implicity
319
320         type_expr = self.generate_expr(t.type_decl)
321         type_expr += '.subtype(%s=%s)' % (tag_implicity, self.build_tag_expr(t))
322
323         return type_expr
324
325     def inline_selection_type(self, t):
326         selected_type = self.sema_module.resolve_selection_type(t)
327         assert selected_type is not None, "Found no member %s in %s" % (t.identifier, t.type_decl)
328
329         return self.generate_expr(selected_type)
330
331     def build_tag_expr(self, tag_def):
332         context = _translate_tag_class(tag_def.class_name)
333
334         tagged_type_decl = self.sema_module.resolve_type_decl(tag_def.type_decl)
335         if isinstance(tagged_type_decl, ConstructedType):
336             tag_format = 'tag.tagFormatConstructed'
337         else:
338             tag_format = 'tag.tagFormatSimple'
339
340         return 'tag.Tag(%s, %s, %s)' % (context, tag_format, tag_def.class_number)
341
342     def build_constraint_expr(self, constraint):
343         def unpack_size_constraint(nested):
344             if isinstance(nested, SingleValueConstraint):
345                 return _translate_value(nested.value), _translate_value(nested.value)
346             elif isinstance(nested, ValueRangeConstraint):
347                 return _translate_value(nested.min_value), _translate_value(nested.max_value)
348             else:
349                 assert False, "Unrecognized nested size constraint type: %s" % type(constraint.nested).__name__
350
351         if isinstance(constraint, SingleValueConstraint):
352             return 'constraint.SingleValueConstraint(%s)' % (_translate_value(constraint.value))
353         elif isinstance(constraint, SizeConstraint):
354             min_value, max_value = unpack_size_constraint(constraint.nested)
355             return 'constraint.ValueSizeConstraint(%s, %s)' % (_translate_value(min_value), _translate_value(max_value))
356         elif isinstance (constraint, ValueRangeConstraint):
357             return 'constraint.ValueRangeConstraint(%s, %s)' % (_translate_value(constraint.min_value),
358                                                                 _translate_value(constraint.max_value))
359         else:
360             assert False, "Unrecognized constraint type: %s" % type(constraint).__name__
361
362     def build_value_construct_expr(self, type_decl, value):
363         """ Build a valid construct-expression for values, depending on
364         the target pyasn1 type.
365         """
366
367         def build_value_expr(type_name, value):
368             """ Special treatment for bstring and hstring values,
369             which use different construction depending on target type.
370             """
371             if isinstance(value, BinaryStringValue):
372                 if type_name == 'OCTET STRING':
373                     return 'binValue=\'%s\'' % value.value
374                 else:
375                     return '"\'%s\'B"' % value.value
376             elif isinstance(value, HexStringValue):
377                 if type_name == 'OCTET STRING':
378                     return 'hexValue=\'%s\'' % value.value
379                 else:
380                     return '"\'%s\'H"' % value.value
381             else:
382                 return _translate_value(value)
383
384         if isinstance(value, ObjectIdentifierValue):
385             return self.build_object_identifier_value(value)
386         else:
387             value_type = _translate_type(type_decl.type_name)
388             root_type = self.sema_module.resolve_type_decl(type_decl)
389             return '%s(%s)' % (value_type, build_value_expr(root_type.type_name, value))
390
391     def inline_component_type(self, t):
392         if t.components_of_type:
393             # COMPONENTS OF works like a literal include, so just
394             # expand all components of the referenced type.
395             included_type_decl = self.sema_module.resolve_type_decl(t.components_of_type)
396             included_content = self.inline_component_types(included_type_decl.components)
397
398             # Strip trailing newline from inline_component_types
399             # to make the list line up
400             return included_content.strip()
401
402         if t.optional:
403             return "namedtype.OptionalNamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
404         elif t.default_value is not None:
405             type_expr = self.generate_expr(t.type_decl)
406             type_expr += '.subtype(value=%s)' % _translate_value(t.default_value)
407
408             return "namedtype.DefaultedNamedType('%s', %s)" % (t.identifier, type_expr)
409         else:
410             return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
411
412     def inline_named_type(self, t):
413         return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
414
415     def inline_value_list_type(self, t):
416         class_name = _translate_type(t.type_name)
417         if t.named_values:
418             named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
419             return '%s(namedValues=namedval.NamedValues(%s))' % (class_name, ', '.join(named_values))
420         else:
421             return class_name + '()'
422
423     def inline_sequenceof_type(self, t):
424         return 'univ.SequenceOf(componentType=%s)' % self.generate_expr(t.type_decl)
425
426     def inline_setof_type(self, t):
427         return 'univ.SetOf(componentType=%s)' % self.generate_expr(t.type_decl)
428
429     def build_object_identifier_value(self, t):
430         objid_components = []
431
432         for c in t.components:
433             if isinstance(c, NameForm):
434                 if c.name in REGISTERED_OID_NAMES:
435                     objid_components.append(str(REGISTERED_OID_NAMES[c.name]))
436                 else:
437                     objid_components.append(_translate_value(c.name))
438             elif isinstance(c, NumberForm):
439                 objid_components.append(str(c.value))
440             elif isinstance(c, NameAndNumberForm):
441                 objid_components.append(str(c.number.value))
442             else:
443                 assert False
444
445         return '_OID(%s)' % ', '.join(objid_components)
446
447     def generate_OID(self):
448         fragment = self.writer.get_fragment()
449
450         fragment.write_line('def _OID(*components):')
451         fragment.push_indent()
452         fragment.write_line('output = []')
453         fragment.write_line('for x in tuple(components):')
454         fragment.push_indent()
455         fragment.write_line('if isinstance(x, univ.ObjectIdentifier):')
456         fragment.push_indent()
457         fragment.write_line('output.extend(list(x))')
458         fragment.pop_indent()
459         fragment.write_line('else:')
460         fragment.push_indent()
461         fragment.write_line('output.append(int(x))')
462         fragment.pop_indent()
463         fragment.pop_indent()
464         fragment.write_blanks(1)
465         fragment.write_line('return univ.ObjectIdentifier(output)')
466         fragment.pop_indent()
467
468         fragment.pop_indent()
469
470         return str(fragment)
471
472
473 def generate_pyasn1(sema_module, out_stream):
474     return Pyasn1Backend(sema_module, out_stream).generate_code()
475
476
477 # Translation tables from ASN.1 primitives to pyasn1 primitives
478 _ASN1_TAG_CONTEXTS = {
479     'APPLICATION': 'tag.tagClassApplication',
480     'PRIVATE': 'tag.tagClassPrivate',
481     'UNIVERSAL': 'tag.tagClassUniversal'
482 }
483
484
485 _ASN1_BUILTIN_VALUES = {
486     'FALSE': '0',
487     'TRUE': '1'
488 }
489
490
491 _ASN1_BUILTIN_TYPES = {
492     'ANY': 'univ.Any',
493     'INTEGER': 'univ.Integer',
494     'BOOLEAN': 'univ.Boolean',
495     'NULL': 'univ.Null',
496     'ENUMERATED': 'univ.Enumerated',
497     'REAL': 'univ.Real',
498     'BIT STRING': 'univ.BitString',
499     'OCTET STRING': 'univ.OctetString',
500     'CHOICE': 'univ.Choice',
501     'SEQUENCE': 'univ.Sequence',
502     'SET': 'univ.Set',
503     'SEQUENCE OF': 'univ.SequenceOf',
504     'SET OF': 'univ.SetOf',
505     'OBJECT IDENTIFIER': 'univ.ObjectIdentifier',
506     'UTF8String': 'char.UTF8String',
507     'GeneralString': 'char.GeneralString',
508     'NumericString': 'char.NumericString',
509     'PrintableString': 'char.PrintableString',
510     'IA5String': 'char.IA5String',
511     'GraphicString': 'char.GraphicString',
512     'GeneralizedTime': 'useful.GeneralizedTime',
513     'UTCTime': 'useful.UTCTime',
514     'ObjectDescriptor': 'useful.ObjectDescriptor',  # In pyasn1 r1.2
515 }
516
517
518 def _translate_type(type_name):
519     """ Translate ASN.1 built-in types to pyasn1 equivalents.
520     Non-builtins are not translated.
521     """
522     assert isinstance(type_name, str), "Type name must be a string."
523     type_name = _sanitize_identifier(type_name)
524
525     return _ASN1_BUILTIN_TYPES.get(type_name, type_name)
526
527
528 def _translate_tag_class(tag_class):
529     """ Translate ASN.1 tag class names to pyasn1 equivalents.
530     Defaults to tag.tagClassContext if tag_class is not
531     recognized.
532     """
533     return _ASN1_TAG_CONTEXTS.get(tag_class, 'tag.tagClassContext')
534
535
536 def _translate_value(value):
537     """ Translate ASN.1 built-in values to Python equivalents.
538     Unrecognized values are not translated.
539     """
540     if isinstance(value, ReferencedValue) or _heuristic_is_identifier(value):
541         value = _sanitize_identifier(value)
542
543     return _ASN1_BUILTIN_VALUES.get(value, value)
544
545
546 def _heuristic_is_identifier(value):
547     """ Return True if this value is likely an identifier.
548     """
549     first = str(value)[0]
550     return first != '-' and not first.isdigit()
551
552
553 def _sanitize_identifier(name):
554     """ Sanitize ASN.1 type and value identifiers so that they're
555     valid Python identifiers.
556     """
557     name = str(name)
558     name = name.replace('-', '_')
559     if name in keyword.kwlist:
560         name += '_'
561
562     return name
563
564
565 # Simplistic command-line driver
566 def main(args):
567     with open(args[0]) as f:
568         asn1def = f.read()
569
570     parse_tree = parser.parse_asn1(asn1def)
571
572     modules = build_semantic_model(parse_tree)
573     if len(modules) > 1:
574         print('WARNING: More than one module generated to the same stream.', file=sys.stderr)
575
576     for module in modules:
577         print(pygen.auto_generated_header())
578         generate_pyasn1(module, sys.stdout)
579
580     return 0
581
582
583 if __name__ == '__main__':
584     sys.exit(main(sys.argv[1:]))