# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import html import pyparsing as pp # Some useful primitives ident = pp.Word(pp.alphas + "_", pp.alphas + pp.nums + "_") intNum = pp.Word(pp.nums) hexNum = pp.Literal("0x") + pp.Word(pp.hexnums) octalNum = pp.Literal("0") + pp.Word("01234567") integer = (hexNum | octalNum | intNum) + pp.Optional( pp.Literal("ULL") | pp.Literal("LL") | pp.Literal("L") ) floatNum = pp.Regex(r"\d+(\.\d*)?([eE]\d+)?") + pp.Optional(pp.Literal("f")) char = pp.Literal("'") + pp.Word(pp.printables, exact=1) + pp.Literal("'") arrayIndex = integer | ident lbracket = pp.Literal("(").suppress() rbracket = pp.Literal(")").suppress() lbrace = pp.Literal("{").suppress() rbrace = pp.Literal("}").suppress() comma = pp.Literal(",").suppress() equals = pp.Literal("=").suppress() dot = pp.Literal(".").suppress() semicolon = pp.Literal(";").suppress() # initializer := { [member = ] (variable | expression | { initializer } ) } typeName = ident varName = ident typeSpec = ( pp.Optional("unsigned") + pp.oneOf("int long short float double char u8 i8 void") + pp.Optional(pp.Word("*"), default="") ) typeCast = pp.Combine("(" + (typeSpec | typeName) + ")").suppress() string = pp.Combine( pp.OneOrMore(pp.QuotedString(quoteChar='"', escChar="\\", multiline=True)), adjacent=False, ) literal = pp.Optional(typeCast) + (integer | floatNum | char | string) var = pp.Combine(pp.Optional(typeCast) + varName + pp.Optional("[" + arrayIndex + "]")) # This could be more complete, but suffices for our uses expr = literal | var """Parse and render a block of text into a Python dictionary.""" class Parser(object): """Compiled PyParsing BNF""" _parser = None def __init__(self): super(Parser, self).__init__() self._parser = self.BNF() def BNF(self): raise NotImplementedError def item(self, item): raise NotImplementedError def parse(self, input): item = self._parser.parseString(input).asList() return self.item(item) """Parser for function-like macros - without the closing semi-colon.""" class ParserFunctionMacro(Parser): def BNF(self): # VLIB_CONFIG_FUNCTION (unix_config, "unix") macroName = ident params = pp.Group(pp.ZeroOrMore(expr + comma) + expr) macroParams = lbracket + params + rbracket return macroName + macroParams def item(self, item): r = { "macro": item[0], "name": item[1][1], "function": item[1][0], } return r """Parser for function-like macros with a closing semi-colon.""" class ParseFunctionMacroStmt(ParserFunctionMacro): def BNF(self): # VLIB_CONFIG_FUNCTION (unix_config, "unix"); function_macro = super(ParseFunctionMacroStmt, self).BNF() mi = function_macro + semicolon mi.ignore(pp.cppStyleComment) return mi """ Parser for our struct initializers which are composed from a function-like macro, equals sign, and then a normal C struct initializer block. """ class MacroInitializer(ParserFunctionMacro): def BNF(self): # VLIB_CLI_COMMAND (show_sr_tunnel_command, static) = { # .path = "show sr tunnel", # .short_help = "show sr tunnel [name ]", # .function = show_sr_tunnel_fn, # }; cs = pp.Forward() member = pp.Combine( dot + varName + pp.Optional("[" + arrayIndex + "]"), adjacent=False ) value = expr | cs entry = pp.Group(pp.Optional(member + equals, default="") + value) entries = (pp.ZeroOrMore(entry + comma) + entry + pp.Optional(comma)) | ( pp.ZeroOrMore(entry + comma) ) cs << (lbrace + entries + rbrace) macroName = ident params = pp.Group(pp.ZeroOrMore(expr + comma) + expr) macroParams = lbracket + params + rbracket function_macro = super(MacroInitializer, self).BNF() mi = function_macro + equals + pp.Group(cs) + semicolon mi.ignore(pp.cppStyleComment) return mi def item(self, item): r = { "macro": item[0], "name": item[1][0], "params": item[2], "value": {}, } for param in item[2]: r["value"][param[0]] = html.escape(param[1]) return r