Support extension markers in ENUMERATED.
[asn2quickder] / asn1ate / parser.py
index b87ae45..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, dblQuotedString
+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
@@ -250,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)
@@ -282,27 +283,24 @@ def _build_asn1_grammar():
     # and we use them.
     named_type << (Optional(identifier) + type_)
 
-    # EXT: Trailing semi-colon is not allowed by standard
-    # grammar, but our other ASN.1 code generator 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
@@ -349,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):