1 # Copyright (c) 2013, Schneider Electric Buildings AB
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.
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.
26 from asn1ate import parser
29 def build_semantic_model(parse_result):
30 """ Build a semantic model of the ASN.1 definition
31 from a syntax tree generated by asn1ate.parser.
34 for token in parse_result:
35 _assert_annotated_token(token)
36 root.append(_create_sema_node(token))
41 def topological_sort(assignments):
42 """ Algorithm adapted from:
43 http://en.wikipedia.org/wiki/Topological_sorting.
45 Assumes assignments is an iterable of items with two methods:
46 - reference_name() -- returns the reference name of the assignment
47 - references() -- returns an iterable of reference names
48 upon which the assignment depends.
50 graph = dict((a.reference_name(), set(a.references())) for a in assignments)
52 def has_predecessor(node):
53 for predecessor in graph.keys():
54 if node in graph[predecessor]:
59 # Build a topological order of reference names
60 topological_order = []
61 roots = [name for name in graph.keys() if not has_predecessor(name)]
66 # Remove the current node from the graph
67 # and collect all new roots (the nodes that
68 # were previously only referenced from n)
69 successors = graph.pop(root, set())
70 roots.extend(successor for successor in successors if not has_predecessor(successor))
72 topological_order.insert(0, root)
75 raise Exception('Can\'t sort cyclic references: %s' % graph)
77 # Sort the actual assignments based on the topological order
78 return sorted(assignments, key=lambda a: topological_order.index(a.reference_name()))
81 # Registered object identifier names
82 REGISTERED_OID_NAMES = {
90 'network-operator': 3,
93 'registration-authority': 1,
95 'identified-organization': 3,
98 'registration-procedures': 17
104 Concepts in the ASN.1 specification are mirrored here as a simple object model.
106 Class and member names typically follow the ASN.1 terminology, but there are
107 some concepts captured which are not expressed in the spec.
109 Most notably, we build a dependency graph of all types and values in a module,
110 to allow code generators to build code in dependency order.
112 All nodes that may be referenced (type and value assignments) must have a
113 method called ``reference_name``.
115 All nodes that may reference other types (e.g. assignments, component types)
116 must have a method called ``references`` returning the names of all referenced
119 Typically, if you have a ``reference_name``, you must also have a ``references``,
120 but not the other way around.
123 class SemaNode(object):
125 raise NotImplementedError()
128 class Module(object):
129 def __init__(self, elements):
130 self._user_types = {}
132 module_reference, _, _, _, _, module_body, _ = elements
133 self.name = module_reference.elements[0]
135 if module_body.elements:
136 _, assignments = module_body.elements
137 self.assignments = [_create_sema_node(token) for token in assignments.elements]
139 self.assignments = []
141 def user_types(self):
142 if not self._user_types:
143 # Index all type assignments by name
144 type_assignments = [a for a in self.assignments if isinstance(a, TypeAssignment)]
145 for user_defined in type_assignments:
146 self._user_types[user_defined.type_name] = user_defined.type_decl
148 return self._user_types
150 def resolve_type_decl(self, type_decl):
151 """ Recursively resolve user-defined types to their
152 built-in declaration.
154 user_types = self.user_types()
156 # Drill through Type, it acts as a container
157 # for type + metadata like constraints.
158 if isinstance(type_decl, Type):
159 type_decl = type_decl.type_decl
161 if isinstance(type_decl, UserDefinedType):
162 return self.resolve_type_decl(user_types[type_decl.type_name])
168 return '%s DEFINITIONS ::=\n' % self.name \
170 + '\n'.join(map(str, self.assignments)) + '\n' \
176 class TypeAssignment(object):
177 def __init__(self, elements):
178 assert(len(elements) == 3)
179 type_name, _, type_decl = elements
180 self.type_name = type_name
181 self.type_decl = _create_sema_node(type_decl)
183 def reference_name(self):
184 return self.type_name
186 def references(self):
187 refs = self.type_decl.references()
189 # Remove any self-references
190 refs = [ref for ref in refs if ref != self.type_name]
195 return '%s ::= %s' % (self.type_name, self.type_decl)
200 class ValueAssignment(object):
201 def __init__(self, elements):
202 value_name, type_name, _, value = elements
203 self.value_name = ValueReference(value_name.elements) # First token is always a valuereference
204 self.type_decl = _create_sema_node(type_name)
206 if isinstance(value, parser.AnnotatedToken):
207 self.value = _create_sema_node(value)
211 def reference_name(self):
212 return self.value_name.reference_name()
214 def references(self):
215 refs = [self.type_decl.reference_name()]
216 if isinstance(self.value, ValueReference):
217 refs.append(self.value.reference_name())
218 elif isinstance(self.value, ObjectIdentifierValue):
219 refs.extend(self.value.references())
221 # It's a literal, and they don't play into declaration order.
224 # Remove any self-references
225 refs = [ref for ref in refs if ref != self.value_name]
230 return '%s %s ::= %s' % (self.value_name, self.type_decl, self.value)
235 class ValueReference(object):
236 def __init__(self, elements):
237 self.name = elements[0]
239 def reference_name(self):
242 def references(self):
252 """ A container for all types, contains
253 additional metadata such as constraints.
255 def __init__(self, elements):
256 self.type_decl = _create_sema_node(elements[0])
257 if len(elements) == 2:
258 assert elements[1].ty == 'Constraint'
259 self.constraint = Constraint(elements[1].elements)
261 self.constraint = None
265 return self.type_decl.type_name
267 def reference_name(self):
268 return self.type_decl.type_name
270 def references(self):
272 return self.constraint.references()
278 return '%s %s' % (self.type_decl, self.constraint)
280 return str(self.type_decl)
285 class ConstructedType(object):
286 """ Base type for SEQUENCE, SET and CHOICE. """
287 def __init__(self, elements):
288 type_name, component_tokens = elements
289 self.type_name = type_name
290 self.components = [_create_sema_node(token) for token in component_tokens]
292 def references(self):
294 for component in self.components:
295 references.extend(component.references())
299 component_type_list = ', '.join(map(str, self.components))
300 return '%s { %s }' % (self.type_name, component_type_list)
305 class ChoiceType(ConstructedType):
306 def __init__(self, elements):
307 super(ChoiceType, self).__init__(elements)
310 class SequenceType(ConstructedType):
311 def __init__(self, elements):
312 super(SequenceType, self).__init__(elements)
315 class SetType(ConstructedType):
316 def __init__(self, elements):
317 super(SetType, self).__init__(elements)
320 class CollectionType(object):
321 """ Base type for SET OF and SEQUENCE OF. """
322 def __init__(self, kind, elements):
324 self.type_name = self.kind + ' OF'
326 if elements[0].ty == 'Type':
327 self.size_constraint = None
328 self.type_decl = _create_sema_node(elements[0])
329 elif elements[0].ty == 'SizeConstraint':
330 self.size_constraint = _create_sema_node(elements[0])
331 self.type_decl = _create_sema_node(elements[1])
333 assert False, 'Unknown form of %s OF declaration: %s' % (self.kind, elements)
335 def references(self):
336 return self.type_decl.references()
339 if self.size_constraint:
340 return '%s %s OF %s' % (self.kind, self.size_constraint, self.type_decl)
342 return '%s OF %s' % (self.kind, self.type_decl)
347 class SequenceOfType(CollectionType):
348 def __init__(self, elements):
349 super(SequenceOfType, self).__init__('SEQUENCE', elements)
352 class SetOfType(CollectionType):
353 def __init__(self, elements):
354 super(SetOfType, self).__init__('SET', elements)
357 class TaggedType(object):
358 def __init__(self, elements):
359 self.class_name = None
360 self.class_number = None
361 self.implicit = False
363 tag_token = elements[0]
364 if type(elements[1]) is parser.AnnotatedToken:
365 type_token = elements[1]
367 self.implicit = elements[1] == 'IMPLICIT'
368 type_token = elements[2]
370 for tag_element in tag_token.elements:
371 if tag_element.ty == 'TagClassNumber':
372 self.class_number = tag_element.elements[0]
373 elif tag_element.ty == 'TagClass':
374 self.class_name = tag_element.elements[0]
376 assert False, 'Unknown tag element: %s' % tag_element
378 self.type_decl = _create_sema_node(type_token)
382 return self.type_decl.type_name
384 def reference_name(self):
385 return self.type_decl.type_name
387 def references(self):
388 return self.type_decl.references()
393 class_spec.append(self.class_name)
394 class_spec.append(self.class_number)
396 result = '[%s] ' % ' '.join(class_spec)
398 result += 'IMPLICIT '
400 result += str(self.type_decl)
407 class SimpleType(object):
408 def __init__(self, elements):
409 self.constraint = None
410 self.type_name = elements[0]
411 if len(elements) > 1 and elements[1].ty == 'Constraint':
412 self.constraint = Constraint(elements[1].elements)
414 def reference_name(self):
415 return self.type_name
417 def references(self):
418 refs = [self.type_name]
420 refs.extend(self.constraint.references())
425 if self.constraint is None:
426 return self.type_name
428 return '%s %s' % (self.type_name, self.constraint)
433 class UserDefinedType(object):
434 def __init__(self, elements):
435 self.type_name = elements[0]
437 def references(self):
438 return [self.type_name]
441 return self.type_name
446 class Constraint(object):
447 def __init__(self, elements):
448 min_value, max_value = elements
450 self.min_value = _maybe_create_sema_node(min_value)
451 self.max_value = _maybe_create_sema_node(max_value)
453 def references(self):
455 if isinstance(self.min_value, ValueReference):
456 refs.append(self.min_value.reference_name())
458 if isinstance(self.max_value, ValueReference):
459 refs.append(self.max_value.reference_name())
464 return '(%s..%s)' % (self.min_value, self.max_value)
469 class SizeConstraint(Constraint):
470 """ Size constraints have the same form as any value range constraints."""
472 return 'SIZE(%s..%s)' % (self.min_value, self.max_value)
477 class ComponentType(object):
478 def __init__(self, elements):
479 self.identifier = None
480 self.type_decl = None
481 self.default_value = None
482 self.optional = False
483 self.components_of_type = None
485 def crack_named_type(token):
486 named_type = NamedType(token)
487 self.identifier = named_type.identifier
488 self.type_decl = named_type.type_decl
490 first_token = elements[0]
491 if first_token.ty == 'NamedType':
492 crack_named_type(first_token.elements)
493 elif first_token.ty == 'ComponentTypeOptional':
494 crack_named_type(first_token.elements[0].elements)
496 elif first_token.ty == 'ComponentTypeDefault':
497 crack_named_type(first_token.elements[0].elements)
498 self.default_value = _maybe_create_sema_node(first_token.elements[1])
499 elif first_token.ty == 'ComponentTypeComponentsOf':
500 self.components_of_type = _create_sema_node(first_token.elements[0])
502 assert False, 'Unknown component type %s' % first_token
504 def references(self):
505 if self.components_of_type:
506 return [self.components_of_type.type_name]
508 refs = [self.type_decl.type_name]
509 refs.extend(self.type_decl.references())
511 if self.default_value is not None:
512 refs.append(str(self.default_value))
517 if self.components_of_type:
518 return 'COMPONENTS OF %s' % self.components_of_type
520 result = '%s %s' % (self.identifier, self.type_decl)
522 result += ' OPTIONAL'
523 elif self.default_value is not None:
524 result += ' DEFAULT %s' % self.default_value
531 class NamedType(object):
532 def __init__(self, elements):
533 first_token = elements[0]
534 if first_token.ty == 'Type':
535 # EXT: unnamed member
536 type_token = first_token
537 self.identifier = _get_next_unnamed()
538 elif first_token.ty == 'Identifier':
540 self.identifier = first_token.elements[0]
541 type_token = elements[1]
543 self.type_decl = _create_sema_node(type_token)
545 def references(self):
546 return self.type_decl.references()
549 return '%s %s' % (self.identifier, self.type_decl)
554 class ValueListType(object):
555 def __init__(self, elements):
556 self.type_name = elements[0]
557 if len(elements) > 1:
558 self.named_values = [_create_sema_node(token) for token in elements[1]]
560 self.named_values = None
562 def references(self):
563 # TODO: Value references
567 if self.named_values:
568 named_value_list = ', '.join(map(str, self.named_values))
569 return '%s { %s }' % (self.type_name, named_value_list)
571 return '%s' % self.type_name
576 class BitStringType(object):
577 def __init__(self, elements):
578 self.type_name = elements[0]
579 if len(elements) > 1:
580 self.named_bits = [_create_sema_node(token) for token in elements[1]]
582 self.named_bits = None
584 def references(self):
585 # TODO: Value references
590 named_bit_list = ', '.join(map(str, self.named_bits))
591 return '%s { %s }' % (self.type_name, named_bit_list)
593 return '%s' % self.type_name
598 class NamedValue(object):
599 def __init__(self, elements):
600 identifier_token, value_token = elements
601 self.identifier = identifier_token.elements[0]
602 self.value = value_token.elements[0]
604 def references(self):
605 # TODO: This appears to never be called. Investigate.
609 return '%s (%s)' % (self.identifier, self.value)
614 class ExtensionMarker(object):
615 def __init__(self, elements):
618 def references(self):
619 # TODO: This appears to never be called. Investigate.
628 class NameForm(object):
629 def __init__(self, elements):
630 self.name = elements[0]
632 def references(self):
641 class NumberForm(object):
642 def __init__(self, elements):
643 self.value = elements[0]
645 def references(self):
649 return str(self.value)
654 class NameAndNumberForm(object):
655 def __init__(self, elements):
656 # The first element is a NameForm containing only the
657 # name, so unpack it into a string.
658 self.name = elements[0].elements[0]
659 self.number = _create_sema_node(elements[1])
661 def references(self):
662 return [str(self.name), str(self.number)]
665 return '%s(%s)' % (self.name, self.number)
670 class ObjectIdentifierValue(object):
671 def __init__(self, elements):
672 self.components = [_create_sema_node(c) for c in elements]
674 def references(self):
676 for component in self.components:
677 if not isinstance(component, str):
678 refs.extend(component.references())
680 refs.append(component)
685 return '{' + ' '.join(str(x) for x in self.components) + '}'
690 def _maybe_create_sema_node(token):
691 if isinstance(token, parser.AnnotatedToken):
692 return _create_sema_node(token)
697 def _create_sema_node(token):
698 _assert_annotated_token(token)
700 if token.ty == 'ModuleDefinition':
701 return Module(token.elements)
702 elif token.ty == 'TypeAssignment':
703 return TypeAssignment(token.elements)
704 elif token.ty == 'ValueAssignment':
705 return ValueAssignment(token.elements)
706 elif token.ty == 'ValueReference':
707 return ValueReference(token.elements)
708 elif token.ty == 'ComponentType':
709 return ComponentType(token.elements)
710 elif token.ty == 'NamedType':
711 return NamedType(token.elements)
712 elif token.ty == 'ValueListType':
713 return ValueListType(token.elements)
714 elif token.ty == 'BitStringType':
715 return BitStringType(token.elements)
716 elif token.ty == 'NamedValue':
717 return NamedValue(token.elements)
718 elif token.ty == 'Type':
719 return Type(token.elements)
720 elif token.ty == 'SimpleType':
721 return SimpleType(token.elements)
722 elif token.ty == 'ReferencedType':
723 return UserDefinedType(token.elements)
724 elif token.ty == 'TaggedType':
725 return TaggedType(token.elements)
726 elif token.ty == 'SequenceType':
727 return SequenceType(token.elements)
728 elif token.ty == 'ChoiceType':
729 return ChoiceType(token.elements)
730 elif token.ty == 'SetType':
731 return SetType(token.elements)
732 elif token.ty == 'SequenceOfType':
733 return SequenceOfType(token.elements)
734 elif token.ty == 'SetOfType':
735 return SetOfType(token.elements)
736 elif token.ty == 'ExtensionMarker':
737 return ExtensionMarker(token.elements)
738 elif token.ty == 'SizeConstraint':
739 return SizeConstraint(token.elements)
740 elif token.ty == 'ObjectIdentifierValue':
741 return ObjectIdentifierValue(token.elements)
742 elif token.ty == 'NameForm':
743 return NameForm(token.elements)
744 elif token.ty == 'NumberForm':
745 return NumberForm(token.elements)
746 elif token.ty == 'NameAndNumberForm':
747 return NameAndNumberForm(token.elements)
749 raise Exception('Unknown token type: %s' % token.ty)
752 def _assert_annotated_token(obj):
753 assert(type(obj) is parser.AnnotatedToken)
756 # HACK: Generate unique names for unnamed members
758 def _get_next_unnamed():
759 global _unnamed_counter
760 _unnamed_counter += 1
761 return 'unnamed%d' % _unnamed_counter