blob: 1a7d1f59539b7f06126d323933df77e6c167651b [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
Paul Vinciguerrab55aec12020-03-01 01:42:28 -050013import html
14import pyparsing as pp
Chris Luke90f52bf2016-09-12 08:55:13 -040015
16# Some useful primitives
17ident = pp.Word(pp.alphas + "_", pp.alphas + pp.nums + "_")
18intNum = pp.Word(pp.nums)
19hexNum = pp.Literal("0x") + pp.Word(pp.hexnums)
20octalNum = pp.Literal("0") + pp.Word("01234567")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020021integer = (hexNum | octalNum | intNum) + pp.Optional(
22 pp.Literal("ULL") | pp.Literal("LL") | pp.Literal("L")
23)
24floatNum = pp.Regex(r"\d+(\.\d*)?([eE]\d+)?") + pp.Optional(pp.Literal("f"))
Chris Luke90f52bf2016-09-12 08:55:13 -040025char = pp.Literal("'") + pp.Word(pp.printables, exact=1) + pp.Literal("'")
26arrayIndex = integer | ident
27
28lbracket = pp.Literal("(").suppress()
29rbracket = pp.Literal(")").suppress()
30lbrace = pp.Literal("{").suppress()
31rbrace = pp.Literal("}").suppress()
32comma = pp.Literal(",").suppress()
33equals = pp.Literal("=").suppress()
34dot = pp.Literal(".").suppress()
35semicolon = pp.Literal(";").suppress()
36
37# initializer := { [member = ] (variable | expression | { initializer } ) }
38typeName = ident
39varName = ident
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020040typeSpec = (
41 pp.Optional("unsigned")
42 + pp.oneOf("int long short float double char u8 i8 void")
43 + pp.Optional(pp.Word("*"), default="")
44)
45typeCast = pp.Combine("(" + (typeSpec | typeName) + ")").suppress()
Chris Luke90f52bf2016-09-12 08:55:13 -040046
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020047string = pp.Combine(
48 pp.OneOrMore(pp.QuotedString(quoteChar='"', escChar="\\", multiline=True)),
49 adjacent=False,
50)
Chris Luke90f52bf2016-09-12 08:55:13 -040051literal = pp.Optional(typeCast) + (integer | floatNum | char | string)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020052var = pp.Combine(pp.Optional(typeCast) + varName + pp.Optional("[" + arrayIndex + "]"))
Chris Luke90f52bf2016-09-12 08:55:13 -040053
54# This could be more complete, but suffices for our uses
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020055expr = literal | var
Chris Luke90f52bf2016-09-12 08:55:13 -040056
57"""Parse and render a block of text into a Python dictionary."""
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020058
59
Chris Luke90f52bf2016-09-12 08:55:13 -040060class Parser(object):
61 """Compiled PyParsing BNF"""
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020062
Chris Luke90f52bf2016-09-12 08:55:13 -040063 _parser = None
64
65 def __init__(self):
66 super(Parser, self).__init__()
67 self._parser = self.BNF()
68
69 def BNF(self):
70 raise NotImplementedError
71
72 def item(self, item):
73 raise NotImplementedError
74
75 def parse(self, input):
76 item = self._parser.parseString(input).asList()
77 return self.item(item)
78
79
80"""Parser for function-like macros - without the closing semi-colon."""
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020081
82
Chris Luke90f52bf2016-09-12 08:55:13 -040083class ParserFunctionMacro(Parser):
84 def BNF(self):
85 # VLIB_CONFIG_FUNCTION (unix_config, "unix")
86 macroName = ident
87 params = pp.Group(pp.ZeroOrMore(expr + comma) + expr)
88 macroParams = lbracket + params + rbracket
89
90 return macroName + macroParams
91
92 def item(self, item):
93 r = {
94 "macro": item[0],
95 "name": item[1][1],
96 "function": item[1][0],
97 }
98
99 return r
100
101
102"""Parser for function-like macros with a closing semi-colon."""
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200103
104
Chris Luke90f52bf2016-09-12 08:55:13 -0400105class ParseFunctionMacroStmt(ParserFunctionMacro):
106 def BNF(self):
107 # VLIB_CONFIG_FUNCTION (unix_config, "unix");
108 function_macro = super(ParseFunctionMacroStmt, self).BNF()
109 mi = function_macro + semicolon
110 mi.ignore(pp.cppStyleComment)
111
112 return mi
113
114
115"""
116Parser for our struct initializers which are composed from a
Paul Vinciguerrab55aec12020-03-01 01:42:28 -0500117function-like macro, equals sign, and then a normal C struct initializer
Chris Luke90f52bf2016-09-12 08:55:13 -0400118block.
119"""
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200120
121
Chris Luke90f52bf2016-09-12 08:55:13 -0400122class MacroInitializer(ParserFunctionMacro):
123 def BNF(self):
124 # VLIB_CLI_COMMAND (show_sr_tunnel_command, static) = {
125 # .path = "show sr tunnel",
126 # .short_help = "show sr tunnel [name <sr-tunnel-name>]",
127 # .function = show_sr_tunnel_fn,
128 # };
129 cs = pp.Forward()
130
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200131 member = pp.Combine(
132 dot + varName + pp.Optional("[" + arrayIndex + "]"), adjacent=False
133 )
134 value = expr | cs
Chris Luke90f52bf2016-09-12 08:55:13 -0400135
136 entry = pp.Group(pp.Optional(member + equals, default="") + value)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200137 entries = (pp.ZeroOrMore(entry + comma) + entry + pp.Optional(comma)) | (
138 pp.ZeroOrMore(entry + comma)
139 )
Chris Luke90f52bf2016-09-12 08:55:13 -0400140
141 cs << (lbrace + entries + rbrace)
142
143 macroName = ident
144 params = pp.Group(pp.ZeroOrMore(expr + comma) + expr)
145 macroParams = lbracket + params + rbracket
146
147 function_macro = super(MacroInitializer, self).BNF()
148 mi = function_macro + equals + pp.Group(cs) + semicolon
149 mi.ignore(pp.cppStyleComment)
150
151 return mi
152
153 def item(self, item):
154 r = {
155 "macro": item[0],
156 "name": item[1][0],
157 "params": item[2],
158 "value": {},
159 }
160
161 for param in item[2]:
Paul Vinciguerrab55aec12020-03-01 01:42:28 -0500162 r["value"][param[0]] = html.escape(param[1])
Chris Luke90f52bf2016-09-12 08:55:13 -0400163
164 return r