def get_match_first(lits, parseAction=None): el = MatchFirst(NoMatch()) for lit in lits: el = el.__ior__(lit) if parseAction: el.setParseAction(parseAction) return el
def sreduce(f, l): ''' Same as reduce, but handle len 1 and len 0 lists sensibly ''' if len(l) == 0: return NoMatch() if len(l) == 1: return l[0] return reduce(f, l)
def parse_imp(input): # Grammar: # # <expr> ::= <integer> # true # false # <identifier> # # <fact> ::= relation(constant, ...). # # <rule> ::= name(constant, ... ) :- name(constant, ...), ... . # idChars = alphas + "_+*!=<>" pNAME = Word(idChars, idChars + "0123456789") pRELATION = pNAME + \ "(" + Group(pNAME + ZeroOrMore(Suppress(",") + pNAME)) + ")" pRELATION.setParseAction(lambda result: (result[0], result[2])) pDECL_RELATION = pRELATION + "." pDECL_RELATION.setParseAction(lambda result: result[0]) pRULE = pRELATION + ":-" + \ Group(pRELATION + ZeroOrMore(Suppress(",") + pRELATION)) + "." pRULE.setParseAction(lambda result: (result[0], result[2])) pFACT = (pDECL_RELATION ^ NoMatch()) pQUERY = pRELATION + "?" pQUERY.setParseAction(lambda result: (result[0][0], result[0][1])) pTOP_QUERY = pQUERY.copy() pTOP_QUERY.setParseAction(lambda result: { "result": "query", "stmt": result[0] }) pTOP_RULE = pRULE.copy() pTOP_RULE.setParseAction(lambda result: {"result": "rule", "rule": result}) pTOP_FACT = pFACT.copy() pTOP_FACT.setParseAction(lambda result: { "result": "fact", "decl": result[0] }) pQUIT = Keyword("#quit") pQUIT.setParseAction(lambda result: {"result": "quit"}) pENV = Keyword("#env") pENV.setParseAction(lambda result: {"result": "env"}) pTOP = (pQUIT ^ pENV ^ pTOP_RULE ^ pTOP_FACT ^ pTOP_QUERY) result = pTOP.parseString(input)[0] return result # the first element of the result is the expression
def make_operations(operand, operators, group_start="(", group_end=")"): """ Build the Pyparsing grammar for the ``operators`` and return it. :param operand: The Pyparsing grammar for the operand to be handled by the ``operators``. :param operators: List of quintuples that define the properties of every operator. The order of the ``operators`` describe the precedence of the operators. Each tuple in ``operators`` are made up of the following five elements, which define the operator: - The Pyparsing expression for the operator. - The arity of the operator (1 or 2). - The associativity of the operator ("left" or "right"), if it's a binary operation; otherwise ``None``. - The notation of the operator (prefix, postfix or infix), if it's a binary one. - Whether the left-hand operand should be the master operand. If ``False``, the left- and right-hand operands will be the slave and master ones. - The parse action of the operator. The parse actions for unary operations will receive the named results for the ``operator`` and the ``operand``, while those for binary operations will receive the named results for the ``operator`` and the ``master_operand`` and the ``slave_operand``. The code for the operatorPrecedence() function in Pyparsing was used as an starting point to make this function. """ operation = Forward() group_start = Suppress(group_start) group_end = Suppress(group_end) last_expression = operand | (group_start + operation + group_end) # The "previous_operators" variable represents the expressions for the # previous operators, so it can be used to prevent wrong matches of # operators when a higher level operator was expected. This would happen # when the string of an operator represents the starting characters of # a higher level operator. See Bug #397807. previous_operators = NoMatch() for (operator_expr, arity, assoc, notation, master_left, pa) in operators: final_operator = ~previous_operators + operator_expr new_expression = _append_operation(operand, last_expression, final_operator, arity, assoc.lower(), notation.lower(), master_left, pa) last_expression = new_expression # Updating the list of previous operators, so they won't be matched in # higher-level operations: previous_operators = previous_operators | operator_expr operation << last_expression return operation
def concatenate(tokenlist): """ Склеить несколько токенов из списка """ if len(tokenlist) == 0: return NoMatch() result = tokenlist[0] for token in tokenlist[1:]: result |= token return result
def reinit_exprs(self): command = reduce(lambda acc, e: acc | e[1], self.commands, NoMatch()) self.dm_expr = self.dm_expr_head + command self.expr = self.expr_head + command
def update_bind_expr(self): self.print_bind_forward << reduce(self._accumulator, self.current_bind_exprs.values(), NoMatch()) + StringEnd()
return Group( Suppress(backslash) + Literal(key) + Suppress(White()) + CharsNotIn(u' \n\t\\') + Suppress(White())) # # DEFINE GRAMMAR # #phrase = Word( alphas + u"-.,!? —–‘“”’;:()'\"[]/&%=*…{}" + nums ) phrase = CharsNotIn(u"\n\\") backslash = Literal(u"\\") plus = Literal(u"+") textBlock = Group( Optional(NoMatch(), u"text") + Optional(Suppress(newline)) + phrase) unknown = Group( Optional(NoMatch(), u"unknown") + Suppress(backslash) + CharsNotIn(u' \n\t\\')) knownTokens = [] simpleTokens = [ 'p', 'pi', 'b', 'q', 'q1', 'q2', 'q3', 'nb', 'm', 'li', 'd', 'sp', 'mi', 'pb', 'ip', 'iot', 'io', 'io1', 'io2', 'ip' ] for st in simpleTokens: knownTokens.append(usfmToken(st)) numberTokens = ['c', 'v'] for nt in numberTokens:
def _getForbiddenToken(self): return NoMatch()
def __init__(self, tokens_list): self._markup = NoMatch() for token in tokens_list: self._markup |= token
def reinit_exprs(self): """Combines all expressions into a single expression which can be used to match commands.""" command = reduce(lambda acc, e: acc | e[1], self.commands, NoMatch()) self.dm_expr = self.dm_expr_head + command self.expr = self.expr_head + command
def parse_imp (input): # parse a string into an element of the abstract representation # Grammar: # # <expr> ::= <integer> # true # false # <identifier> # ( if <expr> <expr> <expr> ) # ( function ( <name ... ) <expr> ) # ( <expr> <expr> ... ) # # <decl> ::= var name = expr ; # # <stmt> ::= if <expr> <stmt> else <stmt> # while <expr> <stmt> # name <- <expr> ; # print <expr> ; # <block> # # <block> ::= { <decl> ... <stmt> ... } # # <toplevel> ::= <decl> # <stmt> # idChars = alphas+"_+*-?!=<>+" QUOTE = Literal('"') INTERNAL_QUOTE = QUOTE.copy().leaveWhitespace() pIDENTIFIER = Word(idChars, idChars+"0123456789") #### NOTE THE DIFFERENCE pIDENTIFIER.setParseAction(lambda result: EPrimCall(oper_deref,[EId(result[0])])) # A name is like an identifier but it does not return an EId... pNAME = Word(idChars,idChars+"0123456789") #| Keyword("&\"") | Keyword("&\'") pNAMECON = "," + pNAME pNAMECON.setParseAction(lambda result: result[1]) pNAMES = pNAME + ZeroOrMore(pNAMECON) | ZeroOrMore(pNAME) pNAMES.setParseAction(lambda result: [result]) pINTEGER = Word("0123456789") pINTEGER.setParseAction(lambda result: EValue(VInteger(int(result[0])))) QUOTE = Literal("&\"") | Literal("&\'") pSTRINGSTART = Literal('"') + ZeroOrMore(Word(" ")).leaveWhitespace() pSTRINGSTART.setParseAction(lambda result: result[1:]) pSTRING = pSTRINGSTART + ZeroOrMore(Combine( Word(idChars+"0123456789'"+" ") | QUOTE)) + Literal('"') pSTRING.setParseAction(lambda result: EValue(VString(str(result[:-1])))) pBOOLEAN = Keyword("true") | Keyword("false") pBOOLEAN.setParseAction(lambda result: EValue(VBoolean(result[0]=="true"))) pEXPR = Forward() pEXPR2 = Forward() pSTMT_BLOCK = Forward() pSTMT = Forward() pEXPRS = ZeroOrMore(pEXPR2) pEXPRS.setParseAction(lambda result: [result]) pIF = pEXPR + Keyword("?") + pEXPR + Keyword(':') + pEXPR pIF.setParseAction(lambda result: EIf(result[0], result[2], result[4])) def mkFunBody (params,body): bindings = [ (p,ERefCell(EId(p))) for p in params ] return ELet(bindings,body) def mkLetBody (bindings,body): bindings = [ (p[0],ERefCell(p[1])) for p in bindings ] return ELet(bindings,body) def multiCallHelper(result, start, i, length): if i < length: start = ECall(result[1][i][0], [result[1][i][1], start]) multiCallHelper(result, start, i + 1, length) return start def multiCall(result): start = ECall(result[1][0][0], [result[0], result[1][0][1]]) return multiCallHelper(result, start, 1, len(result[1])) def eFunHelper(variables, expression): if len(variables) == 1: return EFunction(variables[0], expression) else: return EFunction(variables[0], eFunHelper(variables[1:], expression)) def eFunName(result): varName = result[1] variables = result[3] expression = result[-1] print variables, expression return EFunction(variables, expression, varName) pFUN = Keyword("fun") + "(" + pNAMES + ")" + pSTMT pFUN.setParseAction(lambda result: EFunction(result[2],mkFunBody(result[2],result[4]))) pFUNR = Keyword("fun") + pNAME + "(" + pNAMES + ")" + pSTMT # pFUNR.setParseAction(eFunName) pFUNR.setParseAction(lambda result: EFunction(result[3],mkFunBody(result[3],result[5]), result[1])) pEXPR2CAR = "," + pEXPR2 pEXPR2CAR.setParseAction(lambda result: result[1]) pEXPR2MULTIALL = pEXPR2 + ZeroOrMore(pEXPR2CAR) | ZeroOrMore(pEXPR2) pEXPR2MULTIALL.setParseAction(lambda result: [result]) pFUNCALL = pEXPR + "(" + pEXPR2MULTIALL + ")" pFUNCALL.setParseAction(lambda result: ECall(result[0], result[2])) pBINDINGCAR = "," + pNAME + "=" + pEXPR2 pBINDINGCAR.setParseAction(lambda result: (result[1], result[3])) pBINDINGCON = pNAME + "=" + pEXPR2 pBINDINGCON.setParseAction(lambda result: (result[0], result[2])) pBINDINGS = pBINDINGCON + ZeroOrMore(pBINDINGCAR) pBINDINGS.setParseAction(lambda result: [result]) pLET = Keyword("let") + "(" + pBINDINGS + ")" + pEXPR2 pLET.setParseAction(lambda result: mkLetBody(result[2], result[4])) pCALLG = pIDENTIFIER + pEXPR2 pCALLG.setParseAction(lambda result: (result[0], result[1])) pCALL1S = OneOrMore(pCALLG) pCALL1S.setParseAction(lambda result: [ result ]) pCALL = pEXPR + pCALL1S pCALL.setParseAction(multiCall) pCALL1 = pIDENTIFIER + pEXPR2 pCALL1.setParseAction(lambda result: ECall(result[0], [result[1]])) pNOT = "not" + pEXPR2 pNOT.setParseAction(lambda result: EPrimCall(oper_not, [result[1]])) pARRAYITEM = "," + pEXPR2 pARRAYITEM.setParseAction(lambda result: (result[1])) pARRAYITEMS = ZeroOrMore(pARRAYITEM) pARRAYITEMS.setParseAction(lambda result: [result]) pARRAY = "[" + ZeroOrMore(pEXPR2) + pARRAYITEMS + "]" pARRAY.setParseAction(lambda result: EArray(result[1],result[2])) pDICTPAIR = pNAME + ":" + pEXPR pDICTPAIR.setParseAction(lambda result: (result[0],result[2])) pDICTPAIRWITHCOMMA = "," + pNAME + ":" + pEXPR pDICTPAIRWITHCOMMA.setParseAction(lambda result: (result[1],result[3])) pDICTS = ZeroOrMore(pDICTPAIRWITHCOMMA) pDICTS.setParseAction(lambda result: [ result ]) pDICT = "{" + pDICTPAIR + pDICTS + "}" pDICT.setParseAction(lambda result:EDict(result[1],result[2])) pEXPR2P = "(" + pEXPR2 + ")" pEXPR2P.setParseAction(lambda result: result[1]) pACCESS = pNAME + "[" + pEXPR + "]" pACCESS.setParseAction(lambda result: EPrimCall(oper_access_arr,[EId(result[0]),result[2]])) pLEN = Keyword("len") + "(" + pNAME + ")" pLEN.setParseAction(lambda result: EPrimCall(oper_len,[EId(result[2])])) pEXPR << ( pEXPR2P | pINTEGER | pNOT | pARRAY | pACCESS | pDICT | pSTRING | pBOOLEAN | pIDENTIFIER | pCALL1 | pLEN ) pEXPR2 << ( pLET | pFUN | pFUNR | pFUNCALL | pIF | pCALL | pEXPR ) pDECL_VAR_E = "var" + pNAME + ";" pDECL_VAR_E.setParseAction(lambda result: (result[1], EValue(VNone))) pDECL_VAR = "var" + pNAME + "=" + pEXPR2 + ";" pDECL_VAR.setParseAction(lambda result: (result[1],result[3])) pDECL_PROCEDURE = "def" + pNAME + "(" + pNAMES + ")" + pSTMT pDECL_PROCEDURE.setParseAction(lambda result: (result[1], EProcedure(result[3], mkFunBody(result[3], result[5])))) # hack to get pDECL to match only PDECL_VAR (but still leave room # to add to pDECL later) pDECL = ( pDECL_VAR_E | pDECL_VAR | pDECL_PROCEDURE | NoMatch() | ";" ) pDECLS = ZeroOrMore(pDECL) pDECLS.setParseAction(lambda result: [result]) pSTMT_IF_1 = "if (" + pEXPR2 + ")" + pSTMT + "else" + pSTMT pSTMT_IF_1.setParseAction(lambda result: EIf(result[1],result[3],result[5])) pSTMT_IF_2 = "if (" + pEXPR2 + ")" + pSTMT pSTMT_IF_2.setParseAction(lambda result: EIf(result[1],result[3],EValue(VBoolean(True)))) pSTMT_WHILE = "while (" + pEXPR2 + ")" + pSTMT pSTMT_WHILE.setParseAction(lambda result: EWhile(result[1],result[3])) pSTMT_FOR = "for (" + pNAME + "in" + pEXPR2 + ")" + pSTMT pSTMT_FOR.setParseAction(lambda result: EFor(result[1], result[3], result[5])) pSTMT_PRINT_STMS = "," + pEXPR2 pSTMT_PRINT_STMS.setParseAction(lambda result: [ result[1] ]) pSTMT_PRINT_ZERO = ZeroOrMore(pSTMT_PRINT_STMS) pSTMT_PRINT_ZERO.setParseAction(lambda result: [ result ]) def printStmEval(result): newArray = [] newArray.append(result[1]) for i in result[2]: newArray.append(i) return EPrimCall(oper_print,newArray) pSTMT_PRINT = "print" + pEXPR2 + pSTMT_PRINT_ZERO + ";" pSTMT_PRINT.setParseAction(printStmEval) pSTMT_UPDATE_ARR = pNAME + "[" + pEXPR +"]" + "=" + pEXPR + ";" pSTMT_UPDATE_ARR.setParseAction(lambda result: EPrimCall(oper_update_arr,[EId(result[0]),result[2],result[5]])) pSTMT_UPDATE = pNAME + "=" + pEXPR2 + ";" pSTMT_UPDATE.setParseAction(lambda result: EPrimCall(oper_update,[EId(result[0]),result[2]])) pSTMTS = ZeroOrMore(pSTMT) pSTMTS.setParseAction(lambda result: [result]) def mkBlock (decls,stmts): bindings = [ (n,ERefCell(expr)) for (n,expr) in decls ] return ELet(bindings,EDo(stmts)) pSTMT_BLOCK = "{" + pDECLS + pSTMTS + "}" pSTMT_BLOCK.setParseAction(lambda result: mkBlock(result[1],result[2])) pSTMT_pEXPR2 = pEXPR2 + ";" pSTMT_pEXPR2.setParseAction(lambda result: result[0]) pSTMT << ( pSTMT_IF_1 | pSTMT_IF_2 | pSTMT_WHILE | pSTMT_FOR | pSTMT_PRINT | pSTMT_UPDATE_ARR | pSTMT_UPDATE | pSTMT_BLOCK | pSTMT_pEXPR2 | pEXPR2 ) # can't attach a parse action to pSTMT because of recursion, so let's duplicate the parser pTOP_STMT = pSTMT.copy() pTOP_STMT.setParseAction(lambda result: {"result":"statement", "stmt":result[0]}) pTOP_DECL = pDECL.copy() pTOP_DECL.setParseAction(lambda result: {"result":"declaration", "decl":result[0]}) pABSTRACT = "#abs" + pSTMT pABSTRACT.setParseAction(lambda result: {"result":"abstract", "stmt":result[1]}) pQUIT = Keyword("#quit") pQUIT.setParseAction(lambda result: {"result":"quit"}) pTOP = ZeroOrMore(pTOP_DECL) + ZeroOrMore(pTOP_STMT) return pTOP.parseString(input)
Optional(value)) def usfmTokenNumber(key): return Group( Suppress(backslash) + Literal(key) + Suppress(White()) + Word(nums + '-()') + Suppress(White())) # define grammar # phrase = Word( alphas + "-.,!? —–‘“”’;:()'\"[]/&%=*…{}" + nums ) phrase = CharsNotIn("\n\\") backslash = Literal("\\") plus = Literal("+") textBlock = Group(Optional(NoMatch(), "text") + phrase) unknown = Group( Optional(NoMatch(), "unknown") + Suppress(backslash) + CharsNotIn(u' \n\t\\')) escape = usfmTokenValue("\\", phrase) id_token = usfmTokenValue("id", phrase) ide = usfmTokenValue("ide", phrase) sts = usfmTokenValue("sts", phrase) h = usfmTokenValue("h", phrase) mt = usfmTokenValue("mt", phrase) mt1 = usfmTokenValue("mt1", phrase) mt2 = usfmTokenValue("mt2", phrase) mt3 = usfmTokenValue("mt3", phrase) ms = usfmTokenValue("ms", phrase)
def usfmTokenValue(key, value): return Group(Suppress(backslash) + Literal(key) + Suppress(White()) + Optional(value)) def usfmTokenNumber(key): return Group(Suppress(backslash) + Literal(key) + Suppress(White()) + Word(nums + '-()') + Suppress(White())) # define grammar # phrase = Word( alphas + "-.,!? —–‘“”’;:()'\"[]/&%=*…{}" + nums ) phrase = CharsNotIn("\n\\") backslash = Literal("\\") plus = Literal("+") textBlock = Group(Optional(NoMatch(), "text") + phrase) unknown = Group(Optional(NoMatch(), "unknown") + Suppress(backslash) + CharsNotIn(u' \n\t\\')) id_token = usfmTokenValue("id", phrase) ide = usfmTokenValue("ide", phrase) sts = usfmTokenValue("sts", phrase) h = usfmTokenValue("h", phrase) mt = usfmTokenValue("mt", phrase) mt1 = usfmTokenValue("mt1", phrase) mt2 = usfmTokenValue("mt2", phrase) mt3 = usfmTokenValue("mt3", phrase) ms = usfmTokenValue("ms", phrase) ms1 = usfmTokenValue("ms1", phrase) ms2 = usfmTokenValue("ms2", phrase) mr = usfmTokenValue("mr", phrase)
def parse_imp(input): # parse a string into an element of the abstract representation # Grammar: # # <expr> ::= <integer> # true # false # <identifier> # ( if <expr> <expr> <expr> ) # ( function ( <name ... ) <expr> ) # ( <expr> <expr> ... ) # # <decl> ::= var name = expr ; # # <stmt> ::= if <expr> <stmt> else <stmt> # while <expr> <stmt> # name <- <expr> ; # print <expr> ; # <block> # # <block> ::= { <decl> ... <stmt> ... } # # <toplevel> ::= <decl> # <stmt> # idChars = alphas + "_+*-/?!=<>" pIDENTIFIER = Word(idChars, idChars + "0123456789") #### NOTE THE DIFFERENCE pIDENTIFIER.setParseAction( lambda result: EPrimCall(oper_deref, [EId(result[0])])) pIDENTIFIERS = ZeroOrMore(pIDENTIFIER) pIDENTIFIERS.setParseAction(lambda result: [result]) # A name is like an identifier but it does not return an EId... pNAME = Word(idChars, idChars + "0123456789") pNAMES = ZeroOrMore(pNAME) pNAMES.setParseAction(lambda result: [result]) pINTEGER = Word("0123456789") pINTEGER.setParseAction(lambda result: EValue(VInteger(int(result[0])))) pBOOLEAN = Keyword("true") | Keyword("false") pBOOLEAN.setParseAction( lambda result: EValue(VBoolean(result[0] == "true"))) def escapeString(inStr): inStr = inStr[1:-1] outStr = "" i = 0 while (i < len(inStr) - 1): if (inStr[i] == '\\'): if (inStr[i + 1] == '\\' or inStr[i + 1] == '\"'): i += 1 outStr += inStr[i] i += 1 outStr += inStr[-1] return outStr pSTRING = quotedString.copy() pSTRING.setParseAction( lambda result: EValue(VString(escapeString(result[0])))) pEXPR = Forward() pEXPRS = ZeroOrMore(pEXPR) pEXPRS.setParseAction(lambda result: [result]) pIF = "(" + Keyword("if") + pEXPR + pEXPR + pEXPR + ")" pIF.setParseAction(lambda result: EIf(result[2], result[3], result[4])) def mkFunBody(params, body): bindings = [(p, ERefCell(EId(p))) for p in params] return ELet(bindings, body) pFUN = "(" + Keyword("function") + "(" + pNAMES + ")" + pEXPR + ")" pFUN.setParseAction( lambda result: EFunction(result[3], mkFunBody(result[3], result[5]))) pBINDING = "(" + pNAME + pEXPR + ")" pBINDING.setParseAction(lambda result: (result[1], result[2])) pBINDINGS = ZeroOrMore(pBINDING) pBINDINGS.setParseAction(lambda result: [result]) pCALL = "(" + pEXPR + pEXPRS + ")" pCALL.setParseAction(lambda result: ECall(result[1], result[2])) pEXPR << (pINTEGER | pBOOLEAN | pSTRING | pIDENTIFIER | pIF | pFUN | pCALL) pDECL_VAR = "var" + pNAME + "=" + pEXPR + ";" pDECL_VAR.setParseAction(lambda result: (result[1], result[3])) pDECL_ARRAY = "var" + pNAME + "<-" + "(" + "new-array" + pEXPR + ")" + ";" pDECL_ARRAY.setParseAction(lambda result: (result[1], EArray(result[5]))) # hack to get pDECL to match only PDECL_VAR (but still leave room # to add to pDECL later) pDECL = (pDECL_VAR | pDECL_ARRAY | NoMatch()) pDECLS = ZeroOrMore(pDECL) pDECLS.setParseAction(lambda result: [result]) pSTMT = Forward() pSTMT_IF_1 = "if" + pEXPR + pSTMT + "else" + pSTMT pSTMT_IF_1.setParseAction( lambda result: EIf(result[1], result[2], result[4])) pSTMT_IF_2 = "if" + pEXPR + pSTMT pSTMT_IF_2.setParseAction( lambda result: EIf(result[1], result[2], EValue(VBoolean(True)))) pSTMT_WHILE = "while" + pEXPR + pSTMT pSTMT_WHILE.setParseAction(lambda result: EWhile(result[1], result[2])) pSTMT_PRINT = "print" + pEXPR + ";" pSTMT_PRINT.setParseAction( lambda result: EPrimCall(oper_print, [result[1]])) pSTMT_UPDATE = pNAME + "<-" + pEXPR + ";" pSTMT_UPDATE.setParseAction( lambda result: EPrimCall(oper_update, [EId(result[0]), result[2]])) pSTMTARR_UPDATE = pNAME + "[" + pEXPR + "]" + "<-" + pEXPR + ";" pSTMTARR_UPDATE.setParseAction(lambda result: EPrimCall( oper_update_arr, [EId(result[0]), result[5], result[2]])) # pSTMT_FOR = "for" + "(" + pSTMT_UPDATE + pEXPR + ";" + pSTMT_UPDATE + ")" + pSTMT # pSTMT_FOR.setParseAction(lambda result: EDo([result[2],EWhile(result[3], EDo([result[7],result[5]])) ] )) pSTMT_FOR = "for" + pSTMT_UPDATE + pEXPR + ";" + pSTMT_UPDATE + pSTMT pSTMT_FOR.setParseAction(lambda result: EDo( [result[1], EWhile(result[2], EDo([result[5], result[4]]))])) pSTMTS = ZeroOrMore(pSTMT) pSTMTS.setParseAction(lambda result: [result]) def mkBlock(decls, stmts): bindings = [(n, ERefCell(expr)) for (n, expr) in decls] return ELet(bindings, EDo(stmts)) pSTMT_BLOCK = "{" + pDECLS + pSTMTS + "}" pSTMT_BLOCK.setParseAction(lambda result: mkBlock(result[1], result[2])) pDEFPROC = "procedure" + pNAME + "(" + pNAMES + ")" + pSTMT pDEFPROC.setParseAction( lambda result: { "result": "procedure", "proc": (result[1], EProcedure(result[3], mkFunBody(result[3], result[5]))) }) pSTMT_PROC = pIDENTIFIER + "(" + pEXPRS + ")" + ";" pSTMT_PROC.setParseAction(lambda result: EProcCall(result[0], result[2])) pWITH = "(" + Keyword( "with") + pIDENTIFIER + pIDENTIFIER + "(" + pEXPRS + ")" + ")" pWITH.setParseAction(lambda result: EWith(result[2], result[3], result[5])) pNOTIMPLEMENTED = Keyword("<>") pNOTIMPLEMENTED.setParseAction(lambda result: ENotImplemented()) pSTMT << (pSTMT_IF_1 | pSTMT_IF_2 | pSTMT_WHILE | pSTMT_PRINT | pSTMT_FOR | pSTMT_UPDATE | pSTMTARR_UPDATE | pSTMT_BLOCK | pSTMT_PROC | pWITH) # can't attach a parse action to pSTMT because of recursion, so let's duplicate the parser pTOP_STMT = pSTMT.copy() pTOP_STMT.setParseAction(lambda result: { "result": "statement", "stmt": result[0] }) pTOP_DECL = pDECL.copy() pTOP_DECL.setParseAction(lambda result: { "result": "declaration", "decl": result[0] }) pABSTRACT = "#abs" + pSTMT pABSTRACT.setParseAction(lambda result: { "result": "abstract", "stmt": result[1] }) pQUIT = Keyword("#quit") pQUIT.setParseAction(lambda result: {"result": "quit"}) pDEFFUN = "(" + pNAME + "(" + pNAMES + ")" + pSTMT + ")" pDEFFUN.setParseAction(lambda result: (result[ 1], EProcedure(result[3], mkFunBody(result[3], result[5])))) pDEFABSFUN = "(" + pNAME + "(" + pNAMES + ")" + pNOTIMPLEMENTED + ")" pDEFABSFUN.setParseAction(lambda result: (result[1], ENotImplemented())) pDEFFUNS = ZeroOrMore((pDEFFUN | pDEFABSFUN)) pDEFFUNS.setParseAction(lambda result: [result]) pTEMPLATE = Keyword( "class" ) + "(" + pNAME + pIDENTIFIER + "(" + pNAMES + ")" + "(" + pIDENTIFIERS + ")" + "(" + pDEFFUNS + ")" + ")" pTEMPLATE.setParseAction( lambda result: { "result": "template", "temp": (result[2], ETemplate(False, result[2], result[3], result[5], result[ 8], result[11])) }) pABSTEMPLATE = Keyword( "absclass" ) + "(" + pNAME + pIDENTIFIER + "(" + pNAMES + ")" + "(" + pIDENTIFIERS + ")" + "(" + pDEFFUNS + ")" + ")" pABSTEMPLATE.setParseAction( lambda result: { "result": "template", "temp": (result[2], ETemplate(True, result[2], result[3], result[5], result[ 8], result[11])) }) pNEWOBJ = Keyword("new") + pIDENTIFIER + "(" + pEXPRS + ")" pNEWOBJ.setParseAction(lambda result: EObject(result[1], result[3])) pOBJASS = Keyword("obj") + pIDENTIFIER + pNAME + "=" + pNEWOBJ pOBJASS.setParseAction( lambda result: { "result": "objectassignment", "assignment": (result[2], EObjectBinding(result[1], result[4])) }) pMULTI = Keyword("#multi") pMULTI.setParseAction(lambda result: {"result": "multi"}) pTOP = (pQUIT | pABSTRACT | pTOP_DECL | pTOP_STMT | pDEFPROC | pTEMPLATE | pOBJASS | pMULTI | pABSTEMPLATE) result = pTOP.parseString(input)[0] return result # the first element of the result is the expression
def create(self, pa): def itercalls(): def getbrackets(blankpa, scalarpa): optblank = _getoptblank(blankpa, '') return Literal(o) + ZeroOrMore(optblank + _getarg(callchain, scalarpa, c)) + optblank + Literal(c) for o, c in self.bracketpairs: yield (Suppress(Regex("[$](?:lit|')")) + Suppress(o) + Regex("[^%s]*" % re.escape(c)) + Suppress(c)).setParseAction(Text.pa) yield (Suppress(Regex('[$](?:pass|[.])')) + getbrackets(Text.pa, Text.pa)).setParseAction(self._bracketspa) yield (Suppress('$') + self.identifier + getbrackets(Blank.pa, AnyScalar.pa)).setParseAction(_principalcallpa) yield (Suppress('$') + self.identifier + callchain).setParseAction(_additionalcallpa) optblank = _getoptblank(Blank.pa, self.boundarychars) callchain = Forward() callchain << MatchFirst(itercalls()).leaveWhitespace() return reduce(operator.add, [ self.ormorecls(optblank + _getarg(callchain, self.scalarpa, self.boundarychars)), optblank, Optional(Regex("[%s]+" % re.escape(self.boundarychars)).leaveWhitespace().setParseAction(Boundary.pa) if self.boundarychars else NoMatch()), ]).setParseAction(pa)
def evaluator(variables, functions, string, cs=False): ''' Evaluate an expression. Variables are passed as a dictionary from string to value. Unary functions are passed as a dictionary from string to function. Variables must be floats. cs: Case sensitive TODO: Fix it so we can pass integers and complex numbers in variables dict ''' # log.debug("variables: {0}".format(variables)) # log.debug("functions: {0}".format(functions)) # log.debug("string: {0}".format(string)) def lower_dict(d): return dict([(k.lower(), d[k]) for k in d]) all_variables = copy.copy(default_variables) all_functions = copy.copy(default_functions) if not cs: all_variables = lower_dict(all_variables) all_functions = lower_dict(all_functions) all_variables.update(variables) all_functions.update(functions) if not cs: string_cs = string.lower() all_functions = lower_dict(all_functions) all_variables = lower_dict(all_variables) CasedLiteral = CaselessLiteral else: string_cs = string CasedLiteral = Literal check_variables(string_cs, set( all_variables.keys() + all_functions.keys())) if string.strip() == "": return float('nan') ops = {"^": operator.pow, "*": operator.mul, "/": operator.truediv, "+": operator.add, "-": operator.sub, } # We eliminated extreme ones, since they're rarely used, and potentially # confusing. They may also conflict with variables if we ever allow e.g. # 5R instead of 5*R suffixes = {'%': 0.01, 'k': 1e3, 'M': 1e6, 'G': 1e9, 'T': 1e12, # 'P':1e15,'E':1e18,'Z':1e21,'Y':1e24, 'c': 1e-2, 'm': 1e-3, 'u': 1e-6, 'n': 1e-9, 'p': 1e-12} # ,'f':1e-15,'a':1e-18,'z':1e-21,'y':1e-24} def super_float(text): ''' Like float, but with si extensions. 1k goes to 1000''' if text[-1] in suffixes: return float(text[:-1]) * suffixes[text[-1]] else: return float(text) def number_parse_action(x): # [ '7' ] -> [ 7 ] return [super_float("".join(x))] def exp_parse_action(x): # [ 2 ^ 3 ^ 2 ] -> 512 x = [e for e in x if isinstance(e, numbers.Number)] # Ignore ^ x.reverse() x = reduce(lambda a, b: b ** a, x) return x def parallel(x): # Parallel resistors [ 1 2 ] => 2/3 # convert from pyparsing.ParseResults, which doesn't support '0 in x' x = list(x) if len(x) == 1: return x[0] if 0 in x: return float('nan') x = [1. / e for e in x if isinstance(e, numbers.Number)] # Ignore || return 1. / sum(x) def sum_parse_action(x): # [ 1 + 2 - 3 ] -> 0 total = 0.0 op = ops['+'] for e in x: if e in set('+-'): op = ops[e] else: total = op(total, e) return total def prod_parse_action(x): # [ 1 * 2 / 3 ] => 0.66 prod = 1.0 op = ops['*'] for e in x: if e in set('*/'): op = ops[e] else: prod = op(prod, e) return prod def func_parse_action(x): return [all_functions[x[0]](x[1])] # SI suffixes and percent number_suffix = reduce(lambda a, b: a | b, map( Literal, suffixes.keys()), NoMatch()) (dot, minus, plus, times, div, lpar, rpar, exp) = map(Literal, ".-+*/()^") number_part = Word(nums) # 0.33 or 7 or .34 or 16. inner_number = (number_part + Optional( "." + Optional(number_part))) | ("." + number_part) # 0.33k or -17 number = (Optional(minus | plus) + inner_number + Optional(CaselessLiteral("E") + Optional( (plus | minus)) + number_part) + Optional(number_suffix)) number = number.setParseAction(number_parse_action) # Convert to number # Predefine recursive variables expr = Forward() factor = Forward() def sreduce(f, l): ''' Same as reduce, but handle len 1 and len 0 lists sensibly ''' if len(l) == 0: return NoMatch() if len(l) == 1: return l[0] return reduce(f, l) # Handle variables passed in. E.g. if we have {'R':0.5}, we make the substitution. # Special case for no variables because of how we understand PyParsing is # put together if len(all_variables) > 0: # We sort the list so that var names (like "e2") match before # mathematical constants (like "e"). This is kind of a hack. all_variables_keys = sorted( all_variables.keys(), key=len, reverse=True) varnames = sreduce(lambda x, y: x | y, map( lambda x: CasedLiteral(x), all_variables_keys)) varnames.setParseAction(lambda x: map(lambda y: all_variables[y], x)) else: varnames = NoMatch() # Same thing for functions. if len(all_functions) > 0: funcnames = sreduce(lambda x, y: x | y, map(lambda x: CasedLiteral(x), all_functions.keys())) function = funcnames + lpar.suppress() + expr + rpar.suppress() function.setParseAction(func_parse_action) else: function = NoMatch() atom = number | function | varnames | lpar + expr + rpar factor << (atom + ZeroOrMore( exp + atom)).setParseAction(exp_parse_action) # 7^6 paritem = factor + ZeroOrMore(Literal('||') + factor) # 5k || 4k paritem = paritem.setParseAction(parallel) term = paritem + ZeroOrMore((times | div) + paritem) # 7 * 5 / 4 - 3 term = term.setParseAction(prod_parse_action) expr << Optional((plus | minus)) + term + ZeroOrMore( (plus | minus) + term) # -5 + 4 - 3 expr = expr.setParseAction(sum_parse_action) return (expr + stringEnd).parseString(string)[0]
def parse_imp(input): # parse a string into an element of the abstract representation # Grammar: # # <expr> ::= <integer> # true # false # <identifier> # ( if <expr> <expr> <expr> ) # ( function ( <name ... ) <expr> ) # ( <expr> <expr> ... ) # # <decl> ::= var name = expr ; # # <stmt> ::= if <expr> <stmt> else <stmt> # while <expr> <stmt> # name <- <expr> ; # print <expr> ; # <block> # # <block> ::= { <decl> ... <stmt> ... } # # <toplevel> ::= <decl> # <stmt> # idChars = alphas + "_+*-?!=<>+" QUOTE = Literal('"') INTERNAL_QUOTE = QUOTE.copy().leaveWhitespace() pIDENTIFIER = Word(idChars, idChars + "0123456789") #### NOTE THE DIFFERENCE pIDENTIFIER.setParseAction( lambda result: EPrimCall(oper_deref, [EId(result[0])])) # A name is like an identifier but it does not return an EId... pNAME = Word(idChars, idChars + "0123456789") #| Keyword("&\"") | Keyword("&\'") pNAMES = ZeroOrMore(pNAME) pNAMES.setParseAction(lambda result: [result]) pINTEGER = Word("0123456789") pINTEGER.setParseAction(lambda result: EValue(VInteger(int(result[0])))) QUOTE = Literal("&\"") | Literal("&\'") pSTRING = Literal('"') + ZeroOrMore( Combine(Word(idChars + "0123456789'" + " ") | QUOTE)) + Literal('"') pSTRING.setParseAction(lambda result: EValue(VString(str(result[1:-1])))) pBOOLEAN = Keyword("true") | Keyword("false") pBOOLEAN.setParseAction( lambda result: EValue(VBoolean(result[0] == "true"))) pEXPR = Forward() pEXPRS = ZeroOrMore(pEXPR) pEXPRS.setParseAction(lambda result: [result]) pIF = "(" + Keyword("if") + pEXPR + pEXPR + pEXPR + ")" pIF.setParseAction(lambda result: EIf(result[2], result[3], result[4])) def mkFunBody(params, body): bindings = [(p, ERefCell(EId(p))) for p in params] return ELet(bindings, body) def letToFun(result): func = result[5] binds = result[3] params = [] vals = [] for p, v in binds: params.append(p) vals.append(v) return ECall(EFunction(params, func), vals) pFUN = "(" + Keyword("function") + "(" + pNAMES + ")" + pEXPR + ")" pFUN.setParseAction( lambda result: EFunction(result[3], mkFunBody(result[3], result[5]))) pBINDING = "(" + pNAME + pEXPR + ")" pBINDING.setParseAction(lambda result: (result[1], result[2])) pBINDINGS = OneOrMore(pBINDING) pBINDINGS.setParseAction(lambda result: [result]) pLET = "(" + Keyword("let") + "(" + pBINDINGS + ")" + pEXPR + ")" pLET.setParseAction(letToFun) pCALL = "(" + pEXPR + pEXPRS + ")" pCALL.setParseAction(lambda result: ECall(result[1], result[2])) pARRAY = "(" + Keyword("new-array") + pEXPR + ")" pARRAY.setParseAction(lambda result: EArray(result[2])) pINDEX = Keyword("index") + pINTEGER pCALL.setParseAction(lambda result: ECall(result[1], result[2])) pWITH = "(" + Keyword("with") + pEXPR + pEXPR + ")" pWITH.setParseAction(lambda result: EWithObj(result[2], result[3])) pEXPR << (pINTEGER | pARRAY | pSTRING | pWITH | pBOOLEAN | pIDENTIFIER | pIF | pLET | pFUN | pCALL) pDECL_VAR = "var" + pNAME + "=" + pEXPR + ";" pDECL_VAR.setParseAction(lambda result: (result[1], result[3])) pSTMT = Forward() pDECL_PROCEDURE = "procedure" + pNAME + "(" + pNAMES + ")" + pSTMT pDECL_PROCEDURE.setParseAction(lambda result: (result[ 1], EProcedure(result[3], mkFunBody(result[3], result[5])))) # hack to get pDECL to match only PDECL_VAR (but still leave room # to add to pDECL later) pDECL = (pDECL_VAR | pDECL_PROCEDURE | NoMatch() | ";") pDECLS = ZeroOrMore(pDECL) pDECLS.setParseAction(lambda result: [result]) pSTMT_IF_1 = "if" + pEXPR + pSTMT + "else" + pSTMT pSTMT_IF_1.setParseAction( lambda result: EIf(result[1], result[2], result[4])) pSTMT_IF_2 = "if" + pEXPR + pSTMT pSTMT_IF_2.setParseAction( lambda result: EIf(result[1], result[2], EValue(VBoolean(True)))) pSTMT_WHILE = "while" + pEXPR + pSTMT pSTMT_WHILE.setParseAction(lambda result: EWhile(result[1], result[2])) pSTMT_FOR = "for" + pDECLS + pEXPR + ";" + pSTMT + pSTMT pSTMT_FOR.setParseAction( lambda result: EFor(result[1], result[2], result[4], result[5])) pSTMT_PRINT = "print" + pEXPR + ";" pSTMT_PRINT.setParseAction( lambda result: EPrimCall(oper_print, [result[1]])) pSTMT_UPDATE_ARR = pNAME + "[" + pINTEGER + "]" + "<-" + pEXPR + ";" pSTMT_UPDATE_ARR.setParseAction(lambda result: EPrimCall( oper_update_arr, [EId(result[0]), result[2], result[5]])) pSTMT_UPDATE = pNAME + "<-" + pEXPR + ";" pSTMT_UPDATE.setParseAction( lambda result: EPrimCall(oper_update, [EId(result[0]), result[2]])) pSTMT_PROCEDURE = pEXPR + "(" + pEXPRS + ")" + ";" pSTMT_PROCEDURE.setParseAction(lambda result: ECall(result[0], result[2])) pSTMTS = ZeroOrMore(pSTMT) pSTMTS.setParseAction(lambda result: [result]) def mkBlock(decls, stmts): bindings = [(n, ERefCell(expr)) for (n, expr) in decls] return ELet(bindings, EDo(stmts)) pSTMT_BLOCK = "{" + pDECLS + pSTMTS + "}" pSTMT_BLOCK.setParseAction(lambda result: mkBlock(result[1], result[2])) pSTMT << (pSTMT_IF_1 | pSTMT_IF_2 | pWITH | pSTMT_WHILE | pSTMT_FOR | pSTMT_PRINT | pSTMT_UPDATE_ARR | pSTMT_UPDATE | pSTMT_PROCEDURE | pSTMT_BLOCK) # can't attach a parse action to pSTMT because of recursion, so let's duplicate the parser pTOP_STMT = pSTMT.copy() pTOP_STMT.setParseAction(lambda result: { "result": "statement", "stmt": result[0] }) pTOP_DECL = pDECL.copy() pTOP_DECL.setParseAction(lambda result: { "result": "declaration", "decl": result[0] }) pABSTRACT = "#abs" + pSTMT pABSTRACT.setParseAction(lambda result: { "result": "abstract", "stmt": result[1] }) pQUIT = Keyword("#quit") pQUIT.setParseAction(lambda result: {"result": "quit"}) pTOP = (pQUIT | pABSTRACT | pTOP_DECL | pTOP_STMT) result = pTOP.parseString(input)[0] return result # the first element of the result is the expression
def create_parser(regexes, pattern_matchers, range_fillers, quote_pairs=[('\'', '\''), ('"', '"')], delimiters=[','], require_quotes=False, require_delimiter=False, allow_empty=True): if isinstance(regexes, string_types) or isinstance(regexes, re._pattern_type): regexes = [regexes] pattern_matchers = [pattern_matchers] range_fillers = [range_fillers] assert len(regexes) == len(pattern_matchers) assert len(regexes) == len(range_fillers) code_patterns = list( starmap( lambda regex, pattern_matcher: Regex(regex).setParseAction( lambda s, loc, toks: frozenset(pattern_matcher(toks[0]))), zip(regexes, pattern_matchers))) if require_quotes: quoted_code_patterns = [NoMatch() for _ in code_patterns] else: quoted_code_patterns = code_patterns for opener, closer in quote_pairs: quoted_code_patterns = list( starmap( lambda quoted_code_pattern, code_pattern: quoted_code_pattern | (Literal(opener).suppress() + code_pattern + Literal(closer). suppress()), zip(quoted_code_patterns, code_patterns))) code_ranges = map( lambda quoted_code_pattern: quoted_code_pattern + Literal('-'). suppress() + quoted_code_pattern, quoted_code_patterns) code_ranges = list( starmap( lambda code_range, range_filler: code_range. setParseAction(lambda s, loc, toks: frozenset( range_filler(toks[0], toks[1]))), zip(code_ranges, range_fillers))) quoted_code_ranges = [NoMatch() for _ in code_ranges] for opener, closer in quote_pairs: quoted_code_ranges = list( starmap( lambda quoted_code_range, code_pattern: quoted_code_range | (Literal(opener).suppress() + code_pattern + Literal('-'). suppress() + code_pattern + Literal(closer).suppress()), zip(quoted_code_ranges, code_patterns))) quoted_code_ranges = list( starmap( lambda quoted_code_range, range_filler: quoted_code_range. setParseAction(lambda s, loc, toks: frozenset( range_filler(toks[0], toks[1]))), zip(quoted_code_ranges, range_fillers))) any_code_ranges = list(starmap(or_, zip(quoted_code_ranges, code_ranges))) quoted_code_pattern = reduce(xor, quoted_code_patterns) any_code_range = reduce(xor, any_code_ranges) any_delim = reduce(xor, map(Literal, delimiters)) if allow_empty: any_delim = OneOrMore(any_delim) code_list_continuation = any_delim.suppress() + (any_code_range | quoted_code_pattern) if not require_delimiter: code_list_continuation |= White().suppress() + (any_code_range | quoted_code_pattern) code_list = (any_code_range | quoted_code_pattern) + ZeroOrMore( code_list_continuation) + Optional( reduce(or_, map(Literal, delimiters))).suppress() + StringEnd() return code_list
def parse_imp(input): # parse a string into an element of the abstract representation # Grammar: # # <expr> ::= <integer> # true # false # <identifier> # ( if <expr> <expr> <expr> ) # ( function ( <name ... ) <expr> ) # ( <expr> <expr> ... ) # # <decl> ::= var name = expr ; # # <stmt> ::= if <expr> <stmt> else <stmt> # while <expr> <stmt> # name <- <expr> ; # print <expr> ; # <expr> ; # <block> # # <block> ::= { <decl> ... <stmt> ... } # # <toplevel> ::= <decl> # <stmt> # idChars = alphas + "_+*-?!=<>" pIDENTIFIER = Word(idChars, idChars + "0123456789") #### NOTE THE DIFFERENCE pIDENTIFIER.setParseAction( lambda result: EPrimCall(oper_deref, [EId(result[0])])) # A name is like an identifier but it does not return an EId... pNAME = Word(idChars, idChars + "0123456789") pNAMES = ZeroOrMore(pNAME) pNAMES.setParseAction(lambda result: [result]) pINTEGER = Word("0123456789") pINTEGER.setParseAction(lambda result: EValue(VInteger(int(result[0])))) pBOOLEAN = Keyword("true") | Keyword("false") pBOOLEAN.setParseAction( lambda result: EValue(VBoolean(result[0] == "true"))) pEXPR = Forward() pEXPRS = ZeroOrMore(pEXPR) pEXPRS.setParseAction(lambda result: [result]) pIF = "(" + Keyword("if") + pEXPR + pEXPR + pEXPR + ")" pIF.setParseAction(lambda result: EIf(result[2], result[3], result[4])) def mkFunBody(params, body): bindings = [(p, ERefCell(EId(p))) for p in params] return ELet(bindings, body) pFUN = "(" + Keyword("function") + "(" + pNAMES + ")" + pEXPR + ")" pFUN.setParseAction( lambda result: EFunction(result[3], mkFunBody(result[3], result[5]))) pCALL = "(" + pEXPR + pEXPRS + ")" pCALL.setParseAction(lambda result: ECall(result[1], result[2])) pEXPR << (pINTEGER | pBOOLEAN | pIDENTIFIER | pIF | pFUN | pCALL) pDECL_VAR = "var" + pNAME + "=" + pEXPR + ";" pDECL_VAR.setParseAction(lambda result: (result[1], result[3])) # hack to get pDECL to match only PDECL_VAR (but still leave room # to add to pDECL later) pDECL = (pDECL_VAR | NoMatch()) pDECLS = ZeroOrMore(pDECL) pDECLS.setParseAction(lambda result: [result]) pSTMT = Forward() pSTMT_IF_1 = "if" + pEXPR + pSTMT + "else" + pSTMT pSTMT_IF_1.setParseAction( lambda result: EIf(result[1], result[2], result[4])) pSTMT_IF_2 = "if" + pEXPR + pSTMT pSTMT_IF_2.setParseAction( lambda result: EIf(result[1], result[2], EValue(VBoolean(True)))) pSTMT_WHILE = "while" + pEXPR + pSTMT pSTMT_WHILE.setParseAction(lambda result: EWhile(result[1], result[2])) pSTMT_PRINT = "print" + pEXPR + ";" pSTMT_PRINT.setParseAction( lambda result: EPrimCall(oper_print, [result[1]])) pSTMT_UPDATE = pNAME + "<-" + pEXPR + ";" pSTMT_UPDATE.setParseAction( lambda result: EPrimCall(oper_update, [EId(result[0]), result[2]])) pSTMT_EXPR = pEXPR + ";" pSTMT_EXPR.setParseAction(lambda result: result[0]) pSTMTS = ZeroOrMore(pSTMT) pSTMTS.setParseAction(lambda result: [result]) def mkBlock(decls, stmts): bindings = [(n, ERefCell(expr)) for (n, expr) in decls] return ELet(bindings, EDo(stmts)) pSTMT_BLOCK = "{" + pDECLS + pSTMTS + "}" pSTMT_BLOCK.setParseAction(lambda result: mkBlock(result[1], result[2])) pSTMT << (pSTMT_IF_1 | pSTMT_IF_2 | pSTMT_WHILE | pSTMT_PRINT | pSTMT_UPDATE | pSTMT_EXPR | pSTMT_BLOCK) # can't attach a parse action to pSTMT because of recursion, so let's duplicate the parser pTOP_STMT = pSTMT.copy() pTOP_STMT.setParseAction(lambda result: { "result": "statement", "stmt": result[0] }) pTOP_DECL = pDECL.copy() pTOP_DECL.setParseAction(lambda result: { "result": "declaration", "decl": result[0] }) pABSTRACT = "#abs" + pSTMT pABSTRACT.setParseAction(lambda result: { "result": "abstract", "stmt": result[1] }) pQUIT = Keyword("#quit") pQUIT.setParseAction(lambda result: {"result": "quit"}) pTOP = (pQUIT | pABSTRACT | pTOP_DECL | pTOP_STMT) result = pTOP.parseString(input)[0] return result # the first element of the result is the expression