def __init__(self, *args, **kwargs): super(BonusPointCalculator, self).__init__(*args, **kwargs) self.Operators.update({ "=": operator.eq, ">": operator.gt, "<": operator.lt, ">=": operator.ge, "<=": operator.le, ",": operator.and_, "[": lambda a, b: a and b or Zero, "]": lambda a, b: a and b or Zero, }) BonusPointAddition = Addition | Comparison Expression = Forward() Atom = ((Identifier | Integer).setParseAction(self._push) | (LeftParenthesis + Expression.suppress() + RightParenthesis)) Terminal = Atom + ZeroOrMore( (Multiplication + Atom).setParseAction(self._push)) Expression << Terminal + ZeroOrMore( (BonusPointAddition + Terminal).setParseAction(self._push)) Condition = Expression + ZeroOrMore( (Comma + Expression).setParseAction(self._push)) Rule = (LeftBracket + Condition + Colon + Expression + RightBracket).setParseAction(self._push) Chain = Rule + ZeroOrMore((Addition + Rule).setParseAction(self._push)) self.pattern = Chain + StringEnd()
def bnf(self): ''' The BNF grammar is defined bellow. expop :: '^' multop :: '*' | '/' addop :: '+' | '-' integer :: ['+' | '-'] '0'..'9'+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* ''' if not self._bnf: point = Literal(".") e = CaselessLiteral("E") fnumber = Combine( Word("+-"+nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums)) ) ident = Word(alphas, alphas + nums + "_$") minus = Literal("-") plus = Literal("+") div = Literal("/") mult = Literal("*") rpar = Literal(")").suppress() lpar = Literal("(").suppress() addop = plus | minus multop = mult | div expop = Literal("^") pi = CaselessLiteral("PI") expr = Forward() atom = ( Optional("-") + ( pi | e | fnumber | ident + lpar + delimitedList(expr) + rpar ).setParseAction(self.push_first) | (lpar + expr.suppress() + rpar) ).setParseAction(self.push_minus) # The right way to define exponentiation is -> 2^3^2 = 2^(3^2), # not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore( (expop + factor).setParseAction(self.push_first) ) term = factor + ZeroOrMore( (multop + factor).setParseAction(self.push_first) ) expr << term + ZeroOrMore( (addop + term).setParseAction(self.push_first) ) self._bnf = expr return self._bnf
def compute(input_string): # Debugging flag can be set to either "debug_flag=True" or "debug_flag=False" debug_flag = False explain_list = [] variables = {} # define grammar point = Literal(".") e = CaselessLiteral("E") plusorminus = Literal("+") | Literal("-") number = Word(nums) integer = Combine(Optional(plusorminus) + number) floatnumber = Combine(integer + Optional(point + Optional(number)) + Optional(e + integer)) ident = Word(alphas, alphanums + "_") plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div expop = Literal("^") assign = Literal("=") expr = Forward() atom = (e | floatnumber | integer | ident).setParseAction(pushFirst) | (lpar + expr.suppress() + rpar) factor = Forward() factor << atom + ZeroOrMore((expop + factor).setParseAction(pushFirst)) term = factor + ZeroOrMore((multop + factor).setParseAction(pushFirst)) expr << term + ZeroOrMore((addop + term).setParseAction(pushFirst)) bnf = Optional((ident + assign).setParseAction(assignVar)) + expr pattern = bnf + StringEnd() if input_string != "": try: L = pattern.parseString(input_string) except ParseException, err: raise ComputationException, "Error while parsing" print exprStack if len(exprStack) <= 1: return None result = evaluateStack(exprStack, explain_list) if len(str(result)) > 12: ret = "%e" % result else: ret = str(result) ret = ret.replace("e", " x 10^") ret = ret.replace("+", "") if len(explain_list): return "%s (%s)" % (ret, ", ".join(explain_list)) else: return "%s" % ret
def _get_bnf(self): """ Returns the `Backus–Naur Form` for the parser """ if not self.bnf: # Operators exponent_operator = Literal("^") # negate_operator = Literal("!") # TODO: Implement this so we can write `!True` multiply_operator = oneOf("* / %") add_operator = oneOf("+ -") comparison_operator = oneOf("== != < <= > >= & |") ^ Keyword("in") # Functions e = CaselessLiteral("E") pi = CaselessLiteral("PI") lparen, rparen, lbrack, rbrack = map(Suppress, "()[]") ident = Word(alphas, alphas + nums + "_$") variable = Combine(Literal("$") + Word(alphanums + "_")) boolean = Keyword("True") ^ Keyword("False") string = quotedString.setParseAction(removeQuotes) numeric = Combine( Word("+-" + nums, nums) + Optional(Literal(".") + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums))) none = Keyword("None") expression = Forward() lists = Forward() lists << (lbrack + Optional( delimitedList(numeric ^ variable ^ boolean ^ string)) + rbrack) atom = (Optional("-") + (pi | e | numeric | ident + lparen + expression + rparen).setParseAction(self.push_stack) | (variable | none | boolean | string | Group(lists)).setParseAction(self.push_stack) | (lparen + expression.suppress() + rparen)).setParseAction( self.push_unary_stack) # By defining exponentiation as "atom [^factor]" instead of "atom [^atom], # we get left to right exponents. 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore( (exponent_operator + factor).setParseAction(self.push_stack)) boolean = factor + ZeroOrMore( (comparison_operator + factor).setParseAction(self.push_stack)) term = boolean + ZeroOrMore( (multiply_operator + boolean).setParseAction(self.push_stack)) self.bnf = expression << term + ZeroOrMore( (add_operator + term).setParseAction(self.push_stack)) return self.bnf
def _BNF(self): """ expop :: '^' multop :: '*' | '/' addop :: '+' | '-' integer :: ['+' | '-'] '0'..'9'+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* """ if not self.bnf: point = Literal(".") e = CaselessLiteral("E") fnumber = Combine( Word("+-" + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums))) ident = Word(alphas, alphas + nums + "_$") plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lpar = Literal("(").suppress() rpar = Literal(")").suppress() # comma = Literal( "," ).suppress() comma = Literal(",") addop = plus | minus multop = mult | div expop = Literal("^") pi = CaselessLiteral("PI") var_list = [Literal(i) for i in self.var_names] expr = Forward() arg_func = Forward() or_vars = MatchFirst(var_list) # atom = (Optional("-") + ( pi | e | fnumber | ident + lpar + delimitedList(Group(expr)) + rpar | or_vars ).setParseAction( self._pushFirst ) | ( lpar + delimitedList(Group(expr)).suppress() + rpar ) ).setParseAction(self._pushUMinus) atom = ((Optional("-") + ( pi | e | fnumber | ident + lpar + arg_func + rpar | or_vars ).setParseAction( self._pushFirst )) | \ (Optional("-") + ( lpar + arg_func.suppress() + rpar )) ).setParseAction(self._pushUMinus) # expr + ZeroOrMore( "," + expr ) # by defining exponentiation as "atom [ ^ factor ]..." instead of "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-righ # that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore( (expop + factor).setParseAction(self._pushFirst)) term = factor + ZeroOrMore( (multop + factor).setParseAction(self._pushFirst)) expr << term + ZeroOrMore( (addop + term).setParseAction(self._pushFirst)) arg_func << expr + ZeroOrMore( (comma + expr).setParseAction(self._pushFirst)) self.bnf = expr return self.bnf
def _BNF(self): base16 = Literal("$") hex = Combine(base16 + Word(hexnums + "_")) base4 = Literal("%%") quaternary = Combine(base4 + Word("0123_")) base2 = Literal("%") binary = Combine(base2 + Word("01_")) plusminus = Literal("+") | Literal("-") integer = Combine(Optional(plusminus) + Word(nums+"_")) name_token = Combine(Optional(Literal(":") | Literal("@")) + Word("_" + alphas, "_" + alphanums)) name_token.setParseAction(self._mark_name_token) lparens = Literal("(").suppress() rparens = Literal(")").suppress() # op0 = Literal("@") op1 = (Literal("^^") | Literal("||") | Literal("|<") | Literal(">|") | Literal("!")).setParseAction(self._mark_unary) op2 = Literal("->") | Literal("<-") | Literal(">>") | Literal("<<") | Literal("~>") | Literal("><") op3 = Literal("&") op4 = Literal("|") | Literal("^") op5 = Literal("**") | Literal("*") | Literal("//") | Literal("/") op6 = Literal("+") | Literal("-") op7 = Literal("#>") | Literal("<#") op8 = Literal("<") | Literal(">") | Literal("<>") | Literal("==") | Literal("=<") | Literal("=>") op9 = Literal("NOT").setParseAction(self._mark_unary) op10 = Literal("AND") op11 = Literal("OR") op12 = Literal(",") expr = Forward() atom = name_token | hex | quaternary | binary | integer | quotedString atom.setParseAction(self._push) atom = atom | (lparens + expr.suppress() + rparens) # term0 = atom + ZeroOrMore((op0 + atom) .setParseAction(self._push)) # term1 = term0 + ZeroOrMore((op1 + term0) .setParseAction(self._push)) term1 = atom + ZeroOrMore((op1 + atom) .setParseAction(self._push)) term2 = term1 + ZeroOrMore((op2 + term1) .setParseAction(self._push)) term3 = term2 + ZeroOrMore((op3 + term2) .setParseAction(self._push)) term4 = term3 + ZeroOrMore((op4 + term3) .setParseAction(self._push)) term5 = term4 + ZeroOrMore((op5 + term4) .setParseAction(self._push)) term6 = term5 + ZeroOrMore((op6 + term5) .setParseAction(self._push)) term7 = term6 + ZeroOrMore((op7 + term6) .setParseAction(self._push)) term8 = term7 + ZeroOrMore((op8 + term7) .setParseAction(self._push)) term9 = term8 + ZeroOrMore((op9 + term8) .setParseAction(self._push)) term10 = term9 + ZeroOrMore((op10 + term9) .setParseAction(self._push)) term11 = term10 + ZeroOrMore((op11 + term10).setParseAction(self._push)) expr << term11 + ZeroOrMore((op12 + term11).setParseAction(self._push)) return expr
def BNF(): """ expop :: '^' multop :: '*' | '/' | '>>' | '<<' | '|' | '&' addop :: '+' | '-' hex :: '0x' + integer integer :: ['+' | '-'] '0'..'9'+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* """ global bnf if not bnf: point = Literal(".") e = CaselessLiteral("E") hexnum = CaselessLiteral("0x") + OneOrMore( oneOf(nums + 'a b c d e f A B C D E F')) hexnum.setParseAction(lambda s, l, t: str(int(''.join(t), 16))) fnumber = Combine( Word("+-" + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums))) ident = Word(alphas, alphas + nums + "_$") plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lshift = Literal("<<") rshift = Literal(">>") or_ = Literal("|") and_ = Literal("&") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div | lshift | rshift | or_ | and_ expop = Literal("^") pi = CaselessLiteral("PI") expr = Forward() atom = (Optional("-") + (pi | e | hexnum | fnumber | ident + lpar + expr + rpar).setParseAction(pushFirst) | (lpar + expr.suppress() + rpar)).setParseAction(pushUMinus) # by defining exponentiation as "atom [ ^ factor ]..." instead of "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-righ # that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore((expop + factor).setParseAction(pushFirst)) term = factor + ZeroOrMore((multop + factor).setParseAction(pushFirst)) expr << term + ZeroOrMore((addop + term).setParseAction(pushFirst)) bnf = expr return bnf
def _BNF(self): """ expop :: '^' multop :: '*' | '/' addop :: '+' | '-' integer :: ['+' | '-'] '0'..'9'+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* """ if not self.bnf: point = Literal( "." ) e = CaselessLiteral( "E" ) fnumber = Combine( Word( "+-"+nums, nums ) + Optional( point + Optional( Word( nums ) ) ) + Optional( e + Word( "+-"+nums, nums ) ) ) ident = Word(alphas, alphas+nums+"_$") plus = Literal( "+" ) minus = Literal( "-" ) mult = Literal( "*" ) div = Literal( "/" ) lpar = Literal( "(" ).suppress() rpar = Literal( ")" ).suppress() # comma = Literal( "," ).suppress() comma = Literal( "," ) addop = plus | minus multop = mult | div expop = Literal( "^" ) pi = CaselessLiteral( "PI" ) var_list = [Literal(i) for i in self.var_names] expr = Forward() arg_func = Forward() or_vars = MatchFirst(var_list) # atom = (Optional("-") + ( pi | e | fnumber | ident + lpar + delimitedList(Group(expr)) + rpar | or_vars ).setParseAction( self._pushFirst ) | ( lpar + delimitedList(Group(expr)).suppress() + rpar ) ).setParseAction(self._pushUMinus) atom = ((Optional("-") + ( pi | e | fnumber | ident + lpar + arg_func + rpar | or_vars ).setParseAction( self._pushFirst )) | \ (Optional("-") + ( lpar + arg_func.suppress() + rpar )) ).setParseAction(self._pushUMinus) # expr + ZeroOrMore( "," + expr ) # by defining exponentiation as "atom [ ^ factor ]..." instead of "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-righ # that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore( ( expop + factor ).setParseAction( self._pushFirst ) ) term = factor + ZeroOrMore( ( multop + factor ).setParseAction( self._pushFirst ) ) expr << term + ZeroOrMore( ( addop + term ).setParseAction( self._pushFirst ) ) arg_func << expr + ZeroOrMore( (comma + expr).setParseAction( self._pushFirst)) self.bnf = expr return self.bnf
def bnf(self): ''' The BNF grammar is defined bellow. expop :: '^' multop :: '*' | '/' addop :: '+' | '-' integer :: ['+' | '-'] '0'..'9'+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* ''' if not self._bnf: point = Literal(".") e = CaselessLiteral("E") fnumber = Combine( Word("+-" + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums))) ident = Word(alphas, alphas + nums + "_$") minus = Literal("-") plus = Literal("+") div = Literal("/") mult = Literal("*") rpar = Literal(")").suppress() lpar = Literal("(").suppress() addop = plus | minus multop = mult | div expop = Literal("^") pi = CaselessLiteral("PI") expr = Forward() atom = (Optional("-") + (pi | e | fnumber | ident + lpar + delimitedList(expr) + rpar).setParseAction(self.push_first) | (lpar + expr.suppress() + rpar)).setParseAction( self.push_minus) # The right way to define exponentiation is -> 2^3^2 = 2^(3^2), # not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore( (expop + factor).setParseAction(self.push_first)) term = factor + ZeroOrMore( (multop + factor).setParseAction(self.push_first)) expr << term + ZeroOrMore( (addop + term).setParseAction(self.push_first)) self._bnf = expr return self._bnf
def _dice_grammar(exprStack, varStack): def pushFirst(str, loc, toks): exprStack.append(toks[0]) def assignVar(str, loc, toks): varStack.append(toks[0]) point = Literal('.') e = CaselessLiteral('E') plusorminus = Literal('+') | Literal('-') singledie = Literal('d') number = Word(nums) integer = Combine(Optional(plusorminus) + number) singleroll = Combine(singledie + number) floatnumber = Combine( integer + Optional(point + Optional(number)) + Optional(e + integer)) ident = Word(alphas, alphanums + '_') plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div expop = Literal("^") dieop = Literal("d") assign = Literal("=") expr = Forward() atom = ( (e | floatnumber | integer | ident | singleroll).setParseAction( pushFirst) | (lpar + expr.suppress() + rpar)) roll = Forward() roll << atom + ZeroOrMore((dieop + roll).setParseAction(pushFirst)) factor = Forward() factor << roll + ZeroOrMore((expop + factor).setParseAction(pushFirst)) term = factor + ZeroOrMore((multop + factor).setParseAction(pushFirst)) expr << term + ZeroOrMore((addop + term).setParseAction(pushFirst)) bnf = Optional((ident + assign).setParseAction(assignVar)) + expr return bnf + StringEnd()
def BNF(): """ expop :: '^' multop :: '*' | '/' addop :: '+' | '-' integer :: ['+' | '-'] '0'..'9'+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* """ global bnf if not bnf: point = Literal(".") e = CaselessLiteral("E") fnumber = Combine( Word("+-" + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums))) ident = Word(alphas, alphas + nums + "_$") plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div expop = Literal("^") pi = CaselessLiteral("PI") expr = Forward() atom = ((Optional("-") + (pi | e | fnumber | ident + lpar + expr + rpar).setParseAction(pushFirst) | (lpar + expr.suppress() + rpar)).setParseAction(pushUMinus)) # by defining exponentiation as "atom [ ^ factor ]..." instead of # "atom [ ^ atom ]...", we get right-to-left exponents, instead of # left-to-right # that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore((expop + factor).setParseAction(pushFirst)) term = factor + ZeroOrMore((multop + factor).setParseAction(pushFirst)) expr << term + ZeroOrMore((addop + term).setParseAction(pushFirst)) bnf = expr return bnf
def BNF(): """ expop :: '^' multop :: '*' | '/' addop :: '+' | '-' integer :: ['+' | '-'] '0'..'9'+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* """ global bnf if not bnf: point = Literal(".") e = CaselessLiteral("E") fnumber = Combine(Word("+-"+nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-"+nums, nums))) ident = Word(alphas, alphas+nums+"_$") plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div expop = Literal("^") pi = CaselessLiteral("PI") expr = Forward() atom = ((Optional("-") + (pi | e | fnumber | ident + lpar + expr + rpar).setParseAction(pushFirst) | (lpar + expr.suppress() + rpar)).setParseAction(pushUMinus)) # by defining exponentiation as "atom [ ^ factor ]..." instead of # "atom [ ^ atom ]...", we get right-to-left exponents, instead of # left-to-right # that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore((expop + factor).setParseAction(pushFirst)) term = factor + ZeroOrMore((multop + factor).setParseAction(pushFirst)) expr << term + ZeroOrMore((addop + term).setParseAction(pushFirst)) bnf = expr return bnf
def __parser(expression): """ adopted from Paul McGuire example. http://pyparsing.wikispaces.com/file/view/fourFn.py """ expr_stack = [] def push_first(strg, loc, toks): expr_stack.append(toks[0]) def push_u_minus(strg, loc, toks): if toks and toks[0] == '-': expr_stack.append('unary -') point = Literal('.') _e = CaselessLiteral('E') fnumber = Combine( Word('+-' + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(_e + Word('+-' + nums, nums))) ident = Word(alphas, alphas + nums + '_$') plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div expop = Literal("^") _pi = CaselessLiteral("PI") x = CaselessLiteral("X") expr = Forward() atom = (Optional("-") + (x | _pi | _e | fnumber | ident + lpar + expr + rpar).setParseAction(push_first) | (lpar + expr.suppress() + rpar)).setParseAction(push_u_minus) factor = Forward() factor << atom + ZeroOrMore( (expop + factor).setParseAction(push_first)) term = factor + ZeroOrMore( (multop + factor).setParseAction(push_first)) expr << term + ZeroOrMore((addop + term).setParseAction(push_first)) expr.parseString(expression) return expr_stack
def __init__(self, instance, *args, **kwargs): Expression = Forward() Atom = ((Identifier | Integer).setParseAction(self._push) | (LeftParenthesis + Expression.suppress() + RightParenthesis)).setName('atom') Terminal = Atom + ZeroOrMore( (Multiplication + Atom).setParseAction(self._push)) Expression << Terminal + ZeroOrMore( (Addition + Terminal).setParseAction(self._push)) self.Operators = { "+": operator.add, "-": operator.sub, "*": operator.mul, "/": operator.truediv, } self.instance = instance self.pattern = Expression + StringEnd() self.stack = []
def grammar(self): def push_first(s, loc, toks): self.stack.append(toks[0]) def push_atom(s, loc, toks): atom = toks[0] number = _parse_number(atom) if number is not None: self.stack.append(number) else: self.stack.append(toks[0].lower()) # constant expressions lpar = Literal('(').suppress() rpar = Literal(')').suppress() addop = Literal('+') | Literal('-') multop = Literal('*') | Literal('/') bitop = Literal('<<') | Literal('>>') | \ Literal('&') | Literal('|') | Literal('^') | \ Literal('<-') | Literal('->') number = (Combine(Literal('$') + Word(nums+'_abcdefABCDEF'))) | \ (Combine(Literal('%') + Word('_01'))) | \ Word('_' + nums) id = Combine(Optional('@') + Optional(':') + Regex(r'[a-zA-Z_]\w*')) expr = Forward() term = Forward() bwterm = Forward() atom = Optional('-') + (number | id) rexpr = (lpar + expr.suppress() + rpar).suppress() # bitwise operators have highest precedence, followed by mul/div and # finally add/sub bwterm << ( atom.setParseAction(push_atom) | rexpr ) + ZeroOrMore( (bitop + bwterm).setParseAction(push_first) ) term << bwterm + ZeroOrMore( (multop + bwterm).setParseAction(push_first) ) expr << term + ZeroOrMore( (addop + term).setParseAction(push_first) ) return expr
def create_bnf(stack): point = Literal(".") comma = Literal(",") e = CaselessLiteral("E") inumber = Word(nums) fnumber = Combine( Word("+-" + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums))) _of = Literal('of') _in = Literal('in') _by = Literal('by') _copy = Literal('copy') _mn = Literal('-n').setParseAction(replace('OA_SubN')) _me = Literal('-e').setParseAction(replace('OA_SubE')) _pn = Literal('+n').setParseAction(replace('OA_AddN')) _pe = Literal('+e').setParseAction(replace('OA_AddE')) _inn = Literal('*n').setParseAction(replace('OA_IntersectN')) _ine = Literal('*e').setParseAction(replace('OA_IntersectE')) regop = (_mn | _me | _pn | _pe | _inn | _ine) lpar = Literal("(").suppress() rpar = Literal(")").suppress() _all = Literal('all').setParseAction(replace('KW_All')) node = Literal('node') nodes = Literal('nodes') element = Literal('element') elements = Literal('elements') group = Literal('group') _set = Literal('set') surface = Literal('surface') ident = Word(alphas + '_.', alphanums + '_.') set_name = Word(nums) | ident function = Word(alphas + '_', alphanums + '_') function = Group(function).setParseAction(join_tokens) region = Combine( Literal('r.') + Word(alphas + '_', '_' + alphas + nums + '.')) region = Group(Optional(_copy, default='nocopy') + region) region.setParseAction(replace('KW_Region', keep=True)) coor = oneOf('x y z') boolop = oneOf('& |') relop = oneOf('< > <= >= != ==') bool_term = (ZeroOrMore('(') + (coor | fnumber) + relop + (coor | fnumber) + ZeroOrMore(')')) relation = Forward() relation << (ZeroOrMore('(') + bool_term + ZeroOrMore(boolop + relation) + ZeroOrMore(')')) relation = Group(relation).setParseAction(join_tokens) nos = Group(nodes + _of + surface).setParseAction(replace('E_NOS')) nir = Group(nodes + _in + relation).setParseAction( replace('E_NIR', keep=True)) nbf = Group(nodes + _by + function).setParseAction( replace('E_NBF', keep=True)) ebf = Group(elements + _by + function).setParseAction( replace('E_EBF', keep=True)) eog = Group(elements + _of + group + Word(nums)).setParseAction( replace('E_EOG', keep=True)) nog = Group(nodes + _of + group + Word(nums)).setParseAction( replace('E_NOG', keep=True)) onir = Group(node + _in + region).setParseAction( replace_with_region('E_ONIR', 2)) ni = Group(node + delimitedList(inumber)).setParseAction( replace('E_NI', keep=True)) ei1 = Group(element + delimitedList(inumber)).setParseAction( replace('E_EI1', keep=True)) etuple = (lpar.suppress() + inumber + comma.suppress() + inumber + rpar.suppress()) ei2 = Group(element + delimitedList(etuple)).setParseAction( replace('E_EI2', keep=True)) noset = Group(nodes + _of + _set + set_name).setParseAction( replace('E_NOSET', keep=True)) eoset = Group(elements + _of + _set + set_name).setParseAction( replace('E_EOSET', keep=True)) region_expression = Forward() atom1 = (_all | region | ni | onir | nos | nir | nbf | ei1 | ei2 | ebf | eog | nog | noset | eoset) atom1.setParseAction(to_stack(stack)) atom2 = (lpar + region_expression.suppress() + rpar) atom = (atom1 | atom2) aux = (regop + region_expression) aux.setParseAction(to_stack(stack)) region_expression << atom + ZeroOrMore(aux) region_expression = StringStart() + region_expression + StringEnd() return region_expression
greater_equal_op = Combine(Literal(">") + Literal("=")) less_op = Combine(Literal("<") + ~Literal("=")) less_equal_op = Combine(Literal("<") + Literal("=")) addop = plus | minus multop = mult | div compop = less_op | greater_op | less_equal_op | greater_equal_op | not_equal_op | equal_op expop = Literal("^") #assign = Literal( "=" ) band = Literal("@") expr = Forward() atom = ((e | floatnumber | integer | ident.setParseAction(assignVar) | fn + lpar + expr + rpar | fn + lpar + expr + colon + expr + rpar | fn + lpar + expr + colon + expr + colon + expr + rpar).setParseAction(pushFirst) | (lpar + expr.suppress() + rpar)) factor = Forward() factor << atom + ((band + factor).setParseAction(pushFirst) | ZeroOrMore( (expop + factor).setParseAction(pushFirst))) term = factor + ZeroOrMore((multop + factor).setParseAction(pushFirst)) addterm = term + ZeroOrMore((addop + term).setParseAction(pushFirst)) expr << addterm + ZeroOrMore((compop + addterm).setParseAction(pushFirst)) bnf = expr pattern = bnf + StringEnd() # map operator symbols to corresponding arithmetic operations opn = { "+": (lambda a, b: numpy.add(a, b)),
def create_bnf( stack ): point = Literal( "." ) comma = Literal( "," ) e = CaselessLiteral( "E" ) inumber = Word( nums ) fnumber = Combine( Word( "+-"+nums, nums ) + Optional( point + Optional( Word( nums ) ) ) + Optional( e + Word( "+-"+nums, nums ) ) ) _of = Literal( 'of' ) _in = Literal( 'in' ) _by = Literal( 'by' ) _copy = Literal( 'copy' ) _mn = Literal( '-n' ).setParseAction( replace( 'OA_SubN' ) ) _me = Literal( '-e' ).setParseAction( replace( 'OA_SubE' ) ) _pn = Literal( '+n' ).setParseAction( replace( 'OA_AddN' ) ) _pe = Literal( '+e' ).setParseAction( replace( 'OA_AddE' ) ) _inn = Literal( '*n' ).setParseAction( replace( 'OA_IntersectN' ) ) _ine = Literal( '*e' ).setParseAction( replace( 'OA_IntersectE' ) ) regop = (_mn | _me | _pn | _pe | _inn | _ine) lpar = Literal( "(" ).suppress() rpar = Literal( ")" ).suppress() _all = Literal( 'all' ).setParseAction( replace( 'KW_All' ) ) node = Literal( 'node' ) nodes = Literal( 'nodes' ) element = Literal( 'element' ) elements = Literal( 'elements' ) group = Literal( 'group' ) surface = Literal( 'surface' ) variable = Word( 'xyz', max = 1 ) | Literal( 'domain' ) any_var = Word( alphas + '_', alphanums + '_' ) | fnumber function = Word( alphas, alphanums + '_' ) function = Group( function ).setParseAction( join_tokens ) region = Combine( Literal( 'r.' ) + Word( alphas, '_' + alphas + nums ) ) region = Group( Optional( _copy, default = 'nocopy' ) + region ) region.setParseAction( replace( 'KW_Region', keep = True ) ) coor = oneOf( 'x y z' ) boolop = oneOf( '& |' ) relop = oneOf( '< > <= >= != ==' ) bool_term = ZeroOrMore( '(' ) + (coor | fnumber ) + relop + (coor | fnumber)\ + ZeroOrMore( ')' ) relation = Forward() relation << ZeroOrMore( '(' )\ + bool_term + ZeroOrMore( boolop + relation )\ + ZeroOrMore( ')' ) relation = Group( relation ).setParseAction( join_tokens ) nos = Group( nodes + _of + surface ).setParseAction( replace( 'E_NOS' ) ) nir = Group( nodes + _in + relation ).setParseAction( \ replace( 'E_NIR', keep = True ) ) nbf = Group( nodes + _by + function ).setParseAction( \ replace( 'E_NBF', keep = True ) ) ebf = Group( elements + _by + function ).setParseAction( \ replace( 'E_EBF', keep = True ) ) eog = Group( elements + _of + group + Word( nums ) ).setParseAction( \ replace( 'E_EOG', keep = True ) ) nog = Group( nodes + _of + group + Word( nums ) ).setParseAction( \ replace( 'E_NOG', keep = True ) ) onir = Group( node + _in + region ).setParseAction( \ replace_with_region( 'E_ONIR', 2 ) ) ni = Group( node + delimitedList( inumber ) ).setParseAction( \ replace( 'E_NI', keep = True ) ) ei1 = Group( element + delimitedList( inumber ) ).setParseAction( \ replace( 'E_EI1', keep = True ) ) etuple = lpar.suppress() + inumber + comma.suppress() \ + inumber + rpar.suppress() ei2 = Group( element + delimitedList( etuple ) ).setParseAction( \ replace( 'E_EI2', keep = True ) ) region_expression = Forward() atom1 = (_all | region | ni | onir | nos | nir | nbf | ei1 | ei2 | ebf | eog | nog) atom1.setParseAction( to_stack( stack ) ) atom2 = (lpar + region_expression.suppress() + rpar) atom = (atom1 | atom2) aux = (regop + region_expression) aux.setParseAction( to_stack( stack ) ) region_expression << atom + ZeroOrMore( aux ) region_expression = StringStart() + region_expression + StringEnd() # region.set_debug() # relation.set_debug() # region_expression.set_debug() return region_expression
not_equal_op = Literal( "!=" ) greater_op = Combine( Literal( ">" ) + ~Literal( "=" ) ) greater_equal_op = Combine( Literal( ">" ) + Literal( "=" ) ) less_op = Combine( Literal( "<" ) + ~Literal( "=" ) ) less_equal_op = Combine( Literal( "<" ) + Literal( "=" ) ) addop = plus | minus multop = mult | div compop = less_op | greater_op | less_equal_op | greater_equal_op | not_equal_op | equal_op expop = Literal( "^" ) #assign = Literal( "=" ) band = Literal( "@" ) expr = Forward() atom = ( ( e | floatnumber | integer | ident.setParseAction( assignVar ) | fn + lpar + expr + rpar | fn + lpar + expr + colon + expr + rpar | fn + lpar + expr + colon + expr + colon + expr + rpar ).setParseAction(pushFirst) | ( lpar + expr.suppress() + rpar ) ) factor = Forward() factor << atom + ( ( band + factor ).setParseAction( pushFirst ) | ZeroOrMore( ( expop + factor ).setParseAction( pushFirst ) ) ) term = factor + ZeroOrMore( ( multop + factor ).setParseAction( pushFirst ) ) addterm = term + ZeroOrMore( ( addop + term ).setParseAction( pushFirst ) ) expr << addterm + ZeroOrMore( ( compop + addterm ).setParseAction( pushFirst ) ) bnf = expr pattern = bnf + StringEnd() # map operator symbols to corresponding arithmetic operations opn = { "+" : ( lambda a,b: numpy.add( a, b ) ), "-" : ( lambda a,b: numpy.subtract( a, b ) ),
def create_bnf(stack): point = Literal(".") comma = Literal(",") e = CaselessLiteral("E") inumber = Word(nums) fnumber = Combine(Word("+-"+nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-"+nums, nums))) _of = Literal('of') _in = Literal('in') _by = Literal('by') _copy = Literal('copy') _mv = Literal('-v').setParseAction(replace('OA_SubV')) _me = Literal('-e').setParseAction(replace('OA_SubE')) _mf = Literal('-f').setParseAction(replace('OA_SubF')) _mc = Literal('-c').setParseAction(replace('OA_SubC')) _ms = Literal('-s').setParseAction(replace('OA_SubS')) _pv = Literal('+v').setParseAction(replace('OA_AddV')) _pe = Literal('+e').setParseAction(replace('OA_AddE')) _pf = Literal('+f').setParseAction(replace('OA_AddF')) _pc = Literal('+c').setParseAction(replace('OA_AddC')) _ps = Literal('+s').setParseAction(replace('OA_AddS')) _inv = Literal('*v').setParseAction(replace('OA_IntersectV')) _ine = Literal('*e').setParseAction(replace('OA_IntersectE')) _inf = Literal('*f').setParseAction(replace('OA_IntersectF')) _inc = Literal('*c').setParseAction(replace('OA_IntersectC')) _ins = Literal('*s').setParseAction(replace('OA_IntersectS')) regop = (_mv | _me | _mf | _mc | _ms | _pv | _pe | _pf | _pc | _ps | _inv | _ine | _inf | _inc | _ins) lpar = Literal("(").suppress() rpar = Literal(")").suppress() _all = Literal('all').setParseAction(replace('KW_All')) vertex = Literal('vertex') vertices = Literal('vertices') cell = Literal('cell') cells = Literal('cells') group = Literal('group') _set = Literal('set') surface = Literal('surface') ident = Word(alphas + '_.', alphanums + '_.') set_name = Word(nums) | ident function = Word(alphas + '_', alphanums + '_') function = Group(function).setParseAction(join_tokens) region = Combine(Literal('r.') + Word(alphas + '_', '_' + alphas + nums + '.')) region = Group(Optional(_copy, default='nocopy') + region) region.setParseAction(replace('KW_Region', keep=True)) coor = oneOf('x y z') boolop = oneOf('& |') relop = oneOf('< > <= >= != ==') bool_term = (ZeroOrMore('(') + (coor | fnumber) + relop + (coor | fnumber) + ZeroOrMore(')')) relation = Forward() relation << (ZeroOrMore('(') + bool_term + ZeroOrMore(boolop + relation) + ZeroOrMore(')')) relation = Group(relation).setParseAction(join_tokens) nos = Group(vertices + _of + surface).setParseAction(replace('E_VOS')) nir = Group(vertices + _in + relation).setParseAction( replace('E_VIR', keep=True)) nbf = Group(vertices + _by + function).setParseAction( replace('E_VBF', keep=True)) ebf = Group(cells + _by + function).setParseAction( replace('E_CBF', keep=True)) eog = Group(cells + _of + group + Word(nums)).setParseAction( replace('E_COG', keep=True)) nog = Group(vertices + _of + group + Word(nums)).setParseAction( replace('E_VOG', keep=True)) onir = Group(vertex + _in + region).setParseAction( replace_with_region('E_OVIR', 2)) ni = Group(vertex + delimitedList(inumber)).setParseAction( replace('E_VI', keep=True)) ei1 = Group(cell + delimitedList(inumber)).setParseAction( replace('E_CI1', keep=True)) etuple = (lpar.suppress() + inumber + comma.suppress() + inumber + rpar.suppress()) ei2 = Group(cell + delimitedList(etuple)).setParseAction( replace('E_CI2', keep=True)) noset = Group(vertices + _of + _set + set_name).setParseAction( replace('E_VOSET', keep=True)) eoset = Group(cells + _of + _set + set_name).setParseAction( replace('E_COSET', keep=True)) region_expression = Forward() atom1 = (_all | region | ni | onir | nos | nir | nbf | ei1 | ei2 | ebf | eog | nog | noset | eoset) atom1.setParseAction(to_stack(stack)) atom2 = (lpar + region_expression.suppress() + rpar) atom = (atom1 | atom2) aux = (regop + region_expression) aux.setParseAction(to_stack(stack)) region_expression << atom + ZeroOrMore(aux) region_expression = StringStart() + region_expression + StringEnd() return region_expression
class EquationGrammar(object): def __init__(self): self._expr_stack=[] # every optional, matchfirst, or ZeroOrMore needs to have a probability defined in this dict self._generators={} # operators plus=Literal("+") minus=Literal("-") mult=Literal("*") div=Literal("/") addsub=plus|minus self._generators[addsub]=flip_gen([plus,minus]) multdiv=mult|div self._generators[multdiv]=flip_gen([mult,div]) # constants pi=CaselessLiteral("PI") e=CaselessLiteral("E") # numbers and identifiers point=Literal(".") integer=Combine(Word("+-"+nums,nums)) self._generators[integer.expr]=num_gen(nums) #ident=Word(alphas,alphas+nums) fnsin=Literal("sin") fncos=Literal("cos") fntan=Literal("tan") fnabs=Literal("abs") fnident=fnsin|fncos|fntan|fnabs self._generators[fnident]=choose_gen([fnsin,fncos,fntan,fnabs],[0.4,0.4,0.1,0.1]) variable=Literal("x") # grouping lparen=Literal("(").suppress() rparen=Literal(")").suppress() # expressions self._expr=Forward() optneg=Optional("-") self._generators[optneg]=flip_gen(["-",None]) functioncall=fnident+lparen+self._expr+rparen atom_base=pi|e|integer|functioncall|variable atom_base_choices=[pi,e,integer,functioncall,variable] self._generators[atom_base]=(choose_gen(atom_base_choices,[0.1,0.1,0.2,0.3,0.3]),\ choose_gen(atom_base_choices,[0.1,0.1,0.4,0.0,0.4])) parenthetical=lparen+self._expr.suppress()+rparen atom=(optneg+atom_base.setParseAction(self.push_first)|parenthetical).setParseAction(\ self.push_uminus) atom_choices=[atom_base,parenthetical] self._generators[atom]=(choose_gen(atom_choices,[0.5,0.5]),choose_gen(atom_choices,[1.0,0.0])) # by defining exponentiation as "atom [ ^ factor ]..." instead of "atom [ ^ atom ]...", we get # right-to-left exponents, instead of left-to-right # that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor=Forward() # parse either curly braces or parenthesis, but only generate curly braces expon=Literal("^") exponlparen=Literal("{").suppress() exponrparen=Literal("}").suppress() mf_lparen=exponlparen|lparen lparen_choices=[exponlparen,lparen] self._generators[mf_lparen]=choose_gen(lparen_choices,[1.0,0.0]) mf_rparen=exponrparen|rparen rparen_choices=[exponrparen,rparen] self._generators[mf_rparen]=choose_gen(rparen_choices,[1.0,0.0]) exponfactor=(expon+mf_lparen.suppress()+factor+mf_rparen.suppress()).setParseAction(\ self.push_first) zom_exponfactor=ZeroOrMore(exponfactor) self._generators[zom_exponfactor]=poisson_gen(exponfactor,0.2) factor << atom + zom_exponfactor multdivfactor=(multdiv+factor).setParseAction(self.push_first) zom_multdivfactor=ZeroOrMore(multdivfactor) self._generators[zom_multdivfactor]=poisson_gen(multdivfactor,0.5) term=factor+zom_multdivfactor addsubterm=(addsub+term).setParseAction(self.push_first) zom_addsubterm=ZeroOrMore(addsubterm) self._generators[zom_addsubterm]=poisson_gen(addsubterm,0.5) self._expr << term+zom_addsubterm self._id_expr=id(self._expr) def set_expr_stack(self,expr_stack): self._expr_stack=expr_stack def push_first(self,strg,loc,toks): self._expr_stack.append(toks[0]) def push_uminus(self,strg,loc,toks): if toks and toks[0]=='-': self._expr_stack.append('unary -') def parse_string(self,s): return self._expr.parseString(s) @property def generators(self): return self._generators @property def id_expr(self): return self._id_expr @property def expr_stack(self): return self._expr_stack @property def root(self): return self._expr
def _BNF(self): base16 = Literal("$") hex = Combine(base16 + Word(hexnums + "_")) base4 = Literal("%%") quaternary = Combine(base4 + Word("0123_")) base2 = Literal("%") binary = Combine(base2 + Word("01_")) plusminus = Literal("+") | Literal("-") integer = Combine(Optional(plusminus) + Word(nums + "_")) name_token = Combine( Optional(Literal(":") | Literal("@")) + Word("_" + alphas, "_" + alphanums)) name_token.setParseAction(self._mark_name_token) lparens = Literal("(").suppress() rparens = Literal(")").suppress() # op0 = Literal("@") op1 = (Literal("^^") | Literal("||") | Literal("|<") | Literal(">|") | Literal("!")).setParseAction(self._mark_unary) op2 = Literal("->") | Literal("<-") | Literal(">>") | Literal( "<<") | Literal("~>") | Literal("><") op3 = Literal("&") op4 = Literal("|") | Literal("^") op5 = Literal("**") | Literal("*") | Literal("//") | Literal("/") op6 = Literal("+") | Literal("-") op7 = Literal("#>") | Literal("<#") op8 = Literal("<") | Literal(">") | Literal("<>") | Literal( "==") | Literal("=<") | Literal("=>") op9 = Literal("NOT").setParseAction(self._mark_unary) op10 = Literal("AND") op11 = Literal("OR") op12 = Literal(",") expr = Forward() atom = name_token | hex | quaternary | binary | integer | quotedString atom.setParseAction(self._push) atom = atom | (lparens + expr.suppress() + rparens) # term0 = atom + ZeroOrMore((op0 + atom) .setParseAction(self._push)) # term1 = term0 + ZeroOrMore((op1 + term0) .setParseAction(self._push)) term1 = atom + ZeroOrMore((op1 + atom).setParseAction(self._push)) term2 = term1 + ZeroOrMore((op2 + term1).setParseAction(self._push)) term3 = term2 + ZeroOrMore((op3 + term2).setParseAction(self._push)) term4 = term3 + ZeroOrMore((op4 + term3).setParseAction(self._push)) term5 = term4 + ZeroOrMore((op5 + term4).setParseAction(self._push)) term6 = term5 + ZeroOrMore((op6 + term5).setParseAction(self._push)) term7 = term6 + ZeroOrMore((op7 + term6).setParseAction(self._push)) term8 = term7 + ZeroOrMore((op8 + term7).setParseAction(self._push)) term9 = term8 + ZeroOrMore((op9 + term8).setParseAction(self._push)) term10 = term9 + ZeroOrMore((op10 + term9).setParseAction(self._push)) term11 = term10 + ZeroOrMore( (op11 + term10).setParseAction(self._push)) expr << term11 + ZeroOrMore((op12 + term11).setParseAction(self._push)) return expr
def __init__(self): self.ae = False self.local_dict = None self.f = None self.user_functions = None self.expr_stack = [] self.texpr_stack = [] # Define constants self.constants = {} # Define Operators self.opn = {"+": operator.add, "-": operator.sub, "*": operator.mul, "/": operator.truediv, ">": operator.gt, ">=": operator.ge, "<": operator.lt, "<=": operator.le, "==": operator.eq, "!=": operator.ne, "|": operator.or_, "&": operator.and_, "!": operator.inv} # Define xarray DataArray operators with 1 input parameter self.xfn1 = {"angle": xr.ufuncs.angle, "arccos": xr.ufuncs.arccos, "arccosh": xr.ufuncs.arccosh, "arcsin": xr.ufuncs.arcsin, "arcsinh": xr.ufuncs.arcsinh, "arctan": xr.ufuncs.arctan, "arctanh": xr.ufuncs.arctanh, "ceil": xr.ufuncs.ceil, "conj": xr.ufuncs.conj, "cos": xr.ufuncs.cos, "cosh": xr.ufuncs.cosh, "deg2rad": xr.ufuncs.deg2rad, "degrees": xr.ufuncs.degrees, "exp": xr.ufuncs.exp, "expm1": xr.ufuncs.expm1, "fabs": xr.ufuncs.fabs, "fix": xr.ufuncs.fix, "floor": xr.ufuncs.floor, "frexp": xr.ufuncs.frexp, "imag": xr.ufuncs.imag, "iscomplex": xr.ufuncs.iscomplex, "isfinite": xr.ufuncs.isfinite, "isinf": xr.ufuncs.isinf, "isnan": xr.ufuncs.isnan, "isreal": xr.ufuncs.isreal, "log": xr.ufuncs.log, "log10": xr.ufuncs.log10, "log1p": xr.ufuncs.log1p, "log2": xr.ufuncs.log2, "rad2deg": xr.ufuncs.rad2deg, "radians": xr.ufuncs.radians, "real": xr.ufuncs.real, "rint": xr.ufuncs.rint, "sign": xr.ufuncs.sign, "signbit": xr.ufuncs.signbit, "sin": xr.ufuncs.sin, "sinh": xr.ufuncs.sinh, "sqrt": xr.ufuncs.sqrt, "square": xr.ufuncs.square, "tan": xr.ufuncs.tan, "tanh": xr.ufuncs.tanh, "trunc": xr.ufuncs.trunc} # Define xarray DataArray operators with 2 input parameter self.xfn2 = {"arctan2": xr.ufuncs.arctan2, "copysign": xr.ufuncs.copysign, "fmax": xr.ufuncs.fmax, "fmin": xr.ufuncs.fmin, "fmod": xr.ufuncs.fmod, "hypot": xr.ufuncs.hypot, "ldexp": xr.ufuncs.ldexp, "logaddexp": xr.ufuncs.logaddexp, "logaddexp2": xr.ufuncs.logaddexp2, "logicaland": xr.ufuncs.logical_and, "logicalnot": xr.ufuncs.logical_not, "logicalor": xr.ufuncs.logical_or, "logicalxor": xr.ufuncs.logical_xor, "maximum": xr.ufuncs.maximum, "minimum": xr.ufuncs.minimum, "nextafter": xr.ufuncs.nextafter} # Define non-xarray DataArray operators with 2 input parameter self.fn2 = {"percentile": np.percentile} # Define xarray DataArray reduction operators self.xrfn = {"all": xr.DataArray.all, "any": xr.DataArray.any, "argmax": xr.DataArray.argmax, "argmin": xr.DataArray.argmin, "max": xr.DataArray.max, "mean": xr.DataArray.mean, "median": xr.DataArray.median, "min": xr.DataArray.min, "prod": xr.DataArray.prod, "sum": xr.DataArray.sum, "std": xr.DataArray.std, "var": xr.DataArray.var} # Define non-xarray DataArray operators with 2 input parameter self.xcond = {"<": np.percentile} # Define Grammar point = Literal(".") e = CaselessLiteral("E") fnumber = Combine(Word("+-"+nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-"+nums, nums))) variable = Word(alphas, alphas+nums+"_$") seq = Literal("=") b_not = Literal("~") plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") gt = Literal(">") gte = Literal(">=") lt = Literal("<") lte = Literal("<=") eq = Literal("==") neq = Literal("!=") b_or = Literal("|") b_and = Literal("&") l_not = Literal("!") lpar = Literal("(").suppress() rpar = Literal(")").suppress() comma = Literal(",") colon = Literal(":") lbrac = Literal("[") rbrac = Literal("]") lcurl = Literal("{") rcurl = Literal("}") qmark = Literal("?") scolon = Literal(";") addop = plus | minus multop = mult | div sliceop = colon compop = gte | lte | gt | lt eqop = eq | neq bitcompop = b_or | b_and bitnotop = b_not logicalnotop = l_not assignop = seq expop = Literal("^") expr = Forward() indexexpr = Forward() atom = (Optional("-") + (variable + seq + expr).setParseAction(self.push_assign) | indexexpr.setParseAction(self.push_index) | (lpar + expr + qmark.setParseAction(self.push_ternary1) + expr + scolon.setParseAction(self.push_ternary2) + expr + rpar).setParseAction(self.push_ternary) | (lpar + expr + qmark + expr + scolon + expr + rpar).setParseAction(self.push_ternary) | (logicalnotop + expr).setParseAction(self.push_ulnot) | (bitnotop + expr).setParseAction(self.push_unot) | (minus + expr).setParseAction(self.push_uminus) | (variable + lcurl + expr + rcurl).setParseAction(self.push_mask) | (variable + lpar + expr + (comma + expr)*3 + rpar).setParseAction(self.push_expr4) | (variable + lpar + expr + (comma + expr)*2 + rpar).setParseAction(self.push_expr3) | (variable + lpar + expr + comma + expr + rpar).setParseAction(self.push_expr2) | (variable + lpar + expr + rpar | variable).setParseAction(self.push_expr1) | fnumber.setParseAction(self.push_expr) | (lpar + expr + ZeroOrMore(comma + expr).setParseAction(self.get_tuple) + rpar).setParseAction(self.push_tuple) | (lpar + expr.suppress() + rpar).setParseAction(self.push_uminus)) # Define order of operations for operators factor = Forward() factor << atom + ZeroOrMore((expop + factor).setParseAction(self.push_op)) term = factor + ZeroOrMore((multop + factor).setParseAction(self.push_op)) term2 = term + ZeroOrMore((addop + term).setParseAction(self.push_op)) term3 = term2 + ZeroOrMore((sliceop + term2).setParseAction(self.push_op)) term4 = term3 + ZeroOrMore((compop + term3).setParseAction(self.push_op)) term5 = term4 + ZeroOrMore((eqop + term4).setParseAction(self.push_op)) term6 = term5 + ZeroOrMore((bitcompop + term5).setParseAction(self.push_op)) expr << term6 + ZeroOrMore((assignop + term6).setParseAction(self.push_op)) # Define index operators colon_expr = (colon + FollowedBy(comma) ^ colon + FollowedBy(rbrac)).setParseAction(self.push_colon) range_expr = colon_expr | expr | colon indexexpr << (variable + lbrac + delimitedList(range_expr, delim=',') + rbrac).setParseAction(self.push_expr) self.parser = expr
class ExpressionParser(ParserElement): """Base class for parsing mathematical expressions from a string format into a symbolic representation of the mathematical operation expressed by it. Parameters ---------- expr_str Mathematical expression in string format. args Dictionary containing all variables and functions needed to evaluate the expression. backend Backend instance in which to parse all variables and operations. See `pyrates.backend.numpy_backend.NumpyBackend` for a full documentation of the backends methods and attributes. kwargs Additional keyword arguments to be passed to the backend functions. Attributes ---------- lhs Boolean, indicates whether expression is left-hand side or right-hand side of an equation rhs PyRatesOp for the evaluation of the right-hand side of the equation args Dictionary containing the variables of an expression solve Only relevant for lhs expressions. If True, lhs will be treated as a first-order ordinary differential equation. expr_str String representation of the mathematical expression expr Symbolic (Pyparsing-based) representation of mathematical expression. expr_stack List representation of the syntax tree of the (parsed) mathematical expression. expr_list List representation of the mathematical expression. op Operator for calculating the mathematical expression (symbolic representation). _op_tmp Helper variable for building `op`. ops Dictionary containing all mathematical operations that are allowed for a specific instance of the `ExpressionParser` (e.g. +, -, *, /). funcs Dictionary containing all additional functions that can be used within mathematical expressions with a specific instance of the `ExpressionParser` (e.g. sum(), reshape(), float32()). dtypes Dictionary containing all data-types that can be used within mathematical expressions with a specific instance of the `ExpressionParser` (e.g. float32, bool, int32). """ def __init__(self, expr_str: str, args: dict, backend: tp.Any, **kwargs) -> None: """Instantiates expression parser. """ # call super init ################# super().__init__() # bind attributes to instance ############################# # input arguments self.vars = args.copy() self.var_map = {} self.backend = backend self.parser_kwargs = kwargs self.lhs, self.rhs, self._diff_eq, self._assign_type, self.lhs_key = self._preprocess_expr_str( expr_str) # add functions from args dictionary to backend, if passed for key, val in args.items(): if callable(val): self.backend.ops[key] = val # additional attributes self.expr_str = expr_str self.expr = None self.expr_stack = [] self.expr_list = [] self.op = None self._finished_rhs = False self._instantaneous = kwargs.pop('instantaneous', False) # define algebra ################ if not self.expr: # general symbols point = Literal(".") comma = Literal(",") colon = Literal(":") e = CaselessLiteral("E") pi = CaselessLiteral("PI") # parentheses par_l = Literal("(") par_r = Literal(")").setParseAction(self._push_first) idx_l = Literal("[") idx_r = Literal("]") # basic mathematical operations plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") mod = Literal("%") dot = Literal("@") exp_1 = Literal("^") exp_2 = Combine(mult + mult) transp = Combine(point + Literal("T")) inv = Combine(point + Literal("I")) # numeric types num_float = Combine( Word("-" + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("-" + nums, nums))) num_int = Word("-" + nums, nums) # variables and functions name = Word(alphas, alphas + nums + "_$") func_name = Combine(name + par_l, adjacent=True) # math operation groups op_add = plus | minus op_mult = mult | div | dot | mod op_exp = exp_1 | exp_2 | inv | transp # logical operations greater = Literal(">") less = Literal("<") equal = Combine(Literal("=") + Literal("=")) unequal = Combine(Literal("!") + Literal("=")) greater_equal = Combine(Literal(">") + Literal("=")) less_equal = Combine(Literal("<") + Literal("=")) # logical operations group op_logical = greater_equal | less_equal | unequal | equal | less | greater # pre-allocations self.expr = Forward() exponential = Forward() index_multiples = Forward() # basic organization units index_start = idx_l.setParseAction(self._push_first) index_end = idx_r.setParseAction(self._push_first) index_comb = colon.setParseAction(self._push_first) arg_comb = comma.setParseAction(self._push_first) arg_tuple = par_l + ZeroOrMore(self.expr.suppress() + Optional(arg_comb)) + par_r func_arg = arg_tuple | self.expr.suppress() # basic computation unit atom = (func_name + Optional(func_arg.suppress()) + ZeroOrMore(arg_comb.suppress() + func_arg.suppress()) + par_r.suppress() | name | pi | e | num_float | num_int).setParseAction(self._push_neg_or_first) | \ (par_l.setParseAction(self._push_last) + self.expr.suppress() + par_r).setParseAction(self._push_neg) # apply indexing to atoms indexed = (Optional(minus) + atom).setParseAction(self._push_neg) + \ ZeroOrMore((index_start + index_multiples + index_end)) index_base = (self.expr.suppress() | index_comb) index_full = index_base + ZeroOrMore( (index_comb + index_base)) + ZeroOrMore(index_comb) index_multiples << index_full + ZeroOrMore((arg_comb + index_full)) # hierarchical relationships between mathematical and logical operations boolean = indexed + Optional( (op_logical + indexed).setParseAction(self._push_first)) exponential << boolean + ZeroOrMore( (op_exp + Optional(exponential)).setParseAction( self._push_first)) factor = exponential + ZeroOrMore( (op_mult + exponential).setParseAction(self._push_first)) expr = factor + ZeroOrMore( (op_add + factor).setParseAction(self._push_first)) self.expr << expr #(Optional(minus) + expr).setParseAction(self._push_neg) def parse_expr(self) -> tuple: """Parses string-based mathematical expression/equation. Returns ------- tuple left-hand side, right-hand side and variables of the parsed equation. """ # extract symbols and operations from equations right-hand side self.expr_list = self.expr.parseString(self.rhs) self._check_parsed_expr(self.rhs) # parse rhs into backend self.rhs = self.parse(self.expr_stack[:]) # post rhs parsing steps if hasattr(self.rhs, 'vtype') or "float" in str(type( self.rhs)) or "int" in str(type(self.rhs)): self.rhs = self.backend.add_op('no_op', self.rhs, **self.parser_kwargs) self.clear() self._finished_rhs = True # extract symbols and operations from left-hand side self.expr_list = self.expr.parseString(self.lhs) self._check_parsed_expr(self.lhs) # parse lhs into backend self._update_lhs() return self.lhs, self.rhs, self.vars def parse(self, expr_stack: list) -> tp.Any: """Parse elements in expression stack into the backend. Parameters ---------- expr_stack Ordered list with expression variables and operations. Needs to be processed from last to first item. Returns ------- tp.Any Parsed expression stack element (object type depends on the backend). """ # get next operation from stack op = expr_stack.pop() # check type of operation ######################### if op == '-one': # multiply expression by minus one self.op = self.backend.add_op('*', self.parse(expr_stack), -1, **self.parser_kwargs) elif op in ["*=", "/=", "+=", "-=", "="]: # collect rhs op1 = self.parse(expr_stack) # collect lhs indexed_lhs = True if "]" in expr_stack else False op2 = self.parse(expr_stack) # combine elements via mathematical/boolean operator if indexed_lhs: self.op = self._apply_idx(op=op2[0], idx=op2[1], update=op1, update_type=op, **self.parser_kwargs) else: self.op = self.backend.add_op(op, op2, op1, **self.parser_kwargs) elif op in "+-/**^@<=>=!==%": # collect elements to combine op2 = self.parse(expr_stack) op1 = self.parse(expr_stack) # combine elements via mathematical/boolean operator self.op = self.backend.add_op(op, op1, op2, **self.parser_kwargs) elif ".T" == op or ".I" == op: # transpose/invert expression self.op = self.backend.add_op(op, self.parse(expr_stack), **self.parser_kwargs) elif op == "]": # parse indices indices = [] while len(expr_stack) > 0 and expr_stack[-1] != "[": index = [] while len(expr_stack) > 0 and expr_stack[-1] not in ",[": if expr_stack[-1] == ":": index.append(expr_stack.pop()) else: try: int(expr_stack[-1]) index.append(expr_stack.pop()) except ValueError: tmp = self._finished_rhs self._finished_rhs = False index.append(self.parse(expr_stack)) self._finished_rhs = tmp indices.append(index[::-1]) if expr_stack[-1] == ",": expr_stack.pop() expr_stack.pop() # build string-based representation of idx if 'idx' not in self.vars.keys(): self.vars['idx'] = {} idx = "" i = 0 for index in indices[::-1]: for ind in index: if type(ind) == str: idx += ind elif isinstance(ind, Number): idx += f"{ind}" else: self.vars['idx'][f'idx_var_{i}'] = ind idx += f"idx_var_{i}" i += 1 idx += "," idx = idx[0:-1] # extract variable and apply idx if its a rhs variable. Else return variable and index if self._finished_rhs: op = expr_stack.pop(-1) if op in self.vars: op_to_idx = self.vars[op] else: op_to_idx = self.parse([op]) self.op = (op_to_idx, idx) else: op_to_idx = self.parse(expr_stack) op_idx = self._apply_idx(op_to_idx, idx, **self.parser_kwargs) self.op = op_idx elif op == "PI": # return float representation of pi self.op = math.pi elif op == "E": # return float representation of e self.op = math.e elif op in self.vars: # extract constant/variable from args dict self.op = self.vars[op] elif op[-1] == "(": expr_stack.pop(-1) # parse arguments args = [] while len(expr_stack) > 0: args.append(self.parse(expr_stack)) if len(expr_stack) == 0 or expr_stack[-1] != ",": break else: expr_stack.pop() # apply function to arguments try: if len(args) == 1: self.op = self.backend.add_op(op[0:-1], args[0], **self.parser_kwargs) else: self.op = self.backend.add_op(op[0:-1], *tuple(args[::-1]), **self.parser_kwargs) except KeyError: if any([ "float" in op, "bool" in op, "int" in op, "complex" in op ]): self.op = self.backend.add_op('cast', args[0], op[0:-1], **self.parser_kwargs) else: raise KeyError( f"Undefined function in expression: {self.expr_str}. {op[0:-1]} needs to be " f"provided in arguments dictionary.") elif op == ")": # check whether expression in parenthesis is a group of arguments to a function start_par = -1 found_end = 0 while found_end < 1: if "(" in expr_stack[start_par]: found_end += 1 if ")" in expr_stack[start_par]: found_end -= 1 start_par -= 1 if "," in expr_stack[start_par + 1:]: args = [] while True: args.append(self.parse(expr_stack)) if expr_stack[-1] == ",": expr_stack.pop(-1) elif expr_stack[-1] == "(": expr_stack.pop(-1) break else: break self.op = args[::-1] else: self.op = self.parse(expr_stack) expr_stack.pop(-1) elif any([op == "True", op == "true", op == "False", op == "false"]): # return boolean self.op = True if op in "Truetrue" else False elif any(["float" in op, "bool" in op, "int" in op, "complex" in op]): expr_stack.pop(-1) # extract data type try: self.op = self.backend.add_op('cast', self.parse(expr_stack), op[0:-1], **self.parser_kwargs) except AttributeError: raise AttributeError( f"Datatype casting error in expression: {self.expr_str}. " f"{op[0:-1]} is not a valid data-type for this parser.") elif "." in op: self.op = float(op) elif op.isnumeric(): self.op = int(op) elif op[0].isalpha(): if self._finished_rhs: if op == 'rhs': self.op = self.rhs else: shape = self.rhs.shape if hasattr(self.rhs, 'shape') else () dtype = self.rhs.dtype if hasattr( self.rhs, 'dtype') else type(self.rhs) self.op = self.backend.add_var(vtype='state_var', name=op, shape=shape, dtype=dtype, **self.parser_kwargs) self.vars[op] = self.op elif op == 't': self.op = self.backend.add_var(vtype='state_var', name=op, shape=(), dtype='float', value=0.0, **self.parser_kwargs) else: raise ValueError( f"Undefined variable detected in expression: {self.expr_str}. {op} was not found " f"in the respective arguments dictionary.") else: raise ValueError( f"Undefined operation detected in expression: {self.expr_str}. {op} cannot be " f"interpreted by this parser.") return self.op def clear(self): """Clears expression list and stack. """ self.expr_list.clear() self.expr_stack.clear() def _update_lhs(self): """Applies update to left-hand side of equation. For differential equations, different solving schemes are available. """ # update left-hand side of equation ################################### diff_eq = self._diff_eq if diff_eq: lhs = self.vars[self.lhs_key] # update state variable vectors y_idx = self._append_to_var(var_name='y', val=lhs) self._append_to_var(var_name='y_delta', val=lhs) # extract left-hand side variable from state variable vector lhs_indexed = self.backend._create_op( 'index', self.backend.ops['index']['name'], self.vars['y'], y_idx) lhs_indexed.short_name = lhs.short_name if 'y_' in lhs_indexed.value: del_start, del_end = lhs_indexed.value.index( '_'), lhs_indexed.value.index('[') lhs_indexed.value = lhs_indexed.value[: del_start] + lhs_indexed.value[ del_end:] self.vars[self.lhs_key] = lhs_indexed # assign rhs to state var delta vector self.rhs = self.backend.add_op('=', self.vars['y_delta'], self.rhs, y_idx, **self.parser_kwargs) # broadcast rhs and lhs and store results in backend self.backend.state_vars.append(lhs.name) self.backend.vars[lhs.name] = self.vars[self.lhs_key] self.rhs.state_var = lhs.name else: # simple update if not self._instantaneous: self.backend.next_layer() indexed_lhs = "]" in self.expr_stack self.lhs = self.parse(self.expr_stack + ['rhs', self._assign_type]) if not indexed_lhs: self.backend.lhs_vars.append(self.vars[self.lhs_key].name) if not self._instantaneous: self.backend.previous_layer() def _preprocess_expr_str(self, expr: str) -> tuple: """Turns differential equations into simple algebraic equations using a certain solver scheme and extracts left-hand side, right-hand side and update type of the equation. Parameters ---------- expr Equation in string format. Returns ------- tuple Contains left hand side, right hand side and left hand side update type """ # collect equation specifics ############################ # split equation into lhs and rhs and assign type lhs, rhs, assign_type = split_equation(expr) if not assign_type: return self._preprocess_expr_str(f"x = {expr}") # for the left-hand side, check whether it includes a differential operator if "d/dt" in lhs: diff_eq = True lhs_split = lhs.split('*') lhs = "".join(lhs_split[1:]) elif "'" in lhs: diff_eq = True lhs = lhs.replace("'", "") elif "d" in lhs and "/dt" in lhs: diff_eq = True lhs = lhs.split('/dt')[0] lhs = lhs.replace("d", "", count=1) else: diff_eq = False # get clean name of lhs lhs_key = lhs.split('[')[0] lhs_key = lhs_key.replace(' ', '') lhs = lhs.replace(' ', '') # store equation specifics if diff_eq and assign_type != '=': raise ValueError( f'Wrong assignment method for equation: {expr}. ' f'A differential equation cannot be combined with an assign type other than `=`.' ) return lhs, rhs, diff_eq, assign_type, lhs_key def _push_first(self, strg, loc, toks): """Push tokens in first-to-last order to expression stack. """ self.expr_stack.append(toks[0]) def _push_neg(self, strg, loc, toks): """Push negative one multiplier if on first position in toks. """ if toks and toks[0] == '-': self.expr_stack.append('-one') def _push_neg_or_first(self, strg, loc, toks): """Push neg one multipler to expression stack if on first position in toks, else push toks from first-to-last. """ if toks and toks[0] == '-': self.expr_stack.append('-one') else: self.expr_stack.append(toks[0]) def _push_last(self, strg, loc, toks): """Push tokens in last-to-first order to expression stack. """ self.expr_stack.append(toks[-1]) def _apply_idx(self, op: tp.Any, idx: tp.Any, update: tp.Optional[tp.Any] = None, update_type: tp.Optional[str] = None, **kwargs) -> tp.Any: """Apply index idx to operation op. Parameters ---------- op Operation to be indexed. idx Index to op. update Update to apply to op at idx. update_type Type of left-hand side update (e.g. `=` or `+=`). kwargs Additional keyword arguments to be passed to the indexing functions. Returns ------- tp.Any Result of applying idx to op. """ kwargs.update(self.parser_kwargs) # get constants/variables that are part of the index args = [] if idx in self.vars['idx']: idx = self.vars['idx'].pop(idx) if type(idx) is str: idx_old = idx idx = [] for idx_tmp in idx_old.split(','): for idx_tmp2 in idx_tmp.split(':'): idx.append(idx_tmp2) if idx_tmp2 in self.vars['idx']: idx_var = self.vars['idx'].pop(idx_tmp2) if not hasattr(idx_var, 'short_name'): if hasattr(idx_var, 'shape') and tuple( idx_var.shape): idx_var = idx_var[0] idx[-1] = f"{idx_var}" else: if "_evaluated" in idx_var.short_name: idx[-1] = f"{idx_var.numpy()}" else: idx[-1] = idx_var.short_name args.append(idx_var) idx.append(':') idx.pop(-1) idx.append(',') idx.pop(-1) idx = "".join(idx) return self.backend.apply_idx(op, idx, update, update_type, *tuple(args)) def _check_parsed_expr(self, expr_str: str) -> None: """check whether parsing of expression string was successful. Parameters ---------- expr_str Expression that has been attempted to be parsed. """ for sub_str in sorted(self.expr_stack, key=len)[::-1]: if sub_str == 'E': sub_str = 'e' expr_str = expr_str.replace(sub_str, "") expr_str = expr_str.replace(" ", "") expr_str = expr_str.replace("(", "") expr_str = expr_str.replace(")", "") expr_str = expr_str.replace("-", "") if len(expr_str) > 0: raise ValueError( f"Error while parsing expression: {self.expr_str}. {expr_str} could not be parsed." ) def _append_to_var(self, var_name: str, val: tp.Any) -> str: # create variable vector if not existent if var_name not in self.vars: self.vars[var_name] = self.backend.add_var(vtype='state_var', name=var_name, shape=(), dtype=val.dtype, value=[], squeeze=False) # append left-hand side variable to variable vector var = self.backend.remove_var(var_name) var_val = var.numpy().tolist() append_val = val.numpy().tolist() if sum(var.shape) and sum(val.shape): new_val = var_val + append_val elif sum(var.shape): new_val = var_val + [append_val] else: new_val = append_val if type(append_val) is list else [append_val] # add updated variable to backend self.vars[var_name] = self.backend.add_var(vtype='state_var', name=var_name, value=new_val, shape=(len(new_val), ), dtype=var.dtype, squeeze=False) # return index to variable vector to retrieve appended values i1 = len(var_val) + self.backend.idx_start i2 = len(new_val) + self.backend.idx_start return f"{i1}:{i2}" if i2 - i1 > 1 else f"{i1}" @staticmethod def _compare(x: tp.Any, y: tp.Any) -> bool: """Checks whether x and y are equal or not. """ test = x == y if hasattr(test, 'shape'): test = test.any() return test
def init_grammar(self): LPAR, RPAR, LBRACK, RBRACK, LBRACE, RBRACE, SEMI, COMMA, EQUAL, COLON = map(Suppress, "()[]{};,=:") USE = Keyword("use") MODULE = Keyword("module") IF = Keyword("if") FOR = Keyword("for") ELSE = Keyword("else") TRUE = Keyword("true") FALSE = Keyword("false") UNDEF = Keyword("undef") mul_operator = oneOf("* /") add_operator = oneOf("+ -") exp_operator = Literal( "^" ) boolOperand = ( TRUE | FALSE ) boolOperand.setParseAction(BoolOperand) boolExpr = infixNotation( boolOperand, [ ("not", 1, opAssoc.RIGHT, BoolNot), ("and", 2, opAssoc.LEFT, BoolAnd), ("or", 2, opAssoc.LEFT, BoolOr), ]) identifier = Word("$" + alphas + "_", alphanums + "_") plusorminus = Literal('+') | Literal('-') numberal = Word(nums) e = CaselessLiteral('E') integer = Combine( Optional(plusorminus) + numberal ) floatnumber = Combine( integer + Optional( Literal('.') + Optional(numberal) ) + Optional( e + integer ) ) number = ( integer | floatnumber ) calculation = Forward() atom = ( ( e | floatnumber | integer | identifier ).setParseAction( self.pushFirst ) | ( LPAR + calculation.suppress() + RPAR ) ) factor = Forward() factor << atom + ZeroOrMore( ( exp_operator + factor ).setParseAction( self.pushFirst ) ) term = factor + ZeroOrMore( ( mul_operator + factor ).setParseAction( self.pushFirst ) ) calculation << term + ZeroOrMore( ( add_operator + term ).setParseAction( self.pushFirst ) ) calculation.setParseAction(self.calculate) constant = number.setParseAction(self.constant_action) modifier_name = ( Keyword("translate") | Keyword("rotate") | Keyword("scale") | Keyword("simplify") ) use = (USE + identifier("name") + SEMI).setParseAction(self.use_action) expression = Forward() arguments = delimitedList(expression("exp").setParseAction(self.argument_action)) module_call = ((identifier("name") + FollowedBy("(")).setParseAction(self.module_call_prepare_action) + LPAR + Optional(arguments)("args") + RPAR) module_call_statement = (module_call + SEMI).setParseAction(self.module_call_action) primitive_argument_assignment_value = (calculation | boolExpr) primitive_argument_assignment = (identifier("variable") + EQUAL + primitive_argument_assignment_value).setParseAction(self.primitive_argument_assignment_action) primitive_argument = (primitive_argument_assignment | expression("exp")) primitive_argument_list = delimitedList(primitive_argument.setParseAction(self.argument_action)) modifier = ( (modifier_name + FollowedBy("(")).setParseAction(self.primitive_modifier_prepare_action) + LPAR + Optional(primitive_argument_list)("args") + RPAR).setParseAction(self.primitive_modifier_action) primitive_call_statement = ( ZeroOrMore(modifier)("modifiers") + (identifier("name") + FollowedBy("(")).setParseAction(self.primitive_call_prepare_action) + LPAR + Optional(primitive_argument_list)("args") + RPAR + SEMI).setParseAction(self.primitive_call_action) expression << (boolExpr | calculation).setParseAction(self.debug_action)#.setParseAction(lambda x: x[0]) statement = Forward() assign_statement = (identifier("variable") + EQUAL + expression("expression") + SEMI).setParseAction(self.assign_action) modifier_scope = ( (ZeroOrMore(modifier)("modifiers") + identifier("name") + LPAR + Optional(primitive_argument_list)("args") + RPAR + FollowedBy("{")).setParseAction(self.modifier_scope_prepare_action) + LBRACE + ZeroOrMore(statement) + RBRACE ).setParseAction(self.modifier_scope_action) vector = (LBRACK + calculation + COLON + calculation + RBRACK).setParseAction(self.vector_action) for_loop_scope = ( ZeroOrMore(modifier)("modifiers") + ( FOR + LPAR + identifier("index") + EQUAL + vector("loop_range") + RPAR + FollowedBy("{") ).setParseAction(self.begin_for_loop_scope) + LBRACE + ZeroOrMore(statement)("body") + RBRACE ).setParseAction(self.for_loop_scope_action) statement << ( for_loop_scope | primitive_call_statement | module_call_statement | Suppress(assign_statement) | modifier_scope ) body = OneOrMore(statement) self.program = (ZeroOrMore(use) + body).setParseAction(self.program_end_action)
def __init__(self): # define grammar point = Literal('.') e = CaselessLiteral('E') plusorminus = Literal('+') | Literal('-') number = Word(nums) integer = Combine( Optional(plusorminus) + number ) floatnumber = Combine( integer + Optional( point + Optional(number) ) + Optional( e + integer ) ) ident = Word('$',alphanums + '_') plus = Literal( "+" ) minus = Literal( "-" ) mult = Literal( "*" ) div = Literal( "/" ) lpar = Literal( "(" ).suppress() rpar = Literal( ")" ).suppress() addop = plus | minus multop = mult | div expop = Literal( "^" ) expr = Forward() def defineFunction(name, parameterCount=None): keyword = CaselessKeyword(name).setParseAction(self.pushEnd) funcPattern = keyword + lpar if parameterCount == None: funcPattern += Optional(expr+ZeroOrMore(Literal(',')+expr)) elif parameterCount > 0: funcPattern += expr for i in range(parameterCount-1): funcPattern += Literal(',') + expr funcPattern += rpar return funcPattern.setParseAction(self.pushFirst) maxFunc = defineFunction('max') minFunc = defineFunction('min') casesFunc = defineFunction('cases') cases1Func = defineFunction('cases1', parameterCount = 5) cases2Func = defineFunction('cases2', parameterCount = 8) cases3Func = defineFunction('cases3', parameterCount = 11) cases333Func = defineFunction('cases333', parameterCount = 11) round3downFunc = defineFunction('round3down', parameterCount = 1) #func = (funcident.setParseAction(self.pushEnd)+lpar +Optional(expr+ZeroOrMore(Literal(',')+expr))+rpar).setParseAction(self.pushFirst) atom = ( maxFunc | minFunc | casesFunc | cases1Func | cases2Func | cases3Func | cases333Func| round3downFunc | ( e | floatnumber | integer | ident ).setParseAction(self.pushFirst) | ( lpar + expr.suppress() + rpar ) ) factor = Forward() factor << atom + ZeroOrMore( ( expop + factor ).setParseAction( self.pushFirst ) ) term = factor + ZeroOrMore( ( multop + factor ).setParseAction( self.pushFirst ) ) expr << term + ZeroOrMore( ( addop + term ).setParseAction( self.pushFirst ) ) self.pattern = expr + StringEnd() # map operator symbols to corresponding arithmetic operations self.opn = { "+" : self.handleNone( lambda a,b: a + b ), "-" : self.handleNone( lambda a,b: a - b ), "*" : self.handleNone( lambda a,b: a * b, none_survives=True ), "/" : self.handleNone( lambda a,b: a / b, none_survives=True ), "^" : self.handleNone( lambda a,b: a ** b, none_survives=True ) } self.functions = { 'max': max, 'min': self.min, 'cases': self.cases, 'cases1': self.cases1, 'cases2': self.cases2, 'cases3': self.cases3, 'cases333': self.cases333, 'round3down': self.round3down }
def BNF(): """ expop :: '^' multop :: '*' | '/' addop :: '+' | '-' integer :: ['+' | '-'] '0'..'9'+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* equation:: expr [equality expr]* logic :: equation [logicalop equation]* """ global bnf if not bnf: point = Literal(".") e = CaselessLiteral("E") fnumber = Combine( Word("+-" + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums))) #columnName = Word(alphanums) ident = Word(alphas + ChineseCharacters.unicodeList + nums, ChineseCharacters.unicodeList + alphas + nums + "_$[]") plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") andand = Literal("&&") oror = Literal("||") is_a1 = Literal("==") is_a2 = Literal("=") is_a = is_a1 | is_a2 less_than = Literal("<") bigger_than = Literal(">") bigger_or_equal = Literal(">=") less_or_equal = Literal("<=") is_not_a = Literal("!=") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div compop = is_a | is_not_a | less_than | bigger_than compop2 = bigger_or_equal | less_or_equal logical = andand | oror expop = Literal("^") pi = CaselessLiteral("PI") logic = Forward() atom = (Optional("-") + (ident | e | fnumber | pi + lpar + logic + rpar).setParseAction(pushFirst) | (lpar + logic.suppress() + rpar)).setParseAction(pushUMinus) # by defining exponentiation as "atom [ ^ factor ]..." instead of "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-righ # that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore((expop + factor).setParseAction(pushFirst)) term = factor + ZeroOrMore((multop + factor).setParseAction(pushFirst)) expr = term + ZeroOrMore((addop + term).setParseAction(pushFirst)) equation = expr + ZeroOrMore((compop + expr).setParseAction(pushFirst)) equation2 = equation + ZeroOrMore( (compop2 + expr).setParseAction(pushFirst)) logic << equation2 + ZeroOrMore( (logical + equation2).setParseAction(pushFirst)) bnf = logic return bnf
def BNF(): """ expop :: '^' multop :: '*' | '/' | '>>' | '<<' | '|' | '&' addop :: '+' | '-' hex :: '0x' + integer integer :: ['+' | '-'] '0'..'9'+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* """ global bnf if not bnf: point = Literal( "." ) e = CaselessLiteral( "E" ) hexnum = CaselessLiteral("0x") + OneOrMore(oneOf(nums + 'a b c d e f A B C D E F')) hexnum.setParseAction(lambda s,l,t:str(int(''.join(t),16))) fnumber = Combine( Word( "+-"+nums, nums ) + Optional( point + Optional( Word( nums ) ) ) + Optional( e + Word( "+-"+nums, nums ) ) ) ident = Word(alphas, alphas+nums+"_$") plus = Literal( "+" ) minus = Literal( "-" ) mult = Literal( "*" ) div = Literal( "/" ) lshift = Literal( "<<" ) rshift = Literal( ">>" ) or_ = Literal( "|" ) and_ = Literal( "&" ) lpar = Literal( "(" ).suppress() rpar = Literal( ")" ).suppress() addop = plus | minus multop = mult | div | lshift | rshift | or_ | and_ expop = Literal( "^" ) pi = CaselessLiteral( "PI" ) expr = Forward() atom = (Optional("-") + ( pi | e | hexnum | fnumber | ident + lpar + expr + rpar ).setParseAction( pushFirst ) | ( lpar + expr.suppress() + rpar )).setParseAction(pushUMinus) # by defining exponentiation as "atom [ ^ factor ]..." instead of "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-righ # that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore( ( expop + factor ).setParseAction( pushFirst ) ) term = factor + ZeroOrMore( ( multop + factor ).setParseAction( pushFirst ) ) expr << term + ZeroOrMore( ( addop + term ).setParseAction( pushFirst ) ) bnf = expr return bnf
def grammar(self): if not self.bnf: point = Literal( "." ) fnumber = Combine( Word( nums ) + Optional( point + Optional( Word( nums ) ) ) ) ident = Word(alphas.lower()+"_", alphanums+"_") plus = Literal( "+" ) minus = Literal( "-" ) mult = Literal( "*" ) # passiverate = Word('infty') | Word('T') div = Literal( "/" ) lpar = Literal( "(" ).suppress() rpar = Literal( ")" ).suppress() addop = plus | minus multop = mult | div assign = Literal('=') expop = Literal( "^" ) expr = Forward() atom = Optional("-") + ( fnumber | ident + lpar + expr + rpar | ident).setParseAction(self._pushFirst ) | lpar + expr.suppress() + rpar factor = Forward() factor << atom + ZeroOrMore( ( expop + factor ) .setParseAction(self._pushFirst) ) term = factor + ZeroOrMore( ( multop + factor ) .setParseAction(self._pushFirst) ) expr << term + ZeroOrMore( ( addop + term ) .setParseAction(self._pushFirst ) ) bnf = (ident + assign).setParseAction(self._assignVar) + expr self.bnf = bnf return self.bnf
def BNF(self): """ expop :: '^' multop :: '*' | '/' addop :: '+' | '-' integer :: ['+' | '-'] '0'..'9'+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* """ #global bnf if not self.bnf: point = Literal(".") fnumber = Combine( Word("+-" + nums, nums) + Optional(point + Optional(Word(nums)))) ident = Word(alphas, alphas + nums + "_$") sensor_ident = (CaselessLiteral('GET_SENSOR_VAL:').suppress() + Word(alphas + nums + "_$")).setParseAction( self.get_sensor_value) and_ = CaselessLiteral('AND') or_ = CaselessLiteral('OR') not_ = CaselessLiteral('NOT') neq = CaselessLiteral('!=') lt = Literal('<') eqlt = CaselessLiteral('<=') gt = Literal('>') eqgt = CaselessLiteral('>=') eq = CaselessLiteral('==') plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lpar = Literal("(").suppress() rpar = Literal(")").suppress() add_op = plus | minus mult_op = mult | div cmp_op = eqlt | eqgt | eq | neq | lt | gt expr = Forward() atom = (Optional("-") + (fnumber | sensor_ident | ident + lpar + expr + rpar).setParseAction(self.pushFirst) | (lpar + expr.suppress() + rpar)).setParseAction( self.pushUMinus) term = atom + ZeroOrMore( (mult_op + atom).setParseAction(self.pushFirst)) add_exp = term + ZeroOrMore( (add_op + term).setParseAction(self.pushFirst)) cmp_exp = add_exp + ZeroOrMore( (cmp_op + add_exp).setParseAction(self.pushFirst)) cmp_not_exp = cmp_exp + ZeroOrMore( (not_ + cmp_exp).setParseAction(self.pushFirst)) cmp_not_and_exp = cmp_not_exp + ZeroOrMore( (and_ + cmp_not_exp).setParseAction(self.pushFirst)) cmp_not_and_or_exp = cmp_not_and_exp + ZeroOrMore( (or_ + cmp_not_and_exp).setParseAction(self.pushFirst)) expr << cmp_not_and_or_exp self.bnf = expr return self.bnf
def init_parser(self): """ expop :: '^' multop :: '*' | '/' addop :: '+' | '-' integer :: ['+' | '-'] '0'..'9'+ atom :: real | Word(alphas) | array | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* """ atoms = self.language.get_parser_atoms() variable = atoms['variable']#.setParseAction(downcaseTokens) func_lpar = atoms['func_lpar'].setParseAction(self._push2stack('(')) func_delim = atoms['func_delim'] func_rpar = atoms['func_rpar'].setParseAction(self._push2stack(')')) array_lpar = atoms['array_lpar'].setParseAction(self._push2stack('[')) array_delim = atoms['array_delim'] array_rpar = atoms['array_rpar'].setParseAction(self._push2stack(']')) lpar = atoms['lpar'].suppress() rpar = atoms['rpar'].suppress() expop = atoms['exp'] addop = atoms['plus'] | atoms['minus'] multop = atoms['mult'] | atoms['div'] cmpop = atoms['equal'] expr = Forward() # forward declaration of an entire expression # this is necessary for defining the recursive grammar # smallest entity of a mathematical expression: array = variable + \ array_lpar + \ atoms['int'].addParseAction(self.push_first) + \ ZeroOrMore(array_delim + atoms['int']) + \ array_rpar func_call = atoms['function'].setParseAction(downcaseTokens) + \ func_lpar + \ expr + \ ZeroOrMore(func_delim + expr) + \ func_rpar obj = (atoms['consts'] | atoms['float'] | array | func_call | variable) atom = ( Optional("-") + # optional unary minus ( obj.addParseAction(self.push_first) | (lpar + expr.suppress() + rpar) # subexpression ) ).setParseAction(self.push_unary_minus) # by defining exponentiation as "atom [ ^ factor ]..." instead of # "atom [ ^ atom ]...", we get right-to-left exponents, instead of # left-to-right. That is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore( (expop + factor).setParseAction(self.push_first) ) # sequence of multiplications term = factor + ZeroOrMore( (multop + factor).setParseAction(self.push_first) ) # sequence of summations expr << term + ZeroOrMore( (addop + term).setParseAction(self.push_first) ) # comparison operators equation = expr + \ Optional(cmpop + expr).setParseAction(self.push_first) # assignment operator self.parser = ( (variable ^ array).setParseAction(self.push_first) + atoms['assign'] + equation ).setParseAction(self._push2stack('=')) | \ equation return self.parser
def __init__(self): # define grammar point = Literal('.') e = CaselessLiteral('E') plusorminus = Literal('+') | Literal('-') number = Word(nums) integer = Combine(Optional(plusorminus) + number) floatnumber = Combine(integer + Optional(point + Optional(number)) + Optional(e + integer)) ident = Word('$', alphanums + '_') plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div expop = Literal("^") expr = Forward() def defineFunction(name, parameterCount=None): keyword = CaselessKeyword(name).setParseAction(self.pushEnd) funcPattern = keyword + lpar if parameterCount == None: funcPattern += Optional(expr + ZeroOrMore(Literal(',') + expr)) elif parameterCount > 0: funcPattern += expr for i in range(parameterCount - 1): funcPattern += Literal(',') + expr funcPattern += rpar return funcPattern.setParseAction(self.pushFirst) maxFunc = defineFunction('max') minFunc = defineFunction('min') casesFunc = defineFunction('cases') cases1Func = defineFunction('cases1', parameterCount=5) cases2Func = defineFunction('cases2', parameterCount=8) cases3Func = defineFunction('cases3', parameterCount=11) cases333Func = defineFunction('cases333', parameterCount=11) round3downFunc = defineFunction('round3down', parameterCount=1) #func = (funcident.setParseAction(self.pushEnd)+lpar +Optional(expr+ZeroOrMore(Literal(',')+expr))+rpar).setParseAction(self.pushFirst) atom = (maxFunc | minFunc | casesFunc | cases1Func | cases2Func | cases3Func | cases333Func | round3downFunc | (e | floatnumber | integer | ident).setParseAction( self.pushFirst) | (lpar + expr.suppress() + rpar)) factor = Forward() factor << atom + ZeroOrMore( (expop + factor).setParseAction(self.pushFirst)) term = factor + ZeroOrMore( (multop + factor).setParseAction(self.pushFirst)) expr << term + ZeroOrMore( (addop + term).setParseAction(self.pushFirst)) self.pattern = expr + StringEnd() # map operator symbols to corresponding arithmetic operations self.opn = { "+": self.handleNone(lambda a, b: a + b), "-": self.handleNone(lambda a, b: a - b), "*": self.handleNone(lambda a, b: a * b, none_survives=True), "/": self.handleNone(lambda a, b: a / b, none_survives=True), "^": self.handleNone(lambda a, b: a**b, none_survives=True) } self.functions = { 'max': self.max, 'min': self.min, 'cases': self.cases, 'cases1': self.cases1, 'cases2': self.cases2, 'cases3': self.cases3, 'cases333': self.cases333, 'round3down': self.round3down }
class FormulaParser(object): """ Deconstruct mathematical algebra expressions and convert those into callable funcitons. Deconstruct mathematical algebra expressions and convert those into callable funcitons. This formula parser was inspired by the fourFun pyparsing example and also benefited from additional substantial contributions by Paul McGuire. This module uses pyparsing for this purpose and the parser is adopted from the `fourFun example`__. Example usage:: >>> parser = FormulaParser() >>> parser.set_formula('log(a * 3 + b)') >>> parser.evaluate({'a': 5, 'b': 23}) ... 3.6375861597263857 >>> parser.evaluate({'a': [5, 6, 7], 'b': [23, 24, 25]}) ... array([ 3.63758616, 3.73766962, 3.8286414 ]) __ http://pyparsing.wikispaces.com/file/view/fourFn.py """ def __init__(self): """ Setup the Backus Normal Form (BNF) parser logic. """ # Set an empty formula attribute self.formula = None # Instantiate blank parser for BNF construction self.bnf = Forward() # Expression for parenthesis, which are suppressed in the atoms # after matching. lpar = Literal(const.LPAR).suppress() rpar = Literal(const.RPAR).suppress() # Expression for mathematical constants: Euler number and Pi e = Keyword(const.EULER) pi = Keyword(const.PI) null = Keyword(const.NULL) _true = Keyword(const.TRUE) _false = Keyword(const.FALSE) # Prepare operator expressions addop = oneOf(const.ADDOP) multop = oneOf(const.MULTOP) powop = oneOf(const.POWOP) unary = reduce(operator.add, (Optional(x) for x in const.UNOP)) # Expression for floating point numbers, allowing for scientific notation. number = Regex(const.NUMBER) # Variables are alphanumeric strings that represent keys in the input # data dictionary. variable = delimitedList(Word(alphanums), delim=const.VARIABLE_NAME_SEPARATOR, combine=True) # Functional calls function = Word(alphanums) + lpar + self.bnf + rpar # Atom core - a single element is either a math constant, # a function or a variable. atom_core = function | pi | e | null | _true | _false | number | variable # Atom subelement between parenthesis atom_subelement = lpar + self.bnf.suppress() + rpar # In atoms, pi and e need to be before the letters for it to be found atom = (unary + atom_core.setParseAction(self.push_first) | atom_subelement).setParseAction(self.push_unary_operator) # By defining exponentiation as "atom [ ^ factor ]..." instead of # "atom [ ^ atom ]...", we get right-to-left exponents, instead of # left-to-right that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore( (powop + factor).setParseAction(self.push_first)) term = factor + ZeroOrMore( (multop + factor).setParseAction(self.push_first)) self.bnf << term + ZeroOrMore( (addop + term).setParseAction(self.push_first)) def push_first(self, strg, loc, toks): self.expr_stack.append(toks[0]) def push_unary_operator(self, strg, loc, toks): """ Set custom flag for unary operators. """ if toks and toks[0] in const.UNARY_REPLACE_MAP: self.expr_stack.append(const.UNARY_REPLACE_MAP[toks[0]]) def evaluate_stack(self, stack): """ Evaluate a stack element. """ op = stack.pop() if op in const.UNARY_OPERATOR_MAP: return const.UNARY_OPERATOR_MAP[op](self.evaluate_stack(stack)) elif op in const.OPERATOR_MAP: op2 = self.evaluate_stack(stack) op1 = self.evaluate_stack(stack) # Handle null case if isinstance(op1, str) and op1 == const.NULL: op2 = self.get_mask(op2, op) op1 = True elif isinstance(op2, str) and op2 == const.NULL: op1 = self.get_mask(op1, op) op2 = True return const.OPERATOR_MAP[op](op1, op2) elif op in const.FUNCTION_MAP: return const.FUNCTION_MAP[op](self.evaluate_stack(stack)) elif op in const.KEYWORD_MAP: return const.KEYWORD_MAP[op] elif op in self.variable_map: return self.variable_map[op] else: try: return numpy.array(op, dtype=const.ALGEBRA_PIXEL_TYPE_NUMPY) except ValueError: raise RasterAlgebraException( 'Found an undeclared variable "{0}" in formula.'.format( op)) @staticmethod def get_mask(data, operator): # Make sure the right operator is used if operator not in (const.EQUAL, const.NOT_EQUAL): raise RasterAlgebraException( 'NULL can only be used with "==" or "!=" operators.') # Get mask if numpy.ma.is_masked(data): return data.mask # If there is no mask, all values are not null return numpy.zeros(data.shape, dtype=numpy.bool) def set_formula(self, formula): """ Store the input formula as the one to evaluate on. """ # Remove any white space and line breaks from formula. self.formula = formula.replace(' ', '').replace('\n', '').replace('\r', '') def prepare_data(self): """ Basic checks and conversion of input data. """ for key, var in self.variable_map.items(): # Keywords are not allowed as variables, variables can not start or # end with separator. if keyword.iskeyword(key) or key != key.strip( const.VARIABLE_NAME_SEPARATOR): raise RasterAlgebraException( 'Invalid variable name found: "{}".'.format(key)) # Convert all data to numpy arrays if not isinstance(var, numpy.ndarray): self.variable_map[key] = numpy.array(var) def evaluate(self, data={}, formula=None): """ Evaluate the input data using the current formula expression stack. The formula is stored as attribute and can be re-evaluated with several input data sets on an existing parser. """ if formula: self.set_formula(formula) if not self.formula: raise RasterAlgebraException('Formula not specified.') # Store data for variables self.variable_map = data # Check and convert input data self.prepare_data() # Reset expression stack self.expr_stack = [] # Populate the expression stack self.bnf.parseString(self.formula) # Evaluate stack on data return self.evaluate_stack(self.expr_stack)
div = Literal("/") solveop = Literal("\\") solvePDop = Literal("\\p") outer = Literal("@") # solveop = Literal("~") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div | outer | solveop | solvePDop # expop = Literal("^") expop = Literal(".") assignop = Literal("=") expr = Forward() atom = (e | floatnumber | integer | ident).setParseAction(_pushFirst) | (lpar + expr.suppress() + rpar) factor = Forward() factor << atom + ZeroOrMore((expop + factor).setParseAction(_pushFirst)) term = factor + ZeroOrMore((multop + factor).setParseAction(_pushFirst)) expr << term + ZeroOrMore((addop + term).setParseAction(_pushFirst)) equation = (ident + assignop).setParseAction(_assignVar) + expr + StringEnd() # End of grammar definition # ----------------------------------------------------------------------------- ## The following are helper variables and functions used by the Binary Infix Operator ## Functions described below. vprefix = "@pvec@" # vprefix = "m3_" vplen = len(vprefix)
def __init__(self): self.ae = False self.local_dict = None self.f = None self.user_functions = None self.expr_stack = [] self.texpr_stack = [] # Define constants self.constants = {} # Define Operators self.opn = { "+": operator.add, "-": operator.sub, "*": operator.mul, "/": operator.truediv, ">": operator.gt, ">=": operator.ge, "<": operator.lt, "<=": operator.le, "==": operator.eq, "!=": operator.ne, "|": operator.or_, "&": operator.and_, "!": operator.inv, "^": operator.pow, "**": operator.pow, "<<": np.left_shift, ">>": np.right_shift } # Define xarray DataArray operators with 1 input parameter self.xfn1 = { "angle": xr.ufuncs.angle, "arccos": xr.ufuncs.arccos, "arccosh": xr.ufuncs.arccosh, "arcsin": xr.ufuncs.arcsin, "arcsinh": xr.ufuncs.arcsinh, "arctan": xr.ufuncs.arctan, "arctanh": xr.ufuncs.arctanh, "ceil": xr.ufuncs.ceil, "conj": xr.ufuncs.conj, "cos": xr.ufuncs.cos, "cosh": xr.ufuncs.cosh, "deg2rad": xr.ufuncs.deg2rad, "degrees": xr.ufuncs.degrees, "exp": xr.ufuncs.exp, "expm1": xr.ufuncs.expm1, "fabs": xr.ufuncs.fabs, "fix": xr.ufuncs.fix, "floor": xr.ufuncs.floor, "frexp": xr.ufuncs.frexp, "imag": xr.ufuncs.imag, "iscomplex": xr.ufuncs.iscomplex, "isfinite": xr.ufuncs.isfinite, "isinf": xr.ufuncs.isinf, "isnan": xr.ufuncs.isnan, "isreal": xr.ufuncs.isreal, "log": xr.ufuncs.log, "log10": xr.ufuncs.log10, "log1p": xr.ufuncs.log1p, "log2": xr.ufuncs.log2, "rad2deg": xr.ufuncs.rad2deg, "radians": xr.ufuncs.radians, "real": xr.ufuncs.real, "rint": xr.ufuncs.rint, "sign": xr.ufuncs.sign, "signbit": xr.ufuncs.signbit, "sin": xr.ufuncs.sin, "sinh": xr.ufuncs.sinh, "sqrt": xr.ufuncs.sqrt, "square": xr.ufuncs.square, "tan": xr.ufuncs.tan, "tanh": xr.ufuncs.tanh, "trunc": xr.ufuncs.trunc } # Define xarray DataArray operators with 2 input parameter self.xfn2 = { "arctan2": xr.ufuncs.arctan2, "copysign": xr.ufuncs.copysign, "fmax": xr.ufuncs.fmax, "fmin": xr.ufuncs.fmin, "fmod": xr.ufuncs.fmod, "hypot": xr.ufuncs.hypot, "ldexp": xr.ufuncs.ldexp, "logaddexp": xr.ufuncs.logaddexp, "logaddexp2": xr.ufuncs.logaddexp2, "logicaland": xr.ufuncs.logical_and, "logicalnot": xr.ufuncs.logical_not, "logicalor": xr.ufuncs.logical_or, "logicalxor": xr.ufuncs.logical_xor, "maximum": xr.ufuncs.maximum, "minimum": xr.ufuncs.minimum, "nextafter": xr.ufuncs.nextafter } # Define non-xarray DataArray operators with 2 input parameter self.fn2 = {"percentile": np.percentile, "pow": np.power} # Define xarray DataArray reduction operators self.xrfn = { "all": xr.DataArray.all, "any": xr.DataArray.any, "argmax": xr.DataArray.argmax, "argmin": xr.DataArray.argmin, "max": xr.DataArray.max, "mean": xr.DataArray.mean, "median": xr.DataArray.median, "min": xr.DataArray.min, "prod": xr.DataArray.prod, "sum": xr.DataArray.sum, "std": xr.DataArray.std, "var": xr.DataArray.var } # Define non-xarray DataArray operators with 2 input parameter self.xcond = {"<": np.percentile} # Define Grammar point = Literal(".") e = CaselessLiteral("E") fnumber = Combine( Word("+-" + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums))) variable = Word(alphas, alphas + nums + "_$") seq = Literal("=") b_not = Literal("~") plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") ls = Literal("<<") rs = Literal(">>") gt = Literal(">") gte = Literal(">=") lt = Literal("<") lte = Literal("<=") eq = Literal("==") neq = Literal("!=") b_or = Literal("|") b_and = Literal("&") l_not = Literal("!") lpar = Literal("(").suppress() rpar = Literal(")").suppress() comma = Literal(",") colon = Literal(":") lbrac = Literal("[") rbrac = Literal("]") lcurl = Literal("{") rcurl = Literal("}") qmark = Literal("?") scolon = Literal(";") addop = plus | minus multop = mult | div sliceop = colon shiftop = ls | rs compop = gte | lte | gt | lt eqop = eq | neq bitcompop = b_or | b_and bitnotop = b_not logicalnotop = l_not assignop = seq expop = Literal("^") | Literal("**") expr = Forward() indexexpr = Forward() atom = ( Optional("-") + (variable + seq + expr).setParseAction(self.push_assign) | indexexpr.setParseAction(self.push_index) | (lpar + expr + qmark.setParseAction(self.push_ternary1) + expr + scolon.setParseAction(self.push_ternary2) + expr + rpar).setParseAction(self.push_ternary) | (lpar + expr + qmark + expr + scolon + expr + rpar).setParseAction( self.push_ternary) | (logicalnotop + expr).setParseAction(self.push_ulnot) | (bitnotop + expr).setParseAction(self.push_unot) | (minus + expr).setParseAction(self.push_uminus) | (variable + lcurl + expr + rcurl).setParseAction(self.push_mask) | (variable + lpar + expr + (comma + expr) * 3 + rpar).setParseAction(self.push_expr4) | (variable + lpar + expr + (comma + expr) * 2 + rpar).setParseAction(self.push_expr3) | (variable + lpar + expr + comma + expr + rpar).setParseAction( self.push_expr2) | (variable + lpar + expr + rpar | variable).setParseAction( self.push_expr1) | fnumber.setParseAction(self.push_expr) | (lpar + expr + ZeroOrMore(comma + expr).setParseAction( self.get_tuple) + rpar).setParseAction(self.push_tuple) | (lpar + expr.suppress() + rpar).setParseAction(self.push_uminus)) # Define order of operations for operators factor = Forward() factor << atom + ZeroOrMore( (expop + factor).setParseAction(self.push_op)) term = factor + ZeroOrMore( (multop + factor).setParseAction(self.push_op)) term2 = term + ZeroOrMore((addop + term).setParseAction(self.push_op)) term3 = term2 + ZeroOrMore( (shiftop + term2).setParseAction(self.push_op)) term4 = term3 + ZeroOrMore( (sliceop + term3).setParseAction(self.push_op)) term5 = term4 + ZeroOrMore( (compop + term4).setParseAction(self.push_op)) term6 = term5 + ZeroOrMore((eqop + term5).setParseAction(self.push_op)) term7 = term6 + ZeroOrMore( (bitcompop + term6).setParseAction(self.push_op)) expr << term7 + ZeroOrMore( (assignop + term7).setParseAction(self.push_op)) # Define index operators colon_expr = (colon + FollowedBy(comma) ^ colon + FollowedBy(rbrac)).setParseAction( self.push_colon) range_expr = colon_expr | expr | colon indexexpr << (variable + lbrac + delimitedList(range_expr, delim=',') + rbrac).setParseAction(self.push_expr) self.parser = expr
ident = Word(alphas, alphanums + '_') plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div expop = Literal("^") assign = Literal("=") expr = Forward() atom = ((e | floatnumber | integer | ident).setParseAction(pushFirst) | (lpar + expr.suppress() + rpar)) factor = Forward() factor << atom + ZeroOrMore((expop + factor).setParseAction(pushFirst)) term = factor + ZeroOrMore((multop + factor).setParseAction(pushFirst)) expr << term + ZeroOrMore((addop + term).setParseAction(pushFirst)) bnf = Optional((ident + assign).setParseAction(assignVar)) + expr pattern = bnf + StringEnd() # map operator symbols to corresponding arithmetic operations opn = { "+": (lambda a, b: a + b), "-": (lambda a, b: a - b), "*": (lambda a, b: a * b),
def __init__(self): """ Setup the Backus Normal Form (BNF) parser logic. """ self.dtype=ALGEBRA_PIXEL_TYPE_NUMPY point = Literal(".") e = CaselessLiteral("E") fnumber = Combine( Word("+-" + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums)) ) ident = Word(alphas, alphas + nums + "_$") # Operators plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") eq = Literal("==") neq = Literal("!=") lt = Literal("<") le = Literal("<=") gt = Literal(">") ge = Literal(">=") ior = Literal("|") iand = Literal("&") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus | eq multop = mult | div | eq | neq | ge | le | gt | lt | ior | iand # Order matters here due to "<=" being caught by "<" expop = Literal("^") pi = CaselessLiteral("PI") # Letters for variables aa = CaselessLiteral("a") bb = CaselessLiteral("b") cc = CaselessLiteral("c") dd = CaselessLiteral("d") ee = CaselessLiteral("e") ff = CaselessLiteral("f") gg = CaselessLiteral("g") hh = CaselessLiteral("h") ii = CaselessLiteral("i") jj = CaselessLiteral("j") kk = CaselessLiteral("k") ll = CaselessLiteral("l") mm = CaselessLiteral("m") nn = CaselessLiteral("n") oo = CaselessLiteral("o") pp = CaselessLiteral("p") qq = CaselessLiteral("q") rr = CaselessLiteral("r") ss = CaselessLiteral("s") tt = CaselessLiteral("t") uu = CaselessLiteral("u") vv = CaselessLiteral("v") ww = CaselessLiteral("w") xx = CaselessLiteral("x") yy = CaselessLiteral("y") zz = CaselessLiteral("z") bnf = Forward() atom = ( Optional('-') + Optional("!") + ( pi | e | fnumber | ident + lpar + bnf + rpar | # pi needs to be before the letters for it to be found aa | bb | cc | dd | ee | ff | gg | hh | ii | jj | kk | ll | mm | nn | oo | pp | qq | rr | ss | tt | uu | vv | ww | xx | yy | zz ).setParseAction(self.push_first) | (lpar + bnf.suppress() + rpar) ).setParseAction(self.push_unary_operator) # By defining exponentiation as "atom [ ^ factor ]..." instead of "atom [ ^ atom ]...", # we get right-to-left exponents, instead of left-to-righ # that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore((expop + factor).setParseAction(self.push_first)) term = factor + ZeroOrMore((multop + factor).setParseAction(self.push_first)) bnf << term + ZeroOrMore((addop + term).setParseAction(self.push_first)) self.bnf = bnf
def init_parser(self): """ expop :: '^' multop :: '*' | '/' addop :: '+' | '-' integer :: ['+' | '-'] '0'..'9'+ atom :: real | Word(alphas) | array | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* """ atoms = self.language.get_parser_atoms() variable = atoms["variable"] # .setParseAction(downcaseTokens) func_lpar = atoms["func_lpar"].setParseAction(self._push2stack("(")) func_delim = atoms["func_delim"] func_rpar = atoms["func_rpar"].setParseAction(self._push2stack(")")) array_lpar = atoms["array_lpar"].setParseAction(self._push2stack("[")) array_delim = atoms["array_delim"] array_rpar = atoms["array_rpar"].setParseAction(self._push2stack("]")) lpar = atoms["lpar"].suppress() rpar = atoms["rpar"].suppress() expop = atoms["exp"] addop = atoms["plus"] | atoms["minus"] multop = atoms["mult"] | atoms["div"] cmpop = atoms["equal"] expr = Forward() # forward declaration of an entire expression # this is necessary for defining the recursive grammar # smallest entity of a mathematical expression: array = ( variable + array_lpar + atoms["int"].addParseAction(self.push_first) + ZeroOrMore(array_delim + atoms["int"]) + array_rpar ) func_call = ( atoms["function"].setParseAction(downcaseTokens) + func_lpar + expr + ZeroOrMore(func_delim + expr) + func_rpar ) obj = atoms["consts"] | atoms["float"] | array | func_call | variable atom = ( Optional("-") + ( # optional unary minus obj.addParseAction(self.push_first) | (lpar + expr.suppress() + rpar) # subexpression ) ).setParseAction(self.push_unary_minus) # by defining exponentiation as "atom [ ^ factor ]..." instead of # "atom [ ^ atom ]...", we get right-to-left exponents, instead of # left-to-right. That is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore((expop + factor).setParseAction(self.push_first)) # sequence of multiplications term = factor + ZeroOrMore((multop + factor).setParseAction(self.push_first)) # sequence of summations expr << term + ZeroOrMore((addop + term).setParseAction(self.push_first)) # comparison operators equation = expr + Optional(cmpop + expr).setParseAction(self.push_first) # assignment operator self.parser = ( (variable ^ array).setParseAction(self.push_first) + atoms["assign"] + equation ).setParseAction(self._push2stack("=")) | equation return self.parser
def parser(self): point = Literal(".") e = CaselessLiteral("E") fnumber = Combine(Word("+-" + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums))) # ident = reduce(lambda x, y: x | y, self.func_name_parsers) ident = Word(alphas, alphas + nums + "_$") plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") _or = Literal("|") _and = Literal("&") lpar = Literal("(").suppress() rpar = Literal(")").suppress() lt = Keyword("<") le = Keyword("<=") gt = Keyword(">") ge = Keyword(">=") addop = plus | minus | le | lt | gt | ge multop = mult | div | _or | _and expop = Literal("^") pi = CaselessLiteral("PI") # column_names = [Literal(col_name) for col_name in self.col_names] # Equivalent to: 'x_1 | x_2 | ... | x_n' # column = self.col_name_parser() # Function arguments plusorminus = Literal('+') | Literal('-') number = Word(nums) integer = Combine(Optional(plusorminus) + number).setParseAction(lambda x: int(x[0])) floatnumber = Combine(integer + Optional(point + Optional(number)) + Optional(e + integer) ).setParseAction(lambda x: float(x[0])) arg = (integer | floatnumber | quotedString.addParseAction(removeQuotes)).setParseAction(lambda x: x[0]) args = Optional(delimitedList(arg), default=None).setParseAction(lambda x: {'args': [z for z in x]}) kwarg = (Word(alphas, alphas + nums + "_$") + Suppress("=") + arg).setParseAction(lambda x: {x[0]: x[1]}) kwargs = Optional(Suppress(",") + delimitedList(kwarg), default=None) kwargs.setParseAction(lambda x: {'kwargs': dict(pair for d in x for pair in d.items())} if x[0] is not None else { 'kwargs': None}) func_inputs = (Suppress(",") + args + kwargs) func_input_block = Optional(func_inputs, default={'args': None, 'kwargs': None}) \ .setParseAction(lambda x: self._add_func_inputs(dict(pair for d in x for pair in d.items()))) expr = Forward() kwarg.setParseAction(lambda x: {x[0]: x[1]}) value = self.value_parser() atom = (Optional("-") + (value | pi | e | fnumber | ident + lpar + expr + func_input_block + rpar).setParseAction(lambda x: self.push_first(tok=x[0])) | ( lpar + expr.suppress() + rpar)).setParseAction(lambda x: self.push_unary_minus(toks=x)) factor = Forward() factor << atom + ZeroOrMore((expop + factor).setParseAction(lambda x: self.push_first(tok=x[0]))) term = factor + ZeroOrMore((multop + factor).setParseAction(lambda x: self.push_first(tok=x[0]))) expr << term + ZeroOrMore((addop + term).setParseAction(lambda x: self.push_first(tok=x[0]))) if self.deferred_eval: expr.setParseAction(lambda x: originalTextFor(expr)) return expr
class FormulaParser(object): """ Deconstruct mathematical algebra expressions and convert those into callable funcitons. Deconstruct mathematical algebra expressions and convert those into callable funcitons. This formula parser was inspired by the fourFun pyparsing example and also benefited from additional substantial contributions by Paul McGuire. This module uses pyparsing for this purpose and the parser is adopted from the `fourFun example`__. Example usage:: >>> parser = FormulaParser() >>> parser.set_formula('log(a * 3 + b)') >>> parser.evaluate({'a': 5, 'b': 23}) ... 3.6375861597263857 >>> parser.evaluate({'a': [5, 6, 7], 'b': [23, 24, 25]}) ... array([ 3.63758616, 3.73766962, 3.8286414 ]) __ http://pyparsing.wikispaces.com/file/view/fourFn.py """ def __init__(self): """ Setup the Backus Normal Form (BNF) parser logic. """ # Set an empty formula attribute self.formula = None # Instantiate blank parser for BNF construction self.bnf = Forward() # Expression for parenthesis, which are suppressed in the atoms # after matching. lpar = Literal(const.LPAR).suppress() rpar = Literal(const.RPAR).suppress() # Expression for mathematical constants: Euler number and Pi e = Keyword(const.EULER) pi = Keyword(const.PI) null = Keyword(const.NULL) _true = Keyword(const.TRUE) _false = Keyword(const.FALSE) # Prepare operator expressions addop = oneOf(const.ADDOP) multop = oneOf(const.MULTOP) powop = oneOf(const.POWOP) unary = reduce(operator.add, (Optional(x) for x in const.UNOP)) # Expression for floating point numbers, allowing for scientific notation. number = Regex(const.NUMBER) # Variables are alphanumeric strings that represent keys in the input # data dictionary. variable = delimitedList(Word(alphanums), delim=const.VARIABLE_NAME_SEPARATOR, combine=True) # Functional calls function = Word(alphanums) + lpar + self.bnf + rpar # Atom core - a single element is either a math constant, # a function or a variable. atom_core = function | pi | e | null | _true | _false | number | variable # Atom subelement between parenthesis atom_subelement = lpar + self.bnf.suppress() + rpar # In atoms, pi and e need to be before the letters for it to be found atom = ( unary + atom_core.setParseAction(self.push_first) | atom_subelement ).setParseAction(self.push_unary_operator) # By defining exponentiation as "atom [ ^ factor ]..." instead of # "atom [ ^ atom ]...", we get right-to-left exponents, instead of # left-to-right that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + ZeroOrMore((powop + factor).setParseAction(self.push_first)) term = factor + ZeroOrMore((multop + factor).setParseAction(self.push_first)) self.bnf << term + ZeroOrMore((addop + term).setParseAction(self.push_first)) def push_first(self, strg, loc, toks): self.expr_stack.append(toks[0]) def push_unary_operator(self, strg, loc, toks): """ Set custom flag for unary operators. """ if toks and toks[0] in const.UNARY_REPLACE_MAP: self.expr_stack.append(const.UNARY_REPLACE_MAP[toks[0]]) def evaluate_stack(self, stack): """ Evaluate a stack element. """ op = stack.pop() if op in const.UNARY_OPERATOR_MAP: return const.UNARY_OPERATOR_MAP[op](self.evaluate_stack(stack)) elif op in const.OPERATOR_MAP: op2 = self.evaluate_stack(stack) op1 = self.evaluate_stack(stack) # Handle null case if isinstance(op1, six.string_types) and op1 == const.NULL: op2 = self.get_mask(op2, op) op1 = True elif isinstance(op2, six.string_types) and op2 == const.NULL: op1 = self.get_mask(op1, op) op2 = True return const.OPERATOR_MAP[op](op1, op2) elif op in const.FUNCTION_MAP: return const.FUNCTION_MAP[op](self.evaluate_stack(stack)) elif op in const.KEYWORD_MAP: return const.KEYWORD_MAP[op] elif op in self.variable_map: return self.variable_map[op] else: try: return numpy.array(op, dtype=const.ALGEBRA_PIXEL_TYPE_NUMPY) except ValueError: raise RasterAlgebraException('Found an undeclared variable "{0}" in formula.'.format(op)) @staticmethod def get_mask(data, operator): # Make sure the right operator is used if operator not in (const.EQUAL, const.NOT_EQUAL): raise RasterAlgebraException('NULL can only be used with "==" or "!=" operators.') # Get mask if numpy.ma.is_masked(data): return data.mask # If there is no mask, all values are not null return numpy.zeros(data.shape, dtype=numpy.bool) def set_formula(self, formula): """ Store the input formula as the one to evaluate on. """ # Remove any white space and line breaks from formula. self.formula = formula.replace(' ', '').replace('\n', '').replace('\r', '') def prepare_data(self): """ Basic checks and conversion of input data. """ for key, var in self.variable_map.items(): # Keywords are not allowed as variables, variables can not start or # end with separator. if keyword.iskeyword(key) or key != key.strip(const.VARIABLE_NAME_SEPARATOR): raise RasterAlgebraException('Invalid variable name found: "{}".'.format(key)) # Convert all data to numpy arrays if not isinstance(var, numpy.ndarray): self.variable_map[key] = numpy.array(var) def evaluate(self, data={}, formula=None): """ Evaluate the input data using the current formula expression stack. The formula is stored as attribute and can be re-evaluated with several input data sets on an existing parser. """ if formula: self.set_formula(formula) if not self.formula: raise RasterAlgebraException('Formula not specified.') # Store data for variables self.variable_map = data # Check and convert input data self.prepare_data() # Reset expression stack self.expr_stack = [] # Populate the expression stack self.bnf.parseString(self.formula) # Evaluate stack on data return self.evaluate_stack(self.expr_stack)
def defineGrammar(self, input_strings): point = Literal('.') e = CaselessLiteral('E') plusorminus = Literal('+') | Literal('-') number = Word(nums) #print "Nums", number integer = Combine(Optional(plusorminus) + number) floatnumber = Combine(integer + Optional(point + Optional(number)) + Optional(e + integer)) ident = Word(alphas, alphanums + '_') #print "ident", ident plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div expop = Literal("^") assign = Literal("=") expr = Forward() #print "Expr" , expr atom = ((e | floatnumber | integer | ident).setParseAction( self.pushFirst) | (lpar + expr.suppress() + rpar)) factor = Forward() factor << atom + ZeroOrMore( (expop + factor).setParseAction(self.pushFirst)) term = factor + ZeroOrMore( (multop + factor).setParseAction(self.pushFirst)) expr << term + ZeroOrMore( (addop + term).setParseAction(self.pushFirst)) bnf = Optional((ident + assign).setParseAction(self.assignVar)) + expr pattern = bnf + StringEnd() #print "pattern" , pattern # map operator symbols to corresponding arithmetic operations self.opn = { "+": (lambda a, b: a + b), "-": (lambda a, b: a - b), "*": (lambda a, b: a * b), "/": (lambda a, b: a / b), "^": (lambda a, b: a**b) } exprns = input_strings.split(",") for input_string in exprns: self.exprStack = [] self.varStack = [] if input_string != '': # try parsing the input string try: L = pattern.parseString(input_string) #print "L", L except ParseException, err: L = ['Parse Failure', input_string] if debug_flag: print input_string, "->", L if len(L) == 0 or L[0] != 'Parse Failure': if debug_flag: print "exprStack=", self.exprStack # calculate result , store a copy in ans , display the result to user result = self.evaluateStack(self.exprStack) self.variables['ans'] = result print input_string, "=", result # Assign result to a variable if required if debug_flag: print "var=", self.varStack if len(self.varStack) == 1: self.variables[self.varStack.pop()] = result if debug_flag: print "variables=", self.variables else: print 'Parse Failure' print err.line print " " * (err.column - 1) + "^" print input_string, "-", err
def create_bnf(stack): point = Literal(".") e = CaselessLiteral("E") inumber = Word(nums) fnumber = Combine( Word("+-" + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums))) _of = Literal('of') _in = Literal('in') _by = Literal('by') _copy = Literal('copy') _mv = Literal('-v').setParseAction(replace('OA_SubV')) _me = Literal('-e').setParseAction(replace('OA_SubE')) _mf = Literal('-f').setParseAction(replace('OA_SubF')) _mc = Literal('-c').setParseAction(replace('OA_SubC')) _ms = Literal('-s').setParseAction(replace('OA_SubS')) _pv = Literal('+v').setParseAction(replace('OA_AddV')) _pe = Literal('+e').setParseAction(replace('OA_AddE')) _pf = Literal('+f').setParseAction(replace('OA_AddF')) _pc = Literal('+c').setParseAction(replace('OA_AddC')) _ps = Literal('+s').setParseAction(replace('OA_AddS')) _inv = Literal('*v').setParseAction(replace('OA_IntersectV')) _ine = Literal('*e').setParseAction(replace('OA_IntersectE')) _inf = Literal('*f').setParseAction(replace('OA_IntersectF')) _inc = Literal('*c').setParseAction(replace('OA_IntersectC')) _ins = Literal('*s').setParseAction(replace('OA_IntersectS')) regop = (_mv | _me | _mf | _mc | _ms | _pv | _pe | _pf | _pc | _ps | _inv | _ine | _inf | _inc | _ins) lpar = Literal("(").suppress() rpar = Literal(")").suppress() _all = Literal('all').setParseAction(replace('KW_All')) vertex = Literal('vertex') vertices = Literal('vertices') cell = Literal('cell') cells = Literal('cells') group = Literal('group') _set = Literal('set') surface = Literal('surface') ident = Word(alphas + '_.', alphanums + '_.') set_name = Word(nums) | ident function = Word(alphas + '_', alphanums + '_') function = Group(function).setParseAction(join_tokens) region = Combine( Literal('r.') + Word(alphas + '_-', '_-' + alphas + nums + '.')) region = Group(Optional(_copy, default='nocopy') + region) region.setParseAction(replace('KW_Region', keep=True)) coor = oneOf('x y z') boolop = oneOf('& |') relop = oneOf('< > <= >= != ==') bool_term = (ZeroOrMore('(') + (coor | fnumber) + relop + (coor | fnumber) + ZeroOrMore(')')) relation = Forward() relation << (ZeroOrMore('(') + bool_term + ZeroOrMore(boolop + relation) + ZeroOrMore(')')) relation = Group(relation).setParseAction(join_tokens) nos = Group(vertices + _of + surface).setParseAction(replace('E_VOS')) nir = Group(vertices + _in + relation).setParseAction( replace('E_VIR', keep=True)) nbf = Group(vertices + _by + function).setParseAction( replace('E_VBF', keep=True)) ebf = Group(cells + _by + function).setParseAction( replace('E_CBF', keep=True)) eog = Group(cells + _of + group + Word(nums)).setParseAction( replace('E_COG', keep=True)) nog = Group(vertices + _of + group + Word(nums)).setParseAction( replace('E_VOG', keep=True)) onir = Group(vertex + _in + region).setParseAction( replace_with_region('E_OVIR', 2)) ni = Group(vertex + delimitedList(inumber)).setParseAction( replace('E_VI', keep=True)) ei = Group(cell + delimitedList(inumber)).setParseAction( replace('E_CI', keep=True)) noset = Group(vertices + _of + _set + set_name).setParseAction( replace('E_VOSET', keep=True)) eoset = Group(cells + _of + _set + set_name).setParseAction( replace('E_COSET', keep=True)) region_expression = Forward() atom1 = (_all | region | ni | onir | nos | nir | nbf | ei | ebf | eog | nog | noset | eoset) atom1.setParseAction(to_stack(stack)) atom2 = (lpar + region_expression.suppress() + rpar) atom = (atom1 | atom2) aux = (regop + region_expression) aux.setParseAction(to_stack(stack)) region_expression << atom + ZeroOrMore(aux) region_expression = StringStart() + region_expression + StringEnd() return region_expression
def parse_math_str(input_string, variables={}): # Uncomment the line below for readline support on interactive terminal # import readline import re from pyparsing import Word, alphas, ParseException, Literal, CaselessLiteral, Combine, Optional, nums, Or, Forward, ZeroOrMore, StringEnd, alphanums import math # Debugging flag can be set to either "debug_flag=True" or "debug_flag=False" debug_flag = False exprStack = [] varStack = [] def pushFirst(str, loc, toks): exprStack.append(toks[0]) def assignVar(str, loc, toks): varStack.append(toks[0]) # define grammar point = Literal('.') e = CaselessLiteral('E') plusorminus = Literal('+') | Literal('-') number = Word(nums) integer = Combine(Optional(plusorminus) + number) floatnumber = Combine(integer + Optional(point + Optional(number)) + Optional(e + integer)) ident = Word(alphas, alphanums + '_') plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div expop = Literal("^") assign = Literal("=") expr = Forward() atom = ((e | floatnumber | integer | ident).setParseAction(pushFirst) | (lpar + expr.suppress() + rpar)) factor = Forward() factor << atom + ZeroOrMore((expop + factor).setParseAction(pushFirst)) term = factor + ZeroOrMore((multop + factor).setParseAction(pushFirst)) expr << term + ZeroOrMore((addop + term).setParseAction(pushFirst)) bnf = Optional((ident + assign).setParseAction(assignVar)) + expr pattern = bnf + StringEnd() # map operator symbols to corresponding arithmetic operations opn = { "+": (lambda a, b: a + b), "-": (lambda a, b: a - b), "*": (lambda a, b: a * b), "/": (lambda a, b: a / b), "^": (lambda a, b: a**b) } # Recursive function that evaluates the stack def evaluateStack(s): op = s.pop() if op in "+-*/^": op2 = evaluateStack(s) op1 = evaluateStack(s) return opn[op](op1, op2) elif op == "PI": return math.pi elif op == "E": return math.e elif re.search('^[a-zA-Z][a-zA-Z0-9_]*$', op): if op in variables: return variables[op] else: return 0 elif re.search('^[-+]?[0-9]+$', op): return int(op) else: return float(op) # Start with a blank exprStack and a blank varStack exprStack = [] varStack = [] if input_string != '': # try parsing the input string try: L = pattern.parseString(input_string) except ParseException as err: L = ['Parse Failure', input_string] # show result of parsing the input string if debug_flag: print(input_string, "->", L) if len(L) == 0 or L[0] != 'Parse Failure': if debug_flag: print("exprStack=", exprStack) # calculate result , store a copy in ans , display the result to user result = evaluateStack(exprStack) variables['ans'] = result #print result return result # Assign result to a variable if required if debug_flag: print("var=", varStack) if len(varStack) == 1: variables[varStack.pop()] = result if debug_flag: print("variables=", variables) else: print('Parse Failure') print(err.line) print(" " * (err.column - 1) + "^") print(err)
def defineGrammar(self, input_strings): point = Literal('.') e = CaselessLiteral('E') plusorminus = Literal('+') | Literal('-') number = Word(nums) #print "Nums", number integer = Combine( Optional(plusorminus) + number ) floatnumber = Combine( integer + Optional( point + Optional(number) ) + Optional( e + integer ) ) ident = Word(alphas,alphanums + '_') #print "ident", ident plus = Literal( "+" ) minus = Literal( "-" ) mult = Literal( "*" ) div = Literal( "/" ) lpar = Literal( "(" ).suppress() rpar = Literal( ")" ).suppress() addop = plus | minus multop = mult | div expop = Literal( "^" ) assign = Literal( "=" ) expr = Forward() #print "Expr" , expr atom = ( ( e | floatnumber | integer | ident ).setParseAction(self.pushFirst) | ( lpar + expr.suppress() + rpar ) ) factor = Forward() factor << atom + ZeroOrMore( ( expop + factor ).setParseAction( self.pushFirst ) ) term = factor + ZeroOrMore( ( multop + factor ).setParseAction( self.pushFirst ) ) expr << term + ZeroOrMore( ( addop + term ).setParseAction( self.pushFirst ) ) bnf = Optional((ident + assign).setParseAction(self.assignVar)) + expr pattern = bnf + StringEnd() #print "pattern" , pattern # map operator symbols to corresponding arithmetic operations self.opn = { "+" : ( lambda a,b: a + b ), "-" : ( lambda a,b: a - b ), "*" : ( lambda a,b: a * b ), "/" : ( lambda a,b: a / b ), "^" : ( lambda a,b: a ** b ) } exprns = input_strings.split(",") for input_string in exprns: self.exprStack = [] self.varStack = [] if input_string != '': # try parsing the input string try: L=pattern.parseString( input_string ) #print "L", L except ParseException,err: L=['Parse Failure',input_string] if debug_flag: print input_string, "->", L if len(L)==0 or L[0] != 'Parse Failure': if debug_flag: print "exprStack=", self.exprStack # calculate result , store a copy in ans , display the result to user result=self.evaluateStack(self.exprStack) self.variables['ans']=result print input_string , "=" , result # Assign result to a variable if required if debug_flag: print "var=",self.varStack if len(self.varStack)==1: self.variables[self.varStack.pop()]=result if debug_flag: print "variables=",self.variables else: print 'Parse Failure' print err.line print " "*(err.column-1) + "^" print input_string , "-" , err
M2X_template = "FFT_M2X_%i.c" X2L_template = "FFT_X2L_%i.c" print ''' /* This file is automatically generated by gen_FFT_M2X_X2L_aux.py */ /* DO NOT EDIT! */ #include"_fmmv.h" ''' if len(argv) > 1 and argv[1] == "cores_init": ident = Word(alphas, alphas + nums + "_") fun_decl = (Literal("void") + ident + Literal("(double *y, double *x)")) group = Forward() group << "{" + ZeroOrMore(group ^ CharsNotIn("{}")) + "}" tail = fun_decl.suppress() + group.suppress() + ZeroOrMore(CharsNotIn("")) tail.ignore(cStyleComment) for M in M_list: file = open(dirname + "/" + (M2X_template % M)) t = tail.parseString("".join(file.readlines())) print t[0].replace("double", "_FLOAT_") file.close() file = open(dirname + "/" + (X2L_template % M)) t = tail.parseString("".join(file.readlines())) print t[0].replace("double", "_FLOAT_") file.close() elif len(argv) > 1 and "cores" in argv[ 1]: #################################################### if "simd2" in argv[1]: