aboutsummaryrefslogtreecommitdiff
path: root/parser
diff options
context:
space:
mode:
authorBobby <[email protected]>2024-01-24 21:03:26 +0000
committerBobby <[email protected]>2024-01-24 21:03:26 +0000
commitc07b5f8fd0cf8bd1654825dcf7401d670fda4c0a (patch)
tree21630a57b47598ddf58f9428e265157ce9206fc8 /parser
parentb7d832da734647049f28c6994292d77b6e2c60eb (diff)
downloadmana-c07b5f8fd0cf8bd1654825dcf7401d670fda4c0a.tar.xz
mana-c07b5f8fd0cf8bd1654825dcf7401d670fda4c0a.zip
If-Else Parsing
Diffstat (limited to 'parser')
-rw-r--r--parser/parser.go44
-rw-r--r--parser/parser_test.go106
2 files changed, 150 insertions, 0 deletions
diff --git a/parser/parser.go b/parser/parser.go
index 310acee..e38e54c 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -67,6 +67,7 @@ func New(l *lexer.Lexer) *Parser {
p.registerPrefix(tokens.MINUS, p.parsePrefixExpression)
p.registerPrefix(tokens.TRUE, p.parseBoolean)
p.registerPrefix(tokens.FALSE, p.parseBoolean)
+ p.registerPrefix(tokens.IF, p.parseIfExpression)
// Initialize the infix parse functions.
p.infixParseFns = make(map[tokens.TokenType]infixParseFn)
@@ -286,6 +287,49 @@ func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
return expression
}
+func (p *Parser) parseIfExpression() ast.Expression {
+ expression := &ast.IfExpression{Token: p.curToken}
+
+ if !p.expectPeek(tokens.LPAREN) { return nil }
+
+ p.nextToken()
+ expression.Condition = p.parseExpression(LOWEST)
+
+ if !p.expectPeek(tokens.RPAREN) { return nil }
+
+ if !p.expectPeek(tokens.LBRACE) { return nil }
+
+ expression.Consequence = p.parseBlockStatement()
+
+ if p.peekTokenIs(tokens.ELSE) {
+ p.nextToken()
+
+ if !p.expectPeek(tokens.LBRACE) { return nil }
+
+ expression.Alternative = p.parseBlockStatement()
+ }
+
+ return expression
+}
+
+func (p *Parser) parseBlockStatement() *ast.BlockStatement {
+ block := &ast.BlockStatement{Token: p.curToken}
+ block.Statements = []ast.Statement{}
+
+ p.nextToken()
+
+ for !p.curTokenIs(tokens.RBRACE) && !p.curTokenIs(tokens.EOF) {
+ stmt := p.parseStatement()
+ if stmt != nil {
+ block.Statements = append(block.Statements, stmt)
+ }
+
+ p.nextToken()
+ }
+
+ return block
+}
+
// 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 d2a8c8f..f26fa1e 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -546,3 +546,109 @@ func testBooleanLiteral(t *testing.T, exp ast.Expression, value bool) bool {
return true
}
+
+// If expression tests.
+
+func TestIfExpression(t *testing.T) {
+ input := "if (x < y) { x }"
+
+ 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.IfExpression)
+
+ if !ok {
+ t.Fatalf("stmt.Expression is not ast.IfExpression. got=%T", stmt.Expression)
+ }
+
+ if !testInfixExpression(t, exp.Condition, "x", "<", "y") {
+ return
+ }
+
+ if len(exp.Consequence.Statements) != 1 {
+ t.Errorf("consequence is not 1 statements. got=%d", len(exp.Consequence.Statements))
+ }
+
+ consequence, ok := exp.Consequence.Statements[0].(*ast.ExpressionStatement)
+
+ if !ok {
+ t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T", exp.Consequence.Statements[0])
+ }
+
+ if !testIdentifier(t, consequence.Expression, "x") {
+ return
+ }
+
+ if exp.Alternative != nil {
+ t.Errorf("exp.Alternative.Statements was not nil. got=%+v", exp.Alternative)
+ }
+}
+
+func TestIfElseExpression(t *testing.T) {
+ input := "if (x < y) { x } else { y }"
+
+ 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.IfExpression)
+
+ if !ok {
+ t.Fatalf("stmt.Expression is not ast.IfExpression. got=%T", stmt.Expression)
+ }
+
+ if !testInfixExpression(t, exp.Condition, "x", "<", "y") {
+ return
+ }
+
+ if len(exp.Consequence.Statements) != 1 {
+ t.Errorf("consequence is not 1 statements. got=%d", len(exp.Consequence.Statements))
+ }
+
+ consequence, ok := exp.Consequence.Statements[0].(*ast.ExpressionStatement)
+
+ if !ok {
+ t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T", exp.Consequence.Statements[0])
+ }
+
+ if !testIdentifier(t, consequence.Expression, "x") {
+ return
+ }
+
+ if len(exp.Alternative.Statements) != 1 {
+ t.Errorf("consequence is not 1 statements. got=%d", len(exp.Alternative.Statements))
+ }
+
+ alternative, ok := exp.Alternative.Statements[0].(*ast.ExpressionStatement)
+
+ if !ok {
+ t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T", exp.Alternative.Statements[0])
+ }
+
+ if !testIdentifier(t, alternative.Expression, "y") {
+ return
+ }
+}