commit c1e17a9489c559cf491fe0761a412b19d7e96427
parent 4b22811f7acc064528a19ddf71c11c337afcd2d6
Author: bsandro <email@bsandro.tech>
Date: Tue, 21 Jun 2022 03:07:55 +0300
function calls parsing
Diffstat:
3 files changed, 81 insertions(+), 0 deletions(-)
diff --git a/ast/ast.go b/ast/ast.go
@@ -242,3 +242,26 @@ func (fl *FunctionLiteral) String() string {
out.WriteString(fl.Body.String())
return out.String()
}
+
+type CallExpression struct {
+ Token token.Token
+ Function Expression
+ Arguments []Expression
+}
+
+func (ce *CallExpression) expressionNode() {}
+func (ce *CallExpression) TokenLiteral() string {
+ return ce.Token.Literal
+}
+func (ce *CallExpression) String() string {
+ var out bytes.Buffer
+ args := []string{}
+ for _, a := range ce.Arguments {
+ args = append(args, a.String())
+ }
+ out.WriteString(ce.Function.String())
+ out.WriteString("(")
+ out.WriteString(strings.Join(args, ", "))
+ out.WriteString(")")
+ return out.String()
+}
diff --git a/parser/parser.go b/parser/parser.go
@@ -28,6 +28,7 @@ var precedences = map[token.TokenType]int{
token.MINUS: SUM,
token.SLASH: PRODUCT,
token.ASTERISK: PRODUCT,
+ token.LPAREN: CALL,
}
type prefixParseFn func() ast.Expression
@@ -75,6 +76,7 @@ func New(l *lexer.Lexer) *Parser {
p.registerPrefix(token.IF, p.parseIfExpression)
p.registerPrefix(token.FUNCTION, p.parseFunctionLiteral)
+ p.registerInfix(token.LPAREN, p.parseCallExpression)
return p
}
@@ -340,3 +342,28 @@ func (p *Parser) parseFunctionParameters() []*ast.Identifier {
}
return identifiers
}
+
+func (p *Parser) parseCallExpression(function ast.Expression) ast.Expression {
+ expr := &ast.CallExpression{Token: p.curToken, Function: function}
+ expr.Arguments = p.parseCallArguments()
+ return expr
+}
+
+func (p *Parser) parseCallArguments() []ast.Expression {
+ args := []ast.Expression{}
+ if p.peekTokenIs(token.RPAREN) {
+ p.nextToken()
+ return args
+ }
+ p.nextToken()
+ args = append(args, p.parseExpression(LOWEST))
+ for p.peekTokenIs(token.COMMA) {
+ p.nextToken()
+ p.nextToken()
+ args = append(args, p.parseExpression(LOWEST))
+ }
+ if !p.expectPeek(token.RPAREN) {
+ return nil
+ }
+ return args
+}
diff --git a/parser/parser_test.go b/parser/parser_test.go
@@ -350,6 +350,9 @@ func TestOperatorPrecedenceParsing(t *testing.T) {
{"!(true == true)", "{!{true == true}}"},
{"2/(5+5)", "{2 / {5 + 5}}"},
{"-(5+5)", "{-{5 + 5}}"},
+ {"a+add(b*c)+d", "{{a + add({b * c})} + d}"},
+ {"add(a+b+c*d/f+g)", "add({{{a + b} + {{c * d} / f}} + g})"},
+ {"add(a,b,1,2*3,4+5,add(6,7+8))", "add(a, b, 1, {2 * 3}, {4 + 5}, add(6, {7 + 8}))"},
}
for _, tt := range tests {
@@ -538,3 +541,31 @@ func TestFunctionParameterParsing(t *testing.T) {
}
}
}
+
+func TestCallParametersParsing(t *testing.T) {
+ input := "add(1, 2*3, 4+5);"
+ l := lexer.New(input)
+ p := New(l)
+ prg := p.ParseProgram()
+ checkParserErrors(t, p)
+ if len(prg.Statements) != 1 {
+ t.Fatalf("prg.Statements count is wrong")
+ }
+ statement, ok := prg.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("prg.Statements[0] is not ExpressionStatement but %T", prg.Statements[0])
+ }
+ expr, ok := statement.Expression.(*ast.CallExpression)
+ if !ok {
+ t.Fatalf("statement.Expression is not CallExpression but %T", statement.Expression)
+ }
+ if !testIdentifier(t, expr.Function, "add") {
+ return
+ }
+ if len(expr.Arguments) != 3 {
+ t.Fatalf("expr.Arguments count is wrong (%d)", len(expr.Arguments))
+ }
+ testLiteralExpression(t, expr.Arguments[0], 1)
+ testInfixExpression(t, expr.Arguments[1], 2, "*", 3)
+ testInfixExpression(t, expr.Arguments[2], 4, "+", 5)
+}