618b23b4b3c67e54389d10c02d919f61f026c37b
[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         }
87
88     def generate_code(self):
89         self.writer.write_line('from pyasn1.type import univ, char, namedtype, namedval, tag, constraint')
90         self.writer.write_blanks(2)
91
92         assignments = topological_sort(self.sema_module.assignments)
93         for assignment in assignments:
94             self.writer.write_block(self.generate_decl(assignment))
95             self.writer.write_blanks(2)
96
97     def generate_expr(self, t):
98         generator = self.expr_generators[type(t)]
99         return generator(t)
100
101     def generate_decl(self, t):
102         generator = self.decl_generators[type(t)]
103         return generator(t)
104
105     def decl_type_assignment(self, assignment):
106         fragment = self.writer.get_fragment()
107
108         assigned_type, type_decl = assignment.type_name, assignment.type_decl
109
110         base_type = _translate_type(type_decl.type_name)
111         fragment.write_line('class %s(%s):' % (assigned_type, base_type))
112
113         fragment.push_indent()
114         fragment.write_block(self.generate_decl(type_decl))
115         fragment.pop_indent()
116
117         return str(fragment)
118
119     def expr_simple_type(self, t):
120         type_expr = _translate_type(t.type_name) + '()'
121         if t.constraint:
122             type_expr += '.subtype(subtypeSpec=constraint.ValueRangeConstraint(%s, %s))' % (t.constraint.min_value, t.constraint.max_value)
123
124         return type_expr
125
126     def decl_simple_type(self, t):
127         if t.constraint:
128             return 'subtypeSpec = constraint.ValueRangeConstraint(%s, %s)' % (t.constraint.min_value, t.constraint.max_value)
129         else:
130             return 'pass'
131
132     def expr_userdefined_type(self, t):
133         return t.type_name + '()'
134
135     def decl_userdefined_type(self, t):
136         return 'pass'
137
138     def decl_constructed_type(self, t):
139         fragment = self.writer.get_fragment()
140
141         fragment.write_line('componentType = namedtype.NamedTypes(')
142
143         fragment.push_indent()
144         fragment.write_block(self.expr_component_types(t.components))
145         fragment.pop_indent()
146
147         fragment.write_line(')')
148
149         return str(fragment)
150
151     def expr_component_types(self, components):
152         fragment = self.writer.get_fragment()
153
154         component_exprs = []
155         for c in components:
156             if not isinstance(c, ExtensionMarker):
157                 component_exprs.append(self.generate_expr(c))
158
159         fragment.write_enumeration(component_exprs)
160
161         return str(fragment)
162
163     def expr_tagged_type(self, t):
164         tag_type = 'implicitTag' if t.implicit else 'explicitTag'
165         type_expr = self.generate_expr(t.type_decl)
166         type_expr += '.subtype(%s=%s)' % (tag_type, self.build_tag_expr(t))
167
168         return type_expr
169
170     def decl_tagged_type(self, t):
171         fragment = self.writer.get_fragment()
172
173         tag_type = 'tagImplicitly' if t.implicit else 'tagExplicitly'
174         base_type = _translate_type(t.type_decl.type_name)
175         fragment.write_line('tagSet = %s.tagSet.%s(%s)' % (base_type, tag_type, self.build_tag_expr(t)))
176         fragment.write_line(self.generate_decl(t.type_decl))  # possibly 'pass'. but that's OK in a decl
177
178         return str(fragment)
179
180     def build_tag_expr(self, tag_def):
181         context = _translate_tag_class(tag_def.class_name)
182
183         tagged_type_decl = self.sema_module.resolve_type_decl(tag_def.type_decl)
184         if isinstance(tagged_type_decl, ConstructedType):
185             tag_format = 'tag.tagFormatConstructed'
186         else:
187             tag_format = 'tag.tagFormatSimple'
188
189         return 'tag.Tag(%s, %s, %s)' % (context, tag_format, tag_def.class_number)
190
191     def expr_component_type(self, t):
192         if t.components_of_type:
193             # COMPONENTS OF works like a literal include, so just
194             # expand all components of the referenced type.
195             included_type_decl = self.sema_module.resolve_type_decl(t.components_of_type)
196             included_content = self.expr_component_types(included_type_decl.components)
197
198             # Strip trailing newline from expr_component_types
199             # to make the list line up
200             return included_content.strip()
201
202         if t.optional:
203             return "namedtype.OptionalNamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
204         elif t.default_value is not None:
205             type_expr = self.generate_expr(t.type_decl)
206             type_expr += '.subtype(value=%s)' % _translate_value(t.default_value)
207
208             return "namedtype.DefaultedNamedType('%s', %s)" % (t.identifier, type_expr)
209         else:
210             return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
211
212     def expr_named_type(self, t):
213         return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
214
215     def decl_value_list_type(self, t):
216         fragment = self.writer.get_fragment()
217
218         if t.named_values:
219             fragment.write_line('namedValues = namedval.NamedValues(')
220             fragment.push_indent()
221
222             named_values = list(map(lambda v: '(\'%s\', %s)' % (v.identifier, v.value), t.named_values))
223             fragment.write_enumeration(named_values)
224
225             fragment.pop_indent()
226             fragment.write_line(')')
227         else:
228             fragment.write_line('pass')
229
230         return str(fragment)
231
232     def expr_value_list_type(self, t):
233         if t.named_values:
234             named_values = list(map(lambda v: '(\'%s\', %s)' % (v.identifier, v.value), t.named_values))
235             return 'namedValues=namedval.NamedValues(%s)' % ', '.join(named_values)
236         else:
237             return ''
238
239     def decl_bitstring_type(self, t):
240         fragment = self.writer.get_fragment()
241
242         if t.named_bits:
243             fragment.write_line('namedValues = namedval.NamedValues(')
244             fragment.push_indent()
245
246             named_bit_list = list(map(lambda v: '(\'%s\', %s)' % (v.identifier, v.value), t.named_bits))
247             fragment.write_enumeration(named_bit_list)
248
249             fragment.pop_indent()
250             fragment.write_line(')')
251         else:
252             fragment.write_line('pass')
253
254         return str(fragment)
255
256     def expr_sequenceof_type(self, t):
257         return 'univ.SequenceOf(componentType=%s)' % self.generate_expr(t.type_decl)
258
259     def decl_sequenceof_type(self, t):
260         return 'componentType = %s' % self.generate_expr(t.type_decl)
261
262     def expr_setof_type(self, t):
263         return 'univ.SetOf(componentType=%s)' % self.generate_expr(t.type_decl)
264
265     def decl_setof_type(self, t):
266         return 'componentType = %s' % self.generate_expr(t.type_decl)
267
268     def decl_value_assignment(self, assignment):
269         assigned_value, type_decl, value = assignment.value_name, assignment.type_decl, assignment.value
270
271         value_type = _translate_type(type_decl.type_name)
272         return '%s = %s(%s)' % (assigned_value, value_type, value)
273
274
275 def generate_pyasn1(sema_module, out_stream):
276     return Pyasn1Backend(sema_module, out_stream).generate_code()
277
278
279 # Translation tables from ASN.1 primitives to pyasn1 primitives
280 _ASN1_TAG_CONTEXTS = {
281     'APPLICATION': 'tag.tagClassApplication',
282     'PRIVATE': 'tag.tagClassPrivate',
283     'UNIVERSAL': 'tag.tagClassUniversal'
284 }
285
286
287 _ASN1_BUILTIN_VALUES = {
288     'FALSE': '0',
289     'TRUE': '1'
290 }
291
292
293 _ASN1_BUILTIN_TYPES = {
294     'INTEGER': 'univ.Integer',
295     'BOOLEAN': 'univ.Boolean',
296     'NULL': 'univ.Null',
297     'ENUMERATED': 'univ.Enumerated',
298     'REAL': 'univ.Real',
299     'BIT STRING': 'univ.BitString',
300     'OCTET STRING': 'univ.OctetString',
301     'CHOICE': 'univ.Choice',
302     'SEQUENCE': 'univ.Sequence',
303     'SET': 'univ.Set',
304     'SEQUENCE OF': 'univ.SequenceOf',
305     'SET OF': 'univ.SetOf',
306     'UTF8String': 'char.UTF8String',
307     'OBJECT IDENTIFIER': 'univ.ObjectIdentifier'
308 }
309
310
311 def _translate_type(type_name):
312     """ Translate ASN.1 built-in types to pyasn1 equivalents.
313     Non-builtins are not translated.
314     """
315     return _ASN1_BUILTIN_TYPES.get(type_name, type_name)
316
317
318 def _translate_tag_class(tag_class):
319     """ Translate ASN.1 tag class names to pyasn1 equivalents.
320     Defaults to tag.tagClassContext if tag_class is not
321     recognized.
322     """
323     return _ASN1_TAG_CONTEXTS.get(tag_class, 'tag.tagClassContext')
324
325
326 def _translate_value(value):
327     """ Translate ASN.1 built-in values to Python equivalents.
328     Unrecognized values are not translated.
329     """
330     return _ASN1_BUILTIN_VALUES.get(value, value)
331
332
333 # Simplistic command-line driver
334 def main(args):
335     with open(args[0]) as f:
336         asn1def = f.read()
337
338     parse_tree = parser.parse_asn1(asn1def)
339
340     modules = build_semantic_model(parse_tree)
341     if len(modules) > 1:
342         print('WARNING: More than one module generated to the same stream.', file=sys.stderr)
343
344     for module in modules:
345         print(pygen.auto_generated_header())
346         generate_pyasn1(module, sys.stdout)
347
348     return 0
349
350
351 if __name__ == '__main__':
352     sys.exit(main(sys.argv[1:]))