Parse if and if else expressions.

Signed-off-by: jmug <u.g.a.mariano@gmail.com>
This commit is contained in:
Mariano Uvalle 2025-01-05 15:35:00 -08:00
parent 5367fbd29d
commit 9e9324bb56
4 changed files with 212 additions and 4 deletions

View file

@ -38,6 +38,7 @@ func New(l *lexer.Lexer) *Parser {
p.registerPrefix(token.TRUE, p.parseBoolean)
p.registerPrefix(token.FALSE, p.parseBoolean)
p.registerPrefix(token.LPAREN, p.parseGroupedExpression)
p.registerPrefix(token.IF, p.parseIfExpression)
// Infix registrations
p.registerInfix(token.PLUS, p.parseInfixExpression)
p.registerInfix(token.MINUS, p.parseInfixExpression)
@ -79,13 +80,28 @@ func (p *Parser) parseStatement() ast.Statement {
return p.parseExpressionStatement()
}
func (p *Parser) parseBlockStatement() *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)
}
// Consume the semicolon.
p.nextToken()
}
return block
}
func (p *Parser) parseLetStatement() ast.Statement {
stmt := &ast.LetStatement{Token: p.curToken}
if !p.expectPeek(token.IDENT) {
if !p.nextTokenIfPeekIs(token.IDENT) {
return nil
}
stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
if !p.expectPeek(token.ASSIGN) {
if !p.nextTokenIfPeekIs(token.ASSIGN) {
return nil
}
// TODO: Skipping until we find the semicolon to avoid parsing the expression.
@ -180,13 +196,43 @@ func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
func (p *Parser) parseGroupedExpression() ast.Expression {
p.nextToken()
exp := p.parseExpression(LOWEST)
if !p.expectPeek(token.RPAREN) {
if !p.nextTokenIfPeekIs(token.RPAREN) {
// TODO: Would probably be good to emit an error here?
return nil
}
return exp
}
func (p *Parser) parseIfExpression() ast.Expression {
exp := &ast.IfExpression{Token: p.curToken}
if !p.nextTokenIfPeekIs(token.LPAREN) {
// TODO: Would be good to emit an error here.
return nil
}
p.nextToken()
exp.Condition = p.parseExpression(LOWEST)
if !p.nextTokenIfPeekIs(token.RPAREN) {
// TODO: Would be good to emit an error here.
return nil
}
if !p.nextTokenIfPeekIs(token.LBRACE) {
// TODO: Would be good to emit an error here.
return nil
}
exp.Consequence = p.parseBlockStatement()
if p.peekTokenIs(token.ELSE) {
p.nextToken()
if !p.nextTokenIfPeekIs(token.LBRACE) {
// TODO: Would be good to emit an error here.
return nil
}
exp.Alternative = p.parseBlockStatement()
}
// We don't consume the RBRACE because it acts as our "end of statement"
// token, and it's consumed by parseProgram.
return exp
}
func (p *Parser) curTokenIs(typ token.TokenType) bool {
return p.curToken.Type == typ
}
@ -198,7 +244,7 @@ func (p *Parser) peekTokenIs(typ token.TokenType) bool {
// NOTE: I'll leave the name as-is to avoid deviating from the book (maybe a
// rename at the end?), but I think `nextTokenIfPeek` would be a much better
// name for this.
func (p *Parser) expectPeek(typ token.TokenType) bool {
func (p *Parser) nextTokenIfPeekIs(typ token.TokenType) bool {
if p.peekTokenIs(typ) {
p.nextToken()
return true