umx_compiler

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

commit bf1df56370215b914d5a0e9ff416da6eb50b1500
parent 65428752a41df1171c0ba4735b8f6f7c4766dc88
Author: bsandro <email@bsandro.tech>
Date:   Sun, 19 Jun 2022 00:12:58 +0300

parser support for if statements

Diffstat:
Mast/ast.go | 39+++++++++++++++++++++++++++++++++++++++
Mparser/parser.go | 33+++++++++++++++++++++++++++++++++
Mparser/parser_test.go | 35+++++++++++++++++++++++++++++++++++
3 files changed, 107 insertions(+), 0 deletions(-)

diff --git a/ast/ast.go b/ast/ast.go @@ -176,3 +176,42 @@ func (il *IntegerLiteral) TokenLiteral() string { func (il *IntegerLiteral) String() string { return il.Token.Literal } + +type IfExpression struct { + Token token.Token + Condition Expression + Consequence *BlockStatement + Alternative *BlockStatement +} +func (ie *IfExpression) expressionNode() {} +func (ie *IfExpression) TokenLiteral() string { + return ie.Token.Literal +} +func (ie *IfExpression) String() string { + var out bytes.Buffer + out.WriteString("if") + out.WriteString(ie.Condition.String()) + out.WriteString(" ") + out.WriteString(ie.Consequence.String()) + if ie.Alternative != nil { + out.WriteString(" else ") + out.WriteString(ie.Alternative.String()) + } + return out.String() +} + +type BlockStatement struct { + Token token.Token + Statements []Statement +} +func (bs *BlockStatement) expressionNode() {} +func (bs *BlockStatement) TokenLiteral() string { + return bs.Token.Literal +} +func (bs *BlockStatement) String() string { + var out bytes.Buffer + for _, s := range bs.Statements { + out.WriteString(s.String()) + } + return out.String() +} diff --git a/parser/parser.go b/parser/parser.go @@ -72,6 +72,8 @@ func New(l *lexer.Lexer) *Parser { p.registerPrefix(token.LPAREN, p.parseGroupedExpression) + p.registerPrefix(token.IF, p.parseIfExpression) + return p } @@ -264,3 +266,34 @@ func (p *Parser) parseGroupedExpression() ast.Expression { } return expr } + +func (p *Parser) parseIfExpression() ast.Expression { + expr := &ast.IfExpression{Token: p.curToken} + if !p.expectPeek(token.LPAREN) { + return nil + } + p.nextToken() + expr.Condition = p.parseExpression(LOWEST) + if !p.expectPeek(token.RPAREN) { + return nil + } + if !p.expectPeek(token.LCURLY) { + return nil + } + expr.Consequence = p.parseBlockStatement() + return expr +} + +func (p *Parser) parseBlockStatement() *ast.BlockStatement { + block := &ast.BlockStatement{Token: p.curToken} + block.Statements = []ast.Statement{} + p.nextToken() + for !p.curTokenIs(token.RCURLY) && !p.curTokenIs(token.EOF) { + statement := p.parseStatement() + if statement != nil { + block.Statements = append(block.Statements, statement) + } + p.nextToken() + } + return block +} diff --git a/parser/parser_test.go b/parser/parser_test.go @@ -400,3 +400,38 @@ func TestBooleanExpression(t *testing.T) { } } } + +func TestIfExpression(t *testing.T) { + input := "if (x<y) { x }" + l := lexer.New(input) + p := New(l) + prg := p.ParseProgram() + checkParserErrors(t, p) + if len(prg.Statements) != 1 { + t.Fatalf("len(prg.Statements) is not 1 but %d", len(prg.Statements)) + } + 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.IfExpression) + if !ok { + t.Fatalf("statement.Expression is not IfExpression but %T", statement.Expression) + } + if !testInfixExpression(t, expr.Condition, "x", "<", "y") { + return + } + if len(expr.Consequence.Statements) != 1 { + t.Fatalf("len(expr.Consequence.Statements) is not 1 but %d", len(expr.Consequence.Statements)) + } + cons, ok := expr.Consequence.Statements[0].(*ast.ExpressionStatement) + if !ok { + t.Fatalf("consequence statement is not Statement but %T", expr.Consequence.Statements[0]) + } + if !testIdentifier(t, cons.Expression, "x") { + return + } + if expr.Alternative != nil { + t.Fatalf("expr alternative is not nil") + } +}