๐ ํด๋น ํฌ์คํ
์ ๋ฐ๋ฐ๋ฅ๋ถํฐ ๋ง๋๋ ์ธํฐํ๋ฆฌํฐ in go ์ฑ
์ ์ฝ๊ณ ๊ฐ์ธ์ ์ธ ์ ๋ฆฌ ๋ชฉ์ ํ์ ์์ฑ๋ ๊ธ์
๋๋ค. ๋ณธ ํฌ์คํ
์ ์ฌ์ฉ๋ ์๋ฃ๋ ๋ชจ๋ ๋ณธ์ธ์ด ์ง์ ์ฌ๊ตฌ์ฑํ์ฌ ์์ฑํ์์์ ์๋ฆฝ๋๋ค.

์ง์ ํฌ์คํ ์์๋ ์ฐ๋ฆฌ๋ง์ ํ์์ ๋ค์ํ ํํ์์ ํ์ฑํ๋ ๊ธฐ๋ฅ์ ํ์ฌํด๋ณด์๋ค. ๋ค๋ง, ๋ง์ง๋ง์ ์ค์ ํํ์์ ํ์ฑํ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ฉด์ ์์ค์ฝ๋๋ฅผ ์ดํด๋ณด๋ ๊ฒ๋ง์ผ๋ก ๋๋๋๋ฐ, ์ด๋ฒ ํฌ์คํ ์์๋ ๋์ํ๋ฅผ ํด๋ณด๋ฉด์ ์์ค์ฝ๋๋ ๋งคํํ ํ, ๊ตฌ์ฒด์ ์ผ๋ก ์ฐ๋ฆฌ๊ฐ ๊ตฌํํ ํ๋ซํ์๊ฐ ์ด๋ป๊ฒ ๋์ํ๋์ง ์ดํด๋ณผ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ๋์๊ฐ ๋ค๋ฅธ ์ข ๋ฅ์ ํํ์๋ ํ์ฑํ ์ ์๋๋ก ๊ธฐ๋ฅ์ ์ถ๊ฐํด๋ณด์.
1. ํ๋ซ(Pratt) ํ์์ ๋์ ์๋ฆฌ
๊ฐ์ฅ ๋ง์ง๋ง์ผ๋ก ๊ตฌํํ๋ ์ค์ ํํ์์ ํ์ฑํ๋ ๋์์ ์ดํด๋ณด๋ฉด์ ํ๋ซ ํ์์ ๋์ ์๋ฆฌ๋ฅผ ์ดํดํด๋ณด์. ํ์ฑํ ์์ ์์ค์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
1 + 2 + 3;
ํ์ฌ ์์ค์ฝ๋๋ก ๊ตฌํํ๊ณ ์๋ Go์ธ์ด ์์ค์ฝ๋์ ์์ฑ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ๊ธฐ์ค์ผ๋ก ํ์ ๋, ๊ฒฐ๊ณผ์ ์ผ๋ก ์ป์ ์ต์ข AST ์๋ฃ๊ตฌ์กฐ๋ฅผ ๋์ํ ํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.

๊ฒฐ๋ก ์ ์ดํด๋ณด์์ผ๋, ์ด์ ์ด ๊ฒฐ๋ก ์ ๋์ถํ๋ ๊ณผ์ ์ ํ๋ฆ์ ๋ฐ๋ผ๊ฐ๋ณด์. Go ์์ค์ฝ๋์์๋ ์ AST ์๋ฃ๊ตฌ์กฐ์ ๋ ๊น์ ๋์ค๋ถํฐ ์ฐจ๋ก๋๋ก ๊ตฌํ๋๋ค.
๊ฐ์ฅ ๋จผ์ ์ ์ ๋ ฅ ์์ค์ฝ๋๋ฅผ ํ์ฑํ๋ ํ ์คํธ ์ฝ๋ ๋ถ๋ถ์ด๋ค.
// parser/parser_test.go
func TestParseInfixExpression(t *testing.T) {
input := "1 + 2 + 3;"
l := lexer.New(input)
p := New(l)
program := p.ParseProgram()
checkParserErrors(t, p)
(... ์๋ต ...)
๋จผ์ ๋ ์์ ํ์๋ฅผ ์ด๊ธฐํ ํ๋ ๋ถ๋ถ์ด ์๋ค. ์์ค์ฝ๋๋ก ์น๋ฉด l := lexer.New(input) ์ p := New(l) ๋ถ๋ถ์ด๋ค. ์ด ๋ถ๋ถ์ด ์คํ๋จ์ผ๋ก์จ ํ์๊ฐ ๊ฐ๋ฆฌํค๋ ํ ํฐ ์์น๋ ๋ค์๊ณผ ๊ฐ๋ค.

์ด์ ๋ ์ฑ๊ณผ ํ์ฑ์ด ์ค์ง์ ์ผ๋ก ์คํ๋๋ ์ํธ๋ฆฌ ํฌ์ธํธ ์ง์ ์ ๋ฐ๋ก p.ParseProgram() ๋ฉ์๋๋ค. ํด๋น ๋ฉ์๋ ์์ค์ฝ๋๋ฅผ ๋ณด์.
// parser/parser.go
func (p *Parser) ParseProgram() *ast.Program {
program := &ast.Program{}
program.Statements = []ast.Statement{}
for p.curToken.Type != token.EOF {
stmt := p.parseStatement()
if stmt != nil {
program.Statements = append(program.Statements, stmt)
}
p.nextToken()
}
return program
}
p.curToken์ด EOF ํ ํฐ์ด ์๋๊ธฐ ๋๋ฌธ์ ์กฐ๊ฑด์ ๋ถํฉํ๊ธฐ ๋๋ฌธ์ for-loop์ ์ง์ ํ๊ธฐ ์์ํ๋ค. ์ฒซ ๋ฒ์งธ loop๋ฅผ ๋๋ฉด์ p.parseStatement ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค. ์ด์ ๊ทธ๋ฌ๋ฉด parseStatement ๋ฉ์๋์ ์๊น์๋ฅผ ๋ณด์.
// parser/parser.go
func (p *Parser) parseStatement() ast.Statement {
switch p.curToken.Type {
case token.LET:
return p.parseLetStatement()
case token.RETURN:
return p.parseReturnStatement()
default:
return p.parseExpressionStatement()
}
}
ํ์ฌ ํ ํฐ์ ํ์ ์ ๋ฐ๋ผ switch-case ๊ตฌ๋ฌธ์ ํ๋ฉด์ ์ด๋ค ๋ฉ์๋๋ฅผ ํธ์ถํ ์ง ๊ฒฐ์ ๋๋ค. ํ์ฌ ํ ํฐ์ let ๋ฌธ๋, return ๋ฌธ๋ ์๋๋ค. ๋ฐ๋ผ์ default ๊ตฌ๋ฌธ์ ํ๊ฒ ๋์ด parseExpressionStatement ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค. ์ด์ parseExpressionStatement ๋ฉ์๋์ ์๊น์๋ฅผ ๋ณด์.
// parser/parser.go
func (p *Parser) parseExpressionStatement() *ast.ExpressionStatement {
stmt := &ast.ExpressionStatement{Token: p.curToken}
stmt.Expression = p.parseExpression(LOWEST)
if p.peekTokenIs(token.SEMICOLON) {
p.nextToken()
}
return stmt
}
ํํ์ ๋ ธ๋๋ฅผ ์๋ฏธํ๋ ExpressionStatement ๊ตฌ์กฐ์ฒด๋ฅผ ์ด๊ธฐํํ๋ค. ์ด ๋ ๊ตฌ์กฐ์ฒด์ ๋ฉค๋ฒ์๋ ํ์ฌ ํ ํฐ์ธ 1์ ๋ด๋๋ค. ์ด 1 ์ด๋ผ๋ ํ ํฐ์ ํ์ ์ ์๋ณ์๋ฅผ ์๋ฏธํ๋ IDENT ํ์ ์ด ๋ด๊ฒจ์ ธ ์๋ค.(์ด IDENT ํ ํฐ์ด ๋ด๊ฒจ์ง๋ ๋์์ด ์ค์ ์ํ๋๋ ํ์ด๋ฐ์ ์์์ ๋ ์๋ฅผ ์ด๊ธฐํํ ๋์ด๋ค. ์์ค์ฝ๋์์ ๋ ์์ NextToken ์ด๋ผ๋ ๋ฉ์๋๋ฅผ ์ดํด๋ณด์!) ์ด์ ํด๋น ๊ตฌ์กฐ์ฒด์ Expression ๋ฉค๋ฒ์๋ ํํ์์ ํ์ฑํ๋ ๊ฒฐ๊ณผ๋ฅผ ํ ๋นํ๋๋ฐ, ํํ์์ ํ์ฑํ๋ ์ญํ ์ ํ๋ ๊ฒ์ ๋ฐ๋ก parseExpression ๋ฉ์๋๋ค. ์ฐธ๊ณ ๋ก ํด๋น ๋ฉ์๋์ LOWEST ๋ผ๋ ๊ฐ์ฅ ๋ฎ์ ์ฐ์ฐ์ ์ฐ์ ์์์ธ 0์ ์ธ์๋ก ๋๊ฒจ์ค๋ค. ์ด์ parseExpression ๋ฉ์๋๋ฅผ ์ดํด๋ณด์.
// parser/parser.go
func (p *Parser) parseExpression(precedence int) ast.Expression {
prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
p.noPrefixParseFnError(p.curToken.Type)
return nil
}
leftExp := prefix()
for !p.peekTokenIs(token.SEMICOLON) && precedence < p.peekPrecedence() {
infix := p.infixParseFns[p.peekToken.Type]
if infix == nil {
return leftExp
}
p.nextToken()
leftExp = infix(leftExp)
}
return leftExp
}
๊ฐ์ฅ ๋จผ์ p.curToken์ ๋งคํ๋๋ ์ ์ ์ฐ์ฐ์ ํ์ฑ ํจ์๊ฐ ์๋์ง ์ฒดํฌํ๋ค. p.curToken์ ์ฌ์ ํ 1์ด๋ผ๋ IDENT(์๋ณ์) ํ์ ์ ํ ํฐ์ด๋ค. ์ด ํ ํฐ์ ๋ง๋ ์ ์ ์ฐ์ฐ์ ํ์ฑ ํจ์๋ ์กด์ฌํ ๊น? ์กด์ฌํ๋์ง ๋ณด๊ธฐ ์ํด์๋ ๊ฐ์ฅ ์ฒ์์ ํ์๋ฅผ ์ด๊ธฐํํ๋ ๋ฉ์๋๋ฅผ ์ดํด๋ณด์์ผ ํ๋ค.
// parser/parser.go
func New(l *lexer.Lexer) *Parser {
p := &Parser{
l: l,
errors: []string{},
}
// ํ์ฌ ์ด๊ธฐ token ๊ฐ์ ๋น ๋ฌธ์์ด๊ธฐ ๋๋ฌธ์, curToken, peekToken์ ํ ํฐ์ ๋ด์ผ๋ ค๋ฉด 2๋ฒ ์ํ
p.nextToken()
p.nextToken()
p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
p.registerPrefix(token.IDENT, p.parseIdentifier) // IDENT ํ ํฐ์ ๋ํ ํ์ฑํจ์!
p.registerPrefix(token.INT, p.parseIntegerLiteral)
p.registerPrefix(token.BANG, p.parsePrefixExpression)
p.registerPrefix(token.MINUS, p.parsePrefixExpression)
p.infixParseFns = make(map[token.TokenType]infixParseFn)
p.registerInfix(token.MINUS, p.parseInfixExpression)
p.registerInfix(token.PLUS, p.parseInfixExpression)
p.registerInfix(token.MINUS, p.parseInfixExpression)
p.registerInfix(token.SLASH, p.parseInfixExpression)
p.registerInfix(token.ASTERISK, p.parseInfixExpression)
p.registerInfix(token.EQ, p.parseInfixExpression)
p.registerInfix(token.NOT_EQ, p.parseInfixExpression)
p.registerInfix(token.LT, p.parseInfixExpression)
p.registerInfix(token.GT, p.parseInfixExpression)
return p
}
์ฃผ์ ์ฒ๋ฆฌ๋ก๋ ํด๋์๋ค. IDENT ํ ํฐ์ ๋งคํ๋๋ ์ ์ ์ฐ์ฐ์ ํ์ฑ ํจ์์ธ parseIdentifier ๋ผ๋ ๋ฉ์๋๋ฅผ ๋ฑ๋กํด๋์๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ๊ทธ๋ฌ๋ฉด ๋ค์ parseExpression ๋ฉ์๋ ์์ค์ฝ๋๋ก ๋์๊ฐ์, IDENT ํ ํฐ์ ๋งคํ๋๋ ์ ์ ์ฐ์ฐ์ ํ์ฑ ํจ์์ธ parseIdentifier๋ฅผ ์ป๊ฒ๋๊ณ , ์ด parseIdentifier ๋ฉ์๋๋ฅผ ํธ์ถํด์ left ๋ผ๋ ๋ณ์์ ํ ๋นํ๊ฒ ๋๋ค. ๊ทธ๋ฌ๋ฉด ์ด์ parseIdentifier ๋ฉ์๋์ ์๊น์๋ฅผ ๋ณผ ์ฐจ๋ก๋ค.
// parser/parser.go
func (p *Parser) parseIdentifier() ast.Expression {
return &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
}
๋ก์ง์ ๋งค์ฐ ๊ฐ๋จํ๋ค. ๋จ์ํ curToken์ ํ ํฐ์ธ IDENT์ ํ ํฐ์ ๊ฐ์ธ 1์ ์๋ณ์ ๊ตฌ์กฐ์ฒด์ ๋ฉค๋ฒ๋ก ํ ๋นํ ํ ์๋ณ์ ๊ตฌ์กฐ์ฒด๋ฅผ ๋ฐํํ๋ค. ๊ทธ๋ฌ๋ฉด ๋ค์ parseExpression ๋ฉ์๋ ์์ค์ฝ๋๋ก ์ฌ๋ผ๊ฐ๋ณด์. ์คํฌ๋กค์ ์๋ค๊ฐ๋ค ํ๋๋ผ ๊ท์ฐฎ์ ํ ๋, ๋ค์ parseExpression ๋ฉ์๋ ์์ค์ฝ๋๋ฅผ ์ฝ๋๋ธ๋ญ์ผ๋ก ๋ค์ ์ดํด๋ณด์.
// parser/parser.go
func (p *Parser) parseExpression(precedence int) ast.Expression {
prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
p.noPrefixParseFnError(p.curToken.Type)
return nil
}
leftExp := prefix()
for !p.peekTokenIs(token.SEMICOLON) && precedence < p.peekPrecedence() {
infix := p.infixParseFns[p.peekToken.Type]
if infix == nil {
return leftExp
}
p.nextToken()
leftExp = infix(leftExp)
}
return leftExp
}
๋ฐฉ๊ธ๊น์งํด์ leftExp := prefix() ๋ผ๋ ์์ค์ฝ๋๊ฐ ์คํ๋์๋ค. ๋ค์์ผ๋ก for-loop์ ์ง์ ํ ์ฐจ๋ก๋ค. peekTokenIs ํจ์๋ ์์ ์ ์ดํด๋ณธ ๋ฉ์๋์ธ๋ฐ, ์ด๋ฆ์๋ ์ ์ ์๋ค์ํผ ํ์๊ฐ ๋ฐ๋ผ๋ณด๋ ๋ค์ ์์น ํ ํฐ์ธ p.peekToken์ด SEMICOLON ํ ํฐ์ธ์ง ํ์ธํ๋ ์ญํ ์ ํ๋ค. ์์ not ์ฐ์ฐ์(!)๊ฐ ๋ถ์๊ธฐ ๋๋ฌธ์ ๊ฒฐ๊ตญ ๋ค์ ์์น ํ ํฐ์ด SEMICOLON์ด ์๋ ๊ฒฝ์ฐ๋ฅผ ์๋ฏธํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ด์ ๋๋์ด ์ธ์๋ก ๋ฐ์ ์ฐ์ฐ์ ์ฐ์ ์์์ธ precedence๋ฅผ ํ์ฉํ ์ฐจ๋ก๋ค. ํ์ฌ precedence์๋ LOWEST ๊ฐ์ด ๋ค์ด์๋ค. ๊ทธ๋ฆฌ๊ณ peekPrecedence ๋ฉ์๋๋ฅผ ํธ์ถํจ์ผ๋ก์จ ํ์ฌ ํ์๊ฐ ๋ฐ๋ผ๋ณด๋ ๋ค์ ์์น ํ ํฐ์ ์ฐ์ฐ์ ์ฐ์ ์์๋ฅผ ๋ฐํ๋ฐ๊ณ , ์ด 2๊ฐ์ ์ฐ์ฐ์ ์ฐ์ ์์ ๊ฐ์ ๋น๊ตํ๋ค ๋ง์ฝ ๋ค์ ์์น ํ ํฐ์ ์ฐ์ฐ์ ์ฐ์ ์์๊ฐ ํฌ๋ค๋ฉด for-loop ๊ตฌ๋ฌธ์ ํ๋๋ก ํ๋ค.
๊ทธ๋ฌ๋ฉด ๋ค์ ์์น ํ ํฐ์ ๋ฌด์์ผ๊น? ์ฌ์ ํ ํ์ฌ ํ์๊ฐ ๋ฐ๋ผ๋ณด๋ ํ ํฐ์ ์์น ์ํ๋ ๋ค์๊ณผ ๊ฐ๋ค.

์ฆ, ๋ค์ ์์น์ ํ ํฐ์ธ peekToken๋ ์ฐ์ฐ์ ํ ํฐ +(PLUS)์ ๋ฐ๋ผ๋ณด๊ณ ์๋ค. ๊ทธ๋ฆฌ๊ณ ์ด์ ์ด peekToken์ ๋งคํ๋ ์ค์ ์ฐ์ฐ์ ํํ์์ ํ์ฑํ๋ ๋ฉ์๋๋ฅผ ์ฐพ์๋ณด์. PLUS ํ ํฐ์ ๋ง๋ ํ์ฑ ๋ฉ์๋๋ parseInfixExpression ์ด๋ค. parseInfixExpression ๋ฉ์๋๋ฅผ ํธ์ถํ๊ธฐ ์ด์ ์ ๋จผ์ nextToken() ํจ์๋ฅผ ํธ์ถํ๋ค. ์ด ํจ์๋ฅผ ํธ์ถํจ์ ๋ฐ๋ผ ํ์๊ฐ ๋ฐ๋ผ๋ณด๋ ํ ํฐ์ ์์น๊ฐ 1์นธ์ฉ ๋ค์์ผ๋ก ์ฎ๊ฒจ์ง๋ค.

ํ ํฐ์ ์์น๋ฅผ ์ฎ๊ธด ํ parseInfixExpression ๋ฉ์๋๋ฅผ ์ด์ ํธ์ถํ ์ฐจ๋ก๋ค. parseInfixExpression ์๊น์๋ฅผ ๋ณด์.
// parser/parser.go
func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
expression := &ast.InfixExpression{
Token: p.curToken,
Left: left,
Operator: p.curToken.Literal,
}
precedence := p.curPrecedence()
p.nextToken()
expression.Right = p.parseExpression(precedence)
return expression
}
parseInfixExpression ๋ฉ์๋๋ ํํ์ ํ๋๋ฅผ ์ธ์๋ก ๋ฐ๋๋ค. ์ธ์ ์ด๋ฆ์ด left ์ธ ๊ฒ์์ ์ถ๋ก ํ ์ ์๋ฏ์ด, left๋ ์ค์ ์ฐ์ฐ์๋ฅผ ๊ธฐ์ค์ผ๋ก ์ผ์ชฝ์ ์๋ ํํ์์ ์๋ฏธํ๋ค. ํ์ฌ ์ํฉ์์ left ์ธ์์ ๋ด๊ฒจ ์๋ ํํ์์ ์๊น ์์์ ์ดํด๋ณธ 1 ์ด๋ผ๋ ์๋ณ์ ํ ํฐ์ผ๋ก ๋ง๋ ํํ์ ๋ ธ๋์ด๋ค. ์ด ๋ ธ๋๋ฅผ InfixExpression ๊ตฌ์กฐ์ฒด์ Left ๋ฉค๋ฒ์ ํ ๋นํ๋ค. ์ด ์๊ฐ์ AST ์๋ฃ๊ตฌ์กฐ๋ก ๋์ํ ํ๋ฉด ์๋์ ๊ฐ์ ์ํ์ด๋ค.

๋ค์ parseInfixExpression ๋ฉ์๋ ์์ค์ฝ๋๋ก ๋์๊ฐ๋ณด์. ์ด์ precedence := p.curPrecedence() ๋ถ๋ถ์ ์ดํด๋ณด์. curPrecedence ๋ฉ์๋๋ ํ์๊ฐ ๋ฐ๋ผ๋ณด๋ ํํ ํ ํฐ์ ์ฐ์ฐ์ ์ฐ์ ์์๋ฅผ ์ป๊ฒ ๋๋ค. ํ์ฌ ํ ํฐ์ +(PLUS) ํ ํฐ์ด๊ธฐ ๋๋ฌธ์ ํด๋น ํ ํฐ์ ์ฐ์ฐ์ ์ฐ์ ์์ ๊ฐ์ 4์ด๋ค.(๊ฐ ํ ํฐ์ ๋งคํ๋ ์ฐ์ฐ์ ์ฐ์ ์์ ์์ ๊ฐ์ parser/parser.go ์์ค์ฝ๋์ ๋ณ์๋ก ์ ์๋์ด ์์ผ๋ ์ฐธ๊ณ ํ์) ์ฐธ๊ณ ๋ก, ์ด ๋ ๋ ํ๋ฒ p.nextToken() ๋ฉ์๋๋ฅผ ํธ์ถํด์ ํ์๊ฐ ๋ฐ๋ผ๋ณด๋ ํ ํฐ์ ์์น๋ฅผ ํ ๋ฒ ๋ 1์นธ์ฉ ์ด๋์ํจ๋ค.

๊ทธ๋ฆฌ๊ณ ์ด 4๋ผ๋ ์ฐ์ฐ์ ์ฐ์ ์์ ๊ฐ์ parseExpression ๋ฉ์๋์ ์ธ์๋ก ๋ฃ์ด์ ๋ค์ ํ๋ฒ ์ฌ๊ท์ ์ผ๋ก parseExpression ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค. ๋ค์ ํ๋ฒ parseExpression ๋ฉ์๋ ์ฝ๋๋ธ๋ก์ ๋ณด์.
// parser/parser.go
func (p *Parser) parseExpression(precedence int) ast.Expression {
prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
p.noPrefixParseFnError(p.curToken.Type)
return nil
}
leftExp := prefix()
for !p.peekTokenIs(token.SEMICOLON) && precedence < p.peekPrecedence() {
infix := p.infixParseFns[p.peekToken.Type]
if infix == nil {
return leftExp
}
p.nextToken()
leftExp = infix(leftExp)
}
return leftExp
}
curToken์ ํ์ฌ 2๋ผ๋ ์๋ณ์ ํ ํฐ์ผ๋ก ์์น๊ฐ ์ฎ๊ฒจ์ง ์ํ์ด๋ค. ์ด ํ ํฐ์ ๋งคํ๋์ด ์๋ ์ ์ ์ฐ์ฐ์ ํ์ฑ ํจ์๊ฐ ์กด์ฌํ๋์ง ์ดํด๋ณด์. ์ด ๋ํ ์๋ณ์์ด๊ธฐ ๋๋ฌธ์ ์์์ 1 ์ด๋ผ๋ ํ ํฐ์ ๋ํด ํ์ฑ์ ์ํํ ๋๋ ๋๊ฐ๋ค. ์ด๋ฒ์๋ 2๋ผ๋ ์๋ณ์ ํํ์์ ๋ํด ํ์ฑ์ ์ํํ๋ ํจ์๋ฅผ ์ดํด๋ณด๋ ๋ถ๋ถ์ ์ค๋ณต๋๋ ์๋ตํ๊ฒ ๋ค. ๋ฐ๋ก for-loop ๊ตฌ๋ฌธ์ผ๋ก ๊ฐ๋ณด์.
peekToken์ +(PLUS)์ธ ์ํ์ด๋ค. ์ด ํ ํฐ์ SEMICOLON ํ ํฐ์ด ์๋๊ธฐ ๋๋ฌธ์ for-loop๋ก ๋ค์ด๊ฐ๊ธฐ ์ํ ์ฒซ ๋ฒ์งธ ์กฐ๊ฑด์ ๋ง์กฑํ๋ค. ํ์ง๋ง ๋๋ฒ์งธ ์กฐ๊ฑด์ ๋ง์กฑํ์ง ๋ชปํ๋ค. ์ฆ, ํ์ฌ ์ธ์๋ก ์ ๋ฌ๋ฐ์ precedence ๊ฐ์ 4์ธ ์ํ์ด๋ค. peekPrecedence ๋ฉ์๋๋ฅผ ํธ์ถํจ์ผ๋ก์จ ์ป๋ ๊ฐ๋ 4์ด๋ค. ์ด ๋ ๊ฐ์ ๊ฐ์ ๊ฐ๊ธฐ ๋๋ฌธ์ for-loop ๊ตฌ๋ฌธ์ผ๋ก ๋ค์ด๊ฐ์ง ์๊ณ , ๊ณง๋ฐ๋ก return ๊ตฌ๋ฌธ์ ๋ง๋์ leftExp๋ฅผ ๋ฐํํ๊ณ , ์ด leftExp ๋ณ์์๋ 2๋ผ๋ ์๋ณ์ ํ ํฐ์ ํ์ฑํ ํํ์์ด ๋ด๊ฒจ ์๋ ์ํ์ด๋ค.
parseExpression ๋ฉ์๋๊ฐ ์ข ๋ฃ๋์์ผ๋ ์ด ๋ฉ์๋๋ฅผ ํธ์ถํ ํธ์ถ์ ๋ฉ์๋์ธ parseInfixExpression ๋ก ๋์๊ฐ๋ณด์.
// parser/parser.go
func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
expression := &ast.InfixExpression{
Token: p.curToken,
Left: left,
Operator: p.curToken.Literal,
}
precedence := p.curPrecedence()
p.nextToken()
expression.Right = p.parseExpression(precedence)
return expression
}
expression ์ด๋ผ๋ ๊ตฌ์กฐ์ฒด์ Right ๋ฉค๋ฒ์ ๋ฐฉ๊ธ parseExpression ๋ฉ์๋๊ฐ ๋ฐํํ leftExp ๋ณ์ ์ฆ, 2๋ผ๋ ์๋ณ์ ํ ํฐ์ ํ์ฑํ ํํ์ ๋ ธ๋๋ฅผ ํ ๋นํ๋ค. ๊ฒฐ๊ตญ, ์ด๋ ๊ฒ ๋๋ฉด์ AST ์๋ฃ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ์ด ๋ ธ๋๊ฐ ์ถ๊ฐ๋๋ค.

์ด์ parseInfixExpression ๋ฉ์๋๋ ๋ชจ๋ ๋์ด ๋ฌ๋ค. ํท๊ฐ๋ฆฌ๊ฒ ์ง๋ง.. ์ด parseInfixExpression ๋ฉ์๋๋ฅผ ํธ์ถํ ํธ์ถ์๋ ๋ค๋ฅธ parseExpression ๋ฉ์๋์๋ค. parseExpression ๋ฉ์๋๋ฅผ ์ดํด๋ณด์.
// parser/parser.go
func (p *Parser) parseExpression(precedence int) ast.Expression {
prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
p.noPrefixParseFnError(p.curToken.Type)
return nil
}
leftExp := prefix()
for !p.peekTokenIs(token.SEMICOLON) && precedence < p.peekPrecedence() {
infix := p.infixParseFns[p.peekToken.Type]
if infix == nil {
return leftExp
}
p.nextToken()
leftExp = infix(leftExp)
}
return leftExp
}
์ด๋ฒ parseInfixExpression ๋ฉ์๋๋ฅผ ํธ์ถํ ๋น์์ ์ธ์๋ก ๋๊ฒจ์ฃผ์๋ precednce ๊ฐ์ LOWEST ์๋ ๊ฒ์ ๊ธฐ์ตํ๋๊ฐ? ์ ํ๋จ์ด ์๋๋ค๋ฉด ๋ค์ ์๋ก ์ฌ๋ผ๊ฐ์ ์ฒ์ฒํ ํ๋ฆ์ ๋ฐ๋ผ์๋ณด์. ๋จ ๋ฒ์ ์ดํดํ์ง ๋ชปํด๋ ๊ด์ฐฎ๋ค! ํ์๋ ๋ช ๋ฒ์ฉ์ด๋ ๋ค์ ํ์ธํ๋ค!
์ด์ 2๋ฒ์จฐ for-loop๋ฅผ ๋ ์ฐจ๋ก๋ค. ๊ทธ ์ ์ ํ์ฌ ํ์๊ฐ ๋ฐ๋ผ๋ณด๋ ํ์ฌ ํ ํฐ๊ณผ ๋ค์ ํ ํฐ์ด ๋ฌด์์ธ์ง ๋ฆฌ๋ง์ธ๋ ํด๋ณด์.

peekToken์ ์ฌ์ ํ ์ธ๋ฏธ์ฝ๋ก ์ด ์๋ ์ํ์ด๋ค. ๊ทธ๋ฆฌ๊ณ precedence ๊ฐ์ ์ฌ์ ํ 0์ด๊ณ , peekPrecedence ๋ฉ์๋๋ฅผ ํธ์ถํจ์ผ๋ก์จ ์ป๋ ๊ฐ ์ฆ, peekToken์ธ +(PLUS)์ ์ฐ์ฐ์ ์ฐ์ ์์ ๊ฐ์ 4์ด๋ค. ์ฌ์ ํ 0์ 4๋ณด๋ค ์๊ธฐ ๋๋ฌธ์ ์ด๋ฒ์๋ for-loop๋ฅผ ํ๋ ์กฐ๊ฑด์ ๋ถํฉํ๋ค.
๋ ๋ค์ +(PLUS) ํ ํฐ์ ๋งคํ๋ ์ค์ ์ฐ์ฐ์ ํ์ฑ ํจ์์ธ parseInfixExpression ๋ฉ์๋๋ฅผ ์ป๋๋ค. ์ด ๋ฉ์๋๋ฅผ ํธ์ถํ๊ธฐ ์ง์ nextToken ๋จผ์ ํ ๋ฒ ํธ์ถํจ์ผ๋ก์จ ํ ํฐ์ ์์น๊ฐ 1์นธ์ฉ ๋ ์ด๋ํ๋ค.

๊ทธ๋ฆฌ๊ณ ์ด์ parseInfixExpression ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค. parseInfixExpression ๋ฉ์๋ ์ฝ๋๋ธ๋ก์ ๋ณด์.
// parser/parser.go
func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
expression := &ast.InfixExpression{
Token: p.curToken,
Left: left,
Operator: p.curToken.Literal,
}
precedence := p.curPrecedence()
p.nextToken()
expression.Right = p.parseExpression(precedence)
return expression
}
left ์ธ์์ ๋ค์ด๊ฐ ์๋ ํํ์ ๋ ธ๋๋ 2๋ผ๋ ํ ํฐ์ ํ์ฑํ ์๋ณ์ ํํ์ ๋ ธ๋์ธ ์ํ์ด๋ค. ์ด ๋ ธ๋๋ฅผ InfixExpression ๊ตฌ์กฐ์ฒด์ Left ๋ฉค๋ฒ์ ํ ๋นํ๋ค. ์ด ์๊ฐ์ ๋ง๋ค์ด์ง๋ AST ์๋ฃ๊ตฌ์กฐ ์ํ๋ ๋ค์๊ณผ ๊ฐ๋ค.

๊ทธ๋ฆฌ๊ณ ํ์ฌ ํ ํฐ์ธ curToken์ +(PLUS)๋ฅผ ๊ฐ๋ฆฌํค๋ ์ํ์ด๊ณ ์ด ํ ํฐ์ ์ฐ์ฐ์์ ์ฐ์ ์์ ๊ฐ์ 4์๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ํ๋ฒ nextToken ๋ฉ์๋๋ฅผ ํธ์ถํด์ ํ ํฐ์ ์์น๋ฅผ ํ ์นธ์ฉ ์ฎ๊ธด๋ค.

๊ทธ๋ฆฌ๊ณ parseExpression ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด์ +(PLUS) ํ ํฐ์ ์ฐ์ฐ์ ์ฐ์ ์์ ๊ฐ์ธ 4๋ฅผ ์ธ์๋ก ๋ฃ์ด์ค๋ค. parseExpression ๋ฉ์๋๋ฅผ ๋ณด์.
// parser/parser.go
func (p *Parser) parseExpression(precedence int) ast.Expression {
prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
p.noPrefixParseFnError(p.curToken.Type)
return nil
}
leftExp := prefix()
for !p.peekTokenIs(token.SEMICOLON) && precedence < p.peekPrecedence() {
infix := p.infixParseFns[p.peekToken.Type]
if infix == nil {
return leftExp
}
p.nextToken()
leftExp = infix(leftExp)
}
return leftExp
}
curToken์ ํ์ฌ 3์ด๋ผ๋ ์๋ณ์ ํ ํฐ์ด๋ค. ์ด ์๋ณ์ ํ ํฐ์ ๋งคํ๋๋ ์ ์ ์ฐ์ฐ์ ํ์ฑ ํจ์์ธ parseIdentifer ๋ฉ์๋๋ฅผ ์ป๊ณ ํธ์ถํด์ ์๋ณ์ ํํ์ ๋ ธ๋๋ฅผ ๋ฐํ๋ฐ๋๋ค. ์ด ๋ ธ๋๋ leftExp ๋ณ์์ ํ ๋น๋์๋ค.
๊ทธ๋ฆฌ๊ณ for-loop์ ๋ ์ง์ ํ๋ค. ์ด ๋๋ peekToken์ด SEMICOLON์ด๊ธฐ ๋๋ฌธ์ ์ผ๋จ ์ด ์กฐ๊ฑด๋ถํฐ false ์ด๊ธฐ ๋๋ฌธ์ for-loop๋ฅผ ํ์ง ์๋๋ค. ๋ฌผ๋ก ์ด ์ธ๋ฏธ์ฝ๋ก ์กฐ๊ฑด์ ๋ถํฉํ๋ค๊ณ ํด๋ precedence < p.peekPrecedence() ์กฐ๊ฑด์ ๋ถํฉํ์ง ๋ชปํ๋ค. peekPrecedence ๋ฉ์๋๋ฅผ ํธ์ถํจ์ผ๋ก์จ peekToken์ธ SEMICOLON์ ์ฐ์ฐ์ ์ฐ์ ์์๋ ์กด์ฌํ์ง ์๊ธฐ ๋๋ฌธ์ LOWEST์ธ 0์ ๋ฐํํ๊ฒ ๋๋ฉด์ ํ์ฌ precedence ๊ฐ์ 4์ด์ง๋ง, peekPrecedence ๋ฉ์๋๊ฐ ๋ฐํํ๋ ๊ฐ์ 0์ผ๋ก ๋์ ๋น๊ต ์กฐ๊ฑด์ ๋ง์กฑํ์ง ๋ชปํ๋ค. ๋ฐ๋ผ์ ๊ณง๋ฐ๋ก 3์ด๋ผ๋ ์๋ณ์ ํ ํฐ์ ํ์ฑํ ์๋ณ์ ํํ์ ๋ ธ๋๋ฅผ ๋ฐํํ๋ค.
์ด 3์ด๋ผ๋ ์๋ณ์ ํํ์ ๋ ธ๋๋ parseInfixExpression ๋ฉ์๋ ์์์ expression ๊ตฌ์กฐ์ฒด์ Right ๋ฉค๋ฒ์ ํ ๋น๋๋ค. ์ด๋ ๊ฒ ํด์ ์ต์ข ์ ์ธ AST ์๋ฃ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ์ด ๋ง๋ค์ด์ง๋ค.

2. Bool ๋ฆฌํฐ๋ด ํ์ฑํ๊ธฐ
์ด์ ํ๋ซ ํ์์ ๋์ ์๋ฆฌ์ ๋ํด ์์๋ณด์์ผ๋, ์ฐ๋ฆฌ์ ํ์๊ฐ ํ์ฑํ๋๋ก ํ ์ ์๋ ๋ช ๊ฐ์ง ํํ์์ ๋ ์ถ๊ฐํด๋ณด๋๋ก ํ์. ๊ฐ์ฅ ๋จผ์ Boolean์ ์๋ฏธํ๋ Bool ๋ฆฌํฐ๋ด์ด๋ค. Monkey ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์ Bool ๋ฆฌํฐ๋ด์ ๋ค์๊ณผ ๊ฐ์ด ์์ค์ฝ๋๋ก ํํ๋ ์ ์๋ค.
true;
false;
let foobar = true;
let barfoo = false;
๊ฐ์ฅ ๋จผ์ ํ ์์ ์ Bool ๋ฆฌํฐ๋ด์ ๋ด์ AST ๋ ธ๋๋ฅผ ์ ์ํ๋ ๊ฒ์ด๋ค.
// ast/ast.go
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 }
๊ทธ๋ฆฌ๊ณ ์ด Bool ๋ฆฌํฐ๋ด์ ํ์ฑํ ์ ์๋๋ก ์ ์ฉ ํ์ฑ ํจ์๋ฅผ map ์๋ฃ๊ตฌ์กฐ์ ๋ฑ๋กํ์.
// parser/parser.go
func New(l *lexer.Lexer) *Parser {
p := &Parser{
l: l,
errors: []string{},
}
(... ์๋ต ...)
p.registerPrefix(token.TRUE, p.parseBoolean)
p.registerPrefix(token.FALSE, p.parseBoolean)
(... ์๋ต ...)
return p
}
์์ง Bool ๋ฆฌํฐ๋ด์ ์ค์ง์ ์ผ๋ก ํ์ฑํ๋ ํจ์ ์ฆ, parseBoolean ์ด๋ผ๋ ๋ฉ์๋์ ์๊น์๋ฅผ ๋ณธ ์ ์ด ์๋ค. ์ด์ parseBoolean ๋ฉ์๋์ ์๊น์๋ฅผ ๋ณด์.
// parser/parser.go
func (p *Parser) parseBoolean() ast.Expression {
return &ast.Boolean{Token: p.curToken, Value: p.curTokenIs(token.TRUE)}
}
curTokenIs ๋ผ๋ ํจ์๋ฅผ ์ฌ์ฉํ๋๋ฐ, ์ด์ ํฌ์คํ ์์ ์๊ฐํ ํจ์๋ค. ์ด๋ ค์ด ๋ก์ง์ ์๋๊ณ , ํ์ฌ ํ์๊ฐ ๋ฐ๋ผ๋ณด๋ ํ ํฐ์ด ์ธ์๋ก ๋ฃ์ด์ค, ์ฌ๊ธฐ์๋ TRUE ํ ํฐ๊ณผ ์ผ์นํ๋ฉด true๋ฅผ, ๋ถ์ผ์นํ๋ฉด false๋ฅผ ๋ฐํํ๋ ๋งค์ฐ ๊ฐ๋จํ ๋ก์ง์ ์ํํ๋ค.
์ด์ ์ด Bool ๋ฆฌํฐ๋ด์ ์ ํ์ฑํ๋์ง ํ ์คํธ ํ๊ธฐ ์ํ ํ ์คํธ ์ฝ๋๋ฅผ ์ดํด๋ณด์. ์ด๋ฒ ํ ์คํธ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ฉด์ ๊ธฐ์กด์ ์์ฑํด์๋ ํ ์คํธ ์ฝ๋์ ์๋กญ๊ฒ ์ถ๊ฐ๋ ๋ฉ์๋๊ฐ ์๋ค. ๊ทธ ์ฃผ์ธ๊ณต์ testLiteralExpression ์ด๋ผ๋ ๋ฉ์๋์ด๋ค.
// parser/parser_test.go
func testLiteralExpression(t *testing.T, exp ast.Expression, expected interface{}) bool {
switch v := expected.(type) {
case int:
return testIntegerLiteral(t, exp, int64(v))
case int64:
return testIntegerLiteral(t, exp, v)
case string:
return testIdentifier(t, exp, v)
case bool:
return testBooleanLiteral(t, exp, v)
}
t.Errorf("type of exp not handled. got=%T", exp)
return false
}
์ฒ์ ๋ณด๋ Go lang ๋ฌธ๋ฒ์ด ๋ฑ์ฅํ๋ค. ์ฐ์ ๋จผ์ ํจ์์ ์ธ์๋ก interface{} ๋ผ๊ณ ๋งค์ฐ ์ผ๋ฐํ๋ ํ์ ์ ํ์์ ์ ์ํ๋ค. interface๋ go-lang์์ ๋ชจ๋ ํ์ ์ ๋ํ๋ผ ์ ์๋ ์ต์์ ํ์ ์ ์๋ฏธํ๋ค. ๊ทธ๋์ ํด๋น ํ์์ผ๋ก ์ธ์๋ฅผ ๋ฐ๋๋ค๋ ๊ฒ์ ๊ทธ ์ธ์์ ์ด๋ค ํ์ ์ด๋ผ๋ ์ฌ ์ ์์์ ์๋ฏธํ๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ๋ ๊ฒ interface ํ์ ์ผ๋ก ์ ๋ฌ๋ฐ์ ์ธ์๋ฅผ .(type) ์ด๋ผ๋ ๋ฌธ๋ฒ์ ์ด์ฉํด์ v ๋ผ๋ ๋ณ์์ ํ ๋นํ๋ค. ์ด .(type) ์ด๋ผ๋ ๋ฌธ๋ฒ์ ํ์ ์ค์์น๋ผ๋ ๊ตฌ๋ฌธ์ผ๋ก, interface ๋ผ๋ ๋งค์ฐ ์ผ๋ฐํ๋ ์ํ์ ๊ตฌ์ฒด์ ์ธ ํน์ ํ์ ์ผ๋ก ํ๋ณํ๊ณ ๋ณํํ๋๋ฐ ์ฌ์ฉํ๋ค. ์ค์ ๋ก ์ v ๋ผ๋ ๋ณ์์๋ expected ๋ผ๋ ๋ณ์์ ๋ด๊ฒจ ์๋ ๊ฐ์ ๊ตฌ์ฒด์ ์ธ ํ์ ์ผ๋ก ๋ณํ์ํจ ํ์ ๊ฐ์ด ํ ๋น๋์ด ์๋ค.
์ด์ ์๋ก์ด testBooleanLiteral ๋ฉ์๋๋ฅผ ์ดํด๋ณด์. ํฌ๊ฒ ๋ณ๊ฑด ์๊ณ , testIdentifier ์ด๋ testIntegerLiteral ๋ฉ์๋์ฒ๋ผ ํน์ AST ๋ ธ๋์ ๋ด๊ฒจ์๋ ๋ฐ์ดํฐ๊ฐ Bool ๋ฆฌํฐ๋ด์ธ์ง ํ์ธํ๋ ์ฉ๋์ด๋ค.
// parser/parser_test.go
func testBooleanLiteral(t *testing.T, exp ast.Expression, value bool) bool {
bo, ok := exp.(*ast.Boolean)
if !ok {
t.Errorf("exp not *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("bo.TokenLiteral not %t. got=%s", value, bo.TokenLiteral())
return false
}
return true
}
3. ๊ทธ๋ฃน ํํ์ ํ์ฑํ๊ธฐ
๋ค์์ผ๋ก ํ์ฑํด๋ณผ ๊ฒ์ ๊ทธ๋ฃน ํํ์์ด๋ค. ๊ทธ๋ฃน ํํ์์ผ๋ก ์ธํด์ ๊ทธ๋ฃน์ด ๋ง๋ค์ด์ง๋ฉด ์ฐ์ฐ ์ฐ์ ์์์ ๋ณ๋์ด ์๊ธฐ๊ณ , ์ด์ ๋ฐ๋ผ ํํ์์ด ํ๊ฐ๋๋ ์์๊ฐ ๋ฌ๋ผ์ง๋ค. ๋ํ์ ์ผ๋ก ์ฌ์น ์ฐ์ฐ ์์์์ ๊ทธ๋ฃน ํํ์์ ์ฌ์ฉํ ์์๋ค.
(5 + 5) * 10;
์ฐ์ฐ์ ์์ฒด๋ง ๋ณด๋ฉด ๊ณฑ์ ์ด ๋ง์ ๋ณด๋ค ์ฐ์ ์์๊ฐ ๋์ง๋ง, ๋ง์ ์ด ๊ทธ๋ฃน ํํ์ ์๊ดํธ ์์ ๊ฐ์ธ์ ธ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฐ ๊ฒฝ์ฐ ๋ง์ ์ด ๊ณฑ์ ๋ณด๋ค ๋์ ์ฐ์ ์์๋ฅผ ๊ฐ๋๋ค. ์ด ๊ทธ๋ฃน ํํ์ ํ์ฑ์ ํ ์คํธ ํ๋ ์ฝ๋๋ TestOperatorPrecedenceParsing ์ด๋ผ๋ ํ ์คํธ ํจ์์ ์๋์ ์์ ์ ๋ ฅ์ ์ถ๊ฐํด๋ณด์.
// parser/parset_test.go
func TestOperatorPrecedenceParsing(t *testing.T) {
tests := []struct {
input string
expected string
}{
(... ์๋ต ...)
{
"1 + (2 + 3) + 4",
"((1 + (2 + 3)) + 4)",
},
{
"(5 + 5) * 2",
"((5 + 5) * 2)",
},
{
"2 / (5 + 5)",
"(2 / (5 + 5))",
},
{
"(5 + 5) * 2 * (5 + 5)",
"(((5 + 5) * 2) * (5 + 5))",
},
{
"-(5 + 5)",
"(-(5 + 5))",
},
{
"!(true == true)",
"(!(true == true))",
},
}
for _, tt := range tests {
l := lexer.New(tt.input)
p := New(l)
program := p.ParseProgram()
checkParserErrors(t, p)
actual := program.String()
if actual != tt.expected {
t.Errorf("expected=%q, got=%q", tt.expected, actual)
}
}
์ด์ ๊ทธ๋ฃน ํํ์์ ํ์ฑํ๋ ํจ์๋ฅผ ๋ฑ๋กํ๊ณ , ์์ฑํด์ฃผ์ด์ผ ํ๋ค. ์๊ฐ๋ณด๋ค ์์ฒญ ๊ฐ๋จํ๋ค. ๋จผ์ ํ์ฑ ํจ์๋ฅผ ๋ฑ๋กํ์.
// parser/parser.go
func New(l *lexer.Lexer) *Parser {
p := &Parser{
l: l,
errors: []string{},
}
(... ์๋ต ...)
p.registerPrefix(token.LPAREN, p.parseGroupedExpression)
(... ์๋ต ...)
return p
}
๊ทธ๋ฆฌ๊ณ parseGroupedExpression ์ด๋ผ๋ ๋ฉ์๋์ ์๊น์๋ฅผ ๋ณด์.
// parser/parser.go
func (p *Parser) parseGroupedExpression() ast.Expression {
p.nextToken()
exp := p.parseExpression(LOWEST)
if !p.expectPeek(token.RPAREN) {
return nil
}
return exp
}
๋จผ์ ํ์ฌ ํ์๊ฐ ๋ฐ๋ผ๋ณด๋ ํ ํฐ์ด ์ผ์ชฝ ์๊ดํธ์ผ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ํ ํฐ์ ์์น๋ฅผ ํ ๋ฒ ์ฎ๊ฒจ์ค๋ค. ๊ทธ๋ฆฌ๊ณ ์ฐ์ ์์ ์ฐ์ฐ์ ๊ฐ์ 0 ์ฆ, LOWEST๋ก ์ค ๋ค ํ์ฑ์ ์ํํ ๋ก ํ๋ค. ์ด ์๊ฐ ์ฌ๊ท์ ์ผ๋ก ํ์ฑ์ด ํธ์ถ๋๊ฒ ๋๋ค. ์ด๋ ๊ฒ ์ฌ๊ท์ ์ผ๋ก ํ์ฑ ํธ์ถ์ด ์๋ฃ๋ ๋ค์ ํ์๊ฐ ๋ฐ๋ผ๋ณด๋ ๋ค์ ํ ํฐ์ ์์น๊ฐ ์ค๋ฅธ์ชฝ ์๊ดํธ๋ผ๋ฉด ํด๋น ํํ์์ return ํ๊ฒ ๋๋ค. ์ด ๋ ๋ฐ๋ก ๊ทธ๋ฃน ํํ์ ํ์ฑ์ด ์๋ฃ๋๋ ๊ฒ์ด๋ค.
4. ์กฐ๊ฑด ํํ์ ํ์ฑํ๊ธฐ
๋ค์์ ์กฐ๊ฑด ํํ์์ ํ์ฑํ ์ฐจ๋ก๋ค. Monkey ์ธ์ด์์๋ if-else ๊ตฌ๋ฌธ์ด ๊ฐ๋ฅํ๋๋ก ๋ง๋ค์ด๋ณด์. Monkey ์ธ์ด์์์ if-else ๊ตฌ๋ฌธ ์ฌ์ฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค.
if (x > y) {
return x;
} else {
return y;
}
๋ฌผ๋ก ์์ฒ๋ผ else ๊ตฌ๋ฌธ๊น์ง ํ์ฉํ ์๋ ์์ง๋ง, else๋ฅผ ์ฌ์ฉํ์ง ์์ ์๋ ์๋ค. ์์๋ ๋ค์๊ณผ ๊ฐ๋ค.
if (x > y) {
return x;
}
if-else ์กฐ๊ฑด์์ ํํ์์ด๋ค. ์ด ๋ง์ if-else ๊ตฌ๋ฌธ์์ ๊ฐ์ ๋ง๋ค์ด๋ธ๋ค๋ ๋ป์ด๋ค. ์ ์์ค์ฝ๋๋ x ์ y ๋ผ๋ ๊ฐ์ ๋ง๋ค์ด๋ธ๋ค๊ณ ํ ์ ์๋ค. ๋ฐ๋ผ์ if-else ์กฐ๊ฑด์์ด ๊ฐ๋ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ๋ค.
if (<CONDITION>) <CONSEQUENCE> else <ALTENATIVE>
์ ๊ตฌ์กฐ ์ค์์ <CONSEQUNECE> ์ <ALTENATIVE>๋ ์ค๊ดํธ๋ก ํฌํ๋๋ ๋ธ๋ก๋ฌธ์ ๊ฐ๋๋ค. ์ด๋ฅธ๋ฐ body ๋ธ๋ก์ ๊ฐ๋๋ค. ์ด์ if-else ์กฐ๊ฑด์์ ๊ตฌ์กฐ๋ฅผ ์ดํด๋ณด์์ผ๋ ํ์ฑ์ด ๊ฐ๋ฅํ๋๋ก ์ฐ๋ฆฌ๋ง์ ํ์๋ฅผ ๊ตฌํํด๋ณด์.
๊ฐ์ฅ ๋จผ์ if-else ์กฐ๊ฑด ํํ์์ ๋ํ๋ด๋ AST ๋ ธ๋๋ฅผ ์ ์ํ์.
// ast/ast.go
type IfExpression struct {
Token token.Token
Condition Expression
Consequence *BlockStatement
Alternative *BlockStatement
}
func (ie *IfExpression) expressionNode() {}
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()
}
์๋ก์ด IfExpression ์ด๋ผ๋ ์ด๋ฆ์ ๊ตฌ์กฐ์ฒด๋ฅผ ์ ์ํ๋ค. ํด๋น ๊ตฌ์กฐ์ฒด๋ 4๊ฐ์ ๋ฉค๋ฒ๋ฅผ ๊ฐ๋๋ฐ, ํ๋๋ ๋ค๋ฅธ ๊ตฌ์กฐ์ฒด๋ค๊ณผ ๋์ผํ๊ฒ Token ๊ฐ์ ๊ฐ๋๋ค. ๊ทธ๋ฆฌ๊ณ 3๊ฐ์ ๋ฉค๋ฒ๋ ์ง์ ์ ์์๋ณธ if-else ์กฐ๊ฑด ํํ์์ ๊ตฌ์กฐ๋ก ์ดํด๋ณด์๋ 3๊ฐ์ง ๊ตฌ์ฑ์์๋ฅผ ๊ฐ๋๋ค. ์ฒซ ๋ฒ์งธ๋ ์กฐ๊ฑด์์ ์๋ฏธํ๋ Condition์ด๋ค. ์ด Condition์ ํํ์์ ํด๋นํ๊ธฐ ๋๋ฌธ์ Expression ์ธํฐํ์ด์ค๋ฅผ ํ์ ์ผ๋ก ๋ช ์ํ๋ค. ๊ทธ๋ฆฌ๊ณ Consequence์ Alternative๋ BlockStatement ๋ผ๋ ์ฒ์๋ณด๋ ํ์ ์ ํฌ์ธํฐ ๋ณ์๋ฅผ ํ์ ์ผ๋ก ๋ช ์ํ๋ค. ์ด 2๊ฐ๋ฅผ ๋ธ๋ก์ ๊ฐ๋ ๊ตฌ๋ฌธ์ด๊ธฐ ๋๋ฌธ์ ์ด ํ์ ์ ๋ช ์ํ๋ค. ๊ทธ๋ฌ๋ฉด ์ด์ BlockStatment๊ฐ ๋ฌด์จ ํ์ ์ธ์ง ๊ตฌ์ฒด์ ์ผ๋ก ๋ณผ ์ฐจ๋ก๋ค.
// ast/ast.go
type BlockStatement struct {
Token token.Token
Statements []Statement
}
func (bs *BlockStatement) statementNode() {}
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()
}
BlockStatment ๋ผ๋ ๊ตฌ์กฐ์ฒด๋ ์ฌ๋ผ์ด์ค ํํ์ ์ฌ๋ฌ ๊ฐ์ Statment ์ธํฐํ์ด์ค๋ฅผ ๊ฐ์ง ์ ์๋ Statments ๋ผ๋ ๋ฉค๋ฒ๋ฅผ ๊ฐ๋๋ค. ์ด Statment๋ 1๊ฐ์ ๋ช ๋ น๋ฌธ์ ๋ปํ๋ค. ๋ค์ ๋งํด, BolckStatement๋ ์ฌ๋ฌ ๊ฐ์ ๋ช ๋ น๋ฌธ์ ๊ฐ๋๋ค. ์ด์ ๋ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ๋ฌธ์ด ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ด๋ค.
if (x > y) {
z = x + y;
return z
} else {
z = x * y;
return z
}
์์ฒ๋ผ ํ๋์ ๋ธ๋ก ์์๋ 2๊ฐ ์ด์์ ๋ช ๋ น๋ฌธ์ผ๋ก ๊ตฌ์ฑ๋ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์, ์ด์ ์กฐ๊ฑด ํํ์์ ํ์ฑํ๊ธฐ ์ํ ๋ ธ๋ ์ ์๋ ๋ชจ๋ ๋ง์ณค๋ค. ์ด์ ํ ์คํธ ์ฝ๋๋ฅผ ์ดํด๋ณด์.
// parser/parser_test.go
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 1 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])
}
exp, ok := stmt.Expression.(*ast.IfExpression)
if !ok {
t.Fatalf("stmt 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
}
}
ํฌ๊ฒ ํ ์คํธ ์ฝ๋ ๊ตฌ์กฐ๊ฐ ๋ณํ ๊ฒ์ ์๋ค. ๋จ์ง ๋ฐฉ๊ธ ์ฐ๋ฆฌ๊ฐ ์๋กญ๊ฒ ์ ์ํ if-else ์กฐ๊ฑด์ AST ๋ ธ๋๋ฅผ ๊ฒ์ฆํ๊ธฐ ์ํด ์ด๋ค ๊ตฌ์กฐ์ฒด์ ์ด๋ค ๋ฉค๋ฒ์ ์ ๊ทผํด์ ๊ฒ์ฆํ๋์ง ์ ๋๋ง ๋ฐ๋์๋ค.
์ด์ ๋ค์์ผ๋ก ํ ์์ ์ ์ค์ง์ ์ธ ํ์ฑ์ ์ํํ ํจ์๋ฅผ ์ ์ํ๊ณ ๋ฑ๋กํ๋ ๊ฒ์ด๋ค.
// parer/parser.go
func New(l *lexer.Lexer) *Parser {
p := &Parser{
l: l,
errors: []string{},
}
// ํ์ฌ ์ด๊ธฐ token ๊ฐ์ ๋น ๋ฌธ์์ด๊ธฐ ๋๋ฌธ์, curToken, peekToken์ ํ ํฐ์ ๋ด์ผ๋ ค๋ฉด 2๋ฒ ์ํ
p.nextToken()
p.nextToken()
(... ์๋ต ...)
p.registerPrefix(token.IF, p.parseIfExpression)
(... ์๋ต ...)
return p
if-else ์กฐ๊ฑด ํํ์์ ํ์ฑํ๋ ํจ์์ธ parseIfExpression ๋ฉ์๋๋ฅผ ๋ฑ๋กํ๋ค. ์ด ๋, ์ ์ํํ์์ ๋ฑ๋กํ ๊ฒ์ ๋ณผ ์ ์๋๋ฐ, ์ด์ ๋ if-else ์กฐ๊ฑด ํํ์์ if ๋ผ๋ ํค์๋๊ฐ ๊ฐ์ฅ ๋จผ์ ๋ฑ์ฅํ๊ธฐ ๋๋ฌธ์ด๋ค. ์ด์ parseIfExpression ๋ฉ์๋๊ฐ ์ด๋ป๊ฒ ์๊ฒผ๋์ง ๋ณผ ์ฐจ๋ก๋ค.
// parser/parser.go
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.parseBlockStatement()
if p.peekTokenIs(token.ELSE) {
p.nextToken()
if !p.expectPeek(token.LBRACE) {
return nil
}
expression.Alternative = p.parseBlockStatement()
}
return expression
}
๋จผ์ IfExpreession ์ด๋ผ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ํ ๋น ๋ฐ ์ ์ธ์ ์ํํ๋ค. ์ด ๋์ curToken ๊ฐ์ if ๋ผ๋ ํค์๋์ด๋ค. ๋ง์ฝ ๋ค์ ํ ํฐ ์ฆ, peekToken ๊ฐ์ด <CONDITION>์ ์์์ธ ์ผ์ชฝ ์๊ดํธ์ผ ๊ฒฝ์ฐ์๋ง ๊ณ์ ๋ก์ง์ ์ํํ๋๋ก ํ๋ค. ์ด ๋, expectPeek ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด์ curToken, peekToken์ ํ๋์ฉ ์ฎ๊ธด๋ค๋ ์ฌ์ค์ ์์ง๋ง์! ์ผ์ชฝ ์๊ดํธ์ธ์ง ๊ฒ์ฌ๊ฐ ๋๋ ํ, ๋ค์ ํ๋ฒ curToken, peekToken์ ํ๋์ฉ ์ด๋์ํจ๋ค. ์ด์ curToken์ ์ผ์ชฝ ์๊ดํธ ๋ค์์ ์๋ ์ฒซ๋ฒ์งธ ํ ํฐ์ ์์ ๊ฒ์ด๋ค. ์ด ์์ ์ ์กฐ๊ฑด์ ์ฆ, <CONDITION>์ ํ์ฑํ๊ธฐ ์ํด ์ด์ ์ ์ดํด๋ณด์๋ parseExpression ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค. ์ด ๋, ์ฐ์ฐ์ ์ฐ์ ์์๋ ๊ฐ์ฅ ๋ฎ์ ๊ฐ์ด LOWEST๋ฅผ ์ง์ ํด ๋๊ธด๋ค. ์ด๋ ๊ฒ <CONDITION> ํ์ฑ์ด ๋๋ฌ์ผ๋ฉด ๋ค์์ผ๋ก๋ if์ body ๋ธ๋ก์ ํ์ฑํ ์ฐจ๋ก๋ค. ๋ง์ฝ ์ฑ๊ณต์ ์ผ๋ก <CONDITION> ํ์ฑ์ด ๋๋ฌ๋ค๋ฉด curToken์ ์ค๋ฅธ์ชฝ ์๊ดํธ์ ์ง์ ํ ํฐ ์์น์ ์์ ๊ฒ์ด๋ค.
์ด์ ๋ค์ ํ ํฐ์ด ์ค๋ฅธ์ชฝ ์๊ดํธ์ธ์ง ๊ฒ์ฌํ๋ค. ์ด ๋๋ ๋ง์ฐฌ๊ฐ์ง๋ก expectPeek ๋ฉ์๋๋ฅผ ํธ์ถํจ์ผ๋ก์จ curToken, peekToken์ ํ๋์ฉ ์ฎ๊ธด๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ํ๋ฒ expectPeek ๋ฉ์๋๋ฅผ ํธ์ถํด ํ์นธ์ฉ ๋ ์ฎ๊ธด๋ค. ๋ฌผ๋ก ์ด๋ฒ์ expectPeek ๋ฉ์๋๋ ๋ค์ ํ ํฐ์ด body ๋ธ๋ก์ ์์์ ์๋ฏธํ๋ ์ผ์ชฝ ์ค๊ดํธ์ธ์ง๋ฅผ ๊ฒ์ฌํ๋ค. ์ด๋ ๊ฒ ๋๋ฉด curToken์ ์์น๋ ์ผ์ชฝ ์ค๊ดํธ์ ์์นํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ ๋ค, ์ด ์์ ์ <CONSEQUENCE> ๋ฅผ ํ์ฑํ๊ธฐ ์ํด parseBlockStatement ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค. ์ด์ parseBlockStatement ๋ฉ์๋์ ๋ก์ง์ ์ดํด๋ณด์.
// parser/parser.go
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)
}
p.nextToken()
}
return block
}
์ฌ์ค ์ ์์ค์ฝ๋ ๋ก์ง์ ์ฐ๋ฆฌ์๊ฒ ๋ฏ์ค์ง๋ ์๋ค. ๋ฐ๋ก ํ์ ๊ด๋ จ ์ฒซ ๋ฒ์งธ ํฌ์คํ ์์ ์ดํด๋ณด์๋ ParseProgram ์ด๋ผ๋ ์ด๋ฆ์ ๋ฉ์๋๋ ์๊น์๊ฐ ๋งค์ฐ ๋น์ทํ๋ค.
๋ค๋ง ์ฐจ์ด์ ์ curTokenIs ๋ผ๋ ๋ฉ์๋๋ก body ๋ธ๋ก์ ๋์ ์๋ฏธํ๋ ์ค๋ฅธ์ชฝ ์ค๊ดํธ๊ฐ ๋์ฌ๋๊น์ง ๊ณ์ for-loop๋ฅผ ์ํํ๋ฉด์ ๋ช ๋ น๋ฌธ๋ค์ ํ์ฑํ๋ ๊ฒ์ด๋ค.
์ด๋ ๊ฒ ํด์ if-else ์กฐ๊ฑด๋ฌธ ํํ์ ํ์ฑ์ด ๋ชจ๋ ๋์ด๋ฌ๋ค. ํด๋น ๋ชฉ์ฐจ ์ด๋ฐ์ ์์ฑํ๋ ํ ์คํธ ์ฝ๋๋ฅผ ์ํํ๋ฉด ์ ํต๊ณผ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
5. ํจ์ ๋ฆฌํฐ๋ด ํ์ฑํ๊ธฐ
๋ค์์ ํจ์ ๋ฆฌํฐ๋ด์ ๋ํ ํ์ฑ์ด๋ค. ํจ์ ๋ฆฌํฐ๋ด์ ๋ง ๊ทธ๋๋ก ํจ์๋ฅผ ์ ์ํ ํํ์์ด๋ค. Monkey ์ธ์ด์์์ ์์ค์ฝ๋ ์์๋ ๋ค์๊ณผ ๊ฐ๋ค.
fn(x, y) {
return x + y;
}
fn() {
return foobar + barfoo;
}
let myFunction = fn(x, y) {return x + y; }
fn() {
return fn(x, y) { return x > y; };
}
myFunc(x, y, fn(x, y) { return x > y; });
์์ฒ๋ผ ํจ์ ๋ฆฌํฐ๋ด์ ์ ์ํ๋ ๋ฐฉ์์ ์ฌ๋ฌ๊ฐ์ง์ด๋ค. ํจ์ ๋ฆฌํฐ๋ด ์์ฒด๋ง ์ ์ํ ์๋ ์๊ณ , ํจ์ ๋ฆฌํฐ๋ด ์์์๋ ํ๋ผ๋ฏธํฐ๊ฐ ์์ ๊ฒฝ์ฐ ๋๋ ์ฌ๋ฌ ๊ฐ์ผ ๊ฒฝ์ฐ๊ฐ ์๋ค. ๋ํ ํจ์ ๋ฆฌํฐ๋ด์ ์ฆ์ ์ด๋ค ๋ณ์์ ํ ๋นํ ์๋ ์๊ณ , ํจ์ ๋ฆฌํฐ๋ด ์์ ๋ ๋ค๋ฅธ ํจ์ ๋ฆฌํฐ๋ด์ ์ค์ฒฉ ์ํ๋ก ์ ์ํ ์๋ ์๋ค. ๋ง์ง๋ง์ผ๋ก ํจ์ ๋ฆฌํฐ๋ด์ ๋ค๋ฅธ ํจ์์ ์ธ์๋ก ๋ฃ์ ์๋ ์๋ค.
์ฐ๋ฆฌ๋ ํจ์ ๋ฆฌํฐ๋ด์ ํ์ฑํ๊ธฐ ์ํด 2๊ฐ์ง ํฌ์ธํธ์ ์ง์คํด์ผ ํ๋ค. ์ฒซ ๋ฒ์งธ๋ ํ๋ผ๋ฏธํฐ ๋ฆฌ์คํธ์ด๋ค. ์ด๋ค ํจ์๋ ํ๋ผ๋ฏธํฐ๋ฅผ 1๊ฐ๋ง ๋ฐ๊ฑฐ๋ 2๊ฐ ์ด์์ ๋ฐ๊ฑฐ๋ ๋๋ ์์ ๋ฐ์ง ์์ ์๋ ์๋ค. ๋ ๋ฒ์งธ๋ ํจ์์ ๋ชธ์ฒด์ธ body ๋ธ๋ก ๋ถ๋ถ์ด๋ค. ์ด ๋ธ๋ก ๋ถ๋ถ์ ์ง์ ๋ชฉ์ฐจ์์ ์ดํด๋ณธ if-else ๊ตฌ๋ฌธ์ body ๋ธ๋ก๋ฌธ์ ํ์ฑํ ๋ ์ฌ์ฉํ๋ ๋ฉ์๋๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ๋ค. ์ฆ, ํจ์ ๋ฆฌํฐ๋ด์ ํํ์์ผ๋ก ์ ์ํ ๋ค, ํจ์ ๋ฆฌํฐ๋ด์ ํ์ฑํ๋ ๋ก์ง์ ์ํํ๋ ํจ์๋ง ์์ฑํด ์ ๊ณตํ๋ฉด ๋๋ค. ๊ทธ๋์ ํจ์ ๋ฆฌํฐ๋ด ํํ์์ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ๋ค.
fn <PARAMETERS> <BLOCKSTATEMENT>
์ด์ ํ๋์ฉ ์ดํด๋ณด์. ๊ฐ์ฅ ๋จผ์ ํด๋ณผ ๋ถ๋ถ์ ํจ์ ๋ฆฌํฐ๋ด์ AST ๋ ธ๋๋ก์ ์ ์ํ๋ ๊ฒ์ด๋ค.
// ast/ast.go
type FunctionLiteral struct {
Token token.Token
Parameters []*Identifier
Body *BlockStatement
}
func (fl *FunctionLiteral) expressionNode() {}
func (fl *FunctionLiteral) TokenLiteral() string { return fl.Token.Literal }
func (fl *FunctionLiteral) String() string {
var out bytes.Buffer
params := []string{}
for _, p := range fl.Parameters {
params = append(params, p.String())
}
out.WriteString(fl.TokenLiteral())
out.WriteString("(")
out.WriteString(strings.Join(params, ", "))
out.WriteString(") ")
out.WriteString(fl.Body.String())
return out.String()
}
ํจ์ ๋ฆฌํฐ๋ด์ AST ๋ ธ๋๋ก ํํํ๊ธฐ ์ํด FunctionLiteral ์ด๋ผ๋ ์๋ก์ด ๊ตฌ์กฐ์ฒด๋ฅผ ์ ์ํ๋ค. ์ด ๊ตฌ์กฐ์ฒด๋ 3๊ฐ์ ๋ฉค๋ฒ๋ฅผ ๊ฐ๋๋ฐ, ํ๋๋ ๋ ๊ทธ๋ฌ๋ฏ์ด Token, ๊ทธ๋ฆฌ๊ณ ํจ์์ ํ๋ผ๋ฏธํฐ๋ฅผ ์๋ฏธํ๋ Parameters์ด๋ค. ํ๋ผ๋ฏธํฐ๋ ์๋ณ์๋ก ์ ์๋๊ณ ํ๋ผ๋ฏธํฐ๊ฐ 0๊ฐ ์ด์์ผ ์ ์๊ธฐ ๋๋ฌธ์ ์ฌ๋ผ์ด์ค ํํ๋ก ์ ์ํ๋ค. ๊ทธ๋ฆฌ๊ณ ํจ์์ ๋ธ๋ก์ ์๋ฏธํ๋ Body๊ฐ ๋ฉค๋ฒ๋ก์ ์กด์ฌํ๋ค.
์ด๋ฒ์๋ ํ ์คํธ ์ฝ๋์ ๋ํ ์ฝ๋ ๋ธ๋ก์ ๋จผ์ ์ธ๊ธํ๋ค. ๋ก์ง ์์ฒด๋ ๊ทธ๋์์ ํ ์คํธ ์ฝ๋๋ ์ญ์ ๋ค๋ฅด์ง ์๊ธฐ ๋๋ฌธ์ ์ค์ฝ ํ ๋ฒ ์ฝ์ด๋ณด๋๋ก ํ์. ๋ณ๋ค๋ฅธ ์ค๋ช ์ ํ์ง ์๊ฒ ๋ค.
// paresr/parser_test.go
func TestFunctionLiteralParsing(t *testing.T) {
input := `fn(x, y) { x + 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])
}
function, ok := stmt.Expression.(*ast.FunctionLiteral)
if !ok {
t.Fatalf("stmt.Expression is not ast.FunctionLiteral. got=%T",
stmt.Expression)
}
if len(function.Parameters) != 2 {
t.Fatalf("function literal parameters wrong. want 2, got=%d\n",
len(function.Parameters))
}
testLiteralExpression(t, function.Parameters[0], "x")
testLiteralExpression(t, function.Parameters[1], "y")
if len(function.Body.Statements) != 1 {
t.Fatalf("function.Body.Statements has not 1 statements. got=%d\n",
len(function.Body.Statements))
}
bodyStmt, ok := function.Body.Statements[0].(*ast.ExpressionStatement)
if !ok {
t.Fatalf("function body stmt is not ast.ExpressionStatement. got=%T",
function.Body.Statements[0])
}
testInfixExpression(t, bodyStmt.Expression, "x", "+", "y")
}
์ด์ ํจ์ ๋ฆฌํฐ๋ด ํํ์์ ํ์ฑํ๊ธฐ ์ํ ํ ํฐ์ ๋ง๋ ํ์ฑ ํจ์๋ฅผ ๋ฑ๋กํ๊ณ ์ ์ํด๋ณด์.
// parer/parser.go
func New(l *lexer.Lexer) *Parser {
p := &Parser{
l: l,
errors: []string{},
}
// ํ์ฌ ์ด๊ธฐ token ๊ฐ์ ๋น ๋ฌธ์์ด๊ธฐ ๋๋ฌธ์, curToken, peekToken์ ํ ํฐ์ ๋ด์ผ๋ ค๋ฉด 2๋ฒ ์ํ
p.nextToken()
p.nextToken()
(... ์๋ต ...)
p.registerPrefix(token.FUNCTION, p.parseFunctionLiteral)
(... ์๋ต ...)
return p
์๋ก์ด ํจ์์ธ parseFunctionLiteral ๋ฉ์๋๋ฅผ ๋ณด์.
// parser/parser.go
func (p *Parser) parseFunctionLiteral() ast.Expression {
lit := &ast.FunctionLiteral{Token: p.curToken}
if !p.expectPeek(token.LPAREN) {
return nil
}
lit.Parameters = p.parseFunctionParameters()
if !p.expectPeek(token.LBRACE) {
return nil
}
lit.Body = p.parseBlockStatement()
return lit
}
๋ฐฉ๊ธ ์ ์ํ ํจ์ ๋ฆฌํฐ๋ด์ AST ๋ ธ๋๋ก ํํํ๊ธฐ ์ํ FunctionLiteral ๊ตฌ์กฐ์ฒด๋ฅผ ์ ์ํ๋ค. curToken์ ํ์ฌ fn ํ ํฐ์ด๋ค. expectPeek ๋ฉ์๋๋ฅผ ํธ์ถํจ์ผ๋ก์จ peekToken์ด ์ผ์ชฝ ์๊ดํธ์ธ์ง ํ์ธํ๋ฉด์ ๋ง๋ค๋ฉด curToken, peekToken์ ํ ์นธ์ฉ ์ฎ๊ธด๋ค. ๊ทธ๋ฌ๋ฉด curToken์ ์ด์ ์ผ์ชฝ ์๊ดํธ๋ฅผ ๊ฐ๋ฆฌํค๊ณ ์๋ค. ์ด ์๊ฐ์ ํจ์ ๋ฆฌํฐ๋ด์ ํ๋ผ๋ฏธํฐ ๋ฆฌ์คํธ๋ฅผ ํ์ฑํ๊ธฐ ์ํด parseFunctionParameters ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค. parseFunctionParameters ๋ฉ์๋๋ก ๋ ์ด๋ํด๋ณด์.
// parser/parser.go
func (p *Parser) parseFunctionParameters() []*ast.Identifier {
identifiers := []*ast.Identifier{}
if p.peekTokenIs(token.RPAREN) {
p.nextToken()
return identifiers
}
p.nextToken()
ident := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
identifiers = append(identifiers, ident)
for p.peekTokenIs(token.COMMA) {
p.nextToken()
p.nextToken()
ident := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
identifiers = append(identifiers, ident)
}
if !p.expectPeek(token.RPAREN) {
return nil
}
return identifiers
}
ํ์ฌ curToken์ ์ผ์ชฝ ์๊ดํธ์ธ ์ํ์ด๋ค. ๋ง์ฝ ๋ค์ ํ ํฐ์ด ์ค๋ฅธ์ชฝ ์๊ดํธ์ธ์ง๋ฅผ ํ์ธํ๋ค. ์ฆ, ๋ฐ๋ก ๋ค์ ํ ํฐ์ด ์ค๋ฅธ์ชฝ ์๊ดํธ๋ผ๋ ๊ฒ์ ํ๋ผ๋ฏธํฐ๊ฐ ์๋ฌด๊ฒ๋ ์์์ ์๋ฏธํ๋ค. ๊ทธ๋ฌ๋ฏ๋ก ๊ทธ ์ฆ์ ํ ํฐ์ ํ์นธ์ฉ ์ฎ๊ธด ๋ค ๋น ์ฌ๋ผ์ด์ค๋ฅผ ๋ฐํํ๋ค.
๋ค์ ํ ํฐ์ด ์ค๋ฅธ์ชฝ ์๊ดํธ๊ฐ ์๋๋ผ๋ฉด nextToken ๋ฉ์๋๋ฅผ ํธ์ถํด์ ํ ํฐ์ ํ ์นธ์ฉ ์ฎ๊ธด๋ค. ์ฆ, ํ์ฌ curToken์ ์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ ์๋ณ์์ ๊ฐ ์์ ๊ฒ์ด๋ค. ์ด๋ฅผ Identifier ๊ตฌ์กฐ์ฒด ํ๋๋ฅผ ์ ์ธ ๋ฐ ์ ์ด๋ฅผ ์ํํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ฌ๋ผ์ด์ค์ ์ด ๊ตฌ์กฐ์ฒด๋ฅผ ํ๋ append ํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ ๋ค, ๋ค์ ํ ํฐ์ด ์ปด๋ง(,)์ธ ๊ฒฝ์ฐ ๊ณ์ for-loop์ body ๋ธ๋ก์ ํ๋ค.(์ปด๋ง์ธ ์ด์ ๋ ํ๋ผ๋ฏธํฐ ๊ตฌ๋ถ์๋ฅผ ์ปด๋ง๋ก ๊ตฌ๋ถํ๊ธฐ ๋๋ฌธ์ด๋ค) ๋ธ๋ก ์์์๋ nextToken 2๋ฒ์ ํธ์ถํด์ curToken์ด ๋ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ ์๋ณ์๋ก ์ฎ๊ธฐ๋๋ก ํ๋ค. ์๋ฅผ ๋ค์ด, ํ๋ผ๋ฏธํฐ๊ฐ fn(x, y, z) ์ด๋ฐ ์์ผ๋ก ์๋ค๊ณ ๊ฐ์ ํด๋ณด์. ๋ง์ฝ nextToken์ ํ ๋ฒ๋ง ํธ์ถํ ๊ฒฝ์ฐ์๋ curToken์ด x๋ค์ ์ปด๋ง(,)์ ์์นํ ๊ฒ์ด๋ค. ๊ทธ๋ฌ๋ฏ๋ก nextToken์ ํ๋ฒ ๋ ํธ์ถํด์ฃผ์ด์ curToken์ด y๋ผ๋ ๋ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ ์๋ณ์๋ก ์์นํ๋๋ก ํด์ค๋ค.
์ด๋ ๊ฒ ๋ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ ์๋ณ์์ ๋ํด์ Identifier ๊ตฌ์กฐ์ฒด๋ฅผ ๋ง๋ค๊ณ ๋ ์ฌ๋ผ์ด์ค์ ์ถ๊ฐํ๋ค. ์ด๋ฌํ ๊ณผ์ ์ ๋ค์ ํ ํฐ์ ์ปด๋ง๊ฐ ๋์ค์ง ์์ ๋๊น์ง ๊ณ์ ๋ฐ๋ณตํ๋ค. ์ด for-loop๊ฐ ์ข ๋ฃ๋๊ณ ๋ ๋ค, ๋ค์ ํ ํฐ์ด ์ค๋ฅธ์ชฝ ์๊ดํธ์ผ ๊ฒฝ์ฐ ๊ทธ๋์ append ํ๋ ์ฌ๋ผ์ด์ค๋ฅผ ๋ฐํํ๋๋ก ํ๋ค.
์ด์ ๋ค์ parseFunctionParameters๋ฅผ ํธ์ถํ๋ ํธ์ถ์ ํจ์์ธ parseFunctionLiteral๋ ๋์์๋ณด์.
// parser/parser.go
func (p *Parser) parseFunctionLiteral() ast.Expression {
lit := &ast.FunctionLiteral{Token: p.curToken}
if !p.expectPeek(token.LPAREN) {
return nil
}
lit.Parameters = p.parseFunctionParameters()
if !p.expectPeek(token.LBRACE) {
return nil
}
lit.Body = p.parseBlockStatement()
return lit
}
FunctionLiteral ๊ตฌ์กฐ์ฒด์ Parmeters ๋ฉค๋ฒ์ ํ๋ผ๋ฏธํฐ๋ค์ ํ ๋นํ๋ ๊ฒ๊น์ง ์ํํ๋ค. ์ด์ ํจ์ ๋ฆฌํฐ๋ด์ body ๋ธ๋ก์ ํ์ฑํ ์ฐจ๋ก๋ค. ์ฌ๊ธฐ๋ ์ง์ ๋ชฉ์ฐจ์ธ if-else ์กฐ๊ฑด ํํ์์ body ๋ธ๋ก์ ํ์ฑํ์ ๋๋ ๋์ผํ๋ค. ๋ค์ ํ ํฐ์ด ์ผ์ชฝ ์ค๊ดํธ๊ฐ ๋์ค๋์ง ํ์ธํ๊ณ , ๋์จ๋ค๋ฉด parseBlockStatement ๋ฉ์๋๋ฅผ ํธ์ถํด์ body ๋ธ๋ก์ ๋ช ๋ น๋ฌธ๋ค์ ํ์ฑํ๋ค.
6. ํจ์ ํธ์ถ ํํ์ ํ์ฑํ๊ธฐ
์ด์ ๋๋ง์ ๋ง์ง๋ง ๊ธฐ๋ฅ์ ํ์ฑ์ ์ถ๊ฐํด๋ณผ ์ฐจ๋ก๋ค. ๋ฐ๋ก ํจ์ ํธ์ถ ํํ์์ด๋ค. ํจ์ ํธ์ถ ํํ์์ ์์ ๋ ๋ค์๊ณผ ๊ฐ๋ค.
add(2, 3)
add(2 + 2, 3 * 3 * 3)
์์ค์ฝ๋์์ add๋ ์๋ณ์์ด๋ค. ์ด ์๋ณ์ add์๋ add ๋ผ๋ ํจ์๋ฅผ ์ ์ํ ํจ์ ๋ฆฌํฐ๋ด์ด ๋ฐ์ธ๋ฉ๋์ด ์๋ค. ๊ทธ๋์ ์ถํ์ ๋ฐฐ์ธ ํ๊ฐํ๋ ๊ณผ์ ์์ ์๋ณ์ add๋ ํ๊ฐ ์ ํจ์๋ฅผ ๋ฐํํ๊ฒ ๋๋ค. ์ฆ ํ์๊ฐ ์์ค์ฝ๋๋ฅผ ์ฝ์ด๋ด๋ ค๊ฐ๋ฉด์ add ๋ผ๋ ์๋ณ์๋ ์ง๋๊ฐ๋ฒ๋ฆฌ๊ณ add๋ฅผ ํจ์ ๋ฆฌํฐ๋ด๋ก ๋์ฒดํ๊ฒ ๋๋ค. ์๋ฅผ ๋ค์ด, ์ ์์ค์ฝ๋์ add(2, 3)์ ์๋์ฒ๋ผ add๊ฐ ํจ์ ๋ฆฌํฐ๋ด๋ก ๋์ฒด๋๋ค.
# add๊ฐ ํจ์ ๋ฆฌํฐ๋ด๋ก ๋์ฒด
fn(x, y) { x + y; }(2, 3)
๋ํ ํจ์ ๋ฆฌํฐ๋ด์ ํจ์ ํธ์ถ์ ์ธ์๋ก ์ฌ์ฉํ ์๋ ์๋ค.
myFunc(2, 3, fn(x, y) { x + y; });
๊ทธ๋์ ํจ์ ํธ์ถ ํํ์์ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ๋ค.
<EXPRESSION>(<EXPRESSION>, <EXPRESSION>, ... , <EXPRESSION>)
์ด์ ํจ์ ํธ์ถ ํํ์์ AST ๋ ธ๋๋ก ํํํด๋ณด์.
// ast/ast.go
type CallExpression struct {
Token token.Token
Function Expression
Arguments []Expression
}
func (ce *CallExpression) expressionNode() {}
func (ce *CallExpression) TokenLiteral() string { return ce.Token.Literal }
func (ce *CallExpression) String() string {
var out bytes.Buffer
args := []string{}
for _, a := range ce.Arguments {
args = append(args, a.String())
}
out.WriteString(ce.Function.String())
out.WriteString("(")
out.WriteString(strings.Join(args, ", "))
out.WriteString(")")
return out.String()
}
CallExpression ์ด๋ผ๋ ์๋ก์ด ๊ตฌ์กฐ์ฒด๋ 3๊ฐ์ ๋ฉค๋ฒ๋ฅผ ๊ฐ๋๋ค. ์ฒซ ๋ฒ์งธ๋ ํ ํฐ, ๋ ๋ฒ์งธ๋ Function ์ด๋ผ๋ ๋ฉค๋ฒ์ธ๋ฐ, ํํ์์ ์๋ฏธํ๋ Expression ์ธํฐํ์ด์ค ํ์ ์ผ๋ก ์ ์๋์๋ค. ๊ทธ๋ฆฌ๊ณ Arguments๋ ์ฌ๋ฌ ๊ฐ์ ํํ์์ผ๋ก ๊ตฌ์ฑ๋์ด ์๋ ๊ฒ์ ์๋ฏธํด์ ์ฌ๋ผ์ด์ค ํํ์ด Expression ์ธํฐํ์ด์ค ํ์ ์ผ๋ก ์ ์๋์๋ค. ์ด์ ํ ์คํธ ์ฝ๋๋ฅผ ๋ณด์.
// parser/parser_test.go
func TestCallExpressionParsing(t *testing.T) {
input := "add(1, 2 * 3, 4 + 5);"
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("stmt is not ast.ExpressionStatement. got=%T",
program.Statements[0])
}
exp, ok := stmt.Expression.(*ast.CallExpression)
if !ok {
t.Fatalf("stmt.Expression is not ast.CallExpression. got=%T",
stmt.Expression)
}
if !testIdentifier(t, exp.Function, "add") {
return
}
if len(exp.Arguments) != 3 {
t.Fatalf("wrong length of arguments. got=%d", len(exp.Arguments))
}
testLiteralExpression(t, exp.Arguments[0], 1)
testInfixExpression(t, exp.Arguments[1], 2, "*", 3)
testInfixExpression(t, exp.Arguments[2], 4, "+", 5)
}
ํจ์ ํธ์ถ ํํ์์ ํ์ฑํ๊ธฐ ์ํด์๋ ํ์ฑ ํจ์๋ฅผ ์ถ๊ฐํ๋ ์๋ก์ด ํ ํฐ์ ์ถ๊ฐํ์ง๋ ์๋๋ค. ์๋ํ๋ฉด ๊ธฐ์กด์ ์๋ ํ ํฐ๋ค์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ด๋ค. ์๋ฅผ ๋ค์ด, add(2, 3) ๊ณผ ๊ฐ์ ํจ์ ํธ์ถ ํํ์์ ํ์ฑํ๋ค๊ณ ํด๋ณด์. add๋ ์๋ณ์์ด๊ธฐ ๋๋ฌธ์ ๊ธฐ์กด์ ์๋ณ์์ ๋ํ ํ ํฐ์ ์ ์ํ์๋ค. ๊ทธ๋ฆฌ๊ณ ๋ฐ๋ก ๋ค์์ ๋ฑ์ฅํ๋ ์ผ์ชฝ ์๊ดํธ๋ ๊ธฐ์กด์ ์ฌ์ฉํ๋ ํ ํฐ์ด๋ค. ๋ค๋ง, ์ด ์ผ์ชฝ ์๊ดํธ๋ฅผ ๊ธฐ์ค์ผ๋ก ์ค์ ํํ์ ํ์ฑ ํจ์๋ฅผ ์ถ๊ฐํด์ผ ํ๋ค.
๋ค์ ํ๊ณ ํด๋ณด์. ์ฐ๋ฆฌ๋ ์ผ์ชฝ ์๊ดํธ ์ผ๋ช LPAREN ์ด๋ผ๋ ํ ํฐ์ ๋ํด์ ์ถ๊ฐํ๋ ํ์ฑ ํจ์๊ฐ ์๋ค. ๋ฐ๋ก ์ด ํฌ์คํ ์ 3๋ฒ ๋ชฉ์ฐจ์์ ์ดํด๋ณธ ๊ทธ๋ฃน ํํ์์์๋ค. ํ์ง๋ง ์ด๋ฒ์ ํจ์ ํธ์ถ ํํ์์ ํ์ฑํ๊ธฐ ์ํด์๋ LPAREN ์ด๋ผ๋ ํ ํฐ์ ๋ํด์ ์ค์ ํํ์์ ํ์ฑํ๋ ํจ์๋ฅผ ์ถ๊ฐํด์ผ ํ๋ค. ๋ฐ๋ผ์ ์๋์ ๊ฐ์ด ํ์๋ฅผ ์ด๊ธฐํํ ๋, LPAREN ํ ํฐ์ ๋ํ ์ค์ ํํ์ ํ์ฑ ํจ์๋ฅผ ์ถ๊ฐํด๋ณด์.
// parser/parser.go
func New(l *lexer.Lexer) *Parser {
p := &Parser{
l: l,
errors: []string{},
}
p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
(... ์๋ต ...)
p.registerPrefix(token.LPAREN, p.parseGroupedExpression)
(... ์๋ต ...)
p.infixParseFns = make(map[token.TokenType]infixParseFn)
(... ์๋ต ...)
p.registerInfix(token.LPAREN, p.parseCallExpression)
return p
}
์๋ก์ด ๋ฉ์๋์ธ parseCallExpression์ ์ดํด๋ณด์.
// parser/parser.go
func (p *Parser) parseCallExpression(function ast.Expression) ast.Expression {
exp := &ast.CallExpression{Token: p.curToken, Function: function}
exp.Arguments = p.parseCallArguments()
return exp
}
ํน์ดํ๊ฒ Expression ์ธํฐํ์ด์ค ์ฆ, ํํ์์ธ ํจ์๋ฅผ ์ธ์๋ก ๋๊ฒจ์ฃผ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ด๋ ์๊น ๋งํ๋ add ๋ผ๋ ์๋ณ์๊ฐ ๋ฏธ๋ฆฌ ํ์ฑ๋ ๊ฒ์ ์๋ฏธํ๋ค. ๊ทธ๋ฆฌ๊ณ parseCallArguments ๋ฉ์๋๋ฅผ ํธ์ถํด์ ํจ์ ํธ์ถ์ ์ธ์์ ๋ช ์๋์ด ์๋ 0๊ฐ ์ด์์ ํํ์๋ค์ ํ์ฑํ๋ค. ์ด์ parseCallArguments ๋ฉ์๋์ ์๊น์๋ฅผ ๋ณด์.
// parser/parser.go
func (p *Parser) parseCallArguments() []ast.Expression {
args := []ast.Expression{}
if p.peekTokenIs(token.RPAREN) {
p.nextToken()
return args
}
p.nextToken()
args = append(args, p.parseExpression(LOWEST))
for p.peekTokenIs(token.COMMA) {
p.nextToken()
p.nextToken()
args = append(args, p.parseExpression(LOWEST))
}
if !p.expectPeek(token.RPAREN) {
return nil
}
return args
}
ํ์ฌ curToken์ ์ผ์ชฝ ์๊ดํธ์ธ ์ํ์ด๋ค. ๋ค์ ํ ํฐ์ธ peekToken์ด ์ค๋ฅธ์ชฝ ์๊ดํธ๊ฐ ์๋ ๊ฒฝ์ฐ ๋ก์ง์ ๊ณ์ ํ๋๋ก ํ๋๋ฐ, ๋จผ์ nextToken์ ํธ์ถํด์ ํ ํฐ์ ์์น๋ฅผ ํ ์นธ์ฉ ์ฎ๊ฒจ์ค๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ๋ค ์ฎ๊ฒจ์ง curToken์ ์๋ ํํ์์ ํ์ฑํ๊ธฐ ์ํด parseExpression์ ํธ์ถํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ๋ค ํจ์ ํธ์ถ์ ๋ค์ ์ธ์๋ฅผ ํ์ฑํ๊ธฐ ์ํด ๋ค์ ํ ํฐ์ด ์ปด๋ง๊ฐ ๋ฑ์ฅํ์ง ์์ ๋๊น์ง ๊ณ์์ ์ผ๋ก ํํ์ ํ์ฑ์ ์ํํ๊ณ , args ๋ผ๋ ์ฌ๋ผ์ด์ค ๋ณ์์ ๋ด์๋ธ๋ค.
๊ทธ๋ฆฌ๊ณ for-loop ๊ตฌ๋ฌธ์ ๋น ์ ธ ๋์ ๋ค์ ํ ํฐ์ด ์ค๋ฅธ์ชฝ ์๊ดํธ์ด๋ฉด ์ฆ์ args ์ฌ๋ผ์ด์ค๋ฅผ ๋ฐํํ๋ค.
๊ทธ๋ฐ๋ฐ ์ด๋ ๊ฒ๋ง ํด์ ํ ์คํธ ์ฝ๋๋ฅผ ์ํํ๋ฉด ํต๊ณผํ์ง ์๋๋ค. ์๋ํ๋ฉด ํ์ฌ ํจ์ ํธ์ถ ํํ์์ ์ผ์ชฝ ์๊ดํธ ์ค์ ์ฐ์ฐ์์ ๋ํ ์ฐ์ฐ์ ์ฐ์ ์์๊ฐ ์ค์ ๋์ง ์์๊ธฐ ๋๋ฌธ์ด๋ค. ํจ์ ํธ์ถ ํํ์์ ์ผ์ชฝ ์๊ดํธ ์ค์ ์ฐ์ฐ์์ ์ฐ์ ์์๋ ๊ฐ์ฅ ๋๋ค. ๋ฐ๋ผ์ ์๋์ฒ๋ผ ๊ฐ์ฅ ๋์ ์ฐ์ ์์ ์ฐ์ฐ์๋ฅผ ์ง์ ํ ํ ํ ์คํธ ์ฝ๋๋ฅผ ๋ค์ ์คํํ๋ฉด ํ ์คํธ๊ฐ ์ ํต๊ณผํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
// parser/parser.go
var precedences = map[token.TokenType]int{
token.EQ: EQUALS,
token.NOT_EQ: EQUALS,
token.LT: LESSGREATER,
token.GT: LESSGREATER,
token.PLUS: SUM,
token.MINUS: SUM,
token.SLASH: PRODUCT,
token.ASTERISK: PRODUCT,
token.LPAREN: CALL, // ํจ์ ํธ์ถ ํํ์์ ์ค์ ์ฐ์ฐ์์ ๋ํ ์ฐ์ ์์ ์ค์
}
์ด๋ ๊ฒ ํด์ ํ์์ ๋ชจ๋ ๊ธฐ๋ฅ ๊ตฌํ์ด ๋๋ฌ๋ค. ๋ค์ ํฌ์คํ ๋ถํฐ๋ ํ์์ ๋ ์๊ฐ ๋ง๋ AST ์๋ฃ๊ตฌ์กฐ๋ฅผ ํ๊ฐ(Evaluation) ํ๋ ๋ฐฉ๋ฒ์ ๋ํด์ ์์๋ณด๋๋ก ํ์.