umx_compiler

UMX virtual machine "Monkey" interpreter / bytecode compiler
git clone git://bsandro.tech/umx_compiler
Log | Files | Refs

commit c1e17a9489c559cf491fe0761a412b19d7e96427
parent 4b22811f7acc064528a19ddf71c11c337afcd2d6
Author: bsandro <email@bsandro.tech>
Date:   Tue, 21 Jun 2022 03:07:55 +0300

function calls parsing

Diffstat:
Mast/ast.go | 23+++++++++++++++++++++++
Mparser/parser.go | 27+++++++++++++++++++++++++++
Mparser/parser_test.go | 31+++++++++++++++++++++++++++++++
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) +}