commit b438a9f973e08ee8d875e56c9d51e65d322bd6b3
parent 7d6e6f37956eefb874d3032c48a625271e259cc2
Author: bsandro <email@bsandro.tech>
Date:   Fri, 17 Jun 2022 02:27:16 +0300
Boolean parsing
Diffstat:
3 files changed, 88 insertions(+), 5 deletions(-)
diff --git a/ast/ast.go b/ast/ast.go
@@ -20,6 +20,19 @@ type Expression interface {
 	expressionNode()
 }
 
+type Boolean struct {
+	Token token.Token
+	Value bool
+}
+
+func (b *Boolean) expressionNode() {}
+func (b *Boolean) TokenLiteral() string {
+	return b.Token.Literal
+}
+func (b *Boolean) String() string {
+	return b.Token.Literal
+}
+
 type PrefixExpression struct {
 	Token    token.Token
 	Operator string
diff --git a/parser/parser.go b/parser/parser.go
@@ -67,6 +67,9 @@ func New(l *lexer.Lexer) *Parser {
 	p.registerInfix(token.LT, p.parseInfixExpression)
 	p.registerInfix(token.GT, p.parseInfixExpression)
 
+	p.registerPrefix(token.TRUE, p.parseBoolean)
+	p.registerPrefix(token.FALSE, p.parseBoolean)
+
 	return p
 }
 
@@ -246,3 +249,7 @@ func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
 	expression.Right = p.parseExpression(precedence)
 	return expression
 }
+
+func (p *Parser) parseBoolean() ast.Expression {
+	return &ast.Boolean{Token: p.curToken, Value: p.curTokenIs(token.TRUE)}
+}
diff --git a/parser/parser_test.go b/parser/parser_test.go
@@ -171,10 +171,11 @@ func TestParsingPrefixExpressions(t *testing.T) {
 	prefixTests := []struct {
 		input    string
 		operator string
-		intValue int64
+		value    interface{}
 	}{
 		{"!5;", "!", 5},
 		{"-15;", "-", 15},
+		{"!true;", "!", true},
 	}
 
 	for _, tt := range prefixTests {
@@ -201,7 +202,7 @@ func TestParsingPrefixExpressions(t *testing.T) {
 			t.Fatalf("exp.Operator is not %s but %s", tt.operator, expr.Operator)
 		}
 
-		if !testIntegerLiteral(t, expr.Right, tt.intValue) {
+		if !testLiteralExpression(t, expr.Right, tt.value) {
 			return
 		}
 	}
@@ -210,9 +211,9 @@ func TestParsingPrefixExpressions(t *testing.T) {
 func TestParsingInfixExpressions(t *testing.T) {
 	infixTests := []struct {
 		input      string
-		leftValue  int64
+		leftValue  interface{}
 		operator   string
-		rightValue int64
+		rightValue interface{}
 	}{
 		{"1 + 2", 1, "+", 2},
 		{"3 - 4", 3, "-", 4},
@@ -222,6 +223,7 @@ func TestParsingInfixExpressions(t *testing.T) {
 		{"1 < 2", 1, "<", 2},
 		{"3 == 3", 3, "==", 3},
 		{"4 != 5", 4, "!=", 5},
+		{"true == true", true, "==", true},
 	}
 
 	for _, tt := range infixTests {
@@ -239,7 +241,9 @@ func TestParsingInfixExpressions(t *testing.T) {
 			t.Fatalf("statement is not ExpressionStatement but %T", prg.Statements[0])
 		}
 
-		testInfixExpression(t, statement.Expression, tt.leftValue, tt.operator, tt.rightValue)
+		if !testInfixExpression(t, statement.Expression, tt.leftValue, tt.operator, tt.rightValue) {
+			return
+		}
 	}
 }
 
@@ -260,6 +264,23 @@ func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
 	return true
 }
 
+func testBooleanLiteral(t *testing.T, expr ast.Expression, value bool) bool {
+	bo, ok := expr.(*ast.Boolean)
+	if !ok {
+		t.Errorf("expr is not boolean but %T", expr)
+		return false
+	}
+	if bo.Value != value {
+		t.Errorf("bo.Value is not %t but %t", value, bo.Value)
+		return false
+	}
+	if bo.TokenLiteral() != fmt.Sprintf("%t", value) {
+		t.Errorf("bo.TokenLiteral is not %t but %s", value, bo.TokenLiteral())
+		return false
+	}
+	return true
+}
+
 func testIdentifier(t *testing.T, expr ast.Expression, value string) bool {
 	ident, ok := expr.(*ast.Identifier)
 	if !ok {
@@ -285,6 +306,8 @@ func testLiteralExpression(t *testing.T, expr ast.Expression, expected interface
 		return testIntegerLiteral(t, expr, v)
 	case string:
 		return testIdentifier(t, expr, v)
+	case bool:
+		return testBooleanLiteral(t, expr, v)
 	}
 	t.Errorf("expr type is not handled: %T", expr)
 	return false
@@ -321,6 +344,9 @@ func TestOperatorPrecedenceParsing(t *testing.T) {
 		{"a*b/c", "{{a * b} / c}"},
 		{"a+b*c+d/e-f", "{{{a + {b * c}} + {d / e}} - f}"},
 		{"5>4==3<4", "{{5 > 4} == {3 < 4}}"},
+		{"true", "true"},
+		{"false", "false"},
+		{"3<5==true==!false", "{{{3 < 5} == true} == {!false}}"},
 	}
 
 	for _, tt := range tests {
@@ -334,3 +360,40 @@ func TestOperatorPrecedenceParsing(t *testing.T) {
 		}
 	}
 }
+
+func TestBooleanExpression(t *testing.T) {
+	tests := []struct {
+		input           string
+		expectedBoolean bool
+	}{
+		{"true;", true},
+		{"false;", false},
+	}
+
+	for _, tt := range tests {
+		l := lexer.New(tt.input)
+		p := New(l)
+		program := p.ParseProgram()
+		checkParserErrors(t, p)
+
+		if len(program.Statements) != 1 {
+			t.Fatalf("program has not enough statements. got=%d",
+				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])
+		}
+
+		boolean, ok := stmt.Expression.(*ast.Boolean)
+		if !ok {
+			t.Fatalf("exp not *ast.Boolean. got=%T", stmt.Expression)
+		}
+		if boolean.Value != tt.expectedBoolean {
+			t.Errorf("boolean.Value not %t. got=%t", tt.expectedBoolean,
+				boolean.Value)
+		}
+	}
+}