[CS] ๋๋ง์ ์ธํฐํ๋ฆฌํฐ๋ฅผ ๋ง๋ค์ด๋ณด์! (2): Parser ๋ง๋ค๊ธฐ - ๋๋ฒ์งธ
๐ ํด๋น ํฌ์คํ
์ ๋ฐ๋ฐ๋ฅ๋ถํฐ ๋ง๋๋ ์ธํฐํ๋ฆฌํฐ in go ์ฑ
์ ์ฝ๊ณ ๊ฐ์ธ์ ์ธ ์ ๋ฆฌ ๋ชฉ์ ํ์ ์์ฑ๋ ๊ธ์
๋๋ค. ๋ณธ ํฌ์คํ
์ ์ฌ์ฉ๋ ์๋ฃ๋ ๋ชจ๋ ๋ณธ์ธ์ด ์ง์ ์ฌ๊ตฌ์ฑํ์ฌ ์์ฑํ์์์ ์๋ฆฝ๋๋ค.

์ด๋ฒ ํฌ์คํ ์์๋ ์ง์ ํฌ์คํ ๊น์ง ํด์ ๋ง๋ค์๋ ์ฐ๋ฆฌ๋ง์ ํ์์ ํํ์์ ํ์ฑํ ์ ์๋ ๊ธฐ๋ฅ์ ํ์ฌํด๋ณผ ๊ฒ์ด๋ค. ์ฝ๋๋ ๋ฒจ๋ก ์์๋ณด๊ธฐ์ ์์ ํํ์ ํ์ฑ์ด๋ผ๋ ๊ฒ์ ๊ตฌํํ ๋ ์์๋์ด์ผํ ์ฌ์ ๊ฐ๋ ๋ช ๊ฐ์ง์ ๊ณ ๋ ค์ฌํญ์ ๋ํด์ ์ง๊ณ ๋์ด๊ฐ๋ณด์.
1. ํํ์ ํ์ฑ์ ํ๊ธฐ ์ ์..
์ง์ ํฌ์คํ ์์ ๊ตฌํํ๋ let ๋ฌธ, return ๋ฌธ ํ์ฑ์ let ๋๋ return ๋ฌธ ๋ค์์ ์ด๋ค ํ ํฐ๋ค์ด ๋ฑ์ฅํ ์ง ๋ช ํํ๋ค. ํ์ง๋ง ํํ์ ํ์ฑ์ let, return ๋ฌธ ํ์ฑ์ฒ๋ผ ์ฝ๊ฒ ๊ตฌํํ ๋งํผ์ ์๋๋ฉฐ ๊ฝค ๊น๋ค๋ก์ด ์์ ์ด๋ค.
๊ฐ์ฅ ๋จผ์ ํํ์์ ํ์ฑํ ๋ ๊ณ ๋ คํด์ผ ํ ์ฌํญ์ ์ฐ์ฐ์ ์ฐ์ ์์(operator precedence)์ด๋ค. ์ํ์์ ์ฌ์น์ฐ์ฐ์ด๋ผ๋ ์ฃผ์ ๊ฐ ์๋ค. ๋ง์ ๊ณผ ๋บ์ ์ ๊ณฑ์ ๊ณผ ๋๋์ ๊ณผ ๊ฐ์ด ๋ฑ์ฅํ๊ฒ ๋๋ฉด ๊ณฑ์ ๊ณผ ๋๋์ ์ ๋ง์ ๊ณผ ๋บ์ ๋ณด๋ค ๋จผ์ ๊ณ์ฐ๋์ด์ผ ํ๋ค๋ ์ฌ์ค์ ๋๋ถ๋ถ์ ์ฌ๋๋ค์ด ์๊ณ ์์ ๊ฒ์ด๋ค. ์ด๋ฌํ ์์น์ด ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์๋ ๋น์ฐํ ์ ์ฉ๋์ด์ผ ํ๋ค. ๋ฟ๋ง ์๋๋ผ ๋ง์ ๊ณผ ๊ณฑ์ ์ด ๊ฐ์ด ๋ฑ์ฅํ์ง๋ง ๋ง์ ์ฐ์ฐ์ ์๊ดํธ๋ก ๊ฐ์ธ์ ธ ์์ผ๋ฉด ๋ง์ ์ด ๋จผ์ ์ํ๋์ด์ผ ํ๋ค. ์์๋ ๋ค์๊ณผ ๊ฐ๋ค.
(5 + 10) * 2
ํํ์์ ํ์ฑํ ๋ ๊ณ ๋ คํด์ผํ ๋ ๋ค๋ฅธ ์ฌํญ์ ๊ฐ์ ํ์ ์ ํ ํฐ๋ค์ด ์ฌ๋ฌ ์์น์ ๋ํ๋ ์ ์๋ค๋ ์ ์ด๋ค. ์๋ฅผ ๋ค์ด, ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๊ฐ ์๋ค๊ณ ํด๋ณด์.
-5 - 10
5 * (add(2, 3) + 10)
์ฒซ๋ฒ์งธ ์ค์ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋จผ์ ๋ฑ์ฅํ๋ - ๋ผ๋ ๊ธฐํธ๋ ์ ์ ์ฐ์ฐ์๋ฅผ ์๋ฏธํ๋ค. ์ฆ 0์์ -5๋งํผ์ ๋นผ๋ ์ฐ์ฐ์ด๋ค. ํ์ง๋ง ๋ค์ - ๋ผ๋ ๊ธฐํธ๋ ์ค์ ์ฐ์ฐ์๋ก์ -5 ์ 10์ด๋ผ๋ ๋ ๊ฐ์ ์ซ์๋ฅผ ๋บ์ ํ๋ ์ญํ ์ ํ๋ค. ๋ค์์ผ๋ก ๋ ๋ฒ์งธ ์ค ์ฝ๋๋ฅผ ๋ณด์. ๊ฐ์ฅ ๋ฐ๊นฅ์ ์๋ ์๊ดํธ๋ ๊ทธ๋ฃน ํํ์์ผ๋ก์, ์ด ์๊ดํธ ์์ ์๋ ํํ์์ ๋จผ์ ์ํํ๋ผ๋ ์๋ฏธ์ด๋ค. ๋ฐ๋ฉด์ add(2, 3)์ ์๋ ์๊ดํธ๋ add ๋ผ๋ ํจ์์ ํธ์ถ ์ฐ์ฐ์๋ก์ ํธ์ถ ํํ์์ ํด๋นํ๋ค. ์ด๋ ๊ฒ ๊ฐ์ ํ์ ์ ํ ํฐ์ด๋ผ๋ ์ฌ๋ฌ ์์น์ ๋ํ๋๊ฒ ๋ ๋ ๊ฐ๊ธฐ์ ๊ธฐ๋ฅ์ด ๋ฌ๋ผ์ง๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ฐ๋ฆฌ์ ํ์๋ ์ด๋ฐ ์ ๋ ๊ณ ๋ คํ๋ฉด์ ํ์ฑ์ ์ํํด์ผ ํ๋ค.
๊ทธ๋ฌ๋ฉด Monkey ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ฅผ ๋์์ผ๋ก ์ฐ๋ฆฌ๋ง์ ํ์๊ฐ ํ์ฑํ ์ ์๋ ํํ์ ์์๋ ๋ํ์ ์ผ๋ก ๋ค์๊ณผ ๊ฐ๋ค.
# ์ ์ ์ฐ์ฐ์
-5
!true
!false
# ์ค์ ์ฐ์ฐ์
5 + 5
5 - 5
5 / 5
5 * 5
# ๋น๊ต ์ฐ์ฐ์
foo == bar
foo != bar
foo < bar
foo > bar
# ๊ทธ๋ฃน ํํ์
5 * (5 + 5)
((5 + 5) * 5) * 5
# ํธ์ถ ํํ์
add(2, 3)
add(add(2, 3), add(5, 10))
max(5, add(5, (5 * 5)))
# ์๋ณ์ ํํ์
foo * bar / foobar
add(foo, bar)
# ํจ์ ๋ฆฌํฐ๋ด ํํ์
fn(x, y) {return x + y}(5, 5)
(fn(x) { return x }(5) + 10 ) * 10
# if ํํ์
let result = if (10 > 5) { true } else { false };
์ฐ๋ฆฌ๋ ์์ ๊ฐ์ ์ข ๋ฅ๋ค์ ํํ์์ ํ์ฑํ ์ ์๋๋ก ํ๊ธฐ ์ํด์ ํ์์ ์ฐ์ฐ์ ์ฐ์ ์์ ํ์ฑ ๋ฐฉ๋ฒ์ธ ํ๋ซ ํ์ฑ์ ์ฌ์ฉํด๋ณผ ๊ฒ์ด๋ค. ํ๋ซ ํ์ฑ์ ํต์ฌ์ ์ธ ๊ตฌ์์ "ํ ํฐ์ ํ์ฑํจ์์ ์ฐ๊ด์ํฌ ๋, ํด๋น ํ ํฐ์ด ์ค์์ธ์ง ์ ์์ธ์ง์ ๋ฐ๋ผ ์๋ก ๋ค๋ฅธ ํ์ฑ ํจ์๋ก ์ฐ๊ด์ํค๋ ๊ฒ"์ด๋ค.
2. AST ๋ ธ๋์ String ๋ฉ์๋ ์ถ๊ฐํ๊ธฐ
๋ณธ๊ฒฉ์ ์ธ ํ๋ซ ํ์๋ฅผ ๊ตฌํํ๊ธฐ ์ ์ ๋๋ฒ๊น ์ ์ฝ๊ฒ ํ ์ ์๋๋ก ๊ตฌํํ AST ๋ ธ๋ ์ฆ, Node ๋ผ๋ ์ธํฐํ์ด์ค์ String() ๋ฉ์๋๋ฅผ ์ถฉ์กฑ์ํฌ ์ ์๋๋ก ๊ตฌ์ฑํ์. ์ด String() ๋ฉ์๋๋ AST๋ฅผ ๊ตฌ์ฑํ๋ ๋ ธ๋์ ๋ด์ฉ๋ฌผ์ ์ถ๋ ฅํจ์ผ๋ก์จ ๋ค๋ฅธ ๋ ธ๋์ ๋น๊ต๋ ํ๊ณ , ์ฐ๋ฆฌ๊ฐ ์๋ํ์ฌ ๋ ธ๋๋ฅผ ์ ๊ตฌ์ฑํ๋์ง๋ ์ดํด๋ณผ ์ ์๋ค. Node ๋ผ๋ ์ธํฐํ์ด์ค๋ ๊ธฐ์กด์ ๊ตฌํํ๋ let ๋ฌธ, return ๋ฌธ ๋ ธ๋์ ์ธํฐํ์ด์ค ๋ฟ๋ง ์๋๋ผ ์๋ณ์ ๋ ธ๋์ธ Identifier, ๊ทธ๋ฆฌ๊ณ ์ด๋ฒ์ ์ถ๊ฐ๋ก ๊ตฌํํ ํํ์ ๋ ธ๋์๋ ์ถ๊ฐํด์ฃผ๋๋ก ํ์.
// ast/ast.go
type Node interface {
TokenLiteral() string
String() string
}
(..์๋ต..)
func (p *Program) String() string {
var out bytes.Buffer
for _, s := range p.Statements {
out.WriteString(s.String())
}
return out.String()
}
func (i *Identifier) String() string { return i.Value }
func (ls *LetStatement) String() string {
var out bytes.Buffer
out.WriteString(ls.TokenLiteral() + " ")
out.WriteString(ls.Name.String())
out.WriteString(" = ")
if ls.Value != nil {
out.WriteString(ls.Value.String())
}
out.WriteString(";")
return out.String()
}
func (rs *ReturnStatement) String() string {
var out bytes.Buffer
out.WriteString(rs.TokenLiteral() + " ")
if rs.ReturnValue != nil {
out.WriteString(rs.ReturnValue.String())
}
out.WriteString(";")
return out.String()
}
func (es *ExpressionStatement) String() string {
if es.Expression != nil {
return es.Expression.String()
}
return ""
}
3. ํ๋ซ ํ์ ๊ตฌํํ๊ธฐ
์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ํ๋ซ ํ์๋ฅผ ๊ตฌํํด๋ณด์. ์์ ์ธ๊ธํ์ง๋ง ํ๋ซ ํ์์ ํต์ฌ์ "ํ ํฐ ํ์ ๊ณผ ํ์ฑ ํจ์๋ฅผ ์ฐ๊ด ์ง๋ ๊ฒ"์ด๋ค. ์ฐ๋ฆฌ๊ฐ ๊ฐ์ฅ ๋จผ์ ํ ์ผ์ ์ ์(prefix) ํ์ฑ ํจ์์ ์ค์(infix) ํ์ฑ ํจ์๋ฅผ ๊ทธ์ ๋ง๋ ํ ํฐ๊ณผ ์ฐ๊ด ์ง๋ ์ผ์ด๋ค.
๋จผ์ ์ฐ๋ฆฌ๋ ์ ์ ๋๋ ์ค์ ํ์ฑ๊ณผ ์ฐ๊ด๋ ํ ํฐ์ด ํธ์ถํ ๋์์ธ ์ ์, ์ค์ ํ์ฑ ํจ์ ํ์ ์ ์ ์ํด๋ณด์.
// parser/parser.go
type (
prefixParseFn func() ast.Expression
infixParseFn func(ast.Expression) ast.Expression
)
prefixParseFn ์ด๋ผ๋ ํ์ ์ ํจ์๋ ์ด๋ฆ์์ ์ ์ถํ ์ ์๋ค์ํผ ์ ์ ํ์ฑ์ ์ํํ๋ ํจ์์ ํด๋นํ๋ค. ์ด๋ ์๋ฌด๊ฒ๋ ์ธ์๋ก ๋ฐ์ง ์๋, ํํ์ ํ์ ์ ๋ฐํํ๋ค. ๋ฐ๋ฉด์ infixParseFn ํ์ ์ ํจ์๋ ์ค์ ํ์ฑ์ ์ํํ๋ฉฐ, ํํ์ 1๊ฐ๋ฅผ ์ธ์๋ก ์ ๋ฌ ๋ฐ๋๋ค. ์ด ์ธ์๋ก ์ ๋ฌ ๋ฐ๋ ํํ์์ ์ค์ ์ฐ์ฐ์์ ์ข์ธก์ ์๋ ํํ์์ ์๋ฏธํ๋ค. ์๋ฅผ ๋ค์ด, ์๋์ฒ๋ผ ์ค์ ์ฐ์ฐ์๊ฐ ์ฌ์ฉ๋๋ ์์ค์ฝ๋๊ฐ ์๋ค๊ณ ํด๋ณด์.
2 * 5
๊ทธ๋ฌ๋ฉด infixParseFn ํ์ ํจ์์ ์ธ์์๋ ์ ์์ค์ฝ๋ ๊ธฐ์ค์ผ๋ก 2 ๋ผ๋ ํํ์์ด ์ธ์๋ก ๋ค์ด๊ฐ๋ ์ ์ด๋ค.
์ด๋ ๊ฒ ์ ์, ์ค์ ํ์ฑ ํจ์ ํ์ ์ ์ ์ํ์ผ๋ ์์ผ๋ก ์ ์ ์ฐ์ฐ์ ๋๋ ์ค์ ์ฐ์ฐ์์ ๊ด๋ จ๋ ํ ํฐ์ ๋ง๋๋ฉด ๊ทธ์ ๋ง๋ ํ์ฑ ํจ์๋ฅผ ํธ์ถํ ์ ์๋๋ก ํด์ฃผ์. ์ฐ๋ฆฌ๋ ์ด๋ฅผ ์ํด Parser ๊ตฌ์กฐ์ฒด์ ๋ค์๊ณผ ๊ฐ์ map ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ถ๊ฐํ์.
// parser/parser.go
type Parser struct {
l *lexer.Lexer
errors []string
curToken token.Token
peekToken token.Token
prefixParseFns map[token.TokenType]prefixParseFn
infixParseFns map[token.TokenType]infixParseFn
}
๊ทธ๋ฆฌ๊ณ ์ด map ์๋ฃ๊ตฌ์กฐ์ ํน์ ํ ํฐ๊ณผ ํน์ ํ์ฑ ํจ์๊ฐ ์ ์ฅ๋ ์ ์๊ฒ ํด์ฃผ๋ ํจ์๋ ๊ฐ์ด ์ ์ํด๋ณด์.
// parser/parser.go
func (p *Parser) registerPrefix(tokenType token.TokenType, fn prefixParseFn) {
p.prefixParseFns[tokenType] = fn
}
func (p *Parser) registerInfix(tokenType token.TokenType, fn infixParseFn) {
p.infixParseFns[tokenType] = fn
}
3-1. ์๋ณ์ ํํ์ ํ์ฑํ๊ธฐ
๊ฐ์ฅ ๋จผ์ ๊ตฌํํด๋ณผ ๊ธฐ๋ฅ์ ์๋ณ์ ํํ์์ ํ์ฑํ๋ ๊ฒ์ด๋ค. ๋ค์ ํ๋ฒ ์๋ณ์ ํํ์์ด๋ ์ด๋ค ์์ค์ฝ๋๋ฅผ ์๋ฏธํ๋์ง ๋ฆฌ๋ง์ธ๋ ํด๋ณด์.
foobar;
add(foobar, barfoo);
foobar + barfoo;
if (foobar) {
(...์๋ต...)
}
์๋ณ์ ํํ์์ ์ ํ์ฑํ๋์ง ํ ์คํธํ๋ ์ฝ๋๋ถํฐ ์ดํด๋ณด์.
// parser/parser_test.go
func TestIdentifierExpression(t *testing.T) {
input := "foobar;"
l := lexer.New(input)
p := New(l)
// run lexing and parsing
program := p.ParseProgram()
if len(program.Statements) != 1 {
t.Fatalf("program.Statements does not contain 1 statements. got=%d", len(program.Statements))
}
// type assertion: downcast from interface to struct that satisfies `Statement` interface
stmt, ok := program.Statements[0].(*ast.ExpressionStatement) // `[0]`์ ์ ์ผํ ๋ช
๋ น๋ฌธ์ด ๋ด๊ฒจ์๋์ง ํ์ธํ๊ธฐ ์ํจ
if !ok {
t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", stmt)
}
ident, ok := stmt.Expression.(*ast.Identifier)
if !ok {
t.Fatalf("stmt.Expression is not ast.Identifier. got=%T", ident)
}
if ident.Value != "foobar" {
t.Errorf("ident.Value not %s. got=%s", "foobar", ident.Value)
}
if ident.TokenLiteral() != "foobar" {
t.Errorf("ident.TokenLiteral() not %s. got=%s", "foobar", ident.TokenLiteral())
}
}
ํ ์คํธ ์ฝ๋๋ ์ด์ ๊ณผ ์์ฑํ ๊ฒ๊ณผ ํฌ๊ฒ ๋ค๋ฅด์ง ์๋ค. ํ ์คํธ ์ฝ๋์ ํด์์ ๋ ์์๊ฒ ๋งก๊ธฐ๋ ค๊ณ ํ๋ค. ์ฒ์ฒํ ์ฝ์ด๋ณด๋ฉด์ ๊ทธ๋์ ๋ฐฐ์ด ๋ด์ฉ์ ๋ณต์ตํ๋ ๊ฒ๋ ์ข๋ค. ํ์ฌ ์ ํ ์คํธ ์ฝ๋๋ฅผ ์คํํ๋ฉด ๋น์ฐํ ์คํจํ๋ค. ์๋ํ๋ฉด ์ฐ๋ฆฌ๋ ์์ง ์๋ณ์ ํํ์์ ์ค์ง์ ์ผ๋ก ํ์ฑํ๋ ๋ก์ง์ ์์ฑํ์ง ์์๊ธฐ ๋๋ฌธ์ด๋ค. ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์์ฑํด๋ณด์.
๊ฐ์ฅ ๋จผ์ ํน์ ํ ํฐ์ ๋ฐ๋ผ switch-case ๊ตฌ๋ฌธ์ผ๋ก ๋ถ๊ธฐํด์ ํน์ ๊ตฌ๋ฌธ์ ํ์ฑํ๋๋ก ํ๋ ํจ์์ ์๋ณ์ ํํ์ ๋ถ๊ธฐ๋ฅผ ์ถ๊ฐํด๋ณด๋๋ก ํ์.
// 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() // ์๋ก ์ถ๊ฐ๋ ํจ์
}
}
์ ์์ค์ฝ๋๋ฅผ ๋ณด๋ฉด ParseExpressionStatment ๋ผ๋ ์๋ก์ด ํจ์๊ฐ ์ถ๊ฐ๋์๋ค. ์ด ํจ์์ ์ญํ ์ ๋ ์ดํด๋ณด์.
// 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
}
์ ํจ์๋ ExpressionStatment ๋ผ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ์ด๊ธฐํํ๊ณ , ํด๋น ๊ตฌ์กฐ์ฒด์ Token ๋ฉค๋ฒ์ ํผ์๊ฐ ๋ฐ๋ผ๋ณด๊ณ ์๋ ํ์ฌ ํ ํฐ ์ฆ, IDENT ์ด๋ผ๋ ํ ํฐ์ ๋ฃ์ด์ค๋ค. ๊ทธ๋ฆฌ๊ณ ํด๋น ๊ตฌ์กฐ์ฒด์ Expression ๋ฉค๋ฒ์ ํํ์์ ์ค์ง์ ์ผ๋ก ํ์ฑํ๋ ์ญํ ์ ํ๋ parseExpression ํจ์๋ฅผ ํธ์ถํ๋ค. ์ด ๋, LOWEST ๋ผ๋ ์ธ์๋ฅผ ๋ฃ์ด์ฃผ๋๋ฐ, ์ด LOWEST๋ ๋ฐ๋ก ๋ค์์ผ๋ก ์ดํด๋ณผ ์ฐ์ฐ์ ์ฐ์ ์์ ์ค ๊ฐ์ฅ ๋ฎ์ ์ฐ์ฐ์์๋ฅผ ์๋ฏธํ๋ค.
์ฐ์ฐ์ ์ฐ์ ์์๋ฅผ ์ ์ํด๋ณด์.
// parser/parser.go
const (
_ int = iota
LOWEST
EQUALS // ==
LESSGREATER // < >
SUM // +
PRODUCT // *
PREFIX // !
CALL // ํธ์ถ ์ฐ์ฐ์๋ก์์ ์๊ดํธ(ex. add())
)
Go ์ธ์ด์๋ iota๋ผ๋ Enumerator๊ฐ ์กด์ฌํ๋ค. ์ด iota๋ฅผ ์ฌ์ฉํ๋ฉด ์์ ๊ฒฝ์ฐ 0์์๋ถํฐ ์์ํด์ 1์ฉ ์ฆ๊ฐํ๋ ์์ ์ ์๋ฅผ ๋ถ์ฌํ ์ ์๊ฒ ๋๋ค. ๊ฒฐ๊ตญ, LOWEST๋ 1์ด๊ณ ์๋๋ก ๋ด๋ ค๊ฐ์๋ก 1์ฉ ์ฆ๊ฐํ์ฌ CALL์ 7์ด ๋ถ์ฌ๋๋ค. ์ด๋ ์ฐ์ฐ์ ์ฐ์ ์์๋ฅผ ์๋ฏธํ๋ฉฐ ๊ฐ์ด ๋์์๋ก ์ฐ์ฐ์ ์ฐ์ ์์๊ฐ ๋๋ค. ๊ณ ๋ก, ์ง์ ์์ค์ฝ๋์์ parseExpression ํจ์์ ์ธ์์ LOWEST๋ฅผ ๋๊ฒจ์ค ๊ฒ์ ๊ฐ์ฅ ๋ฎ์ ์ฐ์ฐ์ ์ฐ์ ์์๋ฅผ ๋๊ฒจ์ค ๊ฒ์ธ ์ ์ด๋ค.
์ด์ parseExpression ํจ์์ ๋ก์ง์ ์ดํด๋ณด์.
// parser/parser.go
func (p *Parser) parseExpression(precedence int) ast.Expression {
prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
return nil
}
leftExp := prefix() // call `prefixParseFn`
return leftExp
}
์๊น ์ ์ํ๋ ํ ํฐ๊ณผ ์ ์ ํ์ฑํจ์๋ก ๊ตฌ์ฑ๋ map ์๋ฃ๊ตฌ์กฐ์์ ํ์ฌ ํ ํฐ ํ์ ์ ๋ง๋ ์ ์ ํ์ฑ ํจ์๋ฅผ ๊ฐ์ ธ์์ ํด๋น ํจ์๋ฅผ ํธ์ถํ๋ค. ์ด ํธ์ถํ๋ ์ ์ ํ์ฑ ํจ์์ ์๊น์๋ฅผ ์์ง์ ์ดํด๋ณด์ง๋ ์์์ง๋ง leftExp ๋ณ์์๋ ๋ถ๋ช ํ์ฑ๋ ํํ์ ๋ ธ๋๊ฐ ์กด์ฌํ ๊ฒ์ด๋ค.
๋ฐฉ๊ธ ํธ์ถํ ์ ์ ํ์ฑ ํจ์์ ์๊น์๋ฅผ ์ดํด๋ณด๊ธฐ ์ํด์ map ์๋ฃ๊ตฌ์กฐ์ ํ ํฐ๊ณผ ๊ทธ์ ๋ง๋ ์ ์ ํ์ฑ ํจ์๋ฅผ ๋ฑ๋กํด๋ณด์.
// 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)
return p
}
// ast.Expression ์ด๋ผ๋ ์ธํฐํ์ด์ค๋ฅผ ast.Identifier ๊ตฌ์กฐ์ฒด๊ฐ ์ถฉ์กฑํ๊ธฐ ๋๋ฌธ์ ์ด์ฒ๋ผ ํํ ๊ฐ๋ฅ
func (p *Parser) parseIdentifier() ast.Expression {
return &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
}
๋ ์๋ฅผ ์ธ์๋ก ๋ฃ์ด ํ์๋ฅผ ๋ฐํํ๋ New ํจ์์์ map ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ด๊ธฐํํ๊ณ , ์ ์ ํ์ฑ ํจ์์ ๊ทธ์ ๋ง๋ IDENT(์๋ณ์) ํ ํฐ์ ์ ์ฅํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ๋ ์ ์ ํ์ฑ ํจ์์ธ parseIdentifer ํจ์๋ ๋จ์ง Identifer ๊ตฌ์กฐ์ฒด์ ํฌ์ธํฐ ๋ณ์๋ฅผ ๋ฐํํ๋ค.
๊ฐ์ธ์ ์ผ๋ก go-lang์ ๋ฌธ๋ฒ์ ์ผ๋ก ๊ถ๊ธํ๋ ์ ์ Identifier ๊ตฌ์กฐ์ฒด ํฌ์ธํฐ ๋ณ์๋ฅผ ๋ฐํํ๋๋ฐ, ์ ํจ์ ์๊ทธ๋์ฒ์๋ ast.Expression ์ธํฐํ์ด์ค๋ฅผ ๋ช ์ํ๋ ๊ถ๊ธํ๋ค. ๊ฒฐ๊ตญ์๋ ๋ฐํํ๋ ๊ฒฐ๊ณผ๋ฌผ์ ๊ตฌ์ฒด์ ์ธ ํ์ ์ ์ข ์์ํค์ง ์๊ณ ์ถ์ํ๋ ๋ฐฉ์ ์ฆ, ํธ์ถ์ ์ ์ฅ์์ parseIdentifer ํจ์ ๋ง๊ณ ๋ ๋ค๋ฅธ ์ข ๋ฅ์ ํ์ฑ ํจ์๋ฅผ ํธ์ถํ ๋ ๋์ผํ ์ธํฐํ์ด์ค๋ก ํธ์ถ์ด ๊ฐ๋ฅํ๋๋ก ๋ง๋ค์ด ์ฃผ๊ธฐ ์ํจ์ด์๋ค. ์์ง go-lang์ ์ต์ํ์ง ์์ ์ด์ํ์ง๋ง ์ ์ํด ๋๊ฐ๋ ์๊ฐ์ด ํ์ํ ๋ฏ ํ๋ค.
์, ์ด์ ์๋ณ์ ํํ์์ ํ์ฑํ ์ ์๋ ๊ธฐ๋ณธ์ ์ธ ์ค๋น๊ฐ ๋์๋ค. ์๊น ์์์ ์ดํด๋ณธ ํ ์คํธ ์ฝ๋๋ฅผ ์๋์ฒ๋ผ ์คํํด๋ณด์. ํ ์คํธ ๊ฒฐ๊ณผ๊ฐ ํต๊ณผํ๋ฉด ์ ๋ฐ๋ผ์จ ๊ฒ์ด๋ค.
go test ./parser
3-2. ์ ์ ๋ฆฌํฐ๋ด ํํ์ ํ์ฑํ๊ธฐ
๋ค์์ผ๋ก ํ์๊ฐ ํ์ฑํ ํํ์์ ์ ์ ๋ฆฌํฐ๋ด ํํ์์ด๋ค. ์ ์ ๋ฆฌํฐ๋ด์ ๊ฐ ์์ฒด๋ฅผ ์์ฑํ๊ธฐ ๋๋ฌธ์ ํํ์์ ํด๋นํ๋ค. ์์๋ ๋ค์๊ณผ ๊ฐ๋ค.
5;
์์ ๊ฒฝ์ฐ๋ ์ ์ ๋ฆฌํฐ๋ด๋ง ์กด์ฌํ์ง๋ง, ์๋์ฒ๋ผ ์ ์ ๋ฆฌํฐ๋ด์ด ๋ค๋ฅธ ํํ์๊ณผ ์์ฌ ์กด์ฌํ ์๋ ์๋ค.
let x = 5; # `5`๊ฐ ์ ์ ๋ฆฌํฐ๋ด
add(5, 10); # `5` ์ `10`์ด ์ ์ ๋ฆฌํฐ๋ด
5 + 5 + 5; # `5`๋ผ๋ ์ซ์ ๊ฐ๊ฐ์ด ์ ์ ๋ฆฌํฐ๋ด
์ด๋ฒ์๋ ํ ์คํธ ์ฝ๋๋ฅผ ๋จผ์ ์ดํด๋ณด์. ์ด ํ ์คํธ ์ฝ๋๋ ์์ผ๋ก ์ฐ๋ฆฌ๊ฐ ๊ตฌํํ ์ ์ ๋ฆฌํฐ๋ด ํํ์์ ํ์๊ฐ ์ ํ์ฑํ๋์ง ์ ๊ฒํ๊ธฐ ์ํ ์ฝ๋์ด๋ค.
// parser/parser_test.go
func TestIntegerLiteralExpression(t *testing.T) {
input := "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 1 statements. got=%d", program.Statements)
}
// type assertion: downcast from interface to struct(`ExpressionStatement`) that satisfies `Statement` interface
stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
if !ok {
t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0])
}
// type assertion: downcast from interface(`Expression`) to struct(`IntegerLiteral`) that satisfies `Expression` interface
literal, ok := stmt.Expression.(*ast.IntegerLiteral)
if literal.Value != 5 {
t.Errorf("literal.Value not %d. got=%d", 5, literal.Value)
}
if literal.TokenLiteral() != "5" {
t.Errorf("literal.TokenLiteral not %s. got=%s", "5", literal.TokenLiteral())
}
}
์๋ณ์ ํํ์์ ํ ์คํธํ๋ ์ฝ๋์ ๊ฑฐ์ ์ ์ฌํ๋ ์ฝ๊ฐ ๋ค๋ฅธ์ ์ด ์๋ค. ๋ฐ๋ก ์ ์ ๋ฆฌํฐ๋ด ํํ์์ ๋ํ๋ด๋ ๊ตฌ์กฐ์ฒด๋ก type assertion ํ๋ ๋ถ๋ถ์ด๋ค. ์์ง์ IntegerLiteral ์ด๋ผ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ์ฐ๋ฆฌ๊ฐ ์ ์ํ์ง๋ ์์๋ค. ๋ฐ๋ก ๋ค์์ ์ ์ํ ๊ฒ์ด๋ค. ์ด์จ๊ฑด IntegerLiteral ์ด๋ผ๋ ๊ตฌ์กฐ์ฒด๋ก ๋ค์ด์บ์คํ ์ ์ํํ ํ, ํด๋น ๊ตฌ์กฐ์ฒด์ Value ๋ฉค๋ฒ์ ๋ค์ด์๋ ๊ฐ๊ณผ TokenLiteral() ๋ฉ์๋๋ฅผ ํธ์ถํจ์ผ๋ก์จ ๋ฐํ๋๋ ๋ฌธ์์ด์ ๋น๊ตํ๋ค. ์ด ๋ ์ฃผ๋ชฉํ ์ ์ ์ด์ ๊ณผ ๋ค๋ฅด๊ฒ ์ด๋ฒ์๋ Value ๋ฉค๋ฒ์ ๋ค์ด์๋ ๊ฐ์ด ๋ฌธ์๊ฐ ์๋ ์ค์ ์ซ์์ธ์ง๋ฅผ ๋น๊ตํ๋ค๋ ์ ์ด๋ค.
์ด์ IntegerLiteral ๊ตฌ์กฐ์ฒด๋ฅผ ์๋์ฒ๋ผ ์ ์ํด๋ณด์.
// ast/ast.go
type IntegerLiteral struct {
Token token.Token
Value int64
}
func (il *IntegerLiteral) expressionNode() {}
func (il *IntegerLiteral) TokenLiteral() string { return il.Token.Literal }
func (il *IntegerLiteral) String() string { return il.Token.Literal }
IntegerLiteral ๊ตฌ์กฐ์ฒด ์ญ์ 2๊ฐ์ ๋ฉค๋ฒ๋ฅผ ๊ฐ๋๋ฐ, ํน์ดํ ์ ์ Value ๋ฉค๋ฒ์ ํ์ ์ด ๋ฌธ์์ด์ด ์๋ int64๋ก ์ ์๋์๋ค. ์ด์ ์ด Value ๋ฉค๋ฒ์๋ ์ ์ ๋ฆฌํฐ๋ด์ด ํํํ๋ ๋ฌธ์๊ฐ ๊ฐ๋ ์ค์ ๊ฐ ์ฆ, ์ ์๊ฐ์ ๋ด์ ๊ฒ์ด๋ค.
์ด์ ์ ์ ๋ฆฌํฐ๋ด์ ๋ํด์ ์ค์ง์ ์ธ ํ์ฑ์ ์ํํ๋ ํจ์๋ฅผ ์ ์ํด์ผ ํ๋ค. ์๋ ์ฝ๋๋ฅผ ๋ณด์.
// parser/parser.go
func (p *Parser) parseIntegerLiteral() ast.Expression {
lit := &ast.IntegerLiteral{Token: p.curToken}
// `base = 0`: ๋ฌธ์์ด ์ ๋์ฌ์ ๋ฐ๋ผ ์๋์ผ๋ก ์ง๋ฒ์ ํ๋จํ์ฌ ์ ์๋ก ๋ณํ
value, err := strconv.ParseInt(p.curToken.Literal, 0, 64)
if err != nil {
msg := fmt.Sprintf("could not parse %q as integer", p.curToken.Literal)
p.errors = append(p.errors, msg)
return nil
}
lit.Value = value
return lit
}
ํต์ฌ ๋ก์ง์ ๋ฌธ์์ด์ ์ ์ํ์ผ๋ก ๋ณํํ๊ธฐ ์ํด strconv.ParseInt ํจ์๋ฅผ ์ฌ์ฉํ๊ณ , ๋ณํ ํ, IntegerLiteral ๊ตฌ์กฐ์ฒด์ Value ๋ฉค๋ฒ์ ๋ณํ๋ ์ ์ ๊ฐ์ ํ ๋นํ๋ค๋ ์ ์ด๋ค.
์ด์ ์ด๋ ๊ฒ ์์ฑํ ์ ์ ๋ฆฌํฐ๋ด์ ํ์ฑํ๋ ํจ์๋ฅผ ํน์ ํ ํฐ๊ณผ ๊ทธ ํ ํฐ์ ๋ง๋ ํ์ฑํจ์๊ฐ ๋ด๊ฒจ์๋ map ์๋ฃ๊ตฌ์กฐ์ ๋ฑ๋กํ๊ธฐ๋ง ํ๋ฉด ๋๋ค. ์๋์ ๊ฐ์ด ๋ง์ด๋ค.
// parser/parser.go
func New(l *lexer.Lexer) *Parser {
(... ์๋ต ...)
p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
p.registerPrefix(token.IDENT, p.parseIdentifier)
p.registerPrefix(token.INT, p.parseIntegerLiteral) // ์๋กญ๊ฒ ํ ํฐ๊ณผ ํ์ฑํจ์๋ฅผ ๋ฑ๋ก!
return p
}
3-3. ์ ์ ์ฐ์ฐ์ ํํ์ ํ์ฑํ๊ธฐ
๋ค์์ผ๋ก ์ดํด๋ณผ ๋ถ๋ถ์ ์ ์ ์ฐ์ฐ์, ๊ทธ๋ฆฌ๊ณ ๊ทธ ์ ์ ์ฐ์ฐ์์ ํจ๊ป ๋ฑ์ฅํ๋ ํํ์์ ํ์ฑํ ์ฐจ๋ก๋ค. ์ ์ ์ฐ์ฐ์๋ ํผ์ฐ์ฐ์ 1๊ฐ๋ฅผ ๊ฐ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ํผ์ฐ์ฐ์๋ฅผ ๊ฐ์ง๊ณ ํํ์์ ํ์ฑํ๋๋ฐ, ์๋์ ๊ฐ์ ๊ตฌ์กฐ๋ก ํํ์์ด ๋ง๋ค์ด์ง๋ค.(์ฐธ๊ณ ๋ก ํด๋น ์ฑ ์์๋ - ๋๋ ! ๋ผ๋ ์ ์ ์ฐ์ฐ์๋ง ํ์ฑํ ์ ์๋๋ก ๋์ด ์๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก + ์ฐ์ฐ์์ ๊ฐ์ ๊ฒ์ ๋ํ ํ์ฑ ๊ธฐ๋ฅ์ด ์ ๊ณต๋์ง๋ ์๋๋ค)
# ๊ตฌ์กฐ
<์ ์ ์ฐ์ฐ์><ํํ์>
# ์์
-5;
!foobar;
!myFunc(2);
-add(5, 5);
์ด๋ฒ์๋ ์ ์ ์ฐ์ฐ์ ํํ์ ํ์ฑ์ด ์ ๋์ํ๋์ง ํ ์คํธํ๋ ํ ์คํธ ์ฝ๋๋ถํฐ ์ดํด๋ณด์.
// parser/parser_test.go
func TestParsingPrefixExpression(t *testing.T) {
prefixTests := []struct {
input string
operator string
integerValue int64
}{
{"!5;", "!", 5},
{"-15;", "-", 15},
}
for _, tt := range prefixTests {
l := lexer.New(tt.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.PrefixExpression)
if !ok {
t.Fatalf("stmt is not ast.PrefixExpression. got=%T", stmt.Expression)
}
if exp.Operator != tt.operator {
t.Fatalf("exp.Operator is not '%s'. got=%s", tt.operator, exp.Operator)
}
if !testIntegerLiteral(t, exp.Right, tt.IntegerValue) {
return
}
}
}
func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
integ, ok := il.(*ast.IntegerLiteral)
if !ok {
t.Errorf("il not *ast.IntegerLiteral. got=%T", il)
return false
}
if integ.Value != value {
t.Errorf("integ.Value not %d. got=%d", value, integ.Value)
return false
}
if integ.TokenLiteral() != fmt.Sprintf("%d", value) {
t.Errorf("integ.TokenLiteral not %d. got=%s", value, integ.TokenLiteral())
return false
}
return true
}
์ ํ ์คํธ ์ฝ๋์์ ์์ง ์ ์ธ์ด ์๋ ๊ฒ๋ค์ด ์๋ ์ํ์ด๋ค. ์๋ฅผ ๋ค์ด, PrefixExpression ๋ผ๋ ๊ตฌ์กฐ์ฒด๋ ์์ง ์ ์๊ฐ ์๋์ด์์ง๋ง, ์ด ๊ตฌ์กฐ์ฒด๋ ์ ์ ์ฐ์ฐ์ ํํ์์ ๋ํ ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ ๊ตฌ์กฐ์ฒด์ด๋ค. ์ด์ ์ด PrefixExpression ์ด๋ผ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ์ ์ํด๋ณด์.
// ast/ast.go
type PrefixExpression struct {
Token token.Token
Operator string
Right Expression
}
func (pe *PrefixExpression) expressionNode() {}
func (pe *PrefixExpression) TokenLiteral() string { return pe.Token.Literal }
func (pe *PrefixExpression) String() string {
var out bytes.Buffer
out.WriteString("(")
out.WriteString(pe.Operator)
out.WriteString(pe.Right.String())
out.WriteString(")")
return out.String()
}
PrefixExpression ๊ตฌ์กฐ์ฒด๋ ํฌ๊ฒ 3๊ฐ์ง ๋ฉค๋ฒ๋ฅผ ๊ฐ์ง๋ค. ์ฒซ๋ฒ์งธ๋ ๋ค๋ฅธ ๊ตฌ์กฐ์ฒด๋ ๋์ผํ๊ฒ ํ ํฐ ๋ฉค๋ฒ๋ฅผ ๊ฐ์ง๋ค. ๊ทธ๋ฆฌ๊ณ Operator๋ ์ฐ์ฐ์๋ฅผ ์๋ฏธํ๋ค. ์ ์ ์ฐ์ฐ์๋ก๋ -, ! ๊ฐ ์๋ค. ์ด ์ฐ์ฐ์ ๋ฌธ์ ์์ฒด๊ฐ ๋ค์ด๊ฐ๋ค. ๊ทธ๋ฆฌ๊ณ Right ๋ผ๋ ๋ฉค๋ฒ๊ฐ ์๋๋ฐ, ์ด๋ ์ ์ ์ฐ์ฐ์์ ์์น๋ฅผ ๊ธฐ์ค์ผ๋ก ์ค๋ฅธ์ชฝ์ ์๋ ํํ์์ ์๋ฏธํํ๋ฉฐ Expression ์ด๋ผ๋ ์ธํฐํ์ด์ค ํ์ ์ผ๋ก ์ ์ธ๋์๋ค. PrefixExpression ๊ตฌ์กฐ์ฒด๋ ์ด์จ๊ฑด AST ๋ ธ๋๋ก ๊ตฌ์ฑ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ๊ตฌ์กฐ์ฒด์ฒ๋ผ ์ธํฐํ์ด์ค๋ฅผ ์ถฉ์กฑ์ํค๊ธฐ ์ํด ์์์ 3๊ฐ์ง ๋ฉ์๋๋ฅผ ์ ์ํ๋ค.
์ฌ๊ธฐ์ ์ ์, 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() // call `prefixParseFn`
return leftExp
}
func (p *Parser) noPrefixParseFnError(t token.TokenType) {
msg := fmt.Sprintf("no prefix parse function for %s found", t)
p.errors = append(p.errors, msg)
}
๊ทธ๋ฌ๋ฉด ์ด์ ์ ์ ์ฐ์ฐ์ ํํ์์ ๋ํ ์ค์ง์ ์ธ ํ์ฑ์ ์ํํ๋ ํจ์๋ฅผ ์์ฑํด๋ณด์. ๊ทธ๋ฆฌ๊ณ ์ด ํ์ฑ ํจ์๋ฅผ ํ ํฐ๊ณผ ๊ทธ ํ ํฐ์ ๋ง๋ ํ์ฑ ํจ์๊ฐ ์ ์ฅ๋์ด ์๋ map ์๋ฃ๊ตฌ์กฐ์ ๋ฑ๋ก์์ผ๋ณด์.
// parser/parser.go
func (p *Parser) parsePrefixExpression() ast.Expression {
expression := &ast.PrefixExpression{
Token: p.curToken,
Operator: p.curToken.Literal,
}
p.nextToken()
expression.Right = p.parseExpression(PREFIX)
return expression
}
func New(l *lexer.Lexer) *Parser {
(... ์๋ต ...)
p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
p.registerPrefix(token.IDENT, p.parseIdentifier)
p.registerPrefix(token.INT, p.parseIntegerLiteral)
p.registerPrefix(token.BANG, p.parsePrefixExpression) // ์ ์ ์ฐ์ฐ์ `!` ์ถ๊ฐ
p.registerPrefix(token.MINUS, p.parsePrefixExpression) // ์ ์ ์ฐ์ฐ์ `-` ์ถ๊ฐ
return p
}
์ ์์ค์ฝ๋์์ ํน์ดํ ์ ์ parsePrefixExpression ํจ์ ๋ด๋ถ์์ nextToken ํจ์๋ฅผ ํ ๋ฒ ํธ์ถํ๋ค๋ ์ ์ด๋ค. ์ ํธ์ถํ ๊น? ์ด์ ๋ถํฐ ๋งํ๋ฉด ์ ์ ์ฐ์ฐ์ ์ค๋ฅธ์ชฝ์ ์๋ ํํ์์ผ๋ก ํ ํฐ ์์น๋ฅผ ์ฎ๊ธฐ๊ธฐ ์ํจ์ด๋ค. ์ฆ, nextToken ํจ์๋ฅผ ํธ์ถํ๊ธฐ ์ ๊น์ง์ curToken์ ์ ์ ์ฐ์ฐ์(! ํน์ -)์ผ ๊ฒ์ด๋ค. ํ์ง๋ง ์ ์ ์ฐ์ฐ์๊ฐ ๋ฑ์ฅํ๋ฉด ๋ฐ๋์ ์ค๋ฅธ์ชฝ์ ํํ์์ด ๋ฑ์ฅํ๊ธฐ ๋ง๋ จ์ด๋ค. ๋ฐ๋ผ์ ์ด ์ค๋ฅธ์ชฝ์ ์๋ ํํ์์ผ๋ก ํ์ฌ ์์น๋ฅผ ์ฎ๊ธฐ๊ธฐ ์ํด nextToken ํจ์๋ฅผ ํ ๋ฒ๋ง ํธ์ถํ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ๋ค์ parseExpression ํจ์๋ก ์ค๋ฅธ์ชฝ์ ํํ์์ ํ์ฑํ๋ค.
๋ค๋ง, ์ฌ๊ธฐ์ parseExpression ํจ์์ ์ธ์๋ก ์ ๋ฌ๋๋ PREFIX ์ฆ, ์ ์ ์ฐ์ฐ์์ ์ฐ์ ์์ ๊ฐ์ด ๊ตฌ์ฒด์ ์ผ๋ก ์ด๋ป๊ฒ ๋์ํ๋์ง๋ ์์ง ์์๋ณด์ง ๋ชปํ๋ค. ์ด๋ ๋ฐ๋ก ๋ค์ ๋ชฉ์ฐจ์ธ ์ค์ ์ฐ์ฐ์์์ ์ดํด๋ณผ ์์ ์ด๋ค.
3-4. ์ค์ ์ฐ์ฐ์ ํํ์ ํ์ฑํ๊ธฐ
๋ง์ง๋ง์ผ๋ก ์ค์ ์ฐ์ฐ์ ํํ์์ ํ์ฑํ๋ ๊ธฐ๋ฅ์ ์ถ๊ฐํด๋ณด์. Monkey ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์ ์ค์ ํํ์์ ์์ ์์ค์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
2 + 2;
2 - 2;
2 * 2;
2 / 2;
2 > 2;
2 < 2;
2 == 2;
2 != 2;
์ ์์ค์ฝ๋์ ํจํด์์ ๋ณด๋ฉด ์๊ฒ ์ง๋ง ์ค์ ์ฐ์ฐ์ ํํ์์ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ฅผ ๊ฐ๋๋ค.
<์ผ์ชฝ ํผ์ฐ์ฐ์> <์ค์ ์ฐ์ฐ์> <์ค๋ฅธ์ชฝ ํผ์ฐ์ฐ์>
์ญ์๋ ์ด๋ฒ์๋ ์ค์ ์ฐ์ฐ์ ํํ์์ ์ ํ์ฑํ๋์ง ํ ์คํธํ๋ ํ ์คํธ ์ฝ๋๋ถํฐ ์ดํด๋ณด์.
// parser/parser_test.go
func TestParsingInfixExpressions(t *testing.T) {
infixTests := []struct {
input string
leftValue int64
operator string
rightValue int64
}{
{"2 + 2;", 2, "+", 2},
{"2 - 2;", 2, "-", 2},
{"2 * 2;", 2, "*", 2},
{"2 / 2;", 2, "/", 2},
{"2 > 2;", 2, ">", 2},
{"2 < 2;", 2, "<", 2},
{"2 == 2;", 2, "==", 2},
{"2 != 2;", 2, "!=", 2},
}
for _, tt := range infixTests {
l := lexer.New(tt.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.InfixExpression)
if !ok {
t.Fatalf("stmt is not ast.InfixExpression. got=%T", stmt.Expression)
}
if !testIntegerLiteral(t, exp.Left, tt.leftValue) {
return
}
if exp.Operator != tt.operator {
t.Fatalf("exp.Operator is not '%s'. got=%s", tt.operator, exp.Operator)
}
if !testIntegerLiteral(t, exp.Right, tt.rightValue) {
return
}
}
}
ํ ์คํธ ์ฝ๋ ์์ฒด๋ ์ ์ ํํ์ ํ ์คํธ ์ฝ๋์ ๊ฑฐ์ ์ ์ฌํ๋ค. ๋ค๋ง, ์ค์ ์ฐ์ฐ์ ํํ์์ ์ฌ์ฉ๋๋ ์ค์ ์ฐ์ฐ์๊ฐ 2๊ฐ์ ํผ์ฐ์ฐ์๋ฅผ ๊ฐ๋ ์ดํญ ์ฐ์ฐ์์ด๋ค ๋ณด๋, ์ฐ์ฐ์๋ฅผ ๊ธฐ์ค์ผ๋ก ์ผ์ชฝ, ์ค๋ฅธ์ชฝ์ ์๋ ๊ฐ ํํ์์ ๊ฒ์ฆํ๋ if ๊ตฌ๋ฌธ๋ง ์ถ๊ฐ๋์์ ๋ฟ์ด๋ค.
์ ํ ์คํธ ์ฝ๋์์ ๋ชป๋ณด๋ ๊ตฌ์กฐ์ฒด๊ฐ ๋ํ๋๊ธฐ ์์ํ๋ค. ๋ฐ๋ก ast.InfixExpression ์ด๋ค. ์ด์ ์ด ๊ตฌ์กฐ์ฒด๋ฅผ ์ ์ํด๋ณด์. ์ด ๊ตฌ์กฐ์ฒด ์ญ์ AST ๋ ธ๋๋ก ํํ๋์ด์ผ ํ๋ฏ๋ก ๋ง์ฐฌ๊ฐ์ง๋ก Expression ์ธํฐํ์ด์ค๋ฅผ ์ถฉ์กฑ์์ผ๋ณด์.
// ast/ast.go
type InfixExpression struct {
Token token.Token
Left Expression
Operator string
Right Expression
}
func (ie *InfixExpression) expressionNode() {}
func (ie *InfixExpression) TokenLiteral() string { return ie.Token.Literal }
func (ie *InfixExpression) String() string {
var out bytes.Buffer
out.WriteString("(")
out.WriteString(ie.Left.String())
out.WriteString(" "+ ie.Operator +" ")
out.WriteString(ie.Right.String())
out.WriteString(")")
return out.String()
}
InfixExpression ๊ตฌ์กฐ์ฒด์ ๋ฉค๋ฒ๋ฅผ ๋ณด๋ฉด ์ ์ ํํ์์ ๋ํ๋ด๋ ๊ตฌ์กฐ์ฒด์ธ PrefixExpression๊ณผ ๋ฌ๋ฆฌ Left ๋ฉค๋ฒ๊ฐ ์ถ๊ฐ๋์์์ ๋ณผ ์ ์๋ค.
์ด์ ์ฐ๋ฆฌ๋ ์ค์ ์ฐ์ฐ์ ํํ์์ด ๋ฑ์ฅํ์ ๋, ํ์ฑํด์ค ๋ก์ง์ ์์ฑํด๋ณด๋๋ก ํ์. ๊ฐ์ฅ ๋จผ์ ์์ฑํ ์ฝ๋๋ ์ค์ ์ฐ์ฐ์๋ฅผ ํ์ฑํ๋ ํ์ฑ ํจ์๋ฅผ ์ ์ํ๊ณ ๋ฑ๋ก์์ผ์ผ ํ๋ค. ๋ค๋ง, ์ด ๋ ์ฃผ์ํ ์ ์ ์ด๋ค ์ฐ์ฐ์ ํ ํฐ์ ๋ง๋ฌ์ ๋ ์ ์ ์ฐ์ฐ์ ํ์ฑ ํจ์๋ฅผ ๊ฐ์ ธ์ฌ ๊ฒ์ธ์ง, ์ค์ ์ฐ์ฐ์ ํ์ฑ ํจ์๋ฅผ ๊ฐ์ ธ์ฌ ๊ฒ์ธ์ง ์ ํ๋จํด์ผ ํ๋ค. ๋จ์ ์ธ ์๋ก, - ์ฐ์ฐ์๊ฐ ๋ฑ์ฅํ์ ๋ ํ๋์ ๋ช ๋ น๋ฌธ ๋ด์ ์๋ก ๋ค๋ฅธ ๊ธฐ๋ฅ์ ํ๋ ํ์ฑ ํจ์๋ฅผ ๊ฐ์ ธ์์ผ ํ ๋๊ฐ ์๋ค.
-5 - 3;
๊ฐ์ฅ ์์ ์๋ - ๋ ๋ถ๋ช ์ ์ ์ฐ์ฐ์์ด๊ณ , ์ด๋ฅผ ํ์ฑํ ๋๋ ์ ์ ์ฐ์ฐ์ ํ์ฑ ํจ์๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ค. ํ์ง๋ง ๊ฐ์ด๋ฐ์ ์๋ - ๋ ์ค์ ์ฐ์ฐ์์ด๊ณ , ์ค์ ์ฐ์ฐ์ ํ์ฑ ํจ์๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ค. ๋ฐ๋ก ์ด๋ฐ ๊ฒฝ์ฐ๋ฅผ ์ ๊ตฌ๋ถํด์ฃผ์ด์ผ ํ๋ค๋ ๊ฒ์ด๋ค. ๋ฐ์์ ์๊ฐํ๊ฒ ์ง๋ง ์ด๋ฌํ ๊ตฌ๋ถ์ ์ฐ์ฐ์ ์ฐ์ ์์๋ฅผ ํ์ฉํ ๊ฒ์ด๋ค. ๋ฐ๋ผ์ ํน์ ์ฐ์ฐ์์ ์ฐ์ ์์๋ฅผ ์ ์ฅํ๋ map ์๋ฃ๊ตฌ์กฐ๋ฅผ ์๋กญ๊ฒ ํ๋ ์ ์ํด๋ณด์.
// 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,
}
์ map ์๋ฃ๊ตฌ์กฐ์ value์ ํด๋นํ๋ ๊ฐ์ [3-1] ๋ชฉ์ฐจ์์ ์์๋ก ์ ์ํ ๊ฐ ์ฐ์ฐ์์ ์ฐ์ ์์ ๊ฐ์ด๋ค. ์ด๋ก์จ ํน์ ํ ํฐ์ ๋ํ ์ฐ์ ์์ ๊ฐ์ ๋งค๊ฒจ๋ ผ ์ ์ด๋ค. ์๋ฅผ ๋ค์ด, == ์ != ํ ํฐ์ ๊ฐ์ ์ฐ์ ์์์ด๊ณ , > ์ < ๊ฐ ๊ฐ์ ์ฐ์ ์์, + ์ -๊ฐ, * ์ /๊ฐ ์๋ก ๊ฐ์ ์ฐ์ ์์์ด๋ค.
๊ทธ๋ฆฌ๊ณ ์ฐ์ฐ์ ์ฐ์ ์์ ๊ด๋ จํ์ฌ ์ ํธ์ฑ ํจ์๋ฅผ 2๊ฐ์ง๋ฅผ ์ ์ํ ๊ฒ์ด๋ค. ์ด๋ ํ์๊ฐ ๋ฐ๋ผ๋ณด๊ณ ์๋ ํ ํฐ, ๊ทธ๋ฆฌ๊ณ ๊ทธ ๋ค์์ ํ ํฐ์ ๋งคํ๋๋ ์ฐ์ฐ์ ์ฐ์ ์์๋ฅผ ๋ฐํํ๋ ํจ์์ด๋ค.
// parser/parser.go
func (p *Parser) peekPrecedence() int {
if p, ok := precedences[p.peekToken.Type]; ok {
return p
}
return LOWEST
}
func (p *Parser) curPrecedence() int {
if p, ok := precedences[p.curToken.Type]; ok {
return p
}
return LOWEST
}
์ ์์ค์ฝ๋์ ๋ก์ง์ ๋งค์ฐ ๊ฐ๋จํ๋ ์ค๋ช ์ ๋ฐ๋ก ํ์ง ์๊ฒ ๋ค. ๊ทธ ๋ค์์ ํ์๋ฅผ ์ด๊ธฐํํ ๋ ๊ฐ ์ค์ ์ฐ์ฐ์ ํ ํฐ์ ๋ง๋ ํ์ฑ ํจ์๋ฅผ ๋ฑ๋กํ์.
// parser/parser.go
func New(l *lexer.Lexer) *Parser {
(... ์๋ต ...)
p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
p.registerPrefix(token.IDENT, p.parseIdentifier)
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
์๋ก์ด ๋ฉ์๋๊ฐ ๋ฑ์ฅํ๋ค. ๋ฐ๋ก 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
}
์ด์ ์ ์์ฑํ parsePrefixExpression ๋ฉ์๋ ์๊น์์ ๊ฑฐ์ ์ ์ฌํ๋ค. ์ฐจ์ด์ ์ precedence ๋ผ๋ ๋ณ์์๋ค๊ฐ ํ์ฌ ํ ํฐ์ ์ฐ์ฐ์ ์ฐ์ ์์๋ฅผ ๋ฐํํ๋ค. ์ฌ๊ธฐ์์๋ nextToken ํจ์๋ฅผ ํธ์ถํด์ ํ์ฌ ์์น๋ฅผ ๋ค์ ํ ํฐ์ผ๋ก ์ฎ๊ธด๋ค. ์ฌ๊ธฐ์๋ ์ด ๋์์ ์ ์ํํ ๊น?
parseInfixExpression ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค๋ ๊ฒ์ ํ์๊ฐ ํ์ฌ ๋ฐ๋ผ๋ณด๋ ํ ํฐ์ด ์ค์ ์ฐ์ฐ์(ex. +, - ..) ํ ํฐ์ด๋ผ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฌ๋ฏ๋ก nextToken ํจ์๋ฅผ ํ ๋ฒ ํธ์ถํ๊ฒ ๋๋ฉด ํ์ฌ ์์น์ ํ ํฐ์ด ์ค์ ์ฐ์ฐ์์ ์ค๋ฅธ์ชฝ์ ์๋ ํํ์์ผ๋ก ์ด๋ํ๊ฒ ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ๋ค parseExpression ํจ์๋ฅผ ํธ์ถํด์ InfixExpression ๊ตฌ์กฐ์ฒด์ Right ๋ฉค๋ฒ์ ํ ๋นํด์ค๋ค.
์์ง ๋๋ ๊ฒ์ ์๋๋ค. ์ค์ ์ฐ์ฐ์ ํํ์ ํ์ฑ ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋จ์ ๋ฐ๋ผ ์๊น ์์์ ์ ์ํ 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()
return leftExp
}
ํ์ฌ๋ ์ ์ ์ฐ์ฐ์ ํํ์๋ง ์ฒ๋ฆฌํ ์ ์๋๋ก ๋์ด ์๋ ์ํ์ด๋ฉฐ precedence ๋ผ๋ ์ธ์ ์ฆ, ์ฐ์ฐ์ ์ฐ์ ์์ ๊ฐ์ ํ์ฉํ์ง๋ ์๋ ์ํ์ด๋ค. ์ค์ ์ฐ์ฐ์ ํํ์๋ ์ฒ๋ฆฌํ ์ ์๋๋ก ๊ฐ์ ํ 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
}
์ฐ์ฐ์ ์ฐ์ ์์๋ฅผ ์ฌ์ฉํด์ for loop๋ฅผ ๋๋๋ฐ, ๊ตฌ์ฒด์ ์ผ๋ก ์ด๋ป๊ฒ ๋์ํ๋์ง๋ ๋ค์ ํฌ์คํ ์์ ํ๋ซ ํ์์ ๋์ ์๋ฆฌ๋ฅผ ์ดํด๋ณด๋ฉด์ ์ดํดํด๋ณด์. ๊ถ๊ธํ๋ค๋ฉด ์ง๊ธ๊น์ง ์์ฑํ ์์ค์ฝ๋์ ๋ํ ์ธํ ์์๋ก "2 == 2;" ๋ฅผ ๋ฃ๋๋ค๊ณ ๊ฐ์ ํ๊ณ ์ฝ๋์ ํ๋ฆ์ ๋ฐ๋ผ๊ฐ๋ณด์.