summaryrefslogtreecommitdiff
path: root/python_modules/spice_parser.py
diff options
context:
space:
mode:
Diffstat (limited to 'python_modules/spice_parser.py')
-rw-r--r--python_modules/spice_parser.py162
1 files changed, 162 insertions, 0 deletions
diff --git a/python_modules/spice_parser.py b/python_modules/spice_parser.py
new file mode 100644
index 0000000..97af8b2
--- /dev/null
+++ b/python_modules/spice_parser.py
@@ -0,0 +1,162 @@
+import six
+
+try:
+ from pyparsing import Literal, CaselessLiteral, Word, OneOrMore, ZeroOrMore, \
+ Forward, delimitedList, Group, Optional, Combine, alphas, nums, restOfLine, cStyleComment, \
+ alphanums, ParseException, ParseResults, Keyword, StringEnd, replaceWith
+except ImportError:
+ six.print_("Module pyparsing not found.")
+ exit(1)
+
+
+from . import ptypes
+import sys
+
+cvtInt = lambda toks: int(toks[0])
+
+def parseVariableDef(toks):
+ t = toks[0][0]
+ pointer = toks[0][1]
+ name = toks[0][2]
+ array_size = toks[0][3]
+ attributes = toks[0][4]
+
+ if array_size != None:
+ t = ptypes.ArrayType(t, array_size)
+
+ if pointer != None:
+ t = ptypes.PointerType(t)
+
+ return ptypes.Member(name, t, attributes)
+
+bnf = None
+def SPICE_BNF():
+ global bnf
+
+ if not bnf:
+
+ # punctuation
+ colon = Literal(":").suppress()
+ lbrace = Literal("{").suppress()
+ rbrace = Literal("}").suppress()
+ lbrack = Literal("[").suppress()
+ rbrack = Literal("]").suppress()
+ lparen = Literal("(").suppress()
+ rparen = Literal(")").suppress()
+ equals = Literal("=").suppress()
+ comma = Literal(",").suppress()
+ semi = Literal(";").suppress()
+
+ # primitive types
+ int8_ = Keyword("int8").setParseAction(replaceWith(ptypes.int8))
+ uint8_ = Keyword("uint8").setParseAction(replaceWith(ptypes.uint8))
+ int16_ = Keyword("int16").setParseAction(replaceWith(ptypes.int16))
+ uint16_ = Keyword("uint16").setParseAction(replaceWith(ptypes.uint16))
+ int32_ = Keyword("int32").setParseAction(replaceWith(ptypes.int32))
+ uint32_ = Keyword("uint32").setParseAction(replaceWith(ptypes.uint32))
+ int64_ = Keyword("int64").setParseAction(replaceWith(ptypes.int64))
+ uint64_ = Keyword("uint64").setParseAction(replaceWith(ptypes.uint64))
+
+ # keywords
+ enum32_ = Keyword("enum32").setParseAction(replaceWith(32))
+ enum16_ = Keyword("enum16").setParseAction(replaceWith(16))
+ enum8_ = Keyword("enum8").setParseAction(replaceWith(8))
+ flags32_ = Keyword("flags32").setParseAction(replaceWith(32))
+ flags16_ = Keyword("flags16").setParseAction(replaceWith(16))
+ flags8_ = Keyword("flags8").setParseAction(replaceWith(8))
+ channel_ = Keyword("channel")
+ server_ = Keyword("server")
+ client_ = Keyword("client")
+ protocol_ = Keyword("protocol")
+ typedef_ = Keyword("typedef")
+ struct_ = Keyword("struct")
+ message_ = Keyword("message")
+ image_size_ = Keyword("image_size")
+ bytes_ = Keyword("bytes")
+ cstring_ = Keyword("cstring")
+ switch_ = Keyword("switch")
+ default_ = Keyword("default")
+ case_ = Keyword("case")
+
+ identifier = Word( alphas, alphanums + "_" )
+ enumname = Word( alphanums + "_" )
+
+ integer = ( Combine( CaselessLiteral("0x") + Word( nums+"abcdefABCDEF" ) ) |
+ Word( nums+"+-", nums ) ).setName("int").setParseAction(cvtInt)
+
+ typename = identifier.copy().setParseAction(lambda toks : ptypes.TypeRef(str(toks[0])))
+
+ # This is just normal "types", i.e. not channels or messages
+ typeSpec = Forward()
+
+ attributeValue = integer ^ identifier
+ attribute = Group(Combine ("@" + identifier) + Optional(lparen + delimitedList(attributeValue) + rparen))
+ attributes = Group(ZeroOrMore(attribute))
+ arraySizeSpecImage = Group(image_size_ + lparen + integer + comma + identifier + comma + identifier + rparen)
+ arraySizeSpecBytes = Group(bytes_ + lparen + identifier + comma + identifier + rparen)
+ arraySizeSpecCString = Group(cstring_ + lparen + rparen)
+ arraySizeSpec = lbrack + Optional(identifier ^ integer ^ arraySizeSpecImage ^ arraySizeSpecBytes ^arraySizeSpecCString, default="") + rbrack
+ variableDef = Group(typeSpec + Optional("*", default=None) + identifier + Optional(arraySizeSpec, default=None) + attributes - semi) \
+ .setParseAction(parseVariableDef)
+
+ switchCase = Group(Group(OneOrMore(default_.setParseAction(replaceWith(None)) + colon | Group(case_.suppress() + Optional("!", default="") + identifier) + colon)) + variableDef) \
+ .setParseAction(lambda toks: ptypes.SwitchCase(toks[0][0], toks[0][1]))
+ switchBody = Group(switch_ + lparen + delimitedList(identifier,delim='.', combine=True) + rparen + lbrace + Group(OneOrMore(switchCase)) + rbrace + identifier + attributes - semi) \
+ .setParseAction(lambda toks: ptypes.Switch(toks[0][1], toks[0][2], toks[0][3], toks[0][4]))
+ messageBody = structBody = Group(lbrace + ZeroOrMore(variableDef | switchBody) + rbrace)
+ structSpec = Group(struct_ + identifier + structBody + attributes).setParseAction(lambda toks: ptypes.StructType(toks[0][1], toks[0][2], toks[0][3]))
+
+ # have to use longest match for type, in case a user-defined type name starts with a keyword type, like "channel_type"
+ typeSpec << ( structSpec ^ int8_ ^ uint8_ ^ int16_ ^ uint16_ ^
+ int32_ ^ uint32_ ^ int64_ ^ uint64_ ^
+ typename).setName("type")
+
+ flagsBody = enumBody = Group(lbrace + delimitedList(Group (enumname + Optional(equals + integer))) + Optional(comma) + rbrace)
+
+ messageSpec = Group(message_ + messageBody + attributes).setParseAction(lambda toks: ptypes.MessageType(None, toks[0][1], toks[0][2])) | typename
+
+ channelParent = Optional(colon + typename, default=None)
+ channelMessage = Group(messageSpec + identifier + Optional(equals + integer, default=None) + semi) \
+ .setParseAction(lambda toks: ptypes.ChannelMember(toks[0][1], toks[0][0], toks[0][2]))
+ channelBody = channelParent + Group(lbrace + ZeroOrMore( server_ + colon | client_ + colon | channelMessage) + rbrace)
+
+ enum_ = (enum32_ | enum16_ | enum8_)
+ flags_ = (flags32_ | flags16_ | flags8_)
+ enumDef = Group(enum_ + identifier + enumBody + attributes - semi).setParseAction(lambda toks: ptypes.EnumType(toks[0][0], toks[0][1], toks[0][2], toks[0][3]))
+ flagsDef = Group(flags_ + identifier + flagsBody + attributes - semi).setParseAction(lambda toks: ptypes.FlagsType(toks[0][0], toks[0][1], toks[0][2], toks[0][3]))
+ messageDef = Group(message_ + identifier + messageBody + attributes - semi).setParseAction(lambda toks: ptypes.MessageType(toks[0][1], toks[0][2], toks[0][3]))
+ channelDef = Group(channel_ + identifier + channelBody + attributes - semi).setParseAction(lambda toks: ptypes.ChannelType(toks[0][1], toks[0][2], toks[0][3], toks[0][4]))
+ structDef = Group(struct_ + identifier + structBody + attributes - semi).setParseAction(lambda toks: ptypes.StructType(toks[0][1], toks[0][2], toks[0][3]))
+ typedefDef = Group(typedef_ + identifier + typeSpec + attributes - semi).setParseAction(lambda toks: ptypes.TypeAlias(toks[0][1], toks[0][2], toks[0][3]))
+
+ definitions = typedefDef | structDef | enumDef | flagsDef | messageDef | channelDef
+
+ protocolChannel = Group(typename + identifier + Optional(equals + integer, default=None) + semi) \
+ .setParseAction(lambda toks: ptypes.ProtocolMember(toks[0][1], toks[0][0], toks[0][2]))
+ protocolDef = Group(protocol_ + identifier + Group(lbrace + ZeroOrMore(protocolChannel) + rbrace) + semi) \
+ .setParseAction(lambda toks: ptypes.ProtocolType(toks[0][1], toks[0][2]))
+
+ bnf = ZeroOrMore (definitions) + protocolDef + StringEnd()
+
+ singleLineComment = "//" + restOfLine
+ bnf.ignore( singleLineComment )
+ bnf.ignore( cStyleComment )
+
+ return bnf
+
+
+def parse(filename):
+ try:
+ bnf = SPICE_BNF()
+ types = bnf.parseFile(filename)
+ except ParseException as err:
+ six.print_(err.line, file=sys.stderr)
+ six.print_(" "*(err.column-1) + "^", file=sys.stderr)
+ six.print_(err, file=sys.stderr)
+ return None
+
+ for t in types:
+ t.resolve()
+ t.register()
+ protocol = types[-1]
+ return protocol