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