aboutsummaryrefslogtreecommitdiff
path: root/parser
diff options
context:
space:
mode:
authorBobby <[email protected]>2024-02-03 05:19:30 -0500
committerBobby <[email protected]>2024-02-03 05:19:30 -0500
commitfebcc8807295c84ec8755b2468cc943cfa882a59 (patch)
tree9d3f04da65846a0a8f18fe28f255992ebf3d28f2 /parser
parent9e1adf759c52595b9b8eaba1376ebb8d1eeb77e5 (diff)
downloadmana-febcc8807295c84ec8755b2468cc943cfa882a59.tar.xz
mana-febcc8807295c84ec8755b2468cc943cfa882a59.zip
CallExpressions Implemented. Parser Done
Diffstat (limited to 'parser')
-rw-r--r--parser/parser.go45
-rw-r--r--parser/parser_test.go49
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)
+}