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)
+ }
+ }
+}