Computer Science

[CS] ๋‚˜๋งŒ์˜ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž! (2): Parser ๋งŒ๋“ค๊ธฐ - ๋‘๋ฒˆ์งธ

YounghunJo 2025. 3. 29. 11:00
๋ฐ˜์‘ํ˜•

๐Ÿ”Š ํ•ด๋‹น ํฌ์ŠคํŒ…์€ ๋ฐ‘๋ฐ”๋‹ฅ๋ถ€ํ„ฐ ๋งŒ๋“œ๋Š” ์ธํ„ฐํ”„๋ฆฌํ„ฐ in go ์ฑ…์„ ์ฝ๊ณ  ๊ฐœ์ธ์ ์ธ ์ •๋ฆฌ ๋ชฉ์  ํ•˜์— ์ž‘์„ฑ๋œ ๊ธ€์ž…๋‹ˆ๋‹ค. ๋ณธ ํฌ์ŠคํŒ…์— ์‚ฌ์šฉ๋œ ์ž๋ฃŒ๋Š” ๋ชจ๋‘ ๋ณธ์ธ์ด ์ง์ ‘ ์žฌ๊ตฌ์„ฑํ•˜์—ฌ ์ž‘์„ฑํ•˜์˜€์Œ์„ ์•Œ๋ฆฝ๋‹ˆ๋‹ค.
 

์ถœ์ฒ˜: Yes24


์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ง์ „ ํฌ์ŠคํŒ…๊นŒ์ง€ ํ•ด์„œ ๋งŒ๋“ค์—ˆ๋˜ ์šฐ๋ฆฌ๋งŒ์˜ ํŒŒ์„œ์— ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ํƒ‘์žฌํ•ด๋ณผ ๊ฒƒ์ด๋‹ค. ์ฝ”๋“œ๋ ˆ๋ฒจ๋กœ ์•Œ์•„๋ณด๊ธฐ์— ์•ž์„œ ํ‘œํ˜„์‹ ํŒŒ์‹ฑ์ด๋ผ๋Š” ๊ฒƒ์„ ๊ตฌํ˜„ํ•  ๋•Œ ์•Œ์•„๋‘์–ด์•ผํ•  ์‚ฌ์ „ ๊ฐœ๋… ๋ช‡ ๊ฐ€์ง€์™€ ๊ณ ๋ ค์‚ฌํ•ญ์— ๋Œ€ํ•ด์„œ ์งš๊ณ  ๋„˜์–ด๊ฐ€๋ณด์ž.

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;" ๋ฅผ ๋„ฃ๋Š”๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ์ฝ”๋“œ์˜ ํ๋ฆ„์„ ๋”ฐ๋ผ๊ฐ€๋ณด์ž.

๋ฐ˜์‘ํ˜•