Support extension markers in ENUMERATED.
[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 from asn1ate import parser
30 from asn1ate.support import pygen
31 from asn1ate.sema import *
32
33
34 class Pyasn1Backend(object):
35     """ Backend to generate pyasn1 declarations from semantic tree.
36     Generators are divided into declarations and expressions,
37     because types in pyasn1 can be declared either as class
38     definitions or inline, e.g.
39
40     # Foo ::= INTEGER
41     # Foo is a decl
42     class Foo(univ.Integer):
43         pass
44
45     # Seq ::= SEQUENCE {
46     #     foo INTEGER
47     # }
48     # Seq is a decl,
49     # univ.Integer is an expr
50     class Seq(univ.Sequence):
51         componentType = namedtype.NamedTypes(
52             namedtype.NamedType('foo', univ.Integer())
53         )
54
55     Typically, declarations can contain other declarations
56     or expressions, expressions can only contain other expressions.
57     """
58     def __init__(self, sema_module, out_stream):
59         self.sema_module = sema_module
60         self.writer = pygen.PythonWriter(out_stream)
61
62         self.decl_generators = {
63             ChoiceType: self.decl_constructed_type,
64             SequenceType: self.decl_constructed_type,
65             SetType: self.decl_constructed_type,
66             TaggedType: self.decl_tagged_type,
67             SimpleType: self.decl_simple_type,
68             UserDefinedType: self.decl_userdefined_type,
69             ValueListType: self.decl_value_list_type,
70             BitStringType: self.decl_bitstring_type,
71             SequenceOfType: self.decl_sequenceof_type,
72             SetOfType: self.decl_setof_type,
73             TypeAssignment: self.decl_type_assignment,
74             ValueAssignment: self.decl_value_assignment
75         }
76
77         self.expr_generators = {
78             TaggedType: self.expr_tagged_type,
79             SimpleType: self.expr_simple_type,
80             UserDefinedType: self.expr_userdefined_type,
81             ComponentType: self.expr_component_type,
82             NamedType: self.expr_named_type,
83             SequenceOfType: self.expr_sequenceof_type,
84             SetOfType: self.expr_setof_type,
85             ValueListType: self.expr_value_list_type,
86             ChoiceType: self.expr_constructed_type,
87             SequenceType: self.expr_constructed_type,
88             SetType: self.expr_constructed_type,
89         }
90
91     def generate_code(self):
92         self.writer.write_line('from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful')
93         self.writer.write_blanks(2)
94
95         # TODO: Only generate _OID if sema_module
96         # contains object identifier values.
97         self.generate_OID()
98         self.writer.write_blanks(2)
99
100         assignments = topological_sort(self.sema_module.assignments)
101         for assignment in assignments:
102             self.writer.write_block(self.generate_decl(assignment))
103             self.writer.write_blanks(2)
104
105     def generate_expr(self, t):
106         generator = self.expr_generators[type(t)]
107         return generator(t)
108
109     def generate_decl(self, t):
110         generator = self.decl_generators[type(t)]
111         return generator(t)
112
113     def decl_type_assignment(self, assignment):
114         fragment = self.writer.get_fragment()
115
116         assigned_type, type_decl = assignment.type_name, assignment.type_decl
117
118         base_type = _translate_type(type_decl.type_name)
119         fragment.write_line('class %s(%s):' % (assigned_type, base_type))
120
121         fragment.push_indent()
122         fragment.write_block(self.generate_decl(type_decl))
123         fragment.pop_indent()
124
125         return str(fragment)
126
127     def expr_simple_type(self, t):
128         type_expr = _translate_type(t.type_name) + '()'
129         if t.constraint:
130             type_expr += '.subtype(subtypeSpec=constraint.ValueRangeConstraint(%s, %s))' % (t.constraint.min_value, t.constraint.max_value)
131
132         return type_expr
133
134     def decl_simple_type(self, t):
135         if t.constraint:
136             return 'subtypeSpec = constraint.ValueRangeConstraint(%s, %s)' % (t.constraint.min_value, t.constraint.max_value)
137         else:
138             return 'pass'
139
140     def expr_userdefined_type(self, t):
141         return t.type_name + '()'
142
143     def decl_userdefined_type(self, t):
144         return 'pass'
145
146     def decl_constructed_type(self, t):
147         fragment = self.writer.get_fragment()
148
149         fragment.write_line('componentType = namedtype.NamedTypes(')
150
151         fragment.push_indent()
152         fragment.write_block(self.expr_component_types(t.components))
153         fragment.pop_indent()
154
155         fragment.write_line(')')
156
157         return str(fragment)
158
159     def expr_constructed_type(self, t):
160         fragment = self.writer.get_fragment()
161
162         class_name = _translate_type(t.type_name)
163
164         fragment.write_line('%s(componentType=namedtype.NamedTypes(' % class_name)
165
166         fragment.push_indent()
167         fragment.write_block(self.expr_component_types(t.components))
168         fragment.pop_indent()
169
170         fragment.write_line('))')
171
172         return str(fragment)
173
174     def expr_component_types(self, components):
175         fragment = self.writer.get_fragment()
176
177         component_exprs = []
178         for c in components:
179             if not isinstance(c, ExtensionMarker):
180                 component_exprs.append(self.generate_expr(c))
181
182         fragment.write_enumeration(component_exprs)
183
184         return str(fragment)
185
186     def expr_tagged_type(self, t):
187         tag_type = 'implicitTag' if t.implicit else 'explicitTag'
188         type_expr = self.generate_expr(t.type_decl)
189         type_expr += '.subtype(%s=%s)' % (tag_type, self.build_tag_expr(t))
190
191         return type_expr
192
193     def decl_tagged_type(self, t):
194         fragment = self.writer.get_fragment()
195
196         tag_type = 'tagImplicitly' if t.implicit else 'tagExplicitly'
197         base_type = _translate_type(t.type_decl.type_name)
198         fragment.write_line('tagSet = %s.tagSet.%s(%s)' % (base_type, tag_type, self.build_tag_expr(t)))
199         fragment.write_line(self.generate_decl(t.type_decl))  # possibly 'pass'. but that's OK in a decl
200
201         return str(fragment)
202
203     def build_tag_expr(self, tag_def):
204         context = _translate_tag_class(tag_def.class_name)
205
206         tagged_type_decl = self.sema_module.resolve_type_decl(tag_def.type_decl)
207         if isinstance(tagged_type_decl, ConstructedType):
208             tag_format = 'tag.tagFormatConstructed'
209         else:
210             tag_format = 'tag.tagFormatSimple'
211
212         return 'tag.Tag(%s, %s, %s)' % (context, tag_format, tag_def.class_number)
213
214     def expr_component_type(self, t):
215         if t.components_of_type:
216             # COMPONENTS OF works like a literal include, so just
217             # expand all components of the referenced type.
218             included_type_decl = self.sema_module.resolve_type_decl(t.components_of_type)
219             included_content = self.expr_component_types(included_type_decl.components)
220
221             # Strip trailing newline from expr_component_types
222             # to make the list line up
223             return included_content.strip()
224
225         if t.optional:
226             return "namedtype.OptionalNamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
227         elif t.default_value is not None:
228             type_expr = self.generate_expr(t.type_decl)
229             type_expr += '.subtype(value=%s)' % _translate_value(t.default_value)
230
231             return "namedtype.DefaultedNamedType('%s', %s)" % (t.identifier, type_expr)
232         else:
233             return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
234
235     def expr_named_type(self, t):
236         return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
237
238     def decl_value_list_type(self, t):
239         fragment = self.writer.get_fragment()
240
241         if t.named_values:
242             fragment.write_line('namedValues = namedval.NamedValues(')
243             fragment.push_indent()
244
245             named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
246             fragment.write_enumeration(named_values)
247
248             fragment.pop_indent()
249             fragment.write_line(')')
250         else:
251             fragment.write_line('pass')
252
253         return str(fragment)
254
255     def expr_value_list_type(self, t):
256         class_name = _translate_type(t.type_name)
257         if t.named_values:
258             named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
259             return '%s(namedValues=namedval.NamedValues(%s))' % (class_name, ', '.join(named_values))
260         else:
261             return class_name + '()'
262
263     def decl_bitstring_type(self, t):
264         fragment = self.writer.get_fragment()
265
266         if t.named_bits:
267             fragment.write_line('namedValues = namedval.NamedValues(')
268             fragment.push_indent()
269
270             named_bit_list = list(map(lambda v: '(\'%s\', %s)' % (v.identifier, v.value), t.named_bits))
271             fragment.write_enumeration(named_bit_list)
272
273             fragment.pop_indent()
274             fragment.write_line(')')
275         else:
276             fragment.write_line('pass')
277
278         return str(fragment)
279
280     def expr_sequenceof_type(self, t):
281         return 'univ.SequenceOf(componentType=%s)' % self.generate_expr(t.type_decl)
282
283     def decl_sequenceof_type(self, t):
284         return 'componentType = %s' % self.generate_expr(t.type_decl)
285
286     def expr_setof_type(self, t):
287         return 'univ.SetOf(componentType=%s)' % self.generate_expr(t.type_decl)
288
289     def decl_setof_type(self, t):
290         return 'componentType = %s' % self.generate_expr(t.type_decl)
291
292     def decl_value_assignment(self, assignment):
293         assigned_value, type_decl, value = assignment.value_name, assignment.type_decl, assignment.value
294
295         if isinstance(value, ObjectIdentifierValue):
296             value_constructor = self.build_object_identifier_value(value)
297         else:
298             value_type = _translate_type(type_decl.type_name)
299             value_constructor = '%s(%s)' % (value_type, value)
300
301         return '%s = %s' % (assigned_value, value_constructor)
302
303     def build_object_identifier_value(self, t):
304         objid_components = []
305
306         for c in t.components:
307             if isinstance(c, NameForm):
308                 if c.name in REGISTERED_OID_NAMES:
309                     objid_components.append(str(REGISTERED_OID_NAMES[c.name]))
310                 else:
311                     objid_components.append(c.name)
312             elif isinstance(c, NumberForm):
313                 objid_components.append(str(c.value))
314             elif isinstance(c, NameAndNumberForm):
315                 objid_components.append(str(c.number.value))
316             else:
317                 assert False
318
319         return '_OID(%s)' % ', '.join(objid_components)
320
321     def generate_OID(self):
322         self.writer.write_line('def _OID(*components):')
323         self.writer.push_indent()
324         self.writer.write_line('output = []')
325         self.writer.write_line('for x in tuple(components):')
326         self.writer.push_indent()
327         self.writer.write_line('if isinstance(x, univ.ObjectIdentifier):')
328         self.writer.push_indent()
329         self.writer.write_line('output.extend(list(x))')
330         self.writer.pop_indent()
331         self.writer.write_line('else:')
332         self.writer.push_indent()
333         self.writer.write_line('output.append(int(x))')
334         self.writer.pop_indent()
335         self.writer.pop_indent()
336         self.writer.write_blanks(1)
337         self.writer.write_line('return univ.ObjectIdentifier(output)')
338         self.writer.pop_indent()
339
340         self.writer.pop_indent()
341
342
343 def generate_pyasn1(sema_module, out_stream):
344     return Pyasn1Backend(sema_module, out_stream).generate_code()
345
346
347 # Translation tables from ASN.1 primitives to pyasn1 primitives
348 _ASN1_TAG_CONTEXTS = {
349     'APPLICATION': 'tag.tagClassApplication',
350     'PRIVATE': 'tag.tagClassPrivate',
351     'UNIVERSAL': 'tag.tagClassUniversal'
352 }
353
354
355 _ASN1_BUILTIN_VALUES = {
356     'FALSE': '0',
357     'TRUE': '1'
358 }
359
360
361 _ASN1_BUILTIN_TYPES = {
362     'INTEGER': 'univ.Integer',
363     'BOOLEAN': 'univ.Boolean',
364     'NULL': 'univ.Null',
365     'ENUMERATED': 'univ.Enumerated',
366     'REAL': 'univ.Real',
367     'BIT STRING': 'univ.BitString',
368     'OCTET STRING': 'univ.OctetString',
369     'CHOICE': 'univ.Choice',
370     'SEQUENCE': 'univ.Sequence',
371     'SET': 'univ.Set',
372     'SEQUENCE OF': 'univ.SequenceOf',
373     'SET OF': 'univ.SetOf',
374     'UTF8String': 'char.UTF8String',
375     'GeneralString': 'char.GeneralString',
376     'NumericString': 'char.NumericString',
377     'PrintableString': 'char.PrintableString',
378     'IA5String': 'char.IA5String',
379     'OBJECT IDENTIFIER': 'univ.ObjectIdentifier',
380     'GeneralizedTime': 'useful.GeneralizedTime',
381     'UTCTime': 'useful.UTCTime',
382     'ObjectDescriptor': 'useful.ObjectDescriptor',  # In pyasn1 r1.2
383 }
384
385
386 def _translate_type(type_name):
387     """ Translate ASN.1 built-in types to pyasn1 equivalents.
388     Non-builtins are not translated.
389     """
390     return _ASN1_BUILTIN_TYPES.get(type_name, type_name)
391
392
393 def _translate_tag_class(tag_class):
394     """ Translate ASN.1 tag class names to pyasn1 equivalents.
395     Defaults to tag.tagClassContext if tag_class is not
396     recognized.
397     """
398     return _ASN1_TAG_CONTEXTS.get(tag_class, 'tag.tagClassContext')
399
400
401 def _translate_value(value):
402     """ Translate ASN.1 built-in values to Python equivalents.
403     Unrecognized values are not translated.
404     """
405     return _ASN1_BUILTIN_VALUES.get(value, value)
406
407
408 # Simplistic command-line driver
409 def main(args):
410     with open(args[0]) as f:
411         asn1def = f.read()
412
413     parse_tree = parser.parse_asn1(asn1def)
414
415     modules = build_semantic_model(parse_tree)
416     if len(modules) > 1:
417         print('WARNING: More than one module generated to the same stream.', file=sys.stderr)
418
419     for module in modules:
420         print(pygen.auto_generated_header())
421         generate_pyasn1(module, sys.stdout)
422
423     return 0
424
425
426 if __name__ == '__main__':
427     sys.exit(main(sys.argv[1:]))