umx_compiler

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

commit b438a9f973e08ee8d875e56c9d51e65d322bd6b3
parent 7d6e6f37956eefb874d3032c48a625271e259cc2
Author: bsandro <email@bsandro.tech>
Date:   Fri, 17 Jun 2022 02:27:16 +0300

Boolean parsing

Diffstat:
Mast/ast.go | 13+++++++++++++
Mparser/parser.go | 7+++++++
Mparser/parser_test.go | 73++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
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) + } + } +}