commit bf1df56370215b914d5a0e9ff416da6eb50b1500
parent 65428752a41df1171c0ba4735b8f6f7c4766dc88
Author: bsandro <email@bsandro.tech>
Date: Sun, 19 Jun 2022 00:12:58 +0300
parser support for if statements
Diffstat:
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")
+ }
+}