Fix issue 11: respect module tag defaults.
authorKim Grasman <kim.grasman@gmail.com>
Sun, 1 Feb 2015 19:50:46 +0000 (20:50 +0100)
committerKim Grasman <kim.grasman@gmail.com>
Sun, 1 Feb 2015 19:50:46 +0000 (20:50 +0100)
Simplify parser (leave None tokens for missing tag implicity.)
Record module tag defaults in sema.
Use module tag defaults if tag implicity is missing.

asn1ate/parser.py
asn1ate/pyasn1gen.py
asn1ate/sema.py
testdata/module_tag_default.asn [new file with mode: 0644]

index 584a989..ae6775d 100644 (file)
@@ -220,10 +220,10 @@ def _build_asn1_grammar():
     class_ = UNIVERSAL | APPLICATION | PRIVATE
     class_number = Unique(number) # todo: consider defined values from 30.1
     tag = Suppress('[') + Optional(class_) + class_number + Suppress(']')
-    tag_default = Optional(EXPLICIT_TAGS | IMPLICIT_TAGS | AUTOMATIC_TAGS)
+    tag_default = EXPLICIT_TAGS | IMPLICIT_TAGS | AUTOMATIC_TAGS
 
     # extensions
-    extension_default = Optional(EXTENSIBILITY_IMPLIED)
+    extension_default = Unique(EXTENSIBILITY_IMPLIED)
 
     # values
 
@@ -253,7 +253,7 @@ def _build_asn1_grammar():
     component_type_components_of = Suppress(COMPONENTS_OF) + type_
     component_type = component_type_components_of | component_type_optional | component_type_default | named_type
 
-    tagged_type = tag + Optional(IMPLICIT | EXPLICIT) + type_
+    tagged_type = tag + Optional(IMPLICIT | EXPLICIT, default=None) + type_
 
     named_number_value = Suppress('(') + signed_number + Suppress(')')
     named_number = identifier + named_number_value
@@ -322,9 +322,9 @@ def _build_asn1_grammar():
     imports = Optional(Suppress(IMPORTS) + symbols_imported + Suppress(';'))
 
     module_body = (exports + imports + assignment_list)
-    module_defaults = Suppress(tag_default + extension_default)  # we don't want these in the AST
     module_identifier = module_reference + definitive_identifier
-    module_definition = module_identifier + DEFINITIONS + module_defaults + '::=' + BEGIN + module_body + END
+    module_definition = module_identifier + Suppress(DEFINITIONS) + Optional(tag_default, default=None) + \
+                        Optional(extension_default, default=None) + Suppress('::=') + Suppress(BEGIN) + module_body + Suppress(END)
 
     module_definition.ignore(comment)
 
index 2f23662..9ae4f72 100644 (file)
@@ -208,10 +208,17 @@ class Pyasn1Backend(object):
     def defn_tagged_type(self, class_name, t):
         fragment = self.writer.get_fragment()
 
-        tag_type = 'tagImplicitly' if t.implicit else 'tagExplicitly'
+        implicity = self.sema_module.resolve_tag_implicity(t.implicity, t.type_decl)
+        if implicity == TagImplicity.IMPLICIT:
+            tag_implicity = 'tagImplicitly'
+        elif implicity == TagImplicity.EXPLICIT:
+            tag_implicity = 'tagExplicitly'
+        else:
+            assert False, "Unexpected implicity: %s" % implicity
+
         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)))
+        fragment.write_line('%s.tagSet = %s.tagSet.%s(%s)' % (class_name, base_type, tag_implicity, self.build_tag_expr(t)))
         nested_dfn = self.generate_defn(class_name, t.type_decl)
         if nested_dfn:
             fragment.write_line(nested_dfn)
@@ -302,9 +309,16 @@ class Pyasn1Backend(object):
         return str(fragment)
 
     def inline_tagged_type(self, t):
-        tag_type = 'implicitTag' if t.implicit else 'explicitTag'
+        implicity = self.sema_module.resolve_tag_implicity(t.implicity, t.type_decl)
+        if implicity == TagImplicity.IMPLICIT:
+            tag_implicity = 'implicitTag'
+        elif implicity == TagImplicity.EXPLICIT:
+            tag_implicity = 'explicitTag'
+        else:
+            assert False, "Unexpected implicity: %s" % implicity
+
         type_expr = self.generate_expr(t.type_decl)
-        type_expr += '.subtype(%s=%s)' % (tag_type, self.build_tag_expr(t))
+        type_expr += '.subtype(%s=%s)' % (tag_implicity, self.build_tag_expr(t))
 
         return type_expr
 
index c3e4278..d313e08 100644 (file)
@@ -181,6 +181,14 @@ REGISTERED_OID_NAMES = {
     'registration-procedures': 17
 }
 
+
+class TagImplicity(object):
+    """ Tag implicit/explicit enumeration """
+    IMPLICIT = 0
+    EXPLICIT = 1
+    AUTOMATIC = 2
+
+
 """
 Sema nodes
 
@@ -232,13 +240,26 @@ class SemaNode(object):
 
 
 class Module(SemaNode):
+
     def __init__(self, elements):
         self._user_types = {}
 
-        module_reference, _, _, _, _, module_body, _ = elements
+        module_reference, definitive_identifier, tag_default, extension_default, module_body = elements
         exports, imports, assignments = module_body.elements
 
         self.name = module_reference.elements[0]
+
+        if tag_default == 'IMPLICIT TAGS':
+            self.tag_default = TagImplicity.IMPLICIT
+        elif tag_default == 'EXPLICIT TAGS':
+            self.tag_default = TagImplicity.EXPLICIT
+        elif tag_default == 'AUTOMATIC TAGS':
+            self.tag_default = TagImplicity.AUTOMATIC
+        else:
+            assert tag_default is None, 'Unexpected tag default: %s' % tag_default
+            # Tag default was not specified, default to explicit
+            self.tag_default = TagImplicity.EXPLICIT
+
         self.assignments = [_create_sema_node(token) for token in assignments.elements]
 
     def user_types(self):
@@ -251,8 +272,8 @@ class Module(SemaNode):
         return self._user_types
 
     def resolve_type_decl(self, type_decl):
-        """ Recursively resolve user-defined types to their
-        built-in declaration.
+        """ Recursively resolve user-defined types to their built-in
+        declaration.
         """
         user_types = self.user_types()
 
@@ -275,6 +296,26 @@ class Module(SemaNode):
 
         return None
 
+    def resolve_tag_implicity(self, tag_implicity, tagged_type_decl):
+        """ The implicity for a tag depends on three things:
+        * Any written implicity on the tag decl itself (``tag_implicity``)
+        * The module's tag default (kept in ``self.tag_default``)
+        * Details of the tagged type according to X.680, 30.6c (not implemented,
+          but should be doable based on ``tagged_type_decl``)
+        """
+        if tag_implicity is not None:
+            return tag_implicity
+
+        # No tag implicity specified, use module-default
+        if self.tag_default is None:
+            # Explicit is default if nothing
+            return TagImplicity.EXPLICIT
+        elif self.tag_default == TagImplicity.AUTOMATIC:
+            # TODO: Expand according to rules for automatic tagging.
+            return TagImplicity.IMPLICIT
+
+        return self.tag_default
+
     def __str__(self):
         return '%s DEFINITIONS ::=\n' % self.name \
             + 'BEGIN\n' \
@@ -386,17 +427,10 @@ class SetOfType(CollectionType):
 
 class TaggedType(SemaNode):
     def __init__(self, elements):
+        tag_token, implicity, type_token = elements
+
         self.class_name = None
         self.class_number = None
-        self.implicit = False
-
-        tag_token = elements[0]
-        if type(elements[1]) is parser.AnnotatedToken:
-            type_token = elements[1]
-        else:
-            self.implicit = elements[1] == 'IMPLICIT'
-            type_token = elements[2]
-
         for tag_element in tag_token.elements:
             if tag_element.ty == 'TagClassNumber':
                 self.class_number = tag_element.elements[0]
@@ -405,6 +439,13 @@ class TaggedType(SemaNode):
             else:
                 assert False, 'Unknown tag element: %s' % tag_element
 
+        if implicity == 'IMPLICIT':
+            self.implicity = TagImplicity.IMPLICIT
+        elif implicity == 'EXPLICIT':
+            self.implicity = TagImplicity.EXPLICIT
+        elif implicity is None:
+            self.implicity = None  # Module-default or automatic
+
         self.type_decl = _create_sema_node(type_token)
 
     @property
@@ -418,8 +459,12 @@ class TaggedType(SemaNode):
         class_spec.append(self.class_number)
 
         result = '[%s] ' % ' '.join(class_spec)
-        if self.implicit:
+        if self.implicity == TagImplicity.IMPLICIT:
             result += 'IMPLICIT '
+        elif self.implicity == TagImplicity.EXPLICIT:
+            result += 'EXPLICIT '
+        else:
+            pass  # module-default
 
         result += str(self.type_decl)
 
diff --git a/testdata/module_tag_default.asn b/testdata/module_tag_default.asn
new file mode 100644 (file)
index 0000000..16203d1
--- /dev/null
@@ -0,0 +1,40 @@
+ExplicitModule DEFINITIONS EXPLICIT TAGS ::=
+BEGIN
+
+Sequence ::= SEQUENCE
+{
+    field1 [0] INTEGER,
+    field2 [1] BOOLEAN,
+    field3 [2] EXPLICIT INTEGER,
+    field4 [3] IMPLICIT BOOLEAN
+}
+
+END
+
+ImplicitModule DEFINITIONS IMPLICIT TAGS ::=
+BEGIN
+
+Sequence ::= SEQUENCE
+{
+    field1 [0] INTEGER,
+    field2 [1] BOOLEAN,
+    field3 [2] EXPLICIT INTEGER,
+    field4 [3] IMPLICIT BOOLEAN
+}
+
+END
+
+
+-- AUTOMATIC TAGS is considered the same as IMPLICIT for now.
+AutomaticModule DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+
+Sequence ::= SEQUENCE
+{
+    field1 [0] INTEGER,
+    field2 [1] BOOLEAN,
+    field3 [2] EXPLICIT INTEGER,
+    field4 [3] IMPLICIT BOOLEAN
+}
+
+END