1import six
2
3try:
4    from pyparsing import Literal, CaselessLiteral, Word, OneOrMore, ZeroOrMore, \
5            Forward, delimitedList, Group, Optional, Combine, alphas, nums, restOfLine, cStyleComment, \
6            alphanums, ParseException, ParseResults, Keyword, StringEnd, replaceWith
7except ImportError:
8    six.print_("Module pyparsing not found.")
9    exit(1)
10
11
12from . import ptypes
13import sys
14
15cvtInt = lambda toks: int(toks[0])
16
17def parseVariableDef(toks):
18    t = toks[0][0]
19    pointer = toks[0][1]
20    name = toks[0][2]
21    array_size = toks[0][3]
22    attributes = toks[0][4]
23
24    if array_size != None:
25        t = ptypes.ArrayType(t, array_size)
26
27    if pointer != None:
28        t = ptypes.PointerType(t)
29
30    return ptypes.Member(name, t, attributes)
31
32bnf = None
33def SPICE_BNF():
34    global bnf
35
36    if not bnf:
37
38        # punctuation
39        colon  = Literal(":").suppress()
40        lbrace = Literal("{").suppress()
41        rbrace = Literal("}").suppress()
42        lbrack = Literal("[").suppress()
43        rbrack = Literal("]").suppress()
44        lparen = Literal("(").suppress()
45        rparen = Literal(")").suppress()
46        equals = Literal("=").suppress()
47        comma  = Literal(",").suppress()
48        semi   = Literal(";").suppress()
49
50        # primitive types
51        int8_      = Keyword("int8").setParseAction(replaceWith(ptypes.int8))
52        uint8_     = Keyword("uint8").setParseAction(replaceWith(ptypes.uint8))
53        int16_     = Keyword("int16").setParseAction(replaceWith(ptypes.int16))
54        uint16_    = Keyword("uint16").setParseAction(replaceWith(ptypes.uint16))
55        int32_     = Keyword("int32").setParseAction(replaceWith(ptypes.int32))
56        uint32_    = Keyword("uint32").setParseAction(replaceWith(ptypes.uint32))
57        int64_     = Keyword("int64").setParseAction(replaceWith(ptypes.int64))
58        uint64_    = Keyword("uint64").setParseAction(replaceWith(ptypes.uint64))
59        unix_fd_   = Keyword("unix_fd").setParseAction(replaceWith(ptypes.unix_fd))
60
61        # keywords
62        enum32_    = Keyword("enum32").setParseAction(replaceWith(32))
63        enum16_    = Keyword("enum16").setParseAction(replaceWith(16))
64        enum8_     = Keyword("enum8").setParseAction(replaceWith(8))
65        flags32_   = Keyword("flags32").setParseAction(replaceWith(32))
66        flags16_   = Keyword("flags16").setParseAction(replaceWith(16))
67        flags8_    = Keyword("flags8").setParseAction(replaceWith(8))
68        channel_   = Keyword("channel")
69        server_    = Keyword("server")
70        client_    = Keyword("client")
71        protocol_  = Keyword("protocol")
72        typedef_   = Keyword("typedef")
73        struct_    = Keyword("struct")
74        message_   = Keyword("message")
75        image_size_ = Keyword("image_size")
76        cstring_   = Keyword("cstring")
77        switch_    = Keyword("switch")
78        default_   = Keyword("default")
79        case_      = Keyword("case")
80
81        identifier = Word( alphas, alphanums + "_" )
82        enumname = Word( alphanums + "_" )
83
84        integer = ( Combine( CaselessLiteral("0x") + Word( nums+"abcdefABCDEF" ) ) |
85                    Word( nums+"+-", nums ) ).setName("int").setParseAction(cvtInt)
86
87        typename = identifier.copy().setParseAction(lambda toks : ptypes.TypeRef(str(toks[0])))
88
89        # This is just normal "types", i.e. not channels or messages
90        typeSpec = Forward()
91
92        attributeValue = integer ^ identifier
93        attribute = Group(Combine ("@" + identifier) + Optional(lparen + delimitedList(attributeValue) + rparen))
94        attributes = Group(ZeroOrMore(attribute))
95        arraySizeSpecImage = Group(image_size_ + lparen + integer + comma + identifier + comma + identifier + rparen)
96        arraySizeSpecCString = Group(cstring_ + lparen + rparen)
97        arraySizeSpec = lbrack + Optional(identifier ^ integer ^ arraySizeSpecImage ^ arraySizeSpecCString, default="") + rbrack
98        variableDef = Group(typeSpec + Optional("*", default=None) + identifier + Optional(arraySizeSpec, default=None) + attributes - semi) \
99            .setParseAction(parseVariableDef)
100
101        switchCase = Group(Group(OneOrMore(default_.setParseAction(replaceWith(None)) + colon | Group(case_.suppress() + Optional("!", default="") + identifier) + colon)) + variableDef) \
102            .setParseAction(lambda toks: ptypes.SwitchCase(toks[0][0], toks[0][1]))
103        switchBody = Group(switch_ + lparen + delimitedList(identifier,delim='.', combine=True) + rparen + lbrace + Group(OneOrMore(switchCase)) + rbrace + identifier + attributes - semi) \
104            .setParseAction(lambda toks: ptypes.Switch(toks[0][1], toks[0][2], toks[0][3], toks[0][4]))
105        messageBody = structBody = Group(lbrace + ZeroOrMore(variableDef | switchBody)  + rbrace)
106        structSpec = Group(struct_ + identifier + structBody + attributes).setParseAction(lambda toks: ptypes.StructType(toks[0][1], toks[0][2], toks[0][3]))
107
108        # have to use longest match for type, in case a user-defined type name starts with a keyword type, like "channel_type"
109        typeSpec << ( structSpec ^ int8_ ^ uint8_ ^ int16_ ^ uint16_ ^
110                     int32_ ^ uint32_ ^ int64_ ^ uint64_ ^ unix_fd_ ^
111                     typename).setName("type")
112
113        flagsBody = enumBody = Group(lbrace + delimitedList(Group (enumname + Optional(equals + integer, default=None) + attributes)) + Optional(comma) + rbrace)
114
115        messageSpec = Group(message_ + messageBody + attributes).setParseAction(lambda toks: ptypes.MessageType(None, toks[0][1], toks[0][2])) | typename
116
117        channelParent = Optional(colon + typename, default=None)
118        channelMessage = Group(messageSpec + identifier + Optional(equals + integer, default=None) + semi) \
119            .setParseAction(lambda toks: ptypes.ChannelMember(toks[0][1], toks[0][0], toks[0][2]))
120        channelBody = channelParent + Group(lbrace + ZeroOrMore( server_ + colon | client_ + colon | channelMessage)  + rbrace)
121
122        enum_ = (enum32_ | enum16_ | enum8_)
123        flags_ = (flags32_ | flags16_ | flags8_)
124        enumDef = Group(enum_ + identifier + enumBody + attributes - semi).setParseAction(lambda toks: ptypes.EnumType(toks[0][0], toks[0][1], toks[0][2], toks[0][3]))
125        flagsDef = Group(flags_ + identifier + flagsBody + attributes  - semi).setParseAction(lambda toks: ptypes.FlagsType(toks[0][0], toks[0][1], toks[0][2], toks[0][3]))
126        messageDef = Group(message_ + identifier + messageBody + attributes - semi).setParseAction(lambda toks: ptypes.MessageType(toks[0][1], toks[0][2], toks[0][3]))
127        channelDef = Group(channel_ + identifier + channelBody + attributes - semi).setParseAction(lambda toks: ptypes.ChannelType(toks[0][1], toks[0][2], toks[0][3], toks[0][4]))
128        structDef = Group(struct_ + identifier + structBody + attributes - semi).setParseAction(lambda toks: ptypes.StructType(toks[0][1], toks[0][2], toks[0][3]))
129        typedefDef = Group(typedef_ + identifier  + typeSpec + attributes - semi).setParseAction(lambda toks: ptypes.TypeAlias(toks[0][1], toks[0][2], toks[0][3]))
130
131        definitions = typedefDef | structDef | enumDef | flagsDef | messageDef | channelDef
132
133        protocolChannel = Group(typename + identifier +  Optional(equals + integer, default=None) + semi) \
134            .setParseAction(lambda toks: ptypes.ProtocolMember(toks[0][1], toks[0][0], toks[0][2]))
135        protocolDef = Group(protocol_ + identifier + Group(lbrace + ZeroOrMore(protocolChannel) + rbrace) + semi) \
136            .setParseAction(lambda toks: ptypes.ProtocolType(toks[0][1], toks[0][2]))
137
138        bnf = ZeroOrMore (definitions) +  protocolDef + StringEnd()
139
140        singleLineComment = "//" + restOfLine
141        bnf.ignore( singleLineComment )
142        bnf.ignore( cStyleComment )
143
144    return bnf
145
146
147def parse(filename):
148    try:
149        bnf = SPICE_BNF()
150        types = bnf.parseFile(filename)
151    except ParseException as err:
152        six.print_(err.line, file=sys.stderr)
153        six.print_(" "*(err.column-1) + "^", file=sys.stderr)
154        six.print_(err, file=sys.stderr)
155        return None
156
157    for t in types:
158        t.resolve()
159        t.register()
160    protocol = types[-1]
161    return protocol
162