tests: replace pycodestyle with black
[vpp.git] / docs / _scripts / siphon / parsers.py
1 # 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
13 import html
14 import pyparsing as pp
15
16 # Some useful primitives
17 ident = pp.Word(pp.alphas + "_", pp.alphas + pp.nums + "_")
18 intNum = pp.Word(pp.nums)
19 hexNum = pp.Literal("0x") + pp.Word(pp.hexnums)
20 octalNum = pp.Literal("0") + pp.Word("01234567")
21 integer = (hexNum | octalNum | intNum) + pp.Optional(
22     pp.Literal("ULL") | pp.Literal("LL") | pp.Literal("L")
23 )
24 floatNum = pp.Regex(r"\d+(\.\d*)?([eE]\d+)?") + pp.Optional(pp.Literal("f"))
25 char = pp.Literal("'") + pp.Word(pp.printables, exact=1) + pp.Literal("'")
26 arrayIndex = integer | ident
27
28 lbracket = pp.Literal("(").suppress()
29 rbracket = pp.Literal(")").suppress()
30 lbrace = pp.Literal("{").suppress()
31 rbrace = pp.Literal("}").suppress()
32 comma = pp.Literal(",").suppress()
33 equals = pp.Literal("=").suppress()
34 dot = pp.Literal(".").suppress()
35 semicolon = pp.Literal(";").suppress()
36
37 # initializer := { [member = ] (variable | expression | { initializer } ) }
38 typeName = ident
39 varName = ident
40 typeSpec = (
41     pp.Optional("unsigned")
42     + pp.oneOf("int long short float double char u8 i8 void")
43     + pp.Optional(pp.Word("*"), default="")
44 )
45 typeCast = pp.Combine("(" + (typeSpec | typeName) + ")").suppress()
46
47 string = pp.Combine(
48     pp.OneOrMore(pp.QuotedString(quoteChar='"', escChar="\\", multiline=True)),
49     adjacent=False,
50 )
51 literal = pp.Optional(typeCast) + (integer | floatNum | char | string)
52 var = pp.Combine(pp.Optional(typeCast) + varName + pp.Optional("[" + arrayIndex + "]"))
53
54 # This could be more complete, but suffices for our uses
55 expr = literal | var
56
57 """Parse and render a block of text into a Python dictionary."""
58
59
60 class Parser(object):
61     """Compiled PyParsing BNF"""
62
63     _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."""
81
82
83 class 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."""
103
104
105 class 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 """
116 Parser for our struct initializers which are composed from a
117 function-like macro, equals sign, and then a normal C struct initializer
118 block.
119 """
120
121
122 class 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
131         member = pp.Combine(
132             dot + varName + pp.Optional("[" + arrayIndex + "]"), adjacent=False
133         )
134         value = expr | cs
135
136         entry = pp.Group(pp.Optional(member + equals, default="") + value)
137         entries = (pp.ZeroOrMore(entry + comma) + entry + pp.Optional(comma)) | (
138             pp.ZeroOrMore(entry + comma)
139         )
140
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]:
162             r["value"][param[0]] = html.escape(param[1])
163
164         return r