boolean and if/else
This commit is contained in:
59
ast/ast.go
59
ast/ast.go
@@ -81,6 +81,23 @@ type InfixExpression struct {
|
|||||||
Right Expression
|
Right Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Boolean struct {
|
||||||
|
Token token.Token
|
||||||
|
Value bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlockStatement struct {
|
||||||
|
Token token.Token // the { token
|
||||||
|
Statements []Statement
|
||||||
|
}
|
||||||
|
|
||||||
|
type IfExpression struct {
|
||||||
|
Token token.Token // The 'if' token
|
||||||
|
Condition Expression
|
||||||
|
Consequence *BlockStatement
|
||||||
|
Alternative *BlockStatement
|
||||||
|
}
|
||||||
|
|
||||||
func (ls *LetStatement) statementNode() {
|
func (ls *LetStatement) statementNode() {
|
||||||
}
|
}
|
||||||
func (ls *LetStatement) TokenLiteral() string {
|
func (ls *LetStatement) TokenLiteral() string {
|
||||||
@@ -180,3 +197,45 @@ func (ie *InfixExpression) String() string {
|
|||||||
|
|
||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b Boolean) TokenLiteral() string {
|
||||||
|
return b.Token.Literal
|
||||||
|
}
|
||||||
|
func (b Boolean) String() string {
|
||||||
|
return b.Token.Literal
|
||||||
|
}
|
||||||
|
func (b Boolean) expressionNode() {}
|
||||||
|
|
||||||
|
func (bs BlockStatement) TokenLiteral() string {
|
||||||
|
return bs.Token.Literal
|
||||||
|
}
|
||||||
|
func (bs BlockStatement) String() string {
|
||||||
|
var out bytes.Buffer
|
||||||
|
|
||||||
|
for _, s := range bs.Statements {
|
||||||
|
out.WriteString(s.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
|
func (bs BlockStatement) statementNode() {}
|
||||||
|
|
||||||
|
func (ie IfExpression) TokenLiteral() string {
|
||||||
|
return ie.Token.Literal
|
||||||
|
}
|
||||||
|
func (ie IfExpression) String() string {
|
||||||
|
var out bytes.Buffer
|
||||||
|
|
||||||
|
out.WriteString("if")
|
||||||
|
out.WriteString(ie.Condition.String())
|
||||||
|
out.WriteString(" ")
|
||||||
|
out.WriteString(ie.Consequence.String())
|
||||||
|
|
||||||
|
if ie.Alternative != nil {
|
||||||
|
out.WriteString("else ")
|
||||||
|
out.WriteString(ie.Alternative.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
|
func (ie IfExpression) expressionNode() {}
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ func New(l *lexer.Lexer) *Parser {
|
|||||||
p.registerPrefix(token.INT, p.parseIntegerLiteral)
|
p.registerPrefix(token.INT, p.parseIntegerLiteral)
|
||||||
p.registerPrefix(token.BANG, p.parsePrefixExpression)
|
p.registerPrefix(token.BANG, p.parsePrefixExpression)
|
||||||
p.registerPrefix(token.MINUS, p.parsePrefixExpression)
|
p.registerPrefix(token.MINUS, p.parsePrefixExpression)
|
||||||
|
p.registerPrefix(token.TRUE, p.parseBoolean)
|
||||||
|
p.registerPrefix(token.FALSE, p.parseBoolean)
|
||||||
|
p.registerPrefix(token.LPAREN, p.parseGroupedExpression)
|
||||||
|
p.registerPrefix(token.IF, p.parseIfExpression)
|
||||||
|
|
||||||
p.infixParseFns = make(map[token.TokenType]infixParseFn)
|
p.infixParseFns = make(map[token.TokenType]infixParseFn)
|
||||||
p.registerInfix(token.PLUS, p.parseInfixExpression)
|
p.registerInfix(token.PLUS, p.parseInfixExpression)
|
||||||
@@ -282,3 +286,72 @@ func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
|
|||||||
|
|
||||||
return expression
|
return expression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseBoolean() ast.Expression {
|
||||||
|
return &ast.Boolean{
|
||||||
|
Token: p.curToken,
|
||||||
|
Value: p.curTokenIs(token.TRUE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseGroupedExpression() ast.Expression {
|
||||||
|
p.nextToken()
|
||||||
|
|
||||||
|
exp := p.parseExpression(LOWEST)
|
||||||
|
|
||||||
|
if p.expectPeek(token.RPAREN) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return exp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseIfExpression() ast.Expression {
|
||||||
|
expression := &ast.IfExpression{Token: p.curToken}
|
||||||
|
|
||||||
|
if !p.expectPeek(token.LPAREN) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p.nextToken()
|
||||||
|
expression.Condition = p.parseExpression(LOWEST)
|
||||||
|
|
||||||
|
if !p.expectPeek(token.RPAREN) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.expectPeek(token.LBRACE) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
expression.Consequence = p.parseBlockExpression()
|
||||||
|
|
||||||
|
if p.peekTokenIs(token.ELSE) {
|
||||||
|
p.nextToken()
|
||||||
|
|
||||||
|
if !p.expectPeek(token.LBRACE) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
expression.Alternative = p.parseBlockExpression()
|
||||||
|
}
|
||||||
|
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseBlockExpression() *ast.BlockStatement {
|
||||||
|
block := &ast.BlockStatement{Token: p.curToken}
|
||||||
|
block.Statements = []ast.Statement{}
|
||||||
|
|
||||||
|
p.nextToken()
|
||||||
|
|
||||||
|
for !p.curTokenIs(token.RBRACE) && !p.curTokenIs(token.EOF) {
|
||||||
|
stmt := p.parseStatement()
|
||||||
|
if stmt != nil {
|
||||||
|
block.Statements = append(block.Statements, stmt)
|
||||||
|
}
|
||||||
|
p.nextToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
return block
|
||||||
|
}
|
||||||
|
|||||||
@@ -356,6 +356,114 @@ func TestOperatorPrecedenceParsing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIfExpression(t *testing.T) {
|
||||||
|
input := `if (x < y) { x }`
|
||||||
|
|
||||||
|
l := lexer.New(input)
|
||||||
|
p := New(l)
|
||||||
|
program := p.ParseProgram()
|
||||||
|
checkParserErrors(t, p)
|
||||||
|
|
||||||
|
if len(program.Statements) != 1 {
|
||||||
|
t.Fatalf("program.Statements does not contain %d statements. got=%d\n",
|
||||||
|
1, 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])
|
||||||
|
}
|
||||||
|
|
||||||
|
exp, ok := stmt.Expression.(*ast.IfExpression)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("stmt.Expression is not ast.IfExpression. got=%T",
|
||||||
|
stmt.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testInfixExpression(t, exp.Condition, "x", "<", "y") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(exp.Consequence.Statements) != 1 {
|
||||||
|
t.Errorf("consequence is not 1 statements. got=%d\n",
|
||||||
|
len(exp.Consequence.Statements))
|
||||||
|
}
|
||||||
|
|
||||||
|
consequence, ok := exp.Consequence.Statements[0].(*ast.ExpressionStatement)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
|
||||||
|
exp.Consequence.Statements[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testIdentifier(t, consequence.Expression, "x") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp.Alternative != nil {
|
||||||
|
t.Errorf("exp.Alternative.Statements was not nil. got=%+v", exp.Alternative)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIfElseExpression(t *testing.T) {
|
||||||
|
input := `if (x < y) { x } else { y }`
|
||||||
|
|
||||||
|
l := lexer.New(input)
|
||||||
|
p := New(l)
|
||||||
|
program := p.ParseProgram()
|
||||||
|
checkParserErrors(t, p)
|
||||||
|
|
||||||
|
if len(program.Statements) != 1 {
|
||||||
|
t.Fatalf("program.Statements does not contain %d statements. got=%d\n",
|
||||||
|
1, 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])
|
||||||
|
}
|
||||||
|
|
||||||
|
exp, ok := stmt.Expression.(*ast.IfExpression)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("stmt.Expression is not ast.IfExpression. got=%T", stmt.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testInfixExpression(t, exp.Condition, "x", "<", "y") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(exp.Consequence.Statements) != 1 {
|
||||||
|
t.Errorf("consequence is not 1 statements. got=%d\n",
|
||||||
|
len(exp.Consequence.Statements))
|
||||||
|
}
|
||||||
|
|
||||||
|
consequence, ok := exp.Consequence.Statements[0].(*ast.ExpressionStatement)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
|
||||||
|
exp.Consequence.Statements[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testIdentifier(t, consequence.Expression, "x") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(exp.Alternative.Statements) != 1 {
|
||||||
|
t.Errorf("exp.Alternative.Statements does not contain 1 statements. got=%d\n",
|
||||||
|
len(exp.Alternative.Statements))
|
||||||
|
}
|
||||||
|
|
||||||
|
alternative, ok := exp.Alternative.Statements[0].(*ast.ExpressionStatement)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
|
||||||
|
exp.Alternative.Statements[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testIdentifier(t, alternative.Expression, "y") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testLetStatement(t *testing.T, s ast.Statement, name string) bool {
|
func testLetStatement(t *testing.T, s ast.Statement, name string) bool {
|
||||||
if s.TokenLiteral() != "let" {
|
if s.TokenLiteral() != "let" {
|
||||||
t.Errorf("s.TokenLiteral not 'let'. got=%q", s.TokenLiteral())
|
t.Errorf("s.TokenLiteral not 'let'. got=%q", s.TokenLiteral())
|
||||||
@@ -419,11 +527,34 @@ func testLiteralExpression(
|
|||||||
return testIntegerLiteral(t, exp, v)
|
return testIntegerLiteral(t, exp, v)
|
||||||
case string:
|
case string:
|
||||||
return testIdentifier(t, exp, v)
|
return testIdentifier(t, exp, v)
|
||||||
|
case bool:
|
||||||
|
return testBooleanLiteral(t, exp, v)
|
||||||
}
|
}
|
||||||
t.Errorf("type of exp not handled. got=%T", exp)
|
t.Errorf("type of exp not handled. got=%T", exp)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testBooleanLiteral(t *testing.T, exp ast.Expression, value bool) bool {
|
||||||
|
bo, ok := exp.(*ast.Boolean)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("exp *ast.Boolean. got=%T", exp)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if bo.Value != value {
|
||||||
|
t.Errorf("bo.Value not %t. got=%t", value, bo.Value)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if bo.TokenLiteral() != fmt.Sprintf("%t", value) {
|
||||||
|
t.Errorf("intboeg.TokenLiteral not %t. got=%s", value,
|
||||||
|
bo.TokenLiteral())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
|
func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
|
||||||
integ, ok := il.(*ast.IntegerLiteral)
|
integ, ok := il.(*ast.IntegerLiteral)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
Reference in New Issue
Block a user