From 2253d6b02cc0a1b6176c1a27cf57a55d7add9036 Mon Sep 17 00:00:00 2001 From: tijani Date: Tue, 31 May 2022 11:32:43 +0000 Subject: [PATCH] Parsing prefix expressions, tests passes git-svn-id: https://svn.tlawal.org/svn/monkey@24 f6afcba9-9ef1-4bdd-9b72-7484f5705bac --- ast/ast.go | 22 +++++++++++++++ parser/parser.go | 66 +++++++++++++++++++++++++++---------------- parser/parser_test.go | 60 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 24 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 041b438..ac2be69 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -58,6 +58,12 @@ type IntegerLiteral struct { Value int64 } +type PrefixExpression struct { + Token token.Token // prefix token i.e. ! + Operator string + Right Expression +} + // Let Statements func (ls *LetStatement) statement_node() {} @@ -139,3 +145,19 @@ func (il *IntegerLiteral) TokenLiteral() string { func (il *IntegerLiteral) String() string { return il.Token.Literal } + +// PrefixExpression +func (pe *PrefixExpression) expression_node() {} +func (pe *PrefixExpression) TokenLiteral() string { + return pe.Token.Literal +} + +func (pe *PrefixExpression) String() string { + var out bytes.Buffer + out.WriteString("(") + out.WriteString(pe.Operator) + out.WriteString(pe.Right.String()) + out.WriteString(")") + + return out.String() +} diff --git a/parser/parser.go b/parser/parser.go index 2749ef2..1609751 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -47,6 +47,8 @@ func New(l_lexer *lexer.Lexer) *Parser { l_parser.prefix_parse_functions = make(map[token.TokenType]prefix_parse_function) l_parser.register_prefix(token.IDENT, l_parser.parse_identifier) l_parser.register_prefix(token.INT, l_parser.parse_integer_literal) + l_parser.register_prefix(token.BANG, l_parser.parse_prefix_expression) + l_parser.register_prefix(token.MINUS, l_parser.parse_prefix_expression) return l_parser } @@ -69,16 +71,17 @@ func (l_parser *Parser) ParseProgram() *ast.Program { return program } -func (l_parser *Parser) register_prefix(l_token_type token.TokenType, l_function prefix_parse_function) { - l_parser.prefix_parse_functions[l_token_type] = l_function +func (l_parser *Parser) peek_error(l_token token.TokenType) { + message := fmt.Sprintf("expected next token to be %s, got %s", l_token, l_parser.peek_token.Type) + l_parser.errors = append(l_parser.errors, message) } -func (l_parser *Parser) parse_identifier() ast.Expression { - return &ast.Identifier{Token: l_parser.current_token, Value: l_parser.current_token.Literal} +func (l_parser *Parser) current_token_is(l_token token.TokenType) bool { + return l_parser.current_token.Type == l_token } -func (l_parser *Parser) register_infix(l_token_type token.TokenType, l_function infix_parse_function) { - l_parser.infix_parse_functions[l_token_type] = l_function +func (l_parser *Parser) peek_token_is(l_token token.TokenType) bool { + return l_parser.peek_token.Type == l_token } func (l_parser *Parser) next_token() { @@ -86,6 +89,16 @@ func (l_parser *Parser) next_token() { l_parser.peek_token = l_parser.lexer.NextToken() } +func (l_parser *Parser) expect_peek(l_token token.TokenType) bool { + if l_parser.peek_token_is(l_token) { + l_parser.next_token() + return true + } else { + l_parser.peek_error(l_token) + return false + } +} + func (l_parser *Parser) parse_statement() ast.Statement { switch l_parser.current_token.Type { case token.LET: @@ -135,6 +148,18 @@ func (l_parser *Parser) parse_expression_statement() *ast.ExpressionStatement { return statement } +func (l_parser *Parser) register_prefix(l_token_type token.TokenType, l_function prefix_parse_function) { + l_parser.prefix_parse_functions[l_token_type] = l_function +} + +func (l_parser *Parser) parse_identifier() ast.Expression { + return &ast.Identifier{Token: l_parser.current_token, Value: l_parser.current_token.Literal} +} + +func (l_parser *Parser) register_infix(l_token_type token.TokenType, l_function infix_parse_function) { + l_parser.infix_parse_functions[l_token_type] = l_function +} + func (l_parser *Parser) parse_integer_literal() ast.Expression { literal := &ast.IntegerLiteral{Token: l_parser.current_token} value, error := strconv.ParseInt(l_parser.current_token.Literal, 0, 64) @@ -150,31 +175,24 @@ func (l_parser *Parser) parse_integer_literal() ast.Expression { func (l_parser *Parser) parse_expression(precedence int) ast.Expression { prefix := l_parser.prefix_parse_functions[l_parser.current_token.Type] if prefix == nil { + l_parser.no_prefix_parse_function_error(l_parser.current_token.Type) return nil } left_expression := prefix() return left_expression } -func (l_parser *Parser) expect_peek(l_token token.TokenType) bool { - if l_parser.peek_token_is(l_token) { - l_parser.next_token() - return true - } else { - l_parser.peek_error(l_token) - return false - } -} - -func (l_parser *Parser) peek_error(l_token token.TokenType) { - message := fmt.Sprintf("expected next token to be %s, got %s", l_token, l_parser.peek_token.Type) +func (l_parser *Parser) no_prefix_parse_function_error(l_token_type token.TokenType) { + message := fmt.Sprintf("no prefix parse function for %s, found", l_token_type) l_parser.errors = append(l_parser.errors, message) } -func (l_parser *Parser) current_token_is(l_token token.TokenType) bool { - return l_parser.current_token.Type == l_token -} - -func (l_parser *Parser) peek_token_is(l_token token.TokenType) bool { - return l_parser.peek_token.Type == l_token +func (l_parser *Parser) parse_prefix_expression() ast.Expression { + expression := &ast.PrefixExpression{ + Token: l_parser.current_token, + Operator: l_parser.current_token.Literal, + } + l_parser.next_token() + expression.Right = l_parser.parse_expression(PREFIX) + return expression } diff --git a/parser/parser_test.go b/parser/parser_test.go index c91179a..337a72b 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -1,6 +1,8 @@ package parser import ( + "fmt" + "monkey/ast" "monkey/lexer" "testing" @@ -175,3 +177,61 @@ func TestIntegerLiteralExpressions(l_test *testing.T) { l_test.Errorf("literal.TokenLiteral not %s, got=%s", "5", literal.TokenLiteral()) } } + +func TestParsingPrefixExpression(l_test *testing.T) { + prefix_tests := []struct { + input string + operator string + integer_value int64 + }{ + {"!5;", "!", 5}, + {"-15", "-", 15}, + } + + for _, tt := range prefix_tests { + l_lexer := lexer.New(tt.input) + l_parser := New(l_lexer) + program := l_parser.ParseProgram() + check_parser_errors(l_test, l_parser) + + if len(program.Statements) != 1 { + l_test.Fatalf("program.Statements does not contain %d statements, got=%d\n", 1, len(program.Statements)) + } + + statement, ok := program.Statements[0].(*ast.ExpressionStatement) + if !ok { + l_test.Fatalf("program.Statements[0] is not ast.ExpressionStatement, got=%T", program.Statements[0]) + } + + expression, ok := statement.Expression.(*ast.PrefixExpression) + if !ok { + l_test.Fatalf("program.Statements[0] is not ast.PrefixEXpression, got=%T", statement.Expression) + } + if expression.Operator != tt.operator { + l_test.Fatalf("exp.Operator is not '%s', got %s", tt.operator, expression.Operator) + } + + if !testIntegerLiteral(l_test, expression.Right, tt.integer_value) { + return + } + } +} + +func testIntegerLiteral(l_test *testing.T, il ast.Expression, value int64) bool { + integer, ok := il.(*ast.IntegerLiteral) + if !ok { + l_test.Errorf("il not *ast.IntegerLiteral, got=%T", il) + return false + } + + if integer.Value != value { + l_test.Errorf("integer.Value not %d, got=%d", value, integer.Value) + return false + } + + if integer.TokenLiteral() != fmt.Sprintf("%d", value) { + l_test.Errorf("integer.TokenLiteral not %d, got=%s", value, integer.TokenLiteral()) + return false + } + return true +}