From 1ac48b0cad06d9e5770f3aa3fa219b970cf5931c Mon Sep 17 00:00:00 2001 From: tijani Date: Sun, 31 Jul 2022 21:55:09 +0000 Subject: [PATCH] Test for IfExpressions, and IfElseExpression git-svn-id: https://svn.tlawal.org/svn/monkey@36 f6afcba9-9ef1-4bdd-9b72-7484f5705bac --- parser/parser.go | 60 +++++++++++++++++++++++++-- parser/parser_test.go | 94 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 3 deletions(-) diff --git a/parser/parser.go b/parser/parser.go index 11e7c69..f3e554f 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -102,7 +102,10 @@ func New(l_lexer *lexer.Lexer) *Parser { // Grouped Expression l_parser.register_prefix(token.LPAREN, l_parser.parse_grouped_expression) - + + // IF Expression + l_parser.register_prefix(token.IF, l_parser.parse_if_expression) + return l_parser } @@ -282,11 +285,62 @@ func (l_parser *Parser) parse_boolean() ast.Expression { } } -func (l_parser *Parser) parse_grouped_expression() ast.Expression{ +func (l_parser *Parser) parse_grouped_expression() ast.Expression { + defer untrace(trace("parse_grouped_expression")) l_parser.next_token() expression := l_parser.parse_expression(LOWEST) - if !l_parser.expect_peek(token.RPAREN){ + if !l_parser.expect_peek(token.RPAREN) { return nil } return expression } + +func (l_parser *Parser) parse_if_expression() ast.Expression { + defer untrace(trace("parse_if_expression")) + expression := &ast.IfExpression{ + Token: l_parser.current_token, + } + + if !l_parser.expect_peek(token.LPAREN) { + return nil + } + + l_parser.next_token() + expression.Condition = l_parser.parse_expression(LOWEST) + + if !l_parser.expect_peek(token.RPAREN) { + return nil + } + + if !l_parser.expect_peek(token.LBRACE) { + return nil + } + + expression.Consequence = l_parser.parse_block_statement() + + if l_parser.peek_token_is(token.ELSE) { + l_parser.next_token() + if !l_parser.expect_peek(token.LBRACE) { + return nil + } + expression.Alternative = l_parser.parse_block_statement() + } + + return expression +} + +func (l_parser *Parser) parse_block_statement() *ast.BlockStatement { + block := &ast.BlockStatement{Token: l_parser.current_token} + block.Statements = []ast.Statement{} + + l_parser.next_token() + + for !l_parser.current_token_is(token.RBRACE) && !l_parser.current_token_is(token.EOF) { + statement := l_parser.parse_statement() + if statement != nil { + block.Statements = append(block.Statements, statement) + } + l_parser.next_token() + } + return block +} diff --git a/parser/parser_test.go b/parser/parser_test.go index 079d43e..d03b72e 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -359,6 +359,100 @@ func TestBooleanExpression(l_test *testing.T) { } } +func TestIfExpression(l_test *testing.T) { + input := `if (x < y) { x }` + + l_lexer := lexer.New(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.IfExpression) + if !ok { + l_test.Fatalf("statement.Expression is not ast.IfExpression, got=%T", statement.Expression) + } + + if !testInfixExpression(l_test, expression.Condition, "x", "<", "y") { + return + } + if len(expression.Consequence.Statements) != 1 { + l_test.Errorf("consequence is not 1 statements, got=%d\n", len(expression.Consequence.Statements)) + } + + consequence, ok := expression.Consequence.Statements[0].(*ast.ExpressionStatement) + if !ok { + l_test.Fatalf("Statements[0] is not ast.ExpressionStatement, got=%T", expression.Consequence.Statements[0]) + } + + if !testIdentifier(l_test, consequence.Expression, "x") { + return + } + + if expression.Alternative != nil { + l_test.Errorf("expression.Alternative.Statements was not nil, got=%+v", expression.Alternative) + } +} + +func TestIfElseExpression(l_test *testing.T) { + input := `if (x < y) { x } else { y }` + + l_lexer := lexer.New(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 an ast.ExpressionStatement, got=%T", program.Statements[0]) + } + + expression, ok := statement.Expression.(*ast.IfExpression) + if !ok { + l_test.Fatalf("statement.Expression is not ast.IfExpression, got=%T", statement.Expression) + } + + if !testInfixExpression(l_test, expression.Condition, "x", "<", "y") { + return + } + + if len(expression.Consequence.Statements) != 1 { + l_test.Errorf("consequence is not 1 statements, got=%d\n", len(expression.Consequence.Statements)) + } + + consequence, ok := expression.Consequence.Statements[0].(*ast.ExpressionStatement) + if !ok { + l_test.Fatalf("Statements[0] is not ast.ExpressionStatement, got=%T", expression.Consequence.Statements[0]) + } + if !testIdentifier(l_test, consequence.Expression, "x") { + return + } + + if len(expression.Alternative.Statements) != 1 { + l_test.Errorf("expression.Alterative.Statements does not contain 1 statement, got=%d\n", len(expression.Alternative.Statements)) + } + + alternative, ok := expression.Alternative.Statements[0].(*ast.ExpressionStatement) + if !ok { + l_test.Fatalf("Statements[0] is not ast.ExpressionStatement, got=%T", expression.Alternative.Statements[0]) + } + + if !testIdentifier(l_test, alternative.Expression, "y") { + return + } +} + // Helpers func check_parser_errors(l_test *testing.T, l_parser *Parser) {