First step towards better constraints handling.
authorKim Grasman <kim.grasman@gmail.com>
Sun, 25 Aug 2013 18:05:43 +0000 (20:05 +0200)
committerKim Grasman <kim.grasman@gmail.com>
Sun, 25 Aug 2013 18:05:43 +0000 (20:05 +0200)
Breaking change: Now adds a Type object to the sema model, and constraints
live on Type instead of SimpleType.

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

index 8bbb7ae..3ece685 100644 (file)
@@ -209,10 +209,6 @@ def _build_asn1_grammar():
     # extensions
     extension_default = EXTENSIBILITY_IMPLIED | empty
 
-    # types
-    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
-
     # Forward-declare these, they can only be fully defined once
     # we have all types defined. There are some circular dependencies.
     named_type = Forward()
@@ -263,12 +259,17 @@ def _build_asn1_grammar():
     characterstring_type = restricted_characterstring_type | unrestricted_characterstring_type
 
     # todo: consider other builtins from 16.2
-    simple_type = (boolean_type | null_type | octetstring_type | characterstring_type | real_type | plain_integer_type | object_identifier_type) + Optional(constraint)
-    constructed_type = choice_type | sequence_type | set_type
+    defined_type = Unique(typereference)  # todo: consider other defined types from 13.1
+
+    # these productions are used for custom parse actions,
+    # because they typically generate similar code.
+    simple_type = (boolean_type | null_type | octetstring_type | characterstring_type | real_type | plain_integer_type | object_identifier_type) # + Optional(constraint)
     value_list_type = restricted_integer_type | enumerated_type
-    builtin_type = tagged_type | simple_type | constructed_type | sequenceof_type | setof_type | value_list_type | bitstring_type
 
-    type_ << (builtin_type | referenced_type)
+    builtin_type = tagged_type | simple_type | choice_type | sequence_type | set_type | sequenceof_type | setof_type | value_list_type | bitstring_type
+    referenced_type = Unique(defined_type)  # todo: consider other ref:d types from 16.3
+
+    type_ << ((builtin_type | referenced_type) + Optional(constraint))
 
     # BUG: identifier should not be Optional here,
     # but our ASN.1 interpreter supports unnamed members,
index 897609e..df8c24f 100644 (file)
@@ -71,7 +71,8 @@ class Pyasn1Backend(object):
             SequenceOfType: self.decl_sequenceof_type,
             SetOfType: self.decl_setof_type,
             TypeAssignment: self.decl_type_assignment,
-            ValueAssignment: self.decl_value_assignment
+            ValueAssignment: self.decl_value_assignment,
+            Type: self.decl_type,
         }
 
         self.expr_generators = {
@@ -86,6 +87,7 @@ class Pyasn1Backend(object):
             ChoiceType: self.expr_constructed_type,
             SequenceType: self.expr_constructed_type,
             SetType: self.expr_constructed_type,
+            Type: self.expr_type,
         }
 
     def generate_code(self):
@@ -119,29 +121,36 @@ class Pyasn1Backend(object):
         fragment.write_line('class %s(%s):' % (assigned_type, base_type))
 
         fragment.push_indent()
-        fragment.write_block(self.generate_decl(type_decl))
+        fragment.write_block(self.generate_decl(type_decl) or 'pass')
         fragment.pop_indent()
 
         return str(fragment)
 
-    def expr_simple_type(self, t):
-        type_expr = _translate_type(t.type_name) + '()'
+    def expr_type(self, t):
+        type_expr = self.generate_expr(t.type_decl)
         if t.constraint:
             type_expr += '.subtype(subtypeSpec=constraint.ValueRangeConstraint(%s, %s))' % (t.constraint.min_value, t.constraint.max_value)
 
         return type_expr
 
-    def decl_simple_type(self, t):
+    def decl_type(self, t):
+        type_decl = self.generate_decl(t.type_decl)
         if t.constraint:
-            return 'subtypeSpec = constraint.ValueRangeConstraint(%s, %s)' % (t.constraint.min_value, t.constraint.max_value)
-        else:
-            return 'pass'
+            type_decl += 'subtypeSpec = constraint.ValueRangeConstraint(%s, %s)' % (t.constraint.min_value, t.constraint.max_value)
+
+        return type_decl
+
+    def expr_simple_type(self, t):
+        return _translate_type(t.type_name) + '()'
+
+    def decl_simple_type(self, t):
+        return ''
 
     def expr_userdefined_type(self, t):
         return t.type_name + '()'
 
     def decl_userdefined_type(self, t):
-        return 'pass'
+        return ''
 
     def decl_constructed_type(self, t):
         fragment = self.writer.get_fragment()
@@ -196,7 +205,7 @@ class Pyasn1Backend(object):
         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
+        fragment.write_line(self.generate_decl(t.type_decl))
 
         return str(fragment)
 
@@ -247,8 +256,6 @@ class Pyasn1Backend(object):
 
             fragment.pop_indent()
             fragment.write_line(')')
-        else:
-            fragment.write_line('pass')
 
         return str(fragment)
 
@@ -272,8 +279,6 @@ class Pyasn1Backend(object):
 
             fragment.pop_indent()
             fragment.write_line(')')
-        else:
-            fragment.write_line('pass')
 
         return str(fragment)
 
@@ -411,7 +416,7 @@ def main(args):
         print('WARNING: More than one module generated to the same stream.', file=sys.stderr)
 
     for module in modules:
-        print(pygen.auto_generated_header())
+        print(pygen.auto_generated_header(args[0]))
         generate_pyasn1(module, sys.stdout)
 
     return 0
index 766a0e0..926aff5 100644 (file)
@@ -153,6 +153,11 @@ class Module(object):
         """
         user_types = self.user_types()
 
+        # Drill through Type, it acts as a container
+        # for type + metadata like constraints.
+        if isinstance(type_decl, Type):
+            type_decl = type_decl.type_decl
+
         if isinstance(type_decl, UserDefinedType):
             return self.resolve_type_decl(user_types[type_decl.type_name])
         else:
@@ -243,6 +248,37 @@ class ValueReference(object):
     __repr__ = __str__
 
 
+class Type(object):
+    def __init__(self, elements):
+        self.type_decl = _create_sema_node(elements[0])
+        if len(elements) == 2:
+            assert elements[1].ty == 'Constraint'
+            self.constraint = Constraint(elements[1].elements)
+        else:
+            self.constraint = None
+
+    @property
+    def type_name(self):
+        return self.type_decl.type_name
+
+    def reference_name(self):
+        return self.type_decl.type_name
+
+    def references(self):
+        if self.constraint:
+            return self.constraint.references()
+
+        return []
+
+    def __str__(self):
+        if self.constraint:
+            return '%s %s' % (self.type_decl, self.constraint)
+        else:
+            return str(self.type_decl)
+
+    __repr__ = __str__
+
+
 class ConstructedType(object):
     """ Base type for SEQUENCE, SET and CHOICE. """
     def __init__(self, elements):
@@ -677,9 +713,7 @@ def _create_sema_node(token):
     elif token.ty == 'NamedValue':
         return NamedValue(token.elements)
     elif token.ty == 'Type':
-        # Type tokens have a more specific type category
-        # embedded as their first element
-        return _create_sema_node(token.elements[0])
+        return Type(token.elements)
     elif token.ty == 'SimpleType':
         return SimpleType(token.elements)
     elif token.ty == 'ReferencedType':
diff --git a/testdata/constraints.asn b/testdata/constraints.asn
new file mode 100644 (file)
index 0000000..6e96f06
--- /dev/null
@@ -0,0 +1,21 @@
+TEST DEFINITIONS ::=
+BEGIN
+  UnsignedInt ::= INTEGER (0..4294967295)
+
+  intMax INTEGER ::= 4294967295
+  UnsignedIntAgain ::= INTEGER (0..intMax)
+
+--  PkcsTricky ::= INTEGER {v3(3)}(v3,...)
+
+  Seq ::= SEQUENCE {
+    valueConstrained INTEGER (0..4294967295),
+    taggedValueConstrained [1] INTEGER (0..4294967295),
+    definedValueConstrained INTEGER (0..intMax)
+--    inlineBracedListAndConstraint INTEGER {v3(3)}(v3,...)
+  }
+
+  ConstrainedList ::= SEQUENCE OF INTEGER (1..64)
+
+  Bob ::= UnsignedInt (10..20)
+
+END