Support extension markers in ENUMERATED.
[asn2quickder] / asn1ate / parser.py
index 8bbb7ae..cef74d0 100644 (file)
 
 import re
 from copy import copy
-from pyparsing import Keyword, Literal, Word, OneOrMore, Combine, Regex, Forward, Optional, Group, Suppress, delimitedList, cStyleComment, nums, alphanums, empty, srange
+from pyparsing import Keyword, Literal, Word, OneOrMore, ZeroOrMore, Combine, Regex, Forward, Optional, Group, Suppress, delimitedList, cStyleComment, nums, alphanums, empty, srange, dblQuotedString
 
 
 __all__ = ['parse_asn1', 'AnnotatedToken']
 
 
-def parse_asn1(asn1_payload):
-    """ Parse a string containing an ASN.1 module definition
-    and return a syntax tree in the form of a list of
+def parse_asn1(asn1_definition):
+    """ Parse a string containing one or more ASN.1 module definitions.
+    Returns a list of module syntax trees represented as nested lists of
     AnnotatedToken objects.
     """
     grammar = _build_asn1_grammar()
-    parse_result = grammar.parseString(asn1_payload)
+    parse_result = grammar.parseString(asn1_definition)
     parse_tree = parse_result.asList()
     return parse_tree
 
@@ -118,6 +118,7 @@ def _build_asn1_grammar():
     SIZE = Keyword('SIZE')
     OF = Keyword('OF')
     IMPORTS = Keyword('IMPORTS')
+    EXPORTS = Keyword('EXPORTS')
     FROM = Keyword('FROM')
 
     # Built-in types
@@ -149,6 +150,11 @@ def _build_asn1_grammar():
     VideotexString = Keyword('VideotexString')
     VisibleString = Keyword('VisibleString')
 
+    # Useful types
+    GeneralizedTime = Keyword('GeneralizedTime')
+    UTCTime = Keyword('UTCTime')
+    ObjectDescriptor = Keyword('ObjectDescriptor')
+
     # Literals
     number = Word(nums)
     signed_number = Combine(Optional('-') + number)  # todo: consider defined values from 18.1
@@ -177,8 +183,9 @@ def _build_asn1_grammar():
     bitstring_value = bstring | hstring     # todo: consider more forms from 21.9
     integer_value = signed_number
     null_value = NULL
+    cstring_value = dblQuotedString
 
-    builtin_value = boolean_value | bitstring_value | real_value | integer_value | null_value
+    builtin_value = boolean_value | bitstring_value | real_value | integer_value | null_value | cstring_value
     defined_value = valuereference # todo: more options from 13.1
 
     # object identifier value
@@ -244,7 +251,7 @@ def _build_asn1_grammar():
     sequenceof_type = Suppress(SEQUENCE) + Optional(size_constraint) + Suppress(OF) + (type_ | named_type)
     setof_type = Suppress(SET) + Optional(size_constraint) + Suppress(OF) + (type_ | named_type)
     choice_type = CHOICE + braced_list(named_type | extension_marker)
-    enumerated_type = ENUMERATED + braced_list(enumeration)
+    enumerated_type = ENUMERATED + braced_list(enumeration | extension_marker)
     bitstring_type = BIT_STRING + braced_list(named_number)
     plain_integer_type = INTEGER
     restricted_integer_type = INTEGER + braced_list(named_number)
@@ -261,40 +268,39 @@ def _build_asn1_grammar():
                                       T61String | UniversalString | \
                                       UTF8String | VideotexString | VisibleString
     characterstring_type = restricted_characterstring_type | unrestricted_characterstring_type
+    useful_type = GeneralizedTime | UTCTime | ObjectDescriptor
 
     # 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)
+    simple_type = (boolean_type | null_type | octetstring_type | characterstring_type | real_type | plain_integer_type | object_identifier_type | useful_type) + Optional(constraint)
     constructed_type = choice_type | sequence_type | set_type
     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
+    builtin_type = value_list_type | tagged_type | simple_type | constructed_type | sequenceof_type | setof_type | bitstring_type
 
     type_ << (builtin_type | referenced_type)
 
-    # BUG: identifier should not be Optional here,
-    # but our ASN.1 interpreter supports unnamed members,
+    # EXT: identifier should not be Optional here, but
+    # our other ASN.1 code generator supports unnamed members,
     # and we use them.
     named_type << (Optional(identifier) + type_)
 
-    # BUG: Trailing semi-colon is not allowed by standard grammar, but our ASN.1 interpreter accepts it
-    # and we happen to use it.
-    type_assignment = typereference + '::=' + type_ + Suppress(Optional(';'))
+    type_assignment = typereference + '::=' + type_
     value_assignment = valuereference + type_ + '::=' + value
 
     assignment = type_assignment | value_assignment
-    assignment_list = OneOrMore(assignment)
+    assignment_list = ZeroOrMore(assignment)
 
     assigned_identifier = Optional(object_identifier_value | defined_value)
     global_module_reference = module_reference + assigned_identifier
 
     symbol = Unique(reference)  # TODO: parameterized reference?
     symbol_list = Group(delimitedList(symbol))
-
     symbols_from_module = symbol_list + Suppress(FROM) + global_module_reference
     symbols_from_module_list = OneOrMore(symbols_from_module)
     symbols_imported = Optional(symbols_from_module_list)
+    exports = Optional(Suppress(EXPORTS) + symbol_list + Suppress(';'))
     imports = Optional(Suppress(IMPORTS) + symbols_imported + Suppress(';'))
 
-    module_body = (imports + assignment_list) | empty
+    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
@@ -341,9 +347,11 @@ def _build_asn1_grammar():
     definitive_number_form.setParseAction(annotate('DefinitiveNumberForm'))
     definitive_name_and_number_form.setParseAction(annotate('DefinitiveNameAndNumberForm'))
     imports.setParseAction(annotate('Imports'))
+    exports.setParseAction(annotate('Exports'))
     assignment_list.setParseAction(annotate('AssignmentList'))
 
-    return module_definition
+    start = ZeroOrMore(module_definition)
+    return start
 
 
 def Unique(token):