Skip to content

Commit

Permalink
✨ Parse boolean literal expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
ChmielewskiKamil committed Aug 27, 2024
1 parent ecd6c8e commit 8f7ae71
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 11 deletions.
19 changes: 19 additions & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ type (
Value big.Int // value of the literal; decimal or hex
}

BooleanLiteral struct {
Pos token.Pos // position of the value
Value bool
}

PrefixExpression struct {
Pos token.Pos // position of the operator
Operator token.Token // operator token
Expand All @@ -87,6 +92,13 @@ func (x *NumberLiteral) Start() token.Pos { return x.Pos }
func (x *NumberLiteral) End() token.Pos {
return token.Pos(int(x.Pos) + len(x.Kind.Literal))
}
func (x *BooleanLiteral) Start() token.Pos { return x.Pos }
func (x *BooleanLiteral) End() token.Pos {
if x.Value {
return token.Pos(int(x.Pos) + 4) // length of "true"
}
return token.Pos(int(x.Pos) + 5) // length of "false"
}
func (x *PrefixExpression) Start() token.Pos { return x.Pos }
func (x *PrefixExpression) End() token.Pos {
return x.Right.End()
Expand All @@ -102,13 +114,20 @@ func (x *InfixExpression) End() token.Pos {

func (*Identifier) expressionNode() {}
func (*NumberLiteral) expressionNode() {}
func (*BooleanLiteral) expressionNode() {}
func (*PrefixExpression) expressionNode() {}
func (*InfixExpression) expressionNode() {}

// String() implementations for Expressions

func (x *Identifier) String() string { return x.Name }
func (x *NumberLiteral) String() string { return x.Kind.Literal }
func (x *BooleanLiteral) String() string {
if x.Value {
return "true"
}
return "false"
}
func (x *PrefixExpression) String() string {
var out bytes.Buffer
out.WriteString("(")
Expand Down
12 changes: 12 additions & 0 deletions parser/exprparsing.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,18 @@ func (p *Parser) parseNumberLiteral() ast.Expression {
return numLit
}

func (p *Parser) parseBooleanLiteral() ast.Expression {
if p.trace {
defer un(trace("parseBooleanLiteral"))
}

bl := &ast.BooleanLiteral{
Pos: p.currTkn.Pos,
Value: p.currTknIs(token.TRUE_LITERAL),
}
return bl
}

func (p *Parser) parsePrefixExpression() ast.Expression {
if p.trace {
defer un(trace("parsePrefixExpression"))
Expand Down
84 changes: 73 additions & 11 deletions parser/exprparsing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,45 @@ func Test_ParseNumberLiteralExpression(t *testing.T) {
}
}

func Test_ParseBooleanLiteralExpression(t *testing.T) {
src := `function test() public {
true;
false;
}`

file := test_helper_parseSource(t, src, false)

fnBody := test_helper_parseFnBody(t, file)

if len(fnBody.Statements) != 2 {
t.Fatalf("Expected 2 statements, got %d", len(fnBody.Statements))
}

tests := []struct {
expectedVal bool
}{
{true},
{false},
}

for i, tt := range tests {
expr := fnBody.Statements[i]
exprStmt, ok := expr.(*ast.ExpressionStatement)
if !ok {
t.Fatalf("Expected ExpressionStatement, got %T", expr)
}

boolLit, ok := exprStmt.Expression.(*ast.BooleanLiteral)
if !ok {
t.Fatalf("Expected BooleanLiteral, got %T", exprStmt.Expression)
}

if boolLit.Value != tt.expectedVal {
t.Fatalf("Expected %t, got %t", tt.expectedVal, boolLit.Value)
}
}
}

func Test_ParsePrefixExpression(t *testing.T) {
src := `function test() public {
-1337;
Expand All @@ -75,14 +114,16 @@ func Test_ParsePrefixExpression(t *testing.T) {
~0x12345;
!a;
delete foo;
!true;
!false;
}`

file := test_helper_parseSource(t, src, false)

fnBody := test_helper_parseFnBody(t, file)

if len(fnBody.Statements) != 6 {
t.Fatalf("Expected 6 statements, got %d", len(fnBody.Statements))
if len(fnBody.Statements) != 8 {
t.Fatalf("Expected 8 statements, got %d", len(fnBody.Statements))
}

tests := []struct {
Expand All @@ -95,8 +136,8 @@ func Test_ParsePrefixExpression(t *testing.T) {
{"~", big.NewInt(0x12345)},
{"!", "a"},
{"delete", "foo"},
// {"!", nil, token.TRUE_LITERAL, "true"},
// {"delete", nil, token.IDENTIFIER, "foo"},
{"!", true},
{"!", false},
}

for i, tt := range tests {
Expand All @@ -116,9 +157,6 @@ func Test_ParsePrefixExpression(t *testing.T) {
}

test_LiteralExpression(t, pExpr.Right, tt.expectedVal)

// @TODO: Implement tests for tokens different than
// numbers and identifiers e.g. TRUE_LITERAL (true).
}
}

Expand All @@ -129,14 +167,17 @@ func Test_ParseInfixExpressions(t *testing.T) {
2 * 2;
2 / 2;
a + b;
true == true;
false != true;
false == false;
}`

file := test_helper_parseSource(t, src, false)

fnBody := test_helper_parseFnBody(t, file)

if len(fnBody.Statements) != 5 {
t.Fatalf("Expected 5 statements, got %d", len(fnBody.Statements))
if len(fnBody.Statements) != 8 {
t.Fatalf("Expected 8 statements, got %d", len(fnBody.Statements))
}

infixTests := []struct {
Expand All @@ -149,6 +190,9 @@ func Test_ParseInfixExpressions(t *testing.T) {
{big.NewInt(2), "*", big.NewInt(2)},
{big.NewInt(2), "/", big.NewInt(2)},
{"a", "+", "b"},
{true, "==", true},
{false, "!=", true},
{false, "==", false},
}

for i, tt := range infixTests {
Expand Down Expand Up @@ -178,14 +222,16 @@ func Test_ParseOperatorPrecedence(t *testing.T) {
++a;
++a + ++b;
1 + 2;
3 > 8 == false;
3 < 8 == true;
}`

file := test_helper_parseSource(t, src, false)

fnBody := test_helper_parseFnBody(t, file)

if len(fnBody.Statements) != 16 {
t.Fatalf("Expected 16 statements, got %d", len(fnBody.Statements))
if len(fnBody.Statements) != 18 {
t.Fatalf("Expected 18 statements, got %d", len(fnBody.Statements))
}

tests := []struct {
Expand All @@ -207,6 +253,8 @@ func Test_ParseOperatorPrecedence(t *testing.T) {
{"(++a)"},
{"((++a) + (++b))"},
{"(1 + 2)"},
{"((3 > 8) == false)"},
{"((3 < 8) == true)"},
}

for i, tt := range tests {
Expand Down Expand Up @@ -286,6 +334,17 @@ func test_NumberLiteral(
}
}

func test_BooleanLiteral(t *testing.T, exp ast.Expression, value bool) {
bl, ok := exp.(*ast.BooleanLiteral)
if !ok {
t.Fatalf("Expected BooleanLiteral, got %T", exp)
}

if bl.Value != value {
t.Fatalf("Expected %t, got %t", value, bl.Value)
}
}

func test_LiteralExpression(t *testing.T, exp ast.Expression, expected interface{}) {
switch v := expected.(type) {
case string:
Expand All @@ -294,6 +353,9 @@ func test_LiteralExpression(t *testing.T, exp ast.Expression, expected interface
case *big.Int:
test_NumberLiteral(t, exp, v)
return
case bool:
test_BooleanLiteral(t, exp, v)
return
}
t.Fatalf("Type %T not handled", expected)
}
Expand Down
2 changes: 2 additions & 0 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ func (p *Parser) Init(file *token.File) {
p.registerPrefix(token.IDENTIFIER, p.parseIdentifier)
p.registerPrefix(token.DECIMAL_NUMBER, p.parseNumberLiteral)
p.registerPrefix(token.HEX_NUMBER, p.parseNumberLiteral)
p.registerPrefix(token.TRUE_LITERAL, p.parseBooleanLiteral)
p.registerPrefix(token.FALSE_LITERAL, p.parseBooleanLiteral)

// Prefix Expressions
p.registerPrefix(token.NOT, p.parsePrefixExpression)
Expand Down
4 changes: 4 additions & 0 deletions token/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,10 @@ func init() {
// of the tokens.
keywords[Tokens[DELETE]] = DELETE

// The same goes for TRUE and FALSE boolean literals.
keywords[Tokens[TRUE_LITERAL]] = TRUE_LITERAL
keywords[Tokens[FALSE_LITERAL]] = FALSE_LITERAL

elementaryTypes = make(map[string]TokenType, elementary_type_end-(elementary_type_beg+1))
for i := elementary_type_beg + 1; i < elementary_type_end; i++ {
elementaryTypes[Tokens[i]] = i
Expand Down

0 comments on commit 8f7ae71

Please sign in to comment.