cstring_value = dblQuotedString
builtin_value = boolean_value | bitstring_value | real_value | integer_value | null_value | cstring_value
- defined_value = valuereference # todo: more options from 13.1
+ defined_value = Unique(valuereference) # todo: more options from 13.1
+ referenced_value = Unique(defined_value) # todo: more options from 16.11
# object identifier value
name_form = Unique(identifier)
(objid_components_list | (defined_value + objid_components_list)) + \
Suppress('}')
- value = builtin_value | defined_value | object_identifier_value
+ value = builtin_value | referenced_value | object_identifier_value
# definitive identifier value
definitive_number_form = Unique(number)
defined_type = Unique(typereference) # todo: consider other defined types from 13.1
referenced_type = Unique(defined_type) # todo: consider other ref:d types from 16.3
+ # values
+
# Forward-declare these, they can only be fully defined once
# we have all types defined. There are some circular dependencies.
named_type = Forward()
# constraints
# todo: consider the full subtype and general constraint syntax described in 45.*
# but for now, just implement a simple integer value range.
- value_range_constraint = (signed_number | valuereference | MIN) + Suppress('..') + (signed_number | valuereference | MAX)
+ value_range_constraint = (signed_number | referenced_value | MIN) + Suppress('..') + (signed_number | referenced_value | MAX)
size_constraint = Optional(Suppress('(')) + Suppress(SIZE) + Suppress('(') + value_range_constraint + Suppress(')') + Optional(Suppress(')'))
constraint = Suppress('(') + value_range_constraint + Suppress(')')
set_type.setParseAction(annotate('SetType'))
value_list_type.setParseAction(annotate('ValueListType'))
bitstring_type.setParseAction(annotate('BitStringType'))
- referenced_type.setParseAction(annotate('ReferencedType'))
sequenceof_type.setParseAction(annotate('SequenceOfType'))
setof_type.setParseAction(annotate('SetOfType'))
named_number.setParseAction(annotate('NamedValue'))
named_type.setParseAction(annotate('NamedType'))
type_assignment.setParseAction(annotate('TypeAssignment'))
value_assignment.setParseAction(annotate('ValueAssignment'))
- valuereference.setParseAction(annotate('ValueReference'))
module_reference.setParseAction(annotate('ModuleReference'))
module_body.setParseAction(annotate('ModuleBody'))
module_definition.setParseAction(annotate('ModuleDefinition'))
assignment_list.setParseAction(annotate('AssignmentList'))
bstring.setParseAction(annotate('BinaryStringValue'))
hstring.setParseAction(annotate('HexStringValue'))
+ referenced_type.setParseAction(annotate('ReferencedType'))
+ referenced_value.setParseAction(annotate('ReferencedValue'))
start = OneOrMore(module_definition)
return start
class Pyasn1Backend(object):
""" Backend to generate pyasn1 declarations from semantic tree.
- Generators are divided into declarations and expressions,
- because types in pyasn1 can be declared either as class
- definitions or inline, e.g.
-
- # Foo ::= INTEGER
- # Foo is a decl
- class Foo(univ.Integer):
- pass
-
- # Seq ::= SEQUENCE {
- # foo INTEGER
- # }
- # Seq is a decl,
- # univ.Integer is an expr
- class Seq(univ.Sequence):
- componentType = namedtype.NamedTypes(
+
+ Pyasn1 represents type assignments as class derivation, e.g.
+
+ # Foo ::= INTEGER
+ class Foo(univ.Integer):
+ pass
+
+ For constructed types, the component types are instantiated inline, e.g.
+
+ # Seq ::= SEQUENCE {
+ # foo INTEGER
+ # }
+ class Seq(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('foo', univ.Integer())
+ )
+
+ (univ.Integer is not a base class here, but a value.)
+
+ To cope with circular dependencies, we define types in two passes so we'll
+ generate the above as:
+
+ class Seq(univ.Sequence):
+ pass
+
+ Seq.componentType = namedtype.NamedTypes(
namedtype.NamedType('foo', univ.Integer())
)
- Typically, declarations can contain other declarations
- or expressions, expressions can only contain other expressions.
+ This is nice, because we separate the introduction of a name (``Seq``) from
+ the details of what it contains, so we can build recursive definitions
+ without getting into trouble with Python's name lookup.
+
+ We call the empty class a *declaration*, and the population of its members
+ *definition*. The instantiation of univ.Integer is called an
+ *inline definition*.
+
+ The translation from ASN.1 constructs to Pyasn1 come in different flavors,
+ depending on whether they're declarations, definitions or inline
+ definitions.
+
+ Only type and value assignments generate declarations. For type assignments
+ we generate a definition once all dependent declarations are created. If the
+ type assignment involves a constructed type, it is filled with inline
+ definitions.
"""
def __init__(self, sema_module, out_stream):
self.sema_module = sema_module
self.writer = pygen.PythonWriter(out_stream)
self.decl_generators = {
- ChoiceType: self.decl_constructed_type,
- SequenceType: self.decl_constructed_type,
- SetType: self.decl_constructed_type,
- TaggedType: self.decl_tagged_type,
- SimpleType: self.decl_simple_type,
- UserDefinedType: self.decl_userdefined_type,
- ValueListType: self.decl_value_list_type,
- BitStringType: self.decl_bitstring_type,
- SequenceOfType: self.decl_sequenceof_type,
- SetOfType: self.decl_setof_type,
TypeAssignment: self.decl_type_assignment,
ValueAssignment: self.decl_value_assignment
}
- self.expr_generators = {
- TaggedType: self.expr_tagged_type,
- SimpleType: self.expr_simple_type,
- UserDefinedType: self.expr_userdefined_type,
- ComponentType: self.expr_component_type,
- NamedType: self.expr_named_type,
- SequenceOfType: self.expr_sequenceof_type,
- SetOfType: self.expr_setof_type,
- ValueListType: self.expr_value_list_type,
- ChoiceType: self.expr_constructed_type,
- SequenceType: self.expr_constructed_type,
- SetType: self.expr_constructed_type,
+ self.defn_generators = {
+ ChoiceType: self.defn_constructed_type,
+ SequenceType: self.defn_constructed_type,
+ SetType: self.defn_constructed_type,
+ TaggedType: self.defn_tagged_type,
+ SimpleType: self.defn_simple_type,
+ ReferencedType: self.defn_referenced_type,
+ ValueListType: self.defn_value_list_type,
+ BitStringType: self.defn_bitstring_type,
+ SequenceOfType: self.defn_sequenceof_type,
+ SetOfType: self.defn_setof_type,
+ }
+
+ self.inline_generators = {
+ TaggedType: self.inline_tagged_type,
+ SimpleType: self.inline_simple_type,
+ ReferencedType: self.inline_referenced_type,
+ ComponentType: self.inline_component_type,
+ NamedType: self.inline_named_type,
+ SequenceOfType: self.inline_sequenceof_type,
+ SetOfType: self.inline_setof_type,
+ ValueListType: self.inline_value_list_type,
+ ChoiceType: self.inline_constructed_type,
+ SequenceType: self.inline_constructed_type,
+ SetType: self.inline_constructed_type,
}
def generate_code(self):
# TODO: Only generate _OID if sema_module
# contains object identifier values.
- self.generate_OID()
+ self.writer.write_block(self.generate_OID())
self.writer.write_blanks(2)
- assignments = topological_sort(self.sema_module.assignments)
- for assignment in assignments:
- self.writer.write_block(self.generate_decl(assignment))
- self.writer.write_blanks(2)
+ assignment_components = dependency_sort(self.sema_module.assignments)
+ for component in assignment_components:
+ for assignment in component:
+ self.writer.write_block(self.generate_decl(assignment))
+ self.writer.write_blanks(2)
- def generate_expr(self, t):
- generator = self.expr_generators[type(t)]
- return generator(t)
+ for assignment in component:
+ details = self.generate_definition(assignment)
+ if details:
+ self.writer.write_block(details)
+ self.writer.write_blanks(2)
+
+ def generate_definition(self, assignment):
+ assert isinstance(assignment, (ValueAssignment, TypeAssignment))
+
+ if isinstance(assignment, ValueAssignment):
+ return None # Nothing to do here.
+
+ assigned_type, type_decl = assignment.type_name, assignment.type_decl
+ return self.generate_defn(assigned_type, type_decl)
def generate_decl(self, t):
generator = self.decl_generators[type(t)]
return generator(t)
+ def generate_expr(self, t):
+ generator = self.inline_generators[type(t)]
+ return generator(t)
+
+ def generate_defn(self, class_name, t):
+ generator = self.defn_generators[type(t)]
+ return generator(class_name, t)
+
def decl_type_assignment(self, assignment):
fragment = self.writer.get_fragment()
base_type = _translate_type(type_decl.type_name)
fragment.write_line('class %s(%s):' % (assigned_type, base_type))
-
fragment.push_indent()
- fragment.write_block(self.generate_decl(type_decl))
+ fragment.write_line('pass')
fragment.pop_indent()
return str(fragment)
- def expr_simple_type(self, t):
- type_expr = _translate_type(t.type_name) + '()'
- if t.constraint:
- type_expr += '.subtype(subtypeSpec=constraint.ValueRangeConstraint(%s, %s))' % (t.constraint.min_value, t.constraint.max_value)
+ def decl_value_assignment(self, assignment):
+ assigned_value, type_decl, value = assignment.value_name, assignment.type_decl, assignment.value
- return type_expr
+ if isinstance(value, ObjectIdentifierValue):
+ value_constructor = self.build_object_identifier_value(value)
+ elif isinstance(value, BinaryStringValue):
+ value_type = _translate_type(type_decl.type_name)
+ value_constructor = '%s(binValue=\'%s\')' % (value_type, value.value)
+ elif isinstance(value, HexStringValue):
+ value_type = _translate_type(type_decl.type_name)
+ value_constructor = '%s(hexValue=\'%s\')' % (value_type, value.value)
+ else:
+ value_type = _translate_type(type_decl.type_name)
+ value_constructor = '%s(%s)' % (value_type, value)
- def decl_simple_type(self, t):
+ return '%s = %s' % (assigned_value, value_constructor)
+
+ def defn_simple_type(self, class_name, t):
if t.constraint:
- return 'subtypeSpec = constraint.ValueRangeConstraint(%s, %s)' % (t.constraint.min_value, t.constraint.max_value)
- else:
- return 'pass'
+ return '%s.subtypeSpec = constraint.ValueRangeConstraint(%s, %s)' % (class_name, t.constraint.min_value, t.constraint.max_value)
- def expr_userdefined_type(self, t):
- return t.type_name + '()'
+ return None
- def decl_userdefined_type(self, t):
- return 'pass'
+ def defn_referenced_type(self, class_name, t):
+ return None
- def decl_constructed_type(self, t):
+ def defn_constructed_type(self, class_name, t):
fragment = self.writer.get_fragment()
- fragment.write_line('componentType = namedtype.NamedTypes(')
-
+ fragment.write_line('%s.componentType = namedtype.NamedTypes(' % class_name)
fragment.push_indent()
- fragment.write_block(self.expr_component_types(t.components))
+ fragment.write_block(self.inline_component_types(t.components))
fragment.pop_indent()
-
fragment.write_line(')')
return str(fragment)
- def expr_constructed_type(self, t):
+ def defn_tagged_type(self, class_name, t):
+ fragment = self.writer.get_fragment()
+
+ tag_type = 'tagImplicitly' if t.implicit else 'tagExplicitly'
+ base_type = _translate_type(t.type_decl.type_name)
+
+ fragment.write_line('%s.tagSet = %s.tagSet.%s(%s)' % (class_name, base_type, tag_type, self.build_tag_expr(t)))
+ nested_dfn = self.generate_defn(class_name, t.type_decl)
+ if nested_dfn:
+ fragment.write_line(nested_dfn)
+
+ return str(fragment)
+
+ def defn_value_list_type(self, class_name, t):
+ if t.named_values:
+ fragment = self.writer.get_fragment()
+ fragment.write_line('%s.namedValues = namedval.NamedValues(' % class_name)
+ fragment.push_indent()
+
+ named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
+ fragment.write_enumeration(named_values)
+
+ fragment.pop_indent()
+ fragment.write_line(')')
+ return str(fragment)
+
+ return None
+
+ def defn_bitstring_type(self, class_name, t):
+ if t.named_bits:
+ fragment = self.writer.get_fragment()
+ fragment.write_line('%s.namedValues = namedval.NamedValues(' % class_name)
+ fragment.push_indent()
+
+ named_bit_list = list(map(lambda v: '(\'%s\', %s)' % (v.identifier, v.value), t.named_bits))
+ fragment.write_enumeration(named_bit_list)
+
+ fragment.pop_indent()
+ fragment.write_line(')')
+ return str(fragment)
+
+ return None
+
+ def defn_sequenceof_type(self, class_name, t):
+ return '%s.componentType = %s' % (class_name, self.generate_expr(t.type_decl))
+
+ def defn_setof_type(self, class_name, t):
+ return '%s.componentType = %s' % (class_name, self.generate_expr(t.type_decl))
+
+ def inline_simple_type(self, t):
+ type_expr = _translate_type(t.type_name) + '()'
+ if t.constraint:
+ type_expr += '.subtype(subtypeSpec=constraint.ValueRangeConstraint(%s, %s))' % (t.constraint.min_value, t.constraint.max_value)
+
+ return type_expr
+
+ def inline_referenced_type(self, t):
+ return t.type_name + '()'
+
+ def inline_constructed_type(self, t):
fragment = self.writer.get_fragment()
class_name = _translate_type(t.type_name)
fragment.write_line('%s(componentType=namedtype.NamedTypes(' % class_name)
fragment.push_indent()
- fragment.write_block(self.expr_component_types(t.components))
+ fragment.write_block(self.inline_component_types(t.components))
fragment.pop_indent()
fragment.write_line('))')
return str(fragment)
- def expr_component_types(self, components):
+ def inline_component_types(self, components):
fragment = self.writer.get_fragment()
component_exprs = []
return str(fragment)
- def expr_tagged_type(self, t):
+ def inline_tagged_type(self, t):
tag_type = 'implicitTag' if t.implicit else 'explicitTag'
type_expr = self.generate_expr(t.type_decl)
type_expr += '.subtype(%s=%s)' % (tag_type, self.build_tag_expr(t))
return type_expr
- def decl_tagged_type(self, t):
- fragment = self.writer.get_fragment()
-
- tag_type = 'tagImplicitly' if t.implicit else 'tagExplicitly'
- base_type = _translate_type(t.type_decl.type_name)
- fragment.write_line('tagSet = %s.tagSet.%s(%s)' % (base_type, tag_type, self.build_tag_expr(t)))
- fragment.write_line(self.generate_decl(t.type_decl)) # possibly 'pass'. but that's OK in a decl
-
- return str(fragment)
-
def build_tag_expr(self, tag_def):
context = _translate_tag_class(tag_def.class_name)
return 'tag.Tag(%s, %s, %s)' % (context, tag_format, tag_def.class_number)
- def expr_component_type(self, t):
+ def inline_component_type(self, t):
if t.components_of_type:
# COMPONENTS OF works like a literal include, so just
# expand all components of the referenced type.
included_type_decl = self.sema_module.resolve_type_decl(t.components_of_type)
- included_content = self.expr_component_types(included_type_decl.components)
+ included_content = self.inline_component_types(included_type_decl.components)
- # Strip trailing newline from expr_component_types
+ # Strip trailing newline from inline_component_types
# to make the list line up
return included_content.strip()
else:
return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
- def expr_named_type(self, t):
+ def inline_named_type(self, t):
return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
- def decl_value_list_type(self, t):
- fragment = self.writer.get_fragment()
-
- if t.named_values:
- fragment.write_line('namedValues = namedval.NamedValues(')
- fragment.push_indent()
-
- named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
- fragment.write_enumeration(named_values)
-
- fragment.pop_indent()
- fragment.write_line(')')
- else:
- fragment.write_line('pass')
-
- return str(fragment)
-
- def expr_value_list_type(self, t):
+ def inline_value_list_type(self, t):
class_name = _translate_type(t.type_name)
if t.named_values:
named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
else:
return class_name + '()'
- def decl_bitstring_type(self, t):
- fragment = self.writer.get_fragment()
-
- if t.named_bits:
- fragment.write_line('namedValues = namedval.NamedValues(')
- fragment.push_indent()
-
- named_bit_list = list(map(lambda v: '(\'%s\', %s)' % (v.identifier, v.value), t.named_bits))
- fragment.write_enumeration(named_bit_list)
-
- fragment.pop_indent()
- fragment.write_line(')')
- else:
- fragment.write_line('pass')
-
- return str(fragment)
-
- def expr_sequenceof_type(self, t):
+ def inline_sequenceof_type(self, t):
return 'univ.SequenceOf(componentType=%s)' % self.generate_expr(t.type_decl)
- def decl_sequenceof_type(self, t):
- return 'componentType = %s' % self.generate_expr(t.type_decl)
-
- def expr_setof_type(self, t):
+ def inline_setof_type(self, t):
return 'univ.SetOf(componentType=%s)' % self.generate_expr(t.type_decl)
- def decl_setof_type(self, t):
- return 'componentType = %s' % self.generate_expr(t.type_decl)
-
- def decl_value_assignment(self, assignment):
- assigned_value, type_decl, value = assignment.value_name, assignment.type_decl, assignment.value
-
- if isinstance(value, ObjectIdentifierValue):
- value_constructor = self.build_object_identifier_value(value)
- elif isinstance(value, BinaryStringValue):
- value_type = _translate_type(type_decl.type_name)
- value_constructor = '%s(binValue=\'%s\')' % (value_type, value.value)
- elif isinstance(value, HexStringValue):
- value_type = _translate_type(type_decl.type_name)
- value_constructor = '%s(hexValue=\'%s\')' % (value_type, value.value)
- else:
- value_type = _translate_type(type_decl.type_name)
- value_constructor = '%s(%s)' % (value_type, value)
-
- return '%s = %s' % (assigned_value, value_constructor)
-
def build_object_identifier_value(self, t):
objid_components = []
return '_OID(%s)' % ', '.join(objid_components)
def generate_OID(self):
- self.writer.write_line('def _OID(*components):')
- self.writer.push_indent()
- self.writer.write_line('output = []')
- self.writer.write_line('for x in tuple(components):')
- self.writer.push_indent()
- self.writer.write_line('if isinstance(x, univ.ObjectIdentifier):')
- self.writer.push_indent()
- self.writer.write_line('output.extend(list(x))')
- self.writer.pop_indent()
- self.writer.write_line('else:')
- self.writer.push_indent()
- self.writer.write_line('output.append(int(x))')
- self.writer.pop_indent()
- self.writer.pop_indent()
- self.writer.write_blanks(1)
- self.writer.write_line('return univ.ObjectIdentifier(output)')
- self.writer.pop_indent()
-
- self.writer.pop_indent()
+ fragment = self.writer.get_fragment()
+
+ fragment.write_line('def _OID(*components):')
+ fragment.push_indent()
+ fragment.write_line('output = []')
+ fragment.write_line('for x in tuple(components):')
+ fragment.push_indent()
+ fragment.write_line('if isinstance(x, univ.ObjectIdentifier):')
+ fragment.push_indent()
+ fragment.write_line('output.extend(list(x))')
+ fragment.pop_indent()
+ fragment.write_line('else:')
+ fragment.push_indent()
+ fragment.write_line('output.append(int(x))')
+ fragment.pop_indent()
+ fragment.pop_indent()
+ fragment.write_blanks(1)
+ fragment.write_line('return univ.ObjectIdentifier(output)')
+ fragment.pop_indent()
+
+ fragment.pop_indent()
+
+ return str(fragment)
def generate_pyasn1(sema_module, out_stream):
""" Algorithm adapted from:
http://en.wikipedia.org/wiki/Topological_sorting.
+ Use this in code generators to sort assignments in dependency order, IFF
+ there are no circular dependencies.
+
Assumes assignments is an iterable of items with two methods:
- reference_name() -- returns the reference name of the assignment
- references() -- returns an iterable of reference names
upon which the assignment depends.
"""
- graph = dict((a.reference_name(), set(a.references())) for a in assignments)
+ graph = dict((a.reference_name(), a.references()) for a in assignments)
def has_predecessor(node):
- for predecessor in graph.keys():
- if node in graph[predecessor]:
+ for predecessors in graph.values():
+ if node in predecessors:
return True
return False
# Build a topological order of reference names
topological_order = []
- roots = [name for name in graph.keys() if not has_predecessor(name)]
+ roots = [name for name in graph.keys()
+ if not has_predecessor(name)]
while roots:
root = roots.pop()
# and collect all new roots (the nodes that
# were previously only referenced from n)
successors = graph.pop(root, set())
- roots.extend(successor for successor in successors if not has_predecessor(successor))
+ roots.extend(successor for successor in successors
+ if not has_predecessor(successor))
topological_order.insert(0, root)
raise Exception('Can\'t sort cyclic references: %s' % graph)
# Sort the actual assignments based on the topological order
- return sorted(assignments, key=lambda a: topological_order.index(a.reference_name()))
+ return sorted(assignments,
+ key=lambda a: topological_order.index(a.reference_name()))
+
+
+def dependency_sort(assignments):
+ """ We define a dependency sort as a Tarjan strongly-connected
+ components resolution. Tarjan's algorithm happens to topologically
+ sort as a by-product of finding strongly-connected components.
+
+ Use this in code generators to sort assignments in dependency order, if
+ there are circular dependencies. It is slower than ``topological_sort``.
+
+ In the sema model, each node depends on types mentioned in its
+ ``descendants``. The model is nominally a tree, except ``descendants``
+ can contain node references forming a cycle.
+
+ Returns a list of tuples, where each item represents a component
+ in the graph. Ideally they're one-tuples, but if the graph has cycles
+ items can have any number of elements constituting a cycle.
+
+ This is nice, because the output is in perfect dependency order,
+ except for the cycle components, where there is no order. They
+ can be detected on the basis of their plurality and handled
+ separately.
+ """
+ # Build reverse-lookup table from name -> node.
+ assignments_by_name = {a.reference_name(): a for a in assignments}
+
+ # Build the dependency graph.
+ graph = {}
+ for assignment in assignments:
+ references = assignment.references()
+ graph[assignment] = [assignments_by_name[r] for r in references
+ if r in assignments_by_name]
+
+ # Now let Tarjan do its work! Adapted from here:
+ # http://www.logarithmic.net/pfh-files/blog/01208083168/tarjan.py
+ index_counter = [0]
+ stack = []
+ lowlinks = {}
+ index = {}
+ result = []
+
+ def strongconnect(node):
+ # Set the depth index for this node to the smallest unused index
+ index[node] = index_counter[0]
+ lowlinks[node] = index_counter[0]
+ index_counter[0] += 1
+ stack.append(node)
+
+ # Consider successors of `node`
+ successors = graph.get(node, [])
+ for successor in successors:
+ if successor not in lowlinks:
+ # Successor has not yet been visited; recurse on it
+ strongconnect(successor)
+ lowlinks[node] = min(lowlinks[node], lowlinks[successor])
+ elif successor in stack:
+ # the successor is in the stack and hence in the current
+ # strongly connected component (SCC)
+ lowlinks[node] = min(lowlinks[node], index[successor])
+
+ # If `node` is a root node, pop the stack and generate an SCC
+ if lowlinks[node] == index[node]:
+ connected_component = []
+
+ while True:
+ successor = stack.pop()
+ connected_component.append(successor)
+ if successor == node: break
+
+ component = tuple(connected_component)
+ result.append(component)
+
+ for node in graph:
+ if node not in lowlinks:
+ strongconnect(node)
+
+ return result
# Registered object identifier names
Most notably, we build a dependency graph of all types and values in a module,
to allow code generators to build code in dependency order.
-All nodes that may be referenced (type and value assignments) must have a
-method called ``reference_name``.
-
-All nodes that may reference other types (e.g. assignments, component types)
-must have a method called ``references`` returning the names of all referenced
-nodes.
-
-Typically, if you have a ``reference_name``, you must also have a ``references``,
-but not the other way around.
+All nodes that somehow denote a referenced type or value, either definitions
+(type and value assignments) or references (referenced types, referenced values,
+etc) must have a method called ``reference_name``.
"""
class SemaNode(object):
+ """ Base class for all sema nodes. """
+
def children(self):
- raise NotImplementedError()
+ """ Return a list of all contained sema nodes.
+
+ This implementation finds all member variables of type
+ SemaNode. It also expands list members, to transparently
+ handle the case where a node holds a list of other
+ sema nodes.
+ """
+ # Collect all SemaNode members.
+ members = list(vars(self).values())
+ children = [m for m in members if isinstance(m, SemaNode)]
+
+ # Expand SemaNodes out of list members, but do not recurse
+ # through lists of lists.
+ list_members = [m for m in members if isinstance(m, list)]
+ for m in list_members:
+ children.extend(n for n in m if isinstance(n, SemaNode))
+ return children
-class Module(object):
+ def descendants(self):
+ """ Return a list of all recursively contained sema nodes.
+ """
+ descendants = []
+ for child in self.children():
+ descendants.append(child)
+ descendants.extend(child.descendants())
+
+ return descendants
+
+
+class Module(SemaNode):
def __init__(self, elements):
self._user_types = {}
"""
user_types = self.user_types()
- if isinstance(type_decl, UserDefinedType):
+ if isinstance(type_decl, ReferencedType):
return self.resolve_type_decl(user_types[type_decl.type_name])
else:
return type_decl
__repr__ = __str__
-class TypeAssignment(object):
+class Assignment(SemaNode):
+ def references(self):
+ """ Return a set of all reference names (both values and types) that
+ this assignment depends on.
+
+ This happens to coincide with all contained SemaNodes as exposed by
+ ``descendants`` with a ``reference_name`` method.
+ """
+ return set(d.reference_name() for d in self.descendants()
+ if hasattr(d, 'reference_name'))
+
+
+class TypeAssignment(Assignment):
def __init__(self, elements):
assert(len(elements) == 3)
type_name, _, type_decl = elements
def reference_name(self):
return self.type_name
- def references(self):
- refs = self.type_decl.references()
-
- # Remove any self-references
- refs = [ref for ref in refs if ref != self.type_name]
-
- return refs
-
def __str__(self):
return '%s ::= %s' % (self.type_name, self.type_decl)
__repr__ = __str__
-class ValueAssignment(object):
+class ValueAssignment(Assignment):
def __init__(self, elements):
value_name, type_name, _, value = elements
- self.value_name = ValueReference(value_name.elements) # First token is always a valuereference
+ self.value_name = value_name
self.type_decl = _create_sema_node(type_name)
-
- if isinstance(value, parser.AnnotatedToken):
- self.value = _create_sema_node(value)
- else:
- self.value = value
+ self.value = _maybe_create_sema_node(value)
def reference_name(self):
- return self.value_name.reference_name()
-
- def references(self):
- refs = [self.type_decl.reference_name()]
- if isinstance(self.value, ValueReference):
- refs.append(self.value.reference_name())
- elif isinstance(self.value, ObjectIdentifierValue):
- refs.extend(self.value.references())
- else:
- # It's a literal, and they don't play into declaration order.
- pass
-
- # Remove any self-references
- refs = [ref for ref in refs if ref != self.value_name]
-
- return refs
+ return self.value_name
def __str__(self):
return '%s %s ::= %s' % (self.value_name, self.type_decl, self.value)
__repr__ = __str__
-class ValueReference(object):
- def __init__(self, elements):
- self.name = elements[0]
-
- def reference_name(self):
- return self.name
-
- def references(self):
- return []
-
- def __str__(self):
- return self.name
-
- __repr__ = __str__
-
-
-class ConstructedType(object):
+class ConstructedType(SemaNode):
""" Base type for SEQUENCE, SET and CHOICE. """
def __init__(self, elements):
type_name, component_tokens = elements
self.type_name = type_name
self.components = [_create_sema_node(token) for token in component_tokens]
- def references(self):
- references = []
- for component in self.components:
- references.extend(component.references())
- return references
-
def __str__(self):
component_type_list = ', '.join(map(str, self.components))
return '%s { %s }' % (self.type_name, component_type_list)
super(SetType, self).__init__(elements)
-class CollectionType(object):
+class CollectionType(SemaNode):
""" Base type for SET OF and SEQUENCE OF. """
def __init__(self, kind, elements):
self.kind = kind
else:
assert False, 'Unknown form of %s OF declaration: %s' % (self.kind, elements)
- def references(self):
- return self.type_decl.references()
-
def __str__(self):
if self.size_constraint:
return '%s %s OF %s' % (self.kind, self.size_constraint, self.type_decl)
super(SetOfType, self).__init__('SET', elements)
-class TaggedType(object):
+class TaggedType(SemaNode):
def __init__(self, elements):
self.class_name = None
self.class_number = None
def type_name(self):
return self.type_decl.type_name
- def reference_name(self):
- return self.type_decl.type_name
-
- def references(self):
- return self.type_decl.references()
-
def __str__(self):
class_spec = []
if self.class_name:
__repr__ = __str__
-class SimpleType(object):
+class SimpleType(SemaNode):
def __init__(self, elements):
self.constraint = None
self.type_name = elements[0]
if len(elements) > 1 and elements[1].ty == 'Constraint':
self.constraint = Constraint(elements[1].elements)
- def reference_name(self):
- return self.type_name
-
- def references(self):
- refs = [self.type_name]
- if self.constraint:
- refs.extend(self.constraint.references())
-
- return refs
-
def __str__(self):
if self.constraint is None:
return self.type_name
__repr__ = __str__
-class UserDefinedType(object):
+class ReferencedType(SemaNode):
def __init__(self, elements):
self.type_name = elements[0]
def reference_name(self):
return self.type_name
- def references(self):
- return [self.type_name]
-
def __str__(self):
return self.type_name
__repr__ = __str__
-class Constraint(object):
+class ReferencedValue(SemaNode):
def __init__(self, elements):
- min_value, max_value = elements
+ self.name = elements[0]
- self.min_value = _maybe_create_sema_node(min_value)
- self.max_value = _maybe_create_sema_node(max_value)
+ def reference_name(self):
+ return self.name
+
+ def __str__(self):
+ return self.name
+
+ __repr__ = __str__
- def references(self):
- refs = []
- if isinstance(self.min_value, ValueReference):
- refs.append(self.min_value.reference_name())
- if isinstance(self.max_value, ValueReference):
- refs.append(self.max_value.reference_name())
+class Constraint(SemaNode):
+ def __init__(self, elements):
+ min_value, max_value = elements
- return refs
+ self.min_value = _maybe_create_sema_node(min_value)
+ self.max_value = _maybe_create_sema_node(max_value)
def __str__(self):
return '(%s..%s)' % (self.min_value, self.max_value)
__repr__ = __str__
-class ComponentType(object):
+class ComponentType(SemaNode):
def __init__(self, elements):
self.identifier = None
self.type_decl = None
else:
assert False, 'Unknown component type %s' % first_token
- def references(self):
- if self.components_of_type:
- return [self.components_of_type.type_name]
-
- refs = [self.type_decl.type_name]
- refs.extend(self.type_decl.references())
-
- if self.default_value is not None:
- refs.append(str(self.default_value))
-
- return refs
-
def __str__(self):
if self.components_of_type:
return 'COMPONENTS OF %s' % self.components_of_type
__repr__ = __str__
-class NamedType(object):
+class NamedType(SemaNode):
def __init__(self, elements):
first_token = elements[0]
if first_token.ty == 'Type':
self.type_decl = _create_sema_node(type_token)
- def references(self):
- return self.type_decl.references()
-
def __str__(self):
return '%s %s' % (self.identifier, self.type_decl)
__repr__ = __str__
-class ValueListType(object):
+class ValueListType(SemaNode):
def __init__(self, elements):
self.type_name = elements[0]
if len(elements) > 1:
else:
self.named_values = None
- def references(self):
- # TODO: Value references
- return []
-
def __str__(self):
if self.named_values:
named_value_list = ', '.join(map(str, self.named_values))
__repr__ = __str__
-class BitStringType(object):
+class BitStringType(SemaNode):
def __init__(self, elements):
self.type_name = elements[0]
if len(elements) > 1:
else:
self.named_bits = None
- def references(self):
- # TODO: Value references
- return []
-
def __str__(self):
if self.named_bits:
named_bit_list = ', '.join(map(str, self.named_bits))
__repr__ = __str__
-class NamedValue(object):
+class NamedValue(SemaNode):
def __init__(self, elements):
if len(elements) == 1:
identifier_token = elements[0]
self.identifier = identifier_token.elements[0]
self.value = value_token.elements[0]
- def references(self):
- # TODO: This appears to never be called. Investigate.
- return []
-
def __str__(self):
return '%s (%s)' % (self.identifier, self.value)
__repr__ = __str__
-class ExtensionMarker(object):
+class ExtensionMarker(SemaNode):
def __init__(self, elements):
pass
- def references(self):
- # TODO: This appears to never be called. Investigate.
- return []
-
def __str__(self):
return '...'
__repr__ = __str__
-class NameForm(object):
+class NameForm(SemaNode):
def __init__(self, elements):
self.name = elements[0]
- def references(self):
- return [self.name]
+ def reference_name(self):
+ return self.name
def __str__(self):
return self.name
__repr__ = __str__
-class NumberForm(object):
+class NumberForm(SemaNode):
def __init__(self, elements):
self.value = elements[0]
- def references(self):
- return []
-
def __str__(self):
return str(self.value)
__repr__ = __str__
-class NameAndNumberForm(object):
+class NameAndNumberForm(SemaNode):
def __init__(self, elements):
- # The first element is a NameForm containing only the
- # name, so unpack it into a string.
- self.name = elements[0].elements[0]
+ self.name = _create_sema_node(elements[0])
self.number = _create_sema_node(elements[1])
- def references(self):
- return [str(self.name), str(self.number)]
-
def __str__(self):
return '%s(%s)' % (self.name, self.number)
__repr__ = __str__
-class ObjectIdentifierValue(object):
+class ObjectIdentifierValue(SemaNode):
def __init__(self, elements):
self.components = [_create_sema_node(c) for c in elements]
- def references(self):
- refs = []
- for component in self.components:
- if isinstance(component, str):
- refs.append(component)
- else:
- refs.extend(component.references())
-
- return refs
-
def __str__(self):
return '{' + ' '.join(str(x) for x in self.components) + '}'
__repr__ = __str__
-class BinaryStringValue(object):
+class BinaryStringValue(SemaNode):
def __init__(self, elements):
self.value = elements[0]
- def references(self):
- return []
-
def __str__(self):
return '\'%s\'B' % self.value
__repr__ = __str__
-class HexStringValue(object):
+class HexStringValue(SemaNode):
def __init__(self, elements):
self.value = elements[0]
- def references(self):
- return []
-
def __str__(self):
return '\'%s\'H' % self.value
return TypeAssignment(token.elements)
elif token.ty == 'ValueAssignment':
return ValueAssignment(token.elements)
- elif token.ty == 'ValueReference':
- return ValueReference(token.elements)
elif token.ty == 'ComponentType':
return ComponentType(token.elements)
elif token.ty == 'NamedType':
elif token.ty == 'SimpleType':
return SimpleType(token.elements)
elif token.ty == 'ReferencedType':
- return UserDefinedType(token.elements)
+ return ReferencedType(token.elements)
+ elif token.ty == 'ReferencedValue':
+ return ReferencedValue(token.elements)
elif token.ty == 'TaggedType':
return TaggedType(token.elements)
elif token.ty == 'SequenceType':