2bdf107f8ccd8d45254b8001bf05e1c7b7fe00c4
[third_party/asn1ate.git] / 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             BitStringType: self.inline_bitstring_type,
120         }
121
122     def generate_code(self):
123         self.writer.write_line('from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful')
124         self.writer.write_blanks(2)
125
126         # Generate _OID if sema_module contains any object identifier values.
127         oids = [n for n in self.sema_module.descendants() if isinstance(n, ObjectIdentifierValue)]
128         if oids:
129             self.writer.write_block(self.generate_OID())
130             self.writer.write_blanks(2)
131
132         assignment_components = dependency_sort(self.sema_module.assignments)
133         for component in assignment_components:
134             for assignment in component:
135                 self.writer.write_block(self.generate_decl(assignment))
136                 self.writer.write_blanks(2)
137
138             for assignment in component:
139                 details = self.generate_definition(assignment)
140                 if details:
141                     self.writer.write_block(details)
142                     self.writer.write_blanks(2)
143
144     def generate_definition(self, assignment):
145         assert isinstance(assignment, (ValueAssignment, TypeAssignment))
146
147         if isinstance(assignment, ValueAssignment):
148             return None  # Nothing to do here.
149
150         assigned_type, type_decl = assignment.type_name, assignment.type_decl
151         assigned_type = _translate_type(assigned_type)
152         return self.generate_defn(assigned_type, type_decl)
153
154     def generate_decl(self, t):
155         generator = self.decl_generators[type(t)]
156         return generator(t)
157
158     def generate_expr(self, t):
159         generator = self.inline_generators[type(t)]
160         return generator(t)
161
162     def generate_defn(self, class_name, t):
163         generator = self.defn_generators[type(t)]
164         return generator(class_name, t)
165
166     def decl_type_assignment(self, assignment):
167         fragment = self.writer.get_fragment()
168
169         assigned_type, type_decl = assignment.type_name, assignment.type_decl
170
171         if isinstance(type_decl, SelectionType):
172             type_decl = self.sema_module.resolve_selection_type(type_decl)
173
174         assigned_type = _translate_type(assigned_type)
175         base_type = _translate_type(type_decl.type_name)
176         fragment.write_line('class %s(%s):' % (assigned_type, base_type))
177         fragment.push_indent()
178         fragment.write_line('pass')
179         fragment.pop_indent()
180
181         return str(fragment)
182
183     def decl_value_assignment(self, assignment):
184         assigned_value, type_decl, value = assignment.value_name, assignment.type_decl, assignment.value
185         assigned_value = _sanitize_identifier(assigned_value)
186         construct_expr = self.build_value_construct_expr(type_decl, value)
187         return '%s = %s' % (assigned_value, construct_expr)
188
189     def defn_simple_type(self, class_name, t):
190         if t.constraint:
191             return '%s.subtypeSpec = %s' % (class_name, self.build_constraint_expr(t.constraint))
192
193         return None
194
195     def defn_defined_type(self, class_name, t):
196         return None
197
198     def defn_constructed_type(self, class_name, t):
199         fragment = self.writer.get_fragment()
200
201         fragment.write_line('%s.componentType = namedtype.NamedTypes(' % class_name)
202         fragment.push_indent()
203         fragment.write_block(self.inline_component_types(t.components))
204         fragment.pop_indent()
205         fragment.write_line(')')
206
207         return str(fragment)
208
209     def defn_tagged_type(self, class_name, t):
210         fragment = self.writer.get_fragment()
211
212         implicity = self.sema_module.resolve_tag_implicity(t.implicity, t.type_decl)
213         if implicity == TagImplicity.IMPLICIT:
214             tag_implicity = 'tagImplicitly'
215         elif implicity == TagImplicity.EXPLICIT:
216             tag_implicity = 'tagExplicitly'
217         else:
218             assert False, "Unexpected implicity: %s" % implicity
219
220         base_type = _translate_type(t.type_decl.type_name)
221
222         fragment.write_line('%s.tagSet = %s.tagSet.%s(%s)' % (class_name, base_type, tag_implicity, self.build_tag_expr(t)))
223         nested_dfn = self.generate_defn(class_name, t.type_decl)
224         if nested_dfn:
225             fragment.write_line(nested_dfn)
226
227         return str(fragment)
228
229     def defn_selection_type(self, class_name, t):
230         return None
231
232     def defn_value_list_type(self, class_name, t):
233         fragment = self.writer.get_fragment()
234
235         if t.named_values:
236             fragment.write_line('%s.namedValues = namedval.NamedValues(' % class_name)
237             fragment.push_indent()
238
239             named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
240             fragment.write_enumeration(named_values)
241
242             fragment.pop_indent()
243             fragment.write_line(')')
244
245         if t.constraint:
246             fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.constraint)))
247
248         return str(fragment)
249
250     def inline_bitstring_type(self, t):
251         return self.inline_simple_type(t)
252
253     def defn_bitstring_type(self, class_name, t):
254         fragment = self.writer.get_fragment()
255
256         if t.named_bits:
257             fragment.write_line('%s.namedValues = namedval.NamedValues(' % class_name)
258             fragment.push_indent()
259             named_bits = ['(\'%s\', %s)' % (b.identifier, b.value) for b in t.named_bits]
260             fragment.write_enumeration(named_bits)
261             fragment.pop_indent()
262             fragment.write_line(')')
263
264         if t.constraint:
265             fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.constraint)))
266
267         return str(fragment)
268
269     def defn_collection_type(self, class_name, t):
270         fragment = self.writer.get_fragment()
271         fragment.write_line('%s.componentType = %s' % (class_name, self.generate_expr(t.type_decl)))
272
273         if t.size_constraint:
274             fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.size_constraint)))
275
276         return str(fragment)
277
278     def inline_simple_type(self, t):
279         type_expr = _translate_type(t.type_name) + '()'
280         if t.constraint:
281             type_expr += '.subtype(subtypeSpec=%s)' % self.build_constraint_expr(t.constraint)
282
283         return type_expr
284
285     def inline_defined_type(self, t):
286         return _translate_type(t.type_name) + '()'
287
288     def inline_constructed_type(self, t):
289         fragment = self.writer.get_fragment()
290
291         class_name = _translate_type(t.type_name)
292
293         fragment.write_line('%s(componentType=namedtype.NamedTypes(' % class_name)
294
295         fragment.push_indent()
296         fragment.write_block(self.inline_component_types(t.components))
297         fragment.pop_indent()
298
299         fragment.write_line('))')
300
301         return str(fragment)
302
303     def inline_component_types(self, components):
304         fragment = self.writer.get_fragment()
305
306         component_exprs = []
307         for c in components:
308             if not isinstance(c, ExtensionMarker):
309                 component_exprs.append(self.generate_expr(c))
310
311         fragment.write_enumeration(component_exprs)
312
313         return str(fragment)
314
315     def inline_tagged_type(self, t):
316         implicity = self.sema_module.resolve_tag_implicity(t.implicity, t.type_decl)
317         if implicity == TagImplicity.IMPLICIT:
318             tag_implicity = 'implicitTag'
319         elif implicity == TagImplicity.EXPLICIT:
320             tag_implicity = 'explicitTag'
321         else:
322             assert False, "Unexpected implicity: %s" % implicity
323
324         type_expr = self.generate_expr(t.type_decl)
325         type_expr += '.subtype(%s=%s)' % (tag_implicity, self.build_tag_expr(t))
326
327         return type_expr
328
329     def inline_selection_type(self, t):
330         selected_type = self.sema_module.resolve_selection_type(t)
331         assert selected_type is not None, "Found no member %s in %s" % (t.identifier, t.type_decl)
332
333         return self.generate_expr(selected_type)
334
335     def build_tag_expr(self, tag_def):
336         context = _translate_tag_class(tag_def.class_name)
337
338         tagged_type_decl = self.sema_module.resolve_type_decl(tag_def.type_decl)
339         if isinstance(tagged_type_decl, ConstructedType):
340             tag_format = 'tag.tagFormatConstructed'
341         else:
342             tag_format = 'tag.tagFormatSimple'
343
344         return 'tag.Tag(%s, %s, %s)' % (context, tag_format, tag_def.class_number)
345
346     def build_constraint_expr(self, constraint):
347         def unpack_size_constraint(nested):
348             if isinstance(nested, SingleValueConstraint):
349                 return _translate_value(nested.value), _translate_value(nested.value)
350             elif isinstance(nested, ValueRangeConstraint):
351                 return _translate_value(nested.min_value), _translate_value(nested.max_value)
352             else:
353                 assert False, "Unrecognized nested size constraint type: %s" % type(constraint.nested).__name__
354
355         if isinstance(constraint, SingleValueConstraint):
356             return 'constraint.SingleValueConstraint(%s)' % (_translate_value(constraint.value))
357         elif isinstance(constraint, SizeConstraint):
358             min_value, max_value = unpack_size_constraint(constraint.nested)
359             return 'constraint.ValueSizeConstraint(%s, %s)' % (_translate_value(min_value), _translate_value(max_value))
360         elif isinstance (constraint, ValueRangeConstraint):
361             return 'constraint.ValueRangeConstraint(%s, %s)' % (_translate_value(constraint.min_value),
362                                                                 _translate_value(constraint.max_value))
363         else:
364             assert False, "Unrecognized constraint type: %s" % type(constraint).__name__
365
366     def build_value_construct_expr(self, type_decl, value):
367         """ Build a valid construct-expression for values, depending on
368         the target pyasn1 type.
369         """
370
371         def build_value_expr(type_name, value):
372             """ Special treatment for bstring and hstring values,
373             which use different construction depending on target type.
374             """
375             if isinstance(value, BinaryStringValue):
376                 if type_name == 'OCTET STRING':
377                     return 'binValue=\'%s\'' % value.value
378                 else:
379                     return '"\'%s\'B"' % value.value
380             elif isinstance(value, HexStringValue):
381                 if type_name == 'OCTET STRING':
382                     return 'hexValue=\'%s\'' % value.value
383                 else:
384                     return '"\'%s\'H"' % value.value
385             else:
386                 return _translate_value(value)
387
388         if isinstance(value, ObjectIdentifierValue):
389             return self.build_object_identifier_value(value)
390         else:
391             value_type = _translate_type(type_decl.type_name)
392             root_type = self.sema_module.resolve_type_decl(type_decl)
393             return '%s(%s)' % (value_type, build_value_expr(root_type.type_name, value))
394
395     def inline_component_type(self, t):
396         if t.components_of_type:
397             # COMPONENTS OF works like a literal include, so just
398             # expand all components of the referenced type.
399             included_type_decl = self.sema_module.resolve_type_decl(t.components_of_type)
400             included_content = self.inline_component_types(included_type_decl.components)
401
402             # Strip trailing newline from inline_component_types
403             # to make the list line up
404             return included_content.strip()
405
406         if t.optional:
407             return "namedtype.OptionalNamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
408         elif t.default_value is not None:
409             type_expr = self.generate_expr(t.type_decl)
410             type_expr += '.subtype(value=%s)' % _translate_value(t.default_value)
411
412             return "namedtype.DefaultedNamedType('%s', %s)" % (t.identifier, type_expr)
413         else:
414             return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
415
416     def inline_named_type(self, t):
417         return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
418
419     def inline_value_list_type(self, t):
420         class_name = _translate_type(t.type_name)
421         if t.named_values:
422             named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
423             return '%s(namedValues=namedval.NamedValues(%s))' % (class_name, ', '.join(named_values))
424         else:
425             return class_name + '()'
426
427     def inline_sequenceof_type(self, t):
428         return 'univ.SequenceOf(componentType=%s)' % self.generate_expr(t.type_decl)
429
430     def inline_setof_type(self, t):
431         return 'univ.SetOf(componentType=%s)' % self.generate_expr(t.type_decl)
432
433     def build_object_identifier_value(self, t):
434         objid_components = []
435
436         for c in t.components:
437             if isinstance(c, NameForm):
438                 if c.name in REGISTERED_OID_NAMES:
439                     objid_components.append(str(REGISTERED_OID_NAMES[c.name]))
440                 else:
441                     objid_components.append(_translate_value(c.name))
442             elif isinstance(c, NumberForm):
443                 objid_components.append(str(c.value))
444             elif isinstance(c, NameAndNumberForm):
445                 objid_components.append(str(c.number.value))
446             else:
447                 assert False
448
449         return '_OID(%s)' % ', '.join(objid_components)
450
451     def generate_OID(self):
452         fragment = self.writer.get_fragment()
453
454         fragment.write_line('def _OID(*components):')
455         fragment.push_indent()
456         fragment.write_line('output = []')
457         fragment.write_line('for x in tuple(components):')
458         fragment.push_indent()
459         fragment.write_line('if isinstance(x, univ.ObjectIdentifier):')
460         fragment.push_indent()
461         fragment.write_line('output.extend(list(x))')
462         fragment.pop_indent()
463         fragment.write_line('else:')
464         fragment.push_indent()
465         fragment.write_line('output.append(int(x))')
466         fragment.pop_indent()
467         fragment.pop_indent()
468         fragment.write_blanks(1)
469         fragment.write_line('return univ.ObjectIdentifier(output)')
470         fragment.pop_indent()
471
472         fragment.pop_indent()
473
474         return str(fragment)
475
476
477 def generate_pyasn1(sema_module, out_stream):
478     return Pyasn1Backend(sema_module, out_stream).generate_code()
479
480
481 # Translation tables from ASN.1 primitives to pyasn1 primitives
482 _ASN1_TAG_CONTEXTS = {
483     'APPLICATION': 'tag.tagClassApplication',
484     'PRIVATE': 'tag.tagClassPrivate',
485     'UNIVERSAL': 'tag.tagClassUniversal'
486 }
487
488
489 _ASN1_BUILTIN_VALUES = {
490     'FALSE': '0',
491     'TRUE': '1'
492 }
493
494
495 _ASN1_BUILTIN_TYPES = {
496     'ANY': 'univ.Any',
497     'INTEGER': 'univ.Integer',
498     'BOOLEAN': 'univ.Boolean',
499     'NULL': 'univ.Null',
500     'ENUMERATED': 'univ.Enumerated',
501     'REAL': 'univ.Real',
502     'BIT STRING': 'univ.BitString',
503     'OCTET STRING': 'univ.OctetString',
504     'CHOICE': 'univ.Choice',
505     'SEQUENCE': 'univ.Sequence',
506     'SET': 'univ.Set',
507     'SEQUENCE OF': 'univ.SequenceOf',
508     'SET OF': 'univ.SetOf',
509     'OBJECT IDENTIFIER': 'univ.ObjectIdentifier',
510     'UTF8String': 'char.UTF8String',
511     'GeneralString': 'char.GeneralString',
512     'NumericString': 'char.NumericString',
513     'PrintableString': 'char.PrintableString',
514     'IA5String': 'char.IA5String',
515     'GraphicString': 'char.GraphicString',
516     'GeneralizedTime': 'useful.GeneralizedTime',
517     'UTCTime': 'useful.UTCTime',
518     'ObjectDescriptor': 'useful.ObjectDescriptor',  # In pyasn1 r1.2
519 }
520
521
522 def _translate_type(type_name):
523     """ Translate ASN.1 built-in types to pyasn1 equivalents.
524     Non-builtins are not translated.
525     """
526     assert isinstance(type_name, str), "Type name must be a string."
527     type_name = _sanitize_identifier(type_name)
528
529     return _ASN1_BUILTIN_TYPES.get(type_name, type_name)
530
531
532 def _translate_tag_class(tag_class):
533     """ Translate ASN.1 tag class names to pyasn1 equivalents.
534     Defaults to tag.tagClassContext if tag_class is not
535     recognized.
536     """
537     return _ASN1_TAG_CONTEXTS.get(tag_class, 'tag.tagClassContext')
538
539
540 def _translate_value(value):
541     """ Translate ASN.1 built-in values to Python equivalents.
542     Unrecognized values are not translated.
543     """
544     if isinstance(value, ReferencedValue) or _heuristic_is_identifier(value):
545         value = _sanitize_identifier(value)
546
547     return _ASN1_BUILTIN_VALUES.get(value, value)
548
549
550 def _heuristic_is_identifier(value):
551     """ Return True if this value is likely an identifier.
552     """
553     first = str(value)[0]
554     return first != '-' and not first.isdigit()
555
556
557 def _sanitize_identifier(name):
558     """ Sanitize ASN.1 type and value identifiers so that they're
559     valid Python identifiers.
560     """
561     name = str(name)
562     name = name.replace('-', '_')
563     if name in keyword.kwlist:
564         name += '_'
565
566     return name
567
568
569 # Simplistic command-line driver
570 def main(args):
571     with open(args[0]) as f:
572         asn1def = f.read()
573
574     parse_tree = parser.parse_asn1(asn1def)
575
576     modules = build_semantic_model(parse_tree)
577     if len(modules) > 1:
578         print('WARNING: More than one module generated to the same stream.', file=sys.stderr)
579
580     for module in modules:
581         print(pygen.auto_generated_header())
582         generate_pyasn1(module, sys.stdout)
583
584     return 0
585
586
587 if __name__ == '__main__':
588     sys.exit(main(sys.argv[1:]))