def _generate_grammar(*, debug_parser: bool = False): pp.ParserElement.setDefaultWhitespaceChars(" \t") EOL = pp.Optional(pp.pythonStyleComment()) + pp.LineEnd() LC = pp.Suppress(pp.OneOrMore(EOL) + pp.White(ws=" \t", min=4)) Identifier = pp.Word(initChars=pp.alphas, bodyChars=pp.alphanums + "_-") MultilineArgument = pp.QuotedString( quoteChar="<<<<", endQuoteChar=">>>>", multiline=True ) SingleQuotedArgument = pp.QuotedString(quoteChar="'", escChar="\\") DoubleQuotedArgument = pp.QuotedString(quoteChar='"', escChar="\\") QuotedArgument = (SingleQuotedArgument | DoubleQuotedArgument | MultilineArgument)( "quoted" ) SimpleArgument = pp.Word(pp.alphanums + "_-+*!$%&/()[]{}.,;:")("simple") Argument = (QuotedArgument | SimpleArgument) + pp.Optional(LC) KwArgument = pp.Combine(Identifier("key") + "=" + Argument) ArgumentList = pp.Group(pp.ZeroOrMore(pp.Group(KwArgument | Argument))) Command = ( pp.locatedExpr(Identifier)("command") + pp.Optional(LC) + ArgumentList("args") ) Grammar = pp.ZeroOrMore(pp.Group(pp.Optional(Command) + pp.Suppress(EOL))) if debug_parser: for expr_name in ( "Grammar Command ArgumentList KwArgument Argument " "SimpleArgument QuotedArgument DoubleQuotedArgument " "SingleQuotedArgument MultilineArgument " "Identifier LC EOL".split() ): expr = locals()[expr_name] expr.setName(expr_name) expr.setDebug() Grammar.parseWithTabs() # Keep tabs unexpanded! return Grammar
def _generate_grammar(*, debug_parser: bool = False): pp.ParserElement.setDefaultWhitespaceChars(' \t') EOL = pp.Optional(pp.pythonStyleComment()) + pp.LineEnd() LC = pp.Suppress(pp.OneOrMore(EOL) + pp.White(ws=' \t', min=4)) Identifier = pp.Word(initChars=pp.alphas, bodyChars=pp.alphanums + '_-') MultilineArgument = pp.QuotedString(quoteChar = '<<<<', endQuoteChar = '>>>>', multiline=True) SingleQuotedArgument = pp.QuotedString(quoteChar = '\'', escChar = '\\') DoubleQuotedArgument = pp.QuotedString(quoteChar = '"', escChar = '\\') QuotedArgument = (SingleQuotedArgument | DoubleQuotedArgument | MultilineArgument)('quoted') SimpleArgument = pp.Word(pp.alphanums + '_-+*!$%&/()[]{}.,;:')('simple') Argument = (QuotedArgument | SimpleArgument) + pp.Optional(LC) KwArgument = pp.Combine(Identifier('key') + '=' + Argument) ArgumentList = pp.Group(pp.ZeroOrMore(pp.Group(KwArgument | Argument))) Command = pp.locatedExpr(Identifier)('command') + pp.Optional(LC) \ + ArgumentList('args') Grammar = pp.ZeroOrMore(pp.Group(pp.Optional(Command) + pp.Suppress(EOL))) if debug_parser: for ename in 'Grammar Command ArgumentList KwArgument Argument ' \ 'SimpleArgument QuotedArgument DoubleQuotedArgument ' \ 'SingleQuotedArgument MultilineArgument ' \ 'Identifier LC EOL'.split(): expr = locals()[ename] expr.setName(ename) expr.setDebug() Grammar.parseWithTabs() # Keep tabs unexpanded! return Grammar
def _generate_grammar(self): # Define grammar: pp.ParserElement.setDefaultWhitespaceChars(" \t") def add_element(name: str, value: pp.ParserElement): nonlocal self if self.debug: value.setName(name) value.setDebug() return value EOL = add_element("EOL", pp.Suppress(pp.LineEnd())) Else = add_element("Else", pp.Keyword("else")) Identifier = add_element( "Identifier", pp.Word(f"{pp.alphas}_", bodyChars=pp.alphanums + "_-./")) BracedValue = add_element( "BracedValue", pp.nestedExpr(ignoreExpr=pp.quotedString | pp.QuotedString(quoteChar="$(", endQuoteChar=")", escQuote="\\", unquoteResults=False)). setParseAction(lambda s, l, t: ["(", *t[0], ")"]), ) Substitution = add_element( "Substitution", pp.Combine( pp.Literal("$") + (((pp.Literal("$") + Identifier + pp.Optional(pp.nestedExpr())) | (pp.Literal("(") + Identifier + pp.Literal(")")) | (pp.Literal("{") + Identifier + pp.Literal("}")) | (pp.Literal("$") + pp.Literal("{") + Identifier + pp.Optional(pp.nestedExpr()) + pp.Literal("}")) | (pp.Literal("$") + pp.Literal("[") + Identifier + pp.Literal("]"))))), ) LiteralValuePart = add_element( "LiteralValuePart", pp.Word(pp.printables, excludeChars="$#{}()")) SubstitutionValue = add_element( "SubstitutionValue", pp.Combine( pp.OneOrMore(Substitution | LiteralValuePart | pp.Literal("$"))), ) FunctionValue = add_element( "FunctionValue", pp.Group( pp.Suppress(pp.Literal("$") + pp.Literal("$")) + Identifier + pp.nestedExpr( ) # .setParseAction(lambda s, l, t: ['(', *t[0], ')']) ).setParseAction(lambda s, l, t: handle_function_value(*t)), ) Value = add_element( "Value", pp.NotAny(Else | pp.Literal("}") | EOL) + (pp.QuotedString(quoteChar='"', escChar="\\") | FunctionValue | SubstitutionValue | BracedValue), ) Values = add_element("Values", pp.ZeroOrMore(Value)("value")) Op = add_element( "OP", pp.Literal("=") | pp.Literal("-=") | pp.Literal("+=") | pp.Literal("*=") | pp.Literal("~="), ) Key = add_element("Key", Identifier) Operation = add_element( "Operation", Key("key") + pp.locatedExpr(Op)("operation") + Values("value")) CallArgs = add_element("CallArgs", pp.nestedExpr()) def parse_call_args(results): out = "" for item in chain(*results): if isinstance(item, str): out += item else: out += "(" + parse_call_args(item) + ")" return out CallArgs.setParseAction(parse_call_args) Load = add_element("Load", pp.Keyword("load") + CallArgs("loaded")) Include = add_element( "Include", pp.Keyword("include") + pp.locatedExpr(CallArgs)("included")) Option = add_element("Option", pp.Keyword("option") + CallArgs("option")) RequiresCondition = add_element("RequiresCondition", pp.originalTextFor(pp.nestedExpr())) def parse_requires_condition(s, l_unused, t): # The following expression unwraps the condition via the additional info # set by originalTextFor. condition_without_parentheses = s[t._original_start + 1:t._original_end - 1] # And this replaces the colons with '&&' similar how it's done for 'Condition'. condition_without_parentheses = ( condition_without_parentheses.strip().replace( ":", " && ").strip(" && ")) return condition_without_parentheses RequiresCondition.setParseAction(parse_requires_condition) Requires = add_element( "Requires", pp.Keyword("requires") + RequiresCondition("project_required_condition")) FunctionArgumentsAsString = add_element( "FunctionArgumentsAsString", pp.originalTextFor(pp.nestedExpr())) QtNoMakeTools = add_element( "QtNoMakeTools", pp.Keyword("qtNomakeTools") + FunctionArgumentsAsString("qt_no_make_tools_arguments"), ) # ignore the whole thing... DefineTestDefinition = add_element( "DefineTestDefinition", pp.Suppress( pp.Keyword("defineTest") + CallArgs + pp.nestedExpr(opener="{", closer="}", ignoreExpr=pp.LineEnd()) ), ) # ignore the whole thing... ForLoop = add_element( "ForLoop", pp.Suppress( pp.Keyword("for") + CallArgs + pp.nestedExpr(opener="{", closer="}", ignoreExpr=pp.LineEnd()) ), ) # ignore the whole thing... ForLoopSingleLine = add_element( "ForLoopSingleLine", pp.Suppress( pp.Keyword("for") + CallArgs + pp.Literal(":") + pp.SkipTo(EOL)), ) # ignore the whole thing... FunctionCall = add_element("FunctionCall", pp.Suppress(Identifier + pp.nestedExpr())) Scope = add_element("Scope", pp.Forward()) Statement = add_element( "Statement", pp.Group(Load | Include | Option | Requires | QtNoMakeTools | ForLoop | ForLoopSingleLine | DefineTestDefinition | FunctionCall | Operation), ) StatementLine = add_element("StatementLine", Statement + (EOL | pp.FollowedBy("}"))) StatementGroup = add_element( "StatementGroup", pp.ZeroOrMore(StatementLine | Scope | pp.Suppress(EOL))) Block = add_element( "Block", pp.Suppress("{") + pp.Optional(EOL) + StatementGroup + pp.Optional(EOL) + pp.Suppress("}") + pp.Optional(EOL), ) ConditionEnd = add_element( "ConditionEnd", pp.FollowedBy( (pp.Optional(pp.White()) + (pp.Literal(":") | pp.Literal("{") | pp.Literal("|")))), ) ConditionPart1 = add_element( "ConditionPart1", (pp.Optional("!") + Identifier + pp.Optional(BracedValue))) ConditionPart2 = add_element("ConditionPart2", pp.CharsNotIn("#{}|:=\\\n")) ConditionPart = add_element( "ConditionPart", (ConditionPart1 ^ ConditionPart2) + ConditionEnd) ConditionOp = add_element("ConditionOp", pp.Literal("|") ^ pp.Literal(":")) ConditionWhiteSpace = add_element( "ConditionWhiteSpace", pp.Suppress(pp.Optional(pp.White(" ")))) ConditionRepeated = add_element( "ConditionRepeated", pp.ZeroOrMore(ConditionOp + ConditionWhiteSpace + ConditionPart)) Condition = add_element("Condition", pp.Combine(ConditionPart + ConditionRepeated)) Condition.setParseAction( lambda x: " ".join(x).strip().replace(":", " && ").strip(" && ")) # Weird thing like write_file(a)|error() where error() is the alternative condition # which happens to be a function call. In this case there is no scope, but our code expects # a scope with a list of statements, so create a fake empty statement. ConditionEndingInFunctionCall = add_element( "ConditionEndingInFunctionCall", pp.Suppress(ConditionOp) + FunctionCall + pp.Empty().setParseAction(lambda x: [[]]).setResultsName( "statements"), ) SingleLineScope = add_element( "SingleLineScope", pp.Suppress(pp.Literal(":")) + pp.Group(Block | (Statement + EOL))("statements"), ) MultiLineScope = add_element("MultiLineScope", Block("statements")) SingleLineElse = add_element( "SingleLineElse", pp.Suppress(pp.Literal(":")) + (Scope | Block | (Statement + pp.Optional(EOL))), ) MultiLineElse = add_element("MultiLineElse", Block) ElseBranch = add_element( "ElseBranch", pp.Suppress(Else) + (SingleLineElse | MultiLineElse)) # Scope is already add_element'ed in the forward declaration above. Scope <<= pp.Group( Condition("condition") + (SingleLineScope | MultiLineScope | ConditionEndingInFunctionCall) + pp.Optional(ElseBranch)("else_statements")) Grammar = StatementGroup("statements") Grammar.ignore(pp.pythonStyleComment()) return Grammar
Keyword("and"), Keyword("or"), Keyword("xor"), Keyword("lsh"), Keyword("rsh"), Keyword("ja"), Keyword("ret"), Keyword("tax"), Keyword("txa"), ]) JUMP_OP = MatchFirst([ Keyword("jeq"), Keyword("jneq"), Keyword("jne"), Keyword("jlt"), Keyword("jle"), Keyword("jgt"), Keyword("jge"), Keyword("jset"), ]) STATEMENT = Group(STATEMENT_OP('op') + ADDRESS('val'))('statement') TARGET = Suppress(',') + ADDRESS JUMP = Group( JUMP_OP('op') + ADDRESS('val') + TARGET('true') + Optional(TARGET)('false'))('jump') TOKEN = Group(STATEMENT ^ JUMP ^ Group(pythonStyleComment('comment'))) PROGRAM = ZeroOrMore(TOKEN)