blob: 6fe8600d4b3bb7eb908a82d205e4867ae3b17eb0 [file] [log] [blame]
Chris Luke90f52bf2016-09-12 08:55:13 -04001# Licensed under the Apache License, Version 2.0 (the "License");
2# you may not use this file except in compliance with the License.
3# You may obtain a copy of the License at:
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS,
9# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10# See the License for the specific language governing permissions and
11# limitations under the License.
12
13import cgi, pyparsing as pp
14
15# Some useful primitives
16ident = pp.Word(pp.alphas + "_", pp.alphas + pp.nums + "_")
17intNum = pp.Word(pp.nums)
18hexNum = pp.Literal("0x") + pp.Word(pp.hexnums)
19octalNum = pp.Literal("0") + pp.Word("01234567")
20integer = (hexNum | octalNum | intNum) + \
21 pp.Optional(pp.Literal("ULL") | pp.Literal("LL") | pp.Literal("L"))
22floatNum = pp.Regex(r'\d+(\.\d*)?([eE]\d+)?') + pp.Optional(pp.Literal("f"))
23char = pp.Literal("'") + pp.Word(pp.printables, exact=1) + pp.Literal("'")
24arrayIndex = integer | ident
25
26lbracket = pp.Literal("(").suppress()
27rbracket = pp.Literal(")").suppress()
28lbrace = pp.Literal("{").suppress()
29rbrace = pp.Literal("}").suppress()
30comma = pp.Literal(",").suppress()
31equals = pp.Literal("=").suppress()
32dot = pp.Literal(".").suppress()
33semicolon = pp.Literal(";").suppress()
34
35# initializer := { [member = ] (variable | expression | { initializer } ) }
36typeName = ident
37varName = ident
38typeSpec = pp.Optional("unsigned") + \
39 pp.oneOf("int long short float double char u8 i8 void") + \
40 pp.Optional(pp.Word("*"), default="")
41typeCast = pp.Combine( "(" + ( typeSpec | typeName ) + ")" ).suppress()
42
43string = pp.Combine(pp.OneOrMore(pp.QuotedString(quoteChar='"',
44 escChar='\\', multiline=True)), adjacent=False)
45literal = pp.Optional(typeCast) + (integer | floatNum | char | string)
46var = pp.Combine(pp.Optional(typeCast) + varName +
47 pp.Optional("[" + arrayIndex + "]"))
48
49# This could be more complete, but suffices for our uses
50expr = (literal | var)
51
52"""Parse and render a block of text into a Python dictionary."""
53class Parser(object):
54 """Compiled PyParsing BNF"""
55 _parser = None
56
57 def __init__(self):
58 super(Parser, self).__init__()
59 self._parser = self.BNF()
60
61 def BNF(self):
62 raise NotImplementedError
63
64 def item(self, item):
65 raise NotImplementedError
66
67 def parse(self, input):
68 item = self._parser.parseString(input).asList()
69 return self.item(item)
70
71
72"""Parser for function-like macros - without the closing semi-colon."""
73class ParserFunctionMacro(Parser):
74 def BNF(self):
75 # VLIB_CONFIG_FUNCTION (unix_config, "unix")
76 macroName = ident
77 params = pp.Group(pp.ZeroOrMore(expr + comma) + expr)
78 macroParams = lbracket + params + rbracket
79
80 return macroName + macroParams
81
82 def item(self, item):
83 r = {
84 "macro": item[0],
85 "name": item[1][1],
86 "function": item[1][0],
87 }
88
89 return r
90
91
92"""Parser for function-like macros with a closing semi-colon."""
93class ParseFunctionMacroStmt(ParserFunctionMacro):
94 def BNF(self):
95 # VLIB_CONFIG_FUNCTION (unix_config, "unix");
96 function_macro = super(ParseFunctionMacroStmt, self).BNF()
97 mi = function_macro + semicolon
98 mi.ignore(pp.cppStyleComment)
99
100 return mi
101
102
103"""
104Parser for our struct initializers which are composed from a
105function-like macro, equals sign, and then a normal C struct initalizer
106block.
107"""
108class MacroInitializer(ParserFunctionMacro):
109 def BNF(self):
110 # VLIB_CLI_COMMAND (show_sr_tunnel_command, static) = {
111 # .path = "show sr tunnel",
112 # .short_help = "show sr tunnel [name <sr-tunnel-name>]",
113 # .function = show_sr_tunnel_fn,
114 # };
115 cs = pp.Forward()
116
117
118 member = pp.Combine(dot + varName + pp.Optional("[" + arrayIndex + "]"),
119 adjacent=False)
120 value = (expr | cs)
121
122 entry = pp.Group(pp.Optional(member + equals, default="") + value)
123 entries = (pp.ZeroOrMore(entry + comma) + entry + pp.Optional(comma)) | \
124 (pp.ZeroOrMore(entry + comma))
125
126 cs << (lbrace + entries + rbrace)
127
128 macroName = ident
129 params = pp.Group(pp.ZeroOrMore(expr + comma) + expr)
130 macroParams = lbracket + params + rbracket
131
132 function_macro = super(MacroInitializer, self).BNF()
133 mi = function_macro + equals + pp.Group(cs) + semicolon
134 mi.ignore(pp.cppStyleComment)
135
136 return mi
137
138 def item(self, item):
139 r = {
140 "macro": item[0],
141 "name": item[1][0],
142 "params": item[2],
143 "value": {},
144 }
145
146 for param in item[2]:
147 r["value"][param[0]] = cgi.escape(param[1])
148
149 return r