def primary(self) -> Expr.Expr: if self.match(Token.TokenType.FALSE): return Expr.Literal(False) if self.match(Token.TokenType.TRUE): return Expr.Literal(True) if self.match(Token.TokenType.NIL): return Expr.Literal(None) if self.match(Token.TokenType.NUMBER, Token.TokenType.STRING): return Expr.Literal(self.previous().literal) if self.match(Token.TokenType.SUPER): keyword = self.previous() self.consume(Token.TokenType.DOT, "Expect '.' after 'super'.") method = self.consume(Token.TokenType.IDENTIFIER, "Expect superclass method name.") return Expr.Super(keyword, method) if self.match(Token.TokenType.THIS): return Expr.This(self.previous()) if (self.match(Token.TokenType.IDENTIFIER)): return Expr.Variable(self.previous()) if self.match(Token.TokenType.LEFT_PAREN): expression = self.expression() self.consume(Token.TokenType.RIGHT_PAREN, "Expect ')' after expression.") return Expr.Grouping(expression) raise self.error(self.peek(), "Expect expression.")
def primary(self): ''' We've reached the end with terminal conditions. This is how we stop the recursive descent Terminals include -> Bools, Null, Numbers, Strings, Parenthetical groups (which as expected have their nested expression start the whole process back up from the top. ''' if self.match(TokenType.THIS): return Expr.This(self.previous()) if self.match(TokenType.FALSE): return Expr.Literal(False) if self.match(TokenType.TRUE): return Expr.Literal(True) if self.match(TokenType.NIL): return Expr.Literal(None) if self.match(TokenType.NUMBER, TokenType.STRING): return Expr.Literal(self.previous().literal) if self.match(TokenType.SUPER): keyword = self.previous() self.consume(TokenType.DOT, "Expect '.' after 'super'.") method = self.consume(TokenType.IDENTIFIER, "Expect superclass method name") return Expr.Super(keyword, method) if self.match(TokenType.IDENTIFIER): return Expr.Variable(self.previous()) if self.match(TokenType.LEFT_PAREN): expr = self.expression() self.consume( TokenType.RIGHT_PAREN, "Expect ')' after expression" ) ## need to find a match otherwise must record as error return Expr.Grouping(expr) self.error(self.peek(), "Expect expression" ) ## indicates we reached a token that can't start an expr
def main(): astPrinter = AstPrinter() expression = Expr.Binary( Expr.Unary(Token(TokenType.MINUS, '-', None, 1), Expr.Literal(123)), Token(TokenType.STAR, '*', None, 1), Expr.Grouping(Expr.Literal(45.67))) print(astPrinter.print(expression))
def primary(self) -> Expr.Expr: if self.match(IDENTIFIER): # IDENTIFIER token contains a word, make it into an Expr return Expr.Variable(self.previous()) # handle the keyword values if self.match(FALSE): return Expr.Literal(False) if self.match(TRUE): return Expr.Literal(True) if self.match(NIL): return Expr.Literal(None) if self.match(THIS): return Expr.This(self.previous()) if self.match(SUPER): keyword = self.previous() # save "super" token self.consume(DOT, "Expect '.' after 'super'") method_name = self.consume(IDENTIFIER, "Expect superclass method name.") return Expr.Super(keyword, method_name) # handle literal values if self.match(NUMBER, STRING): return Expr.Literal(self.previous().literal) ''' The only remaining legitimate option is a left paren, which will contain an expression and must be followed by a right paren. What would this code do, one asks, given input of "()"? As Nystrom wrote it (through Chapter 8, maybe it gets improved later?) it will look at the token list of [LEFT_PAREN,RIGHT_PAREN,...]; it will call self.expression to process the list [RIGHT_PAREN,...], and will fall through to be an error below. Thus Lox doesn't support the null expression. I am changing this to recognize "()" and return "(nil)". ''' if self.match(LEFT_PAREN): if self.peek().type == RIGHT_PAREN: grouped_expr = Expr.Literal(None) else: grouped_expr = self.expression() self.consume(RIGHT_PAREN, "Expected ')' after expression") return Expr.Grouping(grouped_expr) ''' This is the basement of the recursive ladder. It only returns a value when there is a match to one of the ttypes it checks for. Ergo all possible Expression token types must be checked-for here, or above here. A token type not explicitly checked-for ends up at this error. This is also where we end up, if there is a binary operator without a left argument, e.g. /5 or (!=3). Per challenge 3, detect that. Note that BANG and MINUS have been dealt with in unary() above. If one of those is not followed by a valid rhs, it is the following character that drops through to here. ''' bad_token = self.peek() if bad_token.type in (PLUS, SLASH, STAR, EQUAL, GREATER, LESS): self.error(bad_token, 'operator requires a left operand') ''' Finally we just don't know what's going on. ''' self.error(bad_token, 'Unanticipated input')
def run_main(): exp = Expr.Binary( Expr.Unary( Token(TokenType.MINUS, "-", null, 1), Expr.Litaral(123)), Toke(TokenType.Star, "*", null, 1), Expr.Grouping( Expr.Literal(45.67))) print(AstPrinter.print(exp))
def _literal(self): if self._match(TokenType.ALPHA): return Expr.Literal(self._previous().value) if self._match(TokenType.LEFT_PARAN): expr = self._expression() if self._match(TokenType.RIGHT_PARAN): return Expr.Grouping(expr) else: self.__error("Closing Paranthesis not found") self.__error("Unexpected Token")
def primary(self): if self.identifier(exp=None): return Expr.Identifier(self.lastToken()) elif self.match(TokenType.NUMBER, TokenType.STRING, TokenType.TRUE, TokenType.FALSE, TokenType.NIL): return Expr.Literal(self.nextToken()) elif self.consume(TokenType.LEFT_PAREN): ast = self.expression() self.consume(TokenType.RIGHT_PAREN, exp=')') return Expr.Grouping(ast) elif self.consume(TokenType.LAMBDA): return self.funExpr() else: raise self.errUnexpToken("primary expr")
def primary(self): if self.match(TokenType.FALSE): return Expr.Literal(False) if self.match(TokenType.TRUE): return Expr.Literal(True) if self.match(TokenType.NIL): return Expr.Literal(None) if self.match(TokenType.NUMBER, TokenType.STRING): return Expr.Literal(self.previous().literal) if self.match(TokenType.LEFT_PAREN): expr = self.expression() self.consume(TokenType.RIGHT_PAREN, "Expect ')' after expression.") return Expr.Grouping(expr) self.consume(None, "Unexpected token")
def primary(self) -> Expr.Expr: """ Parses a primary expression """ if self.match(TokenType.TRUE): return Expr.Literal(True) elif self.match(TokenType.FALSE): return Expr.Literal(False) elif self.match(TokenType.NIL): return Expr.Literal(None) elif self.match(TokenType.INT, TokenType.FLOAT, TokenType.STRING): return Expr.Literal(self.previous().literal) elif self.match(TokenType.LEFT_PAREN): expr = self.expression() self.consume(TokenType.RIGHT_PAREN, "Expected closing ')' after expression") return Expr.Grouping(expr) elif self.match(TokenType.IDENTIFIER): return Expr.Variable(self.previous()) else: raise self.error(self.peek(), "Expected expression")
return self.parenthesize(expr.operator.lexeme, expr.left, expr.right) def visit_Grouping(self, expr): assert isinstance(expr, Expr.Grouping), "Must be Grouping Expression otherwise cannot print its AST" return self.parenthesize("group", expr.expression) def visit_Literal(self, expr): assert isinstance(expr, Expr.Literal), "Must be Literal Expression otherwise cannot print its AST" return str(expr.value) def visit_Unary(self, expr): assert isinstance(expr, Expr.Unary), "Must be Unary Expression otherwise cannot print its AST" return self.parenthesize(expr.operator.lexeme, expr.right) def parenthesize(self, name, *exprs): builder = ["(", name] for expr in exprs: builder.append(expr.accept(self)) builder.append(")") return " ".join(builder) if __name__ == "__main__": ## Test Expression below left_expression = Expr.Unary(Token(TokenType.MINUS, "-", 1, None), Expr.Literal(123)) operator = Token(TokenType.STAR, "*", 1, None) right_expression = Expr.Grouping(Expr.Literal(45.67)) main_expression = Expr.Binary(left_expression, operator, right_expression) print(ASTPrinter().print_ast(main_expression))
def visit_binary_expr(self, expr: Expr.Binary): return self.parenthesize(expr.operator.lexeme, expr.left, expr.right) def visit_grouping_expr(self, expr: Expr.Grouping): return self.parenthesize("group", expr.expression) def visit_literal_expr(self, expr: Expr.Literal): return str(expr.value) if expr.value != None else "nil" def visit_unary_expr(self, expr: Expr.Unary): return self.parenthesize(expr.operator.lexeme, expr.right) def parenthesize(self, name: str, *expressions): res = "(" + name for expression in expressions: res += " " + expression.accept(self) res += ")" return res if __name__ == "__main__": expression = Expr.Binary( Expr.Unary(Token.Token(Token.TokenType.MINUS, "-", None, 1), Expr.Literal(123)), Token.Token(Token.TokenType.STAR, "*", None, 1), Expr.Grouping(Expr.Literal(45.67))) print(AstPrinter().print_ast(expression))
def _test(): ast = Expr.Binary( Expr.Unary(Token(TokenType.MINUS, "-", None, 1), Expr.Literal(123)), Token(TokenType.STAR, "*", None, 1), Expr.Grouping(Expr.Literal(45.67))) print(LispPrinter().visit(ast))