Skip to content

Commit

Permalink
feat boolean parsing; refactor tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Houcine EL ADDALI committed Dec 15, 2023
1 parent 9c3bd7c commit 3ce02fe
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 24 deletions.
18 changes: 18 additions & 0 deletions AST/ast_imp.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,24 @@ func (resStm *ReturnStatement) ToString() string {
// statements imp
func (reStm *ReturnStatement) statementNode() {} // satisfies the statement interface

/*
the ast node of boolean
*/
type BooleanExp struct {
Token token.Token
Value bool // the boolean value corresponds to bool
}

func (b *BooleanExp) TokenLiteral() string {
return b.Token.Value
}

func (b *BooleanExp) ToString() string {
return b.Token.Value
}

func (b *BooleanExp) expressionNode() {}

/*
* Expressions statement node
* they are wrappers that consists solely of one expression
Expand Down
29 changes: 28 additions & 1 deletion parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ func InitParser(l *lexer.Lexer) *Parser {
// registering prefix parsing functions
p.addPrefixFn(token.IDENTIFIER, p.parseIdentifier)
p.addPrefixFn(token.INT, p.parseInt)
p.addAllPrefixFn([]token.TokenType{
token.TRUE,
token.FALSE,
}, p.parseBoolen)
// prefix expression parser

prefixParseTokens := []token.TokenType{
token.EX_MARK,
token.MINUS,
Expand Down Expand Up @@ -84,6 +87,19 @@ func InitParser(l *lexer.Lexer) *Parser {
return p
}

func (p *Parser) printTrace(a ...any) {
const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . ."
const lnDots = len(dots)

fmt.Print(dots[0:4])
fmt.Println(a...)
}

func trace(msg string, p *Parser) *Parser {
p.printTrace(msg, "(")
return p
}

/*
Helper function to move the pointer of the token in the lexer
reads the next token stores it on the peek but before store the previous
Expand Down Expand Up @@ -171,6 +187,8 @@ func (p *Parser) parseReturnStmt() *ast.ReturnStatement {

func (p *Parser) parseExpressionStatement() *ast.ExpressionStatement {

//debuggin puropos
defer trace("parse expression statements called..", p)
//fmt.Println("------->", p.currToken)
stm := &ast.ExpressionStatement{Token: p.currToken}

Expand Down Expand Up @@ -230,6 +248,15 @@ func (p *Parser) parseInt() ast.Expression {
return exp
}

func (p *Parser) parseBoolen() ast.Expression {
exp := &ast.BooleanExp{
Token: p.currToken,
Value: p.currentTokenEquals(token.TRUE),
}

return exp
}

func (p *Parser) parsePrefixExpression() ast.Expression {
exp := &ast.PrefixExpression{
Token: p.currToken,
Expand Down
135 changes: 112 additions & 23 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func TestReturnStatement(t *testing.T) {
return 101232;
return 0;
`
pr, parser := getProg(input)
pr, _ := getProg(input)

//check is length of the program statement slice is 3
checkIsProgramStmLengthValid(pr, t, 3)
Expand All @@ -72,10 +72,10 @@ func TestReturnStatement(t *testing.T) {
}

// Expression tests
func TestIdentifier(t *testing.T) {
func TestIdentifiers(t *testing.T) {
input := `varName;`

program := getProg(input)
program, parser := getProg(input)

checkParserErrors(parser, t)
// check the length of the program
Expand All @@ -87,20 +87,10 @@ func TestIdentifier(t *testing.T) {
program.Statements[0])
}

ident, ok := stm.Expression.(*ast.Identifier)

if !ok {
t.Fatalf("Expression of type *ast.Identifier instead, got=%T", stm.Expression)
}

if ident.Value != "varName" {
t.Errorf("ident.Value expected=%s, and got=%s", "varName", ident.Value)
if testIdentifier(t, stm.Expression, "varName") {
return
}

if ident.TokenLiteral() != "varName" {
t.Errorf("ident.TokenLiteral is not %s. instead got=%s", "foobar",
ident.TokenLiteral())
}
}

// Integer literals test
Expand Down Expand Up @@ -140,10 +130,12 @@ func TestParsePrefixExp(t *testing.T) {
tests := []struct {
input string
operator string
intOperand int
intOperand interface{}
}{
{input: "!7;", operator: "!", intOperand: 7},
{input: "-42;", operator: "-", intOperand: 42},
{input: "!false;", operator: "!", intOperand: false},
{input: "!true;", operator: "!", intOperand: true},
}

for _, test := range tests {
Expand Down Expand Up @@ -174,21 +166,20 @@ func TestParsePrefixExp(t *testing.T) {
t.Fatalf("exp Operator is not as expected %s, got=%s",
test.operator, exp.Operator)
}
if !testIntegerLiteral(t, exp.Right, test.intOperand) {
if !testLiteralExpression(t, exp.Right, test.intOperand) {
return
}
}
}

// Test infix Expression

func TestInfixExpression(t *testing.T) {

testsData := []struct {
input string
left int
left interface{}
operator string
right int
right any
}{
{
input: "12 + 5;", left: 12, operator: "+", right: 5,
Expand All @@ -210,6 +201,10 @@ func TestInfixExpression(t *testing.T) {
input: "12 == 5;", left: 12, operator: "==", right: 5,
}, {
input: "12 != 5;", left: 12, operator: "!=", right: 5,
}, {
input: "true==true;", left: true, operator: "==", right: true,
}, {
input: "true != false;", left: true, operator: "!=", right: false,
},
}

Expand All @@ -232,8 +227,8 @@ func TestInfixExpression(t *testing.T) {
t.Fatalf("stm.Expression type is not as expected insetead got= %T", stm.Expression)
}

if !testIntegerLiteral(t, exp.Left, test.left) ||
!testIntegerLiteral(t, exp.Right, test.right) {
if !testLiteralExpression(t, exp.Left, test.left) ||
!testLiteralExpression(t, exp.Right, test.right) {
return
}

Expand Down Expand Up @@ -263,6 +258,8 @@ func TestPrecedenceOrderParsing(t *testing.T) {
{"5>4 == 3<=4;", "((5>4)==(3<=4))"},
{"5>=4 != 15<7;", "((5>=4)!=(15<7))"},
{"3 + 4*5 == 3*1 + 4*5;", "((3+(4*5))==((3*1)+(4*5)))"},
{"true;", "true"},
{"3<5 == false;", "((3<5)==false)"},
}

for _, test := range tests {
Expand All @@ -276,6 +273,14 @@ func TestPrecedenceOrderParsing(t *testing.T) {
}
}

// test booleans
/*func TestParseBoolean(t *testing.T){
tests := []struct{
input string
expected
}
}*/

// Tests helper functions
func checkIsProgramStmLengthValid(program *ast.Program, t *testing.T, length int) {
if len(program.Statements) != length {
Expand Down Expand Up @@ -329,7 +334,7 @@ func testDefStatement(t *testing.T, stm ast.Statement, name string) bool {
return true
}

func testIntegerLiteral(t *testing.T, intLit ast.Expression, value int) bool {
func testIntegerLiteral(t *testing.T, intLit ast.Expression, value interface{}) bool {
intVal, ok := intLit.(*ast.IntegerLiteral)

if !ok {
Expand All @@ -350,10 +355,94 @@ func testIntegerLiteral(t *testing.T, intLit ast.Expression, value int) bool {
return true
}

func testIdentifier(t *testing.T, exp ast.Expression, value string) bool {

ident, ok := exp.(*ast.Identifier)

if !ok {
t.Fatalf("expression is not of type *ast.Identifier instead, got=%T", exp)
return false
}

if ident.Value != value {
t.Errorf("ident.Value expected=%s, and got=%s", value, ident.Value)
return false
}

if ident.TokenLiteral() != value {
t.Errorf("ident.TokenLiteral is not %s. instead got=%s", "foobar",
ident.TokenLiteral())
return false
}
return true
}

func testBoolean(t *testing.T, exp ast.Expression, val interface{}) bool {
boolexp, ok := exp.(*ast.BooleanExp)

if !ok {
t.Fatalf("expression is not of type *ast.BooleanExp instead, got=%T", exp)
return false
}

if boolexp.Value != val {
t.Errorf("boolexp.Value expected=%t, and got=%t", val, boolexp.Value)
return false
}

if boolexp.TokenLiteral() != fmt.Sprintf("%t", val) {
t.Errorf("ident.TokenLiteral is not %s. instead got=%s", "foobar",
boolexp.TokenLiteral())
return false
}

return true
}

func getProg(input string) (*ast.Program, *Parser) {
lexer := lexer.InitLexer(input)
parser := InitParser(lexer)
pr := parser.Parse()

return pr, parser
}

func testLiteralExpression(t *testing.T, exp ast.Expression, expected interface{}) bool {
switch val := expected.(type) {
case int:
return testIntegerLiteral(t, exp, val)
case string:
return testIdentifier(t, exp, val)
case bool:
return testBoolean(t, exp, val)
default:
t.Fatalf("type of expression %T not handled", val)
return false
}
}

func testInfixExpression(t *testing.T, exp ast.Expression, left interface{},
right interface{}, operator string) bool {

opExp, ok := exp.(*ast.InfixExpression)
if !ok {
t.Errorf("exp is not of type ast.Expression instead got:%T", opExp)
return false
}

if !testLiteralExpression(t, opExp.Left, left) {
return false
}

if opExp.Operator != operator {
t.Errorf("exp operator is not %s, instead got %s", operator, opExp.Operator)
return false
}

if !testLiteralExpression(t, opExp.Right, right) {
return false
}

return true

}

0 comments on commit 3ce02fe

Please sign in to comment.