diff options
| author | Bobby <[email protected]> | 2024-02-03 05:19:30 -0500 |
|---|---|---|
| committer | Bobby <[email protected]> | 2024-02-03 05:19:30 -0500 |
| commit | febcc8807295c84ec8755b2468cc943cfa882a59 (patch) | |
| tree | 9d3f04da65846a0a8f18fe28f255992ebf3d28f2 /parser | |
| parent | 9e1adf759c52595b9b8eaba1376ebb8d1eeb77e5 (diff) | |
| download | mana-febcc8807295c84ec8755b2468cc943cfa882a59.tar.xz mana-febcc8807295c84ec8755b2468cc943cfa882a59.zip | |
CallExpressions Implemented. Parser Done
Diffstat (limited to 'parser')
| -rw-r--r-- | parser/parser.go | 45 | ||||
| -rw-r--r-- | parser/parser_test.go | 49 |
2 files changed, 94 insertions, 0 deletions
diff --git a/parser/parser.go b/parser/parser.go index afbdf24..31e89f3 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -29,6 +29,7 @@ var precedences = map[tokens.TokenType]int{ tokens.MINUS: SUM, tokens.SLASH: PRODUCT, tokens.ASTERISK: PRODUCT, + tokens.LPAREN: CALL, } type ( @@ -69,6 +70,7 @@ func New(l *lexer.Lexer) *Parser { p.registerPrefix(tokens.FALSE, p.parseBoolean) p.registerPrefix(tokens.IF, p.parseIfExpression) p.registerPrefix(tokens.FUNCTION, p.parseFunctionLiteral) + p.registerPrefix(tokens.LPAREN, p.parseGroupedExpression) // Initialize the infix parse functions. p.infixParseFns = make(map[tokens.TokenType]infixParseFn) @@ -80,6 +82,7 @@ func New(l *lexer.Lexer) *Parser { p.registerInfix(tokens.NOT_EQ, p.parseInfixExpression) p.registerInfix(tokens.LT, p.parseInfixExpression) p.registerInfix(tokens.GT, p.parseInfixExpression) + p.registerInfix(tokens.LPAREN, p.parseCallExpression) return p } @@ -373,6 +376,48 @@ func (p *Parser) parseFunctionParameters() []*ast.Identifier { return identifiers } +// parseGroupedExpression parses a grouped expression. +func (p *Parser) parseGroupedExpression() ast.Expression { + p.nextToken() + + exp := p.parseExpression(LOWEST) + + if !p.expectPeek(tokens.RPAREN) { return nil } + + return exp +} + +// parseCallExpression parses a call expression. +func (p *Parser) parseCallExpression(function ast.Expression) ast.Expression { + exp := &ast.CallExpression{Token: p.curToken, Function: function} + exp.Arguments = p.parseCallArguments() + + return exp +} + +// parseCallArguments parses call arguments. +func (p *Parser) parseCallArguments() []ast.Expression { + args := []ast.Expression{} + + if p.peekTokenIs(tokens.RPAREN) { + p.nextToken() + return args + } + + p.nextToken() + args = append(args, p.parseExpression(LOWEST)) + + for p.peekTokenIs(tokens.COMMA) { + p.nextToken() + p.nextToken() + args = append(args, p.parseExpression(LOWEST)) + } + + if !p.expectPeek(tokens.RPAREN) { return nil } + + return args +} + // curTokenIs returns true if the current token is of the given type. func (p *Parser) curTokenIs(t tokens.TokenType) bool { return p.curToken.Type == t diff --git a/parser/parser_test.go b/parser/parser_test.go index 415b8cb..cd38144 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -510,6 +510,18 @@ func TestOperatorPrecedenceParsing(t *testing.T) { "3 < 5 == true", "((3 < 5) == true)", }, + { + "a + add(b * c) + d", + "((a + add((b * c))) + d)", + }, + { + "add(a, b, 1, 2 * 3, 4 + 5, add(6, 7 * 8))", + "add(a, b, 1, (2 * 3), (4 + 5), add(6, (7 * 8)))", + }, + { + "add(a + b + c * d / f + g)", + "add((((a + b) + ((c * d) / f)) + g))", + }, } for _, tt := range tests { @@ -726,3 +738,40 @@ func TestFunctionParameterParsing(t *testing.T) { } } } + +func TestCallExpressionParsing(t *testing.T) { + var input string = "add(1, 2 * 3, 4 + 5);" + + var l *lexer.Lexer = lexer.New(input) + var p *Parser = New(l) + var program *ast.Program = p.ParseProgram() + checkParserErrors(t, p) + + if len(program.Statements) != 1 { + t.Fatalf("program.Statements does not contain %d statements. got=%d", 1, len(program.Statements)) + } + + stmt, ok := program.Statements[0].(*ast.ExpressionStatement) + + if !ok { + t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0]) + } + + exp, ok := stmt.Expression.(*ast.CallExpression) + + if !ok { + t.Fatalf("stmt.Expression is not ast.CallExpression. got=%T", stmt.Expression) + } + + if !testIdentifier(t, exp.Function, "add") { + return + } + + if len(exp.Arguments) != 3 { + t.Fatalf("wrong length of arguments. got=%d", len(exp.Arguments)) + } + + testLiteralExpression(t, exp.Arguments[0], 1) + testInfixExpression(t, exp.Arguments[1], 2, "*", 3) + testInfixExpression(t, exp.Arguments[2], 4, "+", 5) +} |
