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