def operator(self, lhs, min_precedence): while self.tokens.accept(self._infix_of_min_precedence, min_precedence): operator = self.tokens.matched.operator if operator.prefix: raise ValueError("infix+prefix operators aren't supported.") if operator.suffix: rhs = self.expression() self.tokens.expect(grammar.match_tokens(operator.suffix)) rhs.end = self.tokens.matched.end else: rhs = self.atom() next_min_precedence = operator.precedence if operator.assoc == "left": next_min_precedence += 1 while self.tokens.match(grammar.infix, self.operators): if (self.tokens.matched.operator.precedence < next_min_precedence): break rhs = self.operator(rhs, self.tokens.matched.operator.precedence) if not rhs: raise errors.EfilterParseError( message="Expecting the operator RHS here.", token=self.tokens.peek(0)) lhs = operator.handler(lhs, rhs, start=lhs.start, end=rhs.end, source=self.original) return lhs
def atom(self): # Unary operator. if self.tokens.accept(grammar.prefix, self.operators): operator = self.tokens.matched.operator start = self.tokens.matched.start children = [self.expression(operator.precedence)] # Allow infix to be repeated in circumfix operators. if operator.infix: while self.tokens.accept(grammar.match_tokens(operator.infix)): children.append(self.expression()) # If we have a suffix expect it now. if operator.suffix: self.tokens.expect(grammar.match_tokens(operator.suffix)) return operator.handler(*children, start=start, end=self.tokens.matched.end, source=self.original) if self.tokens.accept(grammar.literal): return ast.Literal(self.tokens.matched.value, source=self.original, start=self.tokens.matched.start, end=self.tokens.matched.end) if self.tokens.accept(grammar.symbol): return ast.Var(self.tokens.matched.value, source=self.original, start=self.tokens.matched.start, end=self.tokens.matched.end) if self.tokens.accept(grammar.lparen): expr = self.expression() self.tokens.expect(grammar.rparen) return expr if self.tokens.peek(0): raise errors.EfilterParseError( message="Was not expecting %r here." % self.tokens.peek(0).name, token=self.tokens.peek(0)) else: raise errors.EfilterParseError("Unexpected end of input.")
def operator(self, lhs, min_precedence): """Climb operator precedence as long as there are operators. This function implements a basic precedence climbing parser to deal with binary operators in a sane fashion. The outer loop will keep spinning as long as the next token is an operator with a precedence of at least 'min_precedence', parsing operands as atoms (which, in turn, recurse into 'expression' which recurses back into 'operator'). This supports both left- and right-associativity. The only part of the code that's not a regular precedence-climber deals with mixfix operators. A mixfix operator in DottySQL consists of an infix part and a suffix (they are still binary, they just have a terminator). """ # Spin as long as the next token is an operator of higher # precedence. (This may not do anything, which is fine.) while self.accept_operator(precedence=min_precedence): operator = self.tokens.matched.operator # If we're parsing a mixfix operator we can keep going until # the suffix. if operator.suffix: rhs = self.expression() self.tokens.expect(common_grammar.match_tokens( operator.suffix)) rhs.end = self.tokens.matched.end elif operator.name == ".": # The dot operator changes the meaning of RHS. rhs = self.dot_rhs() else: # The right hand side is an atom, which might turn out to be # an expression. Isn't recursion exciting? rhs = self.atom() # Keep going as long as the next token is an infix operator of # higher precedence. next_min_precedence = operator.precedence if operator.assoc == "left": next_min_precedence += 1 while self.tokens.match(grammar.infix): if (self.tokens.matched.operator.precedence < next_min_precedence): break rhs = self.operator(rhs, self.tokens.matched.operator.precedence) lhs = operator.handler(lhs, rhs, start=lhs.start, end=rhs.end, source=self.original) return lhs
def operator(self, lhs, min_precedence): """Climb operator precedence as long as there are operators. This function implements a basic precedence climbing parser to deal with binary operators in a sane fashion. The outer loop will keep spinning as long as the next token is an operator with a precedence of at least 'min_precedence', parsing operands as atoms (which, in turn, recurse into 'expression' which recurses back into 'operator'). This supports both left- and right-associativity. The only part of the code that's not a regular precedence-climber deals with mixfix operators. A mixfix operator in DottySQL consists of an infix part and a suffix (they are still binary, they just have a terminator). """ # Spin as long as the next token is an operator of higher # precedence. (This may not do anything, which is fine.) while self.accept_operator(precedence=min_precedence): operator = self.tokens.matched.operator # If we're parsing a mixfix operator we can keep going until # the suffix. if operator.suffix: rhs = self.expression() self.tokens.expect(common_grammar.match_tokens(operator.suffix)) rhs.end = self.tokens.matched.end elif operator.name == ".": # The dot operator changes the meaning of RHS. rhs = self.dot_rhs() else: # The right hand side is an atom, which might turn out to be # an expression. Isn't recursion exciting? rhs = self.atom() # Keep going as long as the next token is an infix operator of # higher precedence. next_min_precedence = operator.precedence if operator.assoc == "left": next_min_precedence += 1 while self.tokens.match(grammar.infix): if (self.tokens.matched.operator.precedence < next_min_precedence): break rhs = self.operator(rhs, self.tokens.matched.operator.precedence) lhs = operator.handler(lhs, rhs, start=lhs.start, end=rhs.end, source=self.original) return lhs