๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Computer Science

[CS] ๋‚˜๋งŒ์˜ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž! (3): ํ‰๊ฐ€

๋ฐ˜์‘ํ˜•

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

์ถœ์ฒ˜: Yes24


์ €๋ฒˆ ํฌ์ŠคํŒ…๊นŒ์ง€ ํ•ด์„œ ์šฐ๋ฆฌ๋Š” Monkey ๋ผ๋Š” ์šฐ๋ฆฌ๋งŒ์˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋ฅผ ๋ ‰์‹ฑํ•˜๋Š” ๋ ‰์„œ์™€ ํŒŒ์‹ฑํ•˜๋Š” ํŒŒ์„œ 2๊ฐ€์ง€๋ฅผ ๋ชจ๋‘ Golang ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์–ด๋ณด์•˜๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ๋Š” ํŒŒ์„œ์˜ ๊ฒฐ๊ณผ๋ฌผ๋กœ AST ์ž๋ฃŒ๊ตฌ์กฐ๊นŒ์ง€ ๋งŒ๋“œ๋Š” ๊ฒƒ๊นŒ์ง€ ๋ชจ๋‘ ํ•ด๋ณด์•˜๋‹ค. ์ด์ œ๋Š” ๋ ‰์„œ์™€ ํŒŒ์„œ์˜ ์ž…๋ ฅ์œผ๋กœ ๋“ค์–ด๊ฐ„ Monkey ์–ธ์–ด ์†Œ์Šค์ฝ”๋“œ ๋ฌธ์ž์—ด์ด ์‹ค์ œ ์˜๋ฏธ๋ฅผ ๊ฐ–๋„๋ก ํ•ด์ฃผ๋Š” ํ‰๊ฐ€(Evaluation)๋ฅผ ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋ฐฐ์›Œ๋ณผ ๊ฒƒ์ด๋‹ค.

1. ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„: ํ‰๊ฐ€(Evaluation) ํ”„๋กœ์„ธ์Šค

์šฐ๋ฆฌ๊ฐ€ ์ฒ˜์Œ์— ๋ ‰์„œ๋ฅผ ๋งŒ๋“ค ๋•Œ ๋ณด์•˜๋˜ ๊ทธ๋ฆผ์„ ๋‹ค์‹œ ๊ฐ–๊ณ ์™€ ๋ณด์ž.

 

๋ ‰์„œ ํฌ์ŠคํŒ…์—์„œ ๋ณด์•˜๋˜ ๊ทธ๋ฆผ

 

๊ฐ€์žฅ ๋จผ์ € ์†Œ์Šค์ฝ”๋“œ ๋ฌธ์ž์—ด์ด ์ž…๋ ฅ์œผ๋กœ ๋“ค์–ด๊ฐ€๋ฉด ๋ ‰์„œ๊ฐ€ ํ† ํฐํ™”์‹œํ‚จ ๋’ค ํŒŒ์„œ๊ฐ€ ํŒŒ์‹ฑ์„ ์ˆ˜ํ–‰ํ•ด ์ถ”์ƒ๊ตฌ๋ฌธํŠธ๋ฆฌ์ธ AST ์ž๋ฃŒ๊ตฌ์กฐ๋กœ ๋งŒ๋“ ๋‹ค๊ณ  ํ–ˆ๋‹ค. ์ด์ œ ์ด๋ ‡๊ฒŒ ๋งŒ๋“ค์–ด์ง„ AST ์ž๋ฃŒ๊ตฌ์กฐ๋Š” ํ‰๊ฐ€ ๋Œ€์ƒ์ด ๋œ๋‹ค. ์ฆ‰, AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ํ‰๊ฐ€ํ•จ์œผ๋กœ์จ ์ž…๋ ฅ์œผ๋กœ ๋“ค์–ด๊ฐ„ ์†Œ์Šค์ฝ”๋“œ๊ฐ€ ์ง„์งœ ์˜๋ฏธ๋ฅผ ๊ฐ–๊ฒŒ ๋œ๋‹ค.

 

ํ‰๊ฐ€ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค

 

์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ํ‰๊ฐ€ ํ”„๋กœ์„ธ์Šค๋Š” ๋ฒˆ์—ญ๋œ ์–ธ์–ด์˜ ๋™์ž‘ ๋ฐฉ์‹์„ ์ •์˜ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์•„๋ž˜์™€ ๊ฐ™์€ Monkey ์–ธ์–ด ์†Œ์Šค์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•ด๋ณด์ž.

 

# Monkey ์–ธ์–ด์ž„
let num = 10;
if (num) {
  return a;
} else {
  return b;
}

 

์‚ฌ๋žŒ์˜ ๋ˆˆ์œผ๋กœ ์ฝ์œผ๋ฉด ๋‹น์—ฐํžˆ a๊ฐ€ ๋ฐ˜ํ•œ๋  ๊ฒƒ์ž„์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๊ฑด ์šฐ๋ฆฌ ์‚ฌ๋žŒ์˜ ํŒ๋‹จ์ด๋‹ค. ์ปดํ“จํ„ฐ๊ฐ€ ์šฐ๋ฆฌ์™€ ๊ฐ™์€ ํŒ๋‹จ์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์œ„ ์†Œ์Šค์ฝ”๋“œ์— ๋Œ€ํ•œ ํ‰๊ฐ€๊ฐ€ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•˜๋‹ค. ์ฆ‰, ํ‰๊ฐ€ ํ”„๋กœ์„ธ์Šค๊ฐ€ num ๋ณ€์ˆ˜์— ํ• ๋‹น๋˜์–ด ์žˆ๋Š” ๊ฐ’ 10์„ ์ฐธ(true)์œผ๋กœ ํ‰๊ฐ€ํ•˜๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค. ๋˜ ๋‹ค๋ฅธ ์˜ˆ์‹œ๋กœ ์•„๋ž˜์™€ ๊ฐ™์€ ์†Œ์Šค์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•ด๋ณด์ž.

 

# Monkey ์–ธ์–ด์ž„
let one = fn() {
  printLine("one");
  return 1;
}

let two = fn() {
  printLine("two");
  return 2;
}

add(one(), two());

 

์œ„ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ–ˆ์„ ๋•Œ, ์ฝ˜์†”์— one ์ด๋ผ๋Š” ๋ฌธ์ž์—ด์ด ๋จผ์ € ๋“ฑ์žฅํ• ๊นŒ? ์•„๋‹ˆ๋ฉด two ๋ผ๋Š” ๋ฌธ์ž์—ด์ด ๋จผ์ € ๋“ฑ์žฅํ• ๊นŒ? ์ด ์ˆœ์„œ๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์€ ์ด Monkey ์–ธ์–ด์˜ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๊ฐ€ ํ˜ธ์ถœ ํ‘œํ˜„์‹์˜ ์ธ์ˆ˜๋ฅผ ํ‰๊ฐ€ํ•˜๋Š” ์ˆœ์„œ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋˜๋А์ง€์— ๋”ฐ๋ผ ๋‹ฌ๋ ค ์žˆ๋‹ค. ๋งŒ์•ฝ ์˜ค๋ฅธ์ชฝ์— ์žˆ๋Š” ์ธ์ˆ˜๋ฅผ ๋จผ์ € ํ‰๊ฐ€ํ•œ๋‹ค๋ฉด two ๋ผ๋Š” ๋ฌธ์ž์—ด์ด ๋จผ์ € ์ฝ˜์†”์— ๋“ฑ์žฅํ•  ๊ฒƒ์ด๋‹ค.(์‹œํ—˜์‚ผ์•„, Python์—์„œ๋„ ํ•ด๋ณด์•˜๋Š”๋ฐ, CPyhton ๊ตฌํ˜„์ œ ๊ธฐ์ค€์˜ Python ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋Š” ์™ผ์ชฝ์— ์žˆ๋Š” ์ธ์ˆ˜๋ฅผ ๋จผ์ € ํ‰๊ฐ€ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค)

2. ๋‹ค์–‘ํ•œ ํ‰๊ฐ€(Evaluation) ์ „๋žต

ํ‰๊ฐ€ ํ”„๋กœ์„ธ์Šค์— ์‚ฌ์šฉ๋˜๋Š” ํ‰๊ฐ€ ์ „๋žต์€ ์ธํ„ฐํ”„๋ฆฌํ„ฐ ๊ตฌํ˜„์ฒด์— ๋”ฐ๋ผ ๋‹ค์–‘ํ•˜๊ฒŒ ๋‹ฌ๋ผ์ง„๋‹ค. ์•ž์œผ๋กœ ํ‰๊ฐ€๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ์— ์•ž์„œ ์šฐ๋ฆฌ๋Š” ๋ ‰์„œ์™€ ํŒŒ์„œ๊ฐ€ ๋งŒ๋“  AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ํ‰๊ฐ€ํ•˜๋Š” ์ „๋žต์—๋Š” ์–ด๋–ค ๊ฒƒ๋“ค์ด ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณผ ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

2-1. AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฒˆ์—ญํ•˜์ž: ํŠธ๋ฆฌ ์ˆœํšŒ(Tree-walking) ์ธํ„ฐํ”„๋ฆฌํ„ฐ

ํŠธ๋ฆฌ ์ˆœํšŒ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋ผ๋Š” ํ‰๊ฐ€ ์ „๋žต์€ ๋‹จ์ˆœ๋ฌด์‹(?)ํ•œ ๋ฐฉ์‹์ด๋‹ค. ๋ง ๊ทธ๋Œ€๋กœ AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ํ•˜๋‚˜์”ฉ ์ˆœํšŒํ•˜๊ณ  ๊ฐ ๋…ธ๋“œ๋ฅผ ๋ฐฉ๋ฌธํ•˜๋ฉด์„œ ๋…ธ๋“œ๊ฐ€ ๊ฐ–๋Š” ์˜๋ฏธ๋Œ€๋กœ ์ฆ‰์‹œ(on the fly) ์ฒ˜๋ฆฌํ•œ๋‹ค. ๊ฐ€์žฅ ๋‹จ์ˆœํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ์›ํ˜•์ด ๋˜๊ธฐ๋„ ํ•œ๋‹ค. ๋‹ค๋งŒ, ํŠธ๋ฆฌ ์ˆœํšŒ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋ผ๋Š” ์ „๋žต์„ ์‚ฌ์šฉํ•ด์„œ ํ‰๊ฐ€๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์ „์— ๋ช‡ ๊ฐ€์ง€ ์ตœ์ ํ™”๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ๋„ ํ•œ๋‹ค. ์ฒซ๋ฒˆ์งธ๋Š” ์ •์˜ํ–ˆ์ง€๋งŒ ์•ˆ ์“ฐ๋Š” ๋ณ€์ˆ˜๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์žฌ๊ตฌ์„ฑํ•œ๋‹ค. ๋‘ ๋ฒˆ์งธ๋กœ๋Š” AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์žฌ๊ท€ ํ˜น์€ ๋ฐ˜๋ณต ํ‰๊ฐ€์— ๋”์šฑ ์ ํ•ฉํ•œ ์ค‘๊ฐ„ ํ‘œํ˜„๋ฌผ(Intermediate Representation, IR)๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ๋„ ํ•œ๋‹ค.

 

๊ทธ๋Ÿฐ๋ฐ ๋‘ ๋ฒˆ์งธ ์ตœ์ ํ™” ๋ฐฉ๋ฒ•์—์„œ ์†Œ๊ฐœํ•˜๋Š” ์ค‘๊ฐ„ ํ‘œํ˜„๋ฌผ ๊ด€๋ จํ•ด์„œ ์ข€ ๋” ์•Œ์•„๋ณผ ๋‚ด์šฉ์ด ์žˆ๋‹ค. ์–ด๋–ค ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋“ค์€ ๋˜‘๊ฐ™์ด AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์ˆœํšง์ง€๋งŒ, AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฒˆ์—ญํ•˜๋Š” ๋Œ€์‹  ๋ฐ”์ดํŠธ์ฝ”๋“œ(Bytecode) ๋ผ๋Š” ๊ฒƒ์œผ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค. ์ด ๋ฐ”์ดํŠธ์ฝ”๋“œ๊ฐ€ ๋ฐ”๋กœ ์ค‘๊ฐ„ ํ‘œํ˜„๋ฌผ์˜ ์œ ํ˜• ์ค‘ ํ•˜๋‚˜์ด๋‹ค. ๋ฐ”์ดํŠธ์ฝ”๋“œ์—๋Š” AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ •๋ณด๊ฐ€ ๋“ค์–ด๊ฐ€ ์žˆ๋‹ค. ์ด ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ํ˜•์‹๊ณผ ๋ช…๋ น์ฝ”๋“œ์˜ ์œ ํ˜•๋“ค์€ ๊ฒŒ์ŠคํŠธ ์–ธ์–ด์™€ ํ˜ธ์ŠคํŠธ ์–ธ์–ด์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง„๋‹ค. ๊ฒŒ์ŠคํŠธ ์–ธ์–ด๋ž€, ์ธํ„ฐํ”„๋ฆฌํ„ฐ๊ฐ€ ๋ฒˆ์—ญํ•  ๋Œ€์ƒ์ธ ์–ธ์–ด ์ฆ‰, ์šฐ๋ฆฌ์˜ ํ™˜๊ฒฝ์—์„œ๋Š” Monkey ์–ธ์–ด๊ฐ€ ๋œ๋‹ค. ๋ฐ˜๋ฉด์— ํ˜ธ์ŠคํŠธ ์–ธ์–ด๋Š” ์ธํ„ฐํ”„๋ฆฌํ„ฐ ์ž์ฒด๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋œ ์–ธ์–ด ์ฆ‰, ์šฐ๋ฆฌ์˜ ํ™˜๊ฒฝ์—์„œ๋Š” Go ์–ธ์–ด๊ฐ€ ๋œ๋‹ค. 

 

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

 

byte code๋Š” ์–ด์…ˆ๋ธ”๋ฆฌ์–ด์™€ ๊ธฐ๊ณ„์–ด ์‚ฌ์ด์˜ ๊ทธ ์ค‘๊ฐ„ ์–ด๋””์ฏค์— ์žˆ๋‹ค

 

๊ทธ๋Ÿฌ๋ฉด ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ผ๋Š” ์ค‘๊ฐ„ ๊ฒฐ๊ณผ๋ฌผ์„ ํ•ด์„์„ ๋ˆ„๊ฐ€, ์–ด๋–ป๊ฒŒ ํ•˜๋Š” ๊ฑธ๊นŒ? ํ•ด์„์„ ํ•˜๋Š” ์ฃผ์ฒด๋Š” ๋ฐ”๋กœ ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ์ผ๋ถ€์ธ ๊ฐ€์ƒ ๋จธ์‹ ์ด๋‹ค. VMWare๋‚˜ VirtualBox์™€ ๊ฐ™์€ ์†Œํ”„ํŠธ์›จ์–ด๋“ค์„ ์จ๋ณธ ๋ถ„๋“ค์ด ์žˆ์„ ๊ฒƒ์ด๋‹ค. ์ด ์†Œํ”„ํŠธ์›จ์–ด๋“ค์€ ์‹ค์ œ ๋จธ์‹ ๊ณผ CPU๋ฅผ ๊ฐ€์ƒํ™”(์†Œํ”„ํŠธ์›จ์–ดํ™”)ํ•œ ์ œํ’ˆ์ด๋‹ค. ์ฆ‰, ์‹ค๋ฉ” ๋จธ์‹ ๊ณผ CPU๋ฅผ ๋ชจ๋ฐฉํ•œ ๊ฒƒ์ด๋‹ค. ์ด์ฒ˜๋Ÿผ ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ๊ฐ€์ƒ ๋จธ์‹ ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค. ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ๊ฐ€์ƒ๋จธ์‹ ๋„ ๋ฐ”์ดํŠธ์ฝ”๋“œ ํ˜•์‹์„ ์ดํ•ดํ•˜๋Š” ์‹œ์Šคํ…œ์„ ๋ชจ๋ฐฉํ•œ๋‹ค. ์ผ๋ก€๋กœ, CPython ๊ตฌํ˜„์ฒด์ธ Python ์—์„œ๋Š” PVM(Python Virtual Machine)์ด ๋ฐ”๋กœ ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ๊ฐ€์ƒ๋จธ์‹ ์ด๋‹ค. 

 

์ด๋ ‡๊ฒŒ ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ผ๋Š” ์ค‘๊ฐ„ ํ‘œํ˜„๋ฌผ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์€ ์„ฑ๋Šฅ ์ƒ์˜ ์ด์ ์„ ํฌ๊ฒŒ ๋งŒ๋“ ๋‹ค. ๋Œ€์ฒด ์–ด๋–ค ์žฅ์ ์ด ์žˆ๊ธธ๋ž˜ ์„ฑ๋Šฅ ์ƒ์˜ ์ด์ ์„ ๋งŒ๋“ค์—ˆ๋Š”์ง€ ์‚ดํŽด๋ณด์ž.

 

๊ฐ€์žฅ ๋จผ์ € ์ค‘๊ฐ„ ํ‘œํ˜„๋ฌผ๋กœ ๋งŒ๋“ค์–ด์ง„ ๋ฐ”์ดํŠธ์ฝ”๋“œ ๋•๋ถ„์— ์ธํ„ฐํ”„๋ฆฌํ„ฐ๊ฐ€ ์ž…๋ ฅ์œผ๋กœ ๋“ค์–ด์˜จ ์†Œ์Šค์ฝ”๋“œ ๋ฌธ์ž์—ด์„ ๋งค๋ฒˆ ๋ ‰์‹ฑ, ํŒŒ์‹ฑํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ์ด๋ ‡๊ฒŒ ๋ ‰์‹ฑํ•˜๊ณ  ํŒŒ์‹ฑํ•˜๋Š” ๊ณผ์ •์€ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ํฐ ์ž‘์—…์ด๋‹ค. ํ•˜์ง€๋งŒ ๋ฐ”์ดํŠธ์ฝ”๋“œ๊ฐ€ ์ค‘๊ฐ„ ํ‘œํ˜„๋ฌผ๋กœ ๋งŒ๋“ค์–ด์ง€๋ฉด ์ตœ์ดˆ์—๋งŒ ๋ ‰์‹ฑ, ํŒŒ์‹ฑ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ทธ ์ดํ›„๋ถ€ํ„ฐ๋Š” ๋ฐ”์ดํŠธ์ฝ”๋“œ์— ๋‚˜์—ด๋œ ๊ฐ€์ƒ๋จธ์‹ ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ๋ช…๋ น์–ด๋“ค๋งŒ ์‹คํ–‰ํ•˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ํ›จ์”ฌ ๋นจ๋ผ์ง„๋‹ค. ๋งˆ์น˜ CPU๊ฐ€ ์–ด์…ˆ๋ธ”๋ฆฌ ๋ช…๋ น์–ด๋ฅผ ๋น ๋ฅด๊ฒŒ ์ˆœ์ฐจ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด๋‹ค.

 

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

 

์„ธ ๋ฒˆ์žฌ๋กœ๋Š” ์บ์‹œ๋ฅผ ํ™œ์šฉํ•ด์„œ ์žฌ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ์ค‘๊ฐ„ ํ‘œํ˜„๋ฌผ์ธ ๋ฐ”์ดํŠธ์ฝ”๋“œ๋Š” ์บ์‹ฑํ•ด๋‘˜ ์ˆ˜ ์žˆ๋‹ค. ์ผ๋ก€๋กœ, Python์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•˜๋‹ค ๋ณด๋ฉด .pyc ๋ผ๋Š” ํ™•์žฅ์ž์˜ ํŒŒ์ผ์ด ์–ด๋А ์ˆœ๊ฐ„ ์ƒ์„ฑ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ด .pyc ํŒŒ์ผ ๋‚ด๋ถ€์—๋Š” ๋ฐ”์ดํŠธ์ฝ”๋“œ๊ฐ€ ๋“ค์–ด์žˆ์œผ๋ฉฐ ๋‹ค์Œ ์‹คํ–‰ ์‹œ์—๋Š” .pyc ํŒŒ์ผ์„ ์‚ฌ์šฉํ•ด์„œ ํŒŒ์‹ฑ๊ณผ ์ปดํŒŒ์ผ ๊ณผ์ •์„ ๊ฑด๋„ˆ ๋›ฐ๊ฒŒ ๋œ๋‹ค.

2-2. AST ์ž๋ฃŒ๊ตฌ์กฐ๋„ ๊ณ ๋ คํ•˜์ง€ ๋ง์ž: JIT(Just In Time) ์ธํ„ฐํ”„๋ฆฌํ„ฐ/์ปดํŒŒ์ผ๋Ÿฌ

๊ทธ๋Ÿฐ๋ฐ ํ•„์ž๋Š” ํ•ด๋‹น ๋‚ด์šฉ์„ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ๊ถ๊ธˆํ•œ ์ ์ด ์ƒ๊ฒผ๋‹ค. AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ƒ๋จธ์‹ ์ด ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ผ๋Š” ์ค‘๊ฐ„ ํ‘œํ˜„๋ฌผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ๊นŒ์ง€๋Š” ์ดํ•ด๊ฐ€ ๋˜์—ˆ๋Š”๋ฐ, ๊ทธ๋Ÿฌ๋ฉด ์‹ค์งˆ์ ์ธ ๋ฌผ๋ฆฌ์  ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋Š” CPU ๊ฐ™์€ ์žฅ์น˜๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๊ณ„์–ด๋กœ ๋ณ€ํ™˜๋˜๊ณ  ์‹คํ–‰๋˜๋Š” ๊ณผ์ •๊นŒ์ง€๋Š” ๋Œ€์ฒด ์–ด๋–ป๊ฒŒ ์ง„ํ–‰๋˜๋Š” ๊ฑด์ง€ ๊ถ๊ธˆํ–ˆ๋‹ค. ๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜๋ฉด ์ธํ„ฐํ”„๋ฆฌํŒ… ๋ฐฉ์‹์ด๋ƒ JIT ์ปดํŒŒ์ผ ๋ฐฉ์‹์ด๋ƒ์— ๋”ฐ๋ผ ๊ณผ์ •์ด ์•ฝ๊ฐ„ ๋‹ฌ๋ผ์ง„๋‹ค. ์ผ๋‹จ ์•„๋ž˜ ์ˆœ์„œ๋„๋ถ€ํ„ฐ ๋ณด์ž.

 

ํ‰๊ฐ€ ์‹œ ์ธํ„ฐํ”„๋ฆฌํŒ… ๋ฐฉ์‹๊ณผ JIT ์ปดํŒŒ์ผ ๋ฐฉ์‹

 

๋‘ ๋ฐฉ์‹ ๋ชจ๋‘ ๋ฐ”์ดํŠธ ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •๊นŒ์ง€๋Š” ๋™์ผํ•˜๋‹ค. ๋จผ์ € ์ธํ„ฐํ”„๋ฆฌํŒ… ๋ฐฉ์‹ ์ฆ‰, ํŠธ๋ฆฌ ์ˆœํšŒ ์ธํ„ฐํ”„๋ฆฌํ„ฐ ์ „๋žต์˜ ๊ฒฝ์šฐ๋ฅผ ์‚ดํŽด๋ณด์ž. CPython ๊ตฌํ˜„์ฒด์ธ Python์„ ์˜ˆ์‹œ๋กœ ๋“ค์–ด๋ณด๊ฒ ๋‹ค. PVM์ด ๋ฐ”์ดํŠธ ์ฝ”๋“œ์™€ ๋Œ€์‘๋˜๋Š” C ํ•จ์ˆ˜๋ฅผ ์•Œ์•„๋‚ธ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด C ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ ์‹ค์ œ ๋ฌผ๋ฆฌ์ ์ธ CPU ์žฅ์น˜๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๊ณ„์–ด๋กœ ๋ณ€ํ™˜์ด ๋˜๊ณ , ์ด ๋•Œ CPU๊ฐ€ ํ•ด๋‹น ๊ธฐ๊ณ„์–ด๋กœ ์‹ค์งˆ์ ์ธ ์—ฐ์‚ฐ์„ ์‹คํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค.

 

๋ฐ˜๋ฉด์—, JIT ์ปดํŒŒ์ผ ๋ฐฉ์‹์€ ๋ฐ”์ดํŠธ ์ฝ”๋“œ์— ์žˆ๋Š” ๋ช…๋ น์–ด๋ฅผ VM์—์„œ ์ง์ ‘ ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. VM์ด ๋ฐ”์ดํŠธ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ์ž์ฃผ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ ์˜ˆ๋ฅผ ๋“ค์–ด, loop ๊ตฌ๋ฌธ์ด๋‚˜ ํ•จ์ˆ˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋“ค์€ ์ฆ‰์‹œ ๊ธฐ๊ณ„์–ด๋กœ ๋ฐ”๋กœ ๋ณ€ํ™˜ ์ฆ‰, ์ปดํŒŒ์ผ์„ ํ•ด๋ฒ„๋ฆฐ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ณ€ํ™˜๋œ ๊ธฐ๊ณ„์–ด๋ฅผ RAM์— ์ €์žฅํ•œ ๋’ค, ๋ฌผ๋ฆฌ์ ์ธ CPU ์žฅ์น˜๋Š” RAM์— ์ €์žฅ๋œ ๊ธฐ๊ณ„์–ด๋ฅผ ์ฝ์–ด๋“ค์—ฌ ์ฆ‰์‹œ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

 

์œ„ 2๊ฐœ ๋ฐฉ์‹์„ ๋น„๊ตํ•ด๋ณด์•˜์„ ๋•Œ ์–ด๋–ค ๋ฐฉ์‹์ด ๋” ์„ฑ๋Šฅ์ด ์ข‹์•„ ๋ณด์ผ๊นŒ? ๋”ฑ ๋ณด์•„๋„ ๋ฌผ๋ฆฌ CPU ์—ฐ์‚ฐ ์žฅ์น˜๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „๊นŒ์ง€ ๊ฑฐ์น˜๋Š” ๋‹จ๊ณ„๊ฐ€ ๊ฐ„์†Œํ™”๋œ๊ฒŒ ๋ณด์ธ๋‹ค. ๊ทธ๋ž˜์„œ JIT ์ปดํŒŒ์ผ๋Ÿฌ ๋ฐฉ์‹์ด ํ›จ์”ฌ ๋” ๋น ๋ฅธ ์„ฑ๋Šฅ์„ ๋ณด์ธ๋‹ค. JIT ์ด๋ผ๋Š” ์šฉ์–ด๋Š” Just In Time ์˜ ์ค„์ž„๋ง๋กœ, VM์ด ์‹คํ–‰ ์ง์ „์— ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ ๊ธฐ๊ณ„์–ด๋กœ ์ฆ‰์„์—์„œ ์ปดํŒŒ์ผํ•œ๋‹ค๊ณ  ํ•˜์—ฌ Just In Time์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

 

์‹ฌ์ง€์–ด ์–ด๋–ค ํ‰๊ฐ€ ์ „๋žต์€ AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๊ณ ๋ คํ•˜์ง€๋„ ์•Š๊ณ  ํŒŒ์„œ๊ฐ€ ๋ฐ”๋กœ ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ๋„ ์žˆ๊ณ , ์–ด๋–ค ๊ตฌํ˜„์ฒด๋Š” ๋ฐ”์ดํŠธ์ฝ”๋“œ๋กœ ์ปดํŒŒ์ผ ํ•˜๋Š” ๊ณผ์ •์„ ์ง€๋‚˜์น˜๊ธฐ๋„ ํ•œ๋‹ค. ๋ชจ๋“  ํ‰๊ฐ€ ์ „๋žต์„ ์—ฌ๊ธฐ์„œ ๋‹ค ์•Œ์•„๋ณผ ์ˆ˜๋Š” ์—†์ง€๋งŒ ์–ด์จŒ๊ฑด ์ •๋ง ๋‹ค์–‘ํ•œ ํ‰๊ฐ€ ์ „๋žต์ด ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์€ ๋ฐ˜๋“œ์‹œ ์•Œ์•„๋‘๋„๋ก ํ•˜์ž. ๋ณดํ†ต์€ ๋ฒˆ์—ญ ๋Œ€์ƒ์ด ๋˜๋Š” ์–ธ์–ด๊ฐ€ ์š”๊ตฌํ•˜๋Š” ์„ฑ๋Šฅ, ์ด์‹์„ฑ, ๊ตฌํ˜„์ž๊ฐ€ ๊ตฌํ˜„ํ•˜๊ณ ์ž ํ•˜๋Š” ๋ชฉํ‘œ์น˜์— ๋”ฐ๋ผ ํ‰๊ฐ€ ์ „๋žต์„ ์„ ํƒํ•œ๋‹ค. ์‚ฌ์‹ค ๊ฐ€์ƒ ๋จธ์‹ ์„ ์ด์šฉํ•ด์„œ ๋ฐ”์ดํŠธ ์ฝ”๋“œ๋ฅผ ์ปดํŒŒ์ผํ•˜๊ณ  ํ‰๊ฐ€ํ•˜๋„๋ก ๋™์ž‘ํ•˜๋Š” ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋Š” ์•ž์œผ๋กœ ๊ณ„์† ์†๋„๊ฐ€ ๋นจ๋ผ์งˆ ๊ฒƒ์ด๋‹ค. ๋‹ค๋งŒ, ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ํ‰๊ฐ€ ์ „๋žต์ด ๋ณต์žกํ•ด์ง์— ๋”ฐ๋ผ ๋งŒ๋“ค๊ธฐ๋„ ์–ด๋ ค์›Œ์งˆ ๋ฟ๋”๋Ÿฌ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋ฅผ ๊ฐ€์ ธ๋‹ค๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๊ฐœ๋ฐœ์ž๋“ค ์ž…์žฅ์—์„œ๋Š” ๋‚ด๋ถ€ ๋™์ž‘์„ ์™„๋ฒฝํ•˜๊ฒŒ ์ดํ•ดํ•˜๊ธฐ๊ฐ€ ์ ์  ์–ด๋ ค์›Œ์ง€๊ฒŒ ๋œ๋‹ค. Java์˜ JVM์ด๋‚˜ Python์˜ PVM๋งŒ ํ•˜๋”๋ผ๋„ ๋ฌผ๋ฆฌ CPU ์—ฐ์‚ฐ ์žฅ์น˜์˜ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•ด์•ผํ•  ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ JVM, PVM ๊ณผ ๊ฐ™์€ ๊ฐ€์ƒ ๋จธ์‹ ์ด ๋ฐ”์ดํŠธ ์ฝ”๋“œ๋ฅผ ์–ด๋–ป๊ฒŒ ์ดํ•ดํ•˜๊ณ  ๋™์ž‘ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์ดํ•ด๋„ ๊ฐ™์ด ๋™๋ฐ˜๋˜์–ด์•ผ ํ•œ๋‹ค. ๋ฌผ๋ก  ๋Œ€๋ถ€๋ถ„์˜ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋ฅผ ์ง์ ‘ ๊ฐœ๋ฐœํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค. ๊ฐœ์ธ์ ์œผ๋กœ ์ด๋Ÿฐ ๊ฒƒ์ด ๋‚˜์œ ํ˜„์ƒ์ด๋ผ๊ณ  ์ ˆ๋Œ€ ์ƒ๊ฐํ•˜์ง€ ์•Š๋Š”๋‹ค. ์˜คํžˆ๋ ค ๋›ฐ์–ด๋‚œ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ์–ธ์–ด์˜ ์†๋„๋ฅผ ๊ฐ€์†ํ™”์‹œ์ผœ์ค€๋‹ค๋Š” ์ ์—์„œ๋Š” ํฐ ๊ฐ์‚ฌ์ธ์‚ฌ๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ์‹ถ์€ ๋ง˜์ด๋‹ค. ๋‹ค๋งŒ, ์šฐ๋ฆฌ๊ฐ€ ๊ธฐ์–ตํ•ด์•ผ ํ•  ๊ฒƒ์€ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๊ฐ€ ๋ณดํ†ต ์ด๋Ÿฐ์‹์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค๋Š” ๊ฐœ๋…์„ ์ˆ™์ง€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด ๊ฐœ๋…์— ๋Œ€ํ•œ ๊ฒƒ๋งŒ์€ ๊ผญ ๋จธ๋ฆฟ์†์— ๋„ฃ๊ณ  ๋„˜์–ด๊ฐ€๋„๋ก ํ•˜์ž.

3. ํ‰๊ฐ€(Evaluation)๊ธฐ ๋งŒ๋“ค๊ธฐ

ํ‰๊ฐ€์— ๋Œ€ํ•œ ๊ฐœ๋… ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ‰๊ฐ€ ์ „๋žต์— ๋Œ€ํ•ด์„œ ์ด๋ก ์ ์ธ ๋ถ€๋ถ„๊นŒ์ง€ ๋ฐฐ์›Œ๋ณด์•˜์œผ๋‹ˆ ์ด์ œ ์ง์ ‘ ํ‰๊ฐ€๊ธฐ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณผ ์ฐจ๋ก€๋‹ค. ํ‰๊ฐ€๊ธฐ์˜ ์ž…๋ ฅ์œผ๋กœ๋Š” (ํŒŒ์„œ๊ฐ€ ๋งŒ๋“ค์–ด๋‚ธ) AST ์ž๋ฃŒ๊ตฌ์กฐ์˜ ๋…ธ๋“œ๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ‰๊ฐ€๊ธฐ์˜ ์ถœ๋ ฅ์€ ํ‰๊ฐ€๋œ ๊ตฌ๋ฌธ์ด ๋‚˜์˜จ๋‹ค. ์†Œ์Šค์ฝ”๋“œ๋กœ ํ‘œํ˜„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

leftEvaluated = eval(astNode.Left)
rightEvaluated = eval(astNode.Right)

 

eval ์ด๋ผ๋Š” ๋ฉ”์†Œ๋“œ๊ฐ€ ๋ฐ”๋กœ ํ‰๊ฐ€๊ธฐ์˜  ์—ญํ• ์„ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด eval ๋ฉ”์†Œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์€ ๋ฌด์—‡์ผ๊นŒ? ์ด ๋ง์„ ๋ฐ”๊ฟ” ๋งํ•˜๋ฉด ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•˜๋Š” ์ธํ„ฐํ”„๋ฆฌํ„ฐ๊ฐ€ ์–ด๋–ค ๋‚ด๋ถ€ ๊ฐ์ฒด ์‹œ์Šคํ…œ์„ ์ •์˜ํ•˜๋Š”์ง€์™€ ๊ฐ™๋‹ค. ํ˜„์žฌ ์šฐ๋ฆฌ๋Š” ํ˜ธ์ŠคํŠธ ์–ธ์–ด๋ฅผ Go ์–ธ์–ด๋กœ ํ•˜์—ฌ ๊ฒŒ์ŠคํŠธ ์–ธ์–ด์ธ Monkey ์–ธ์–ด๋ฅผ ํ•ด์„ํ•˜๋Š” ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋ฅผ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ๋‹ค. ๊ฒฐ๊ตญ, Monkey ์–ธ์–ด๋กœ ํ‘œํ˜„๋˜๋Š” ํƒ€์ž…(ex. ์ •์ˆ˜, Bool, Null, ์‹ค์ˆ˜, ๋ฌธ์ž์—ด ๋“ฑ..)๋“ค์— ๋Œ€ํ•œ ๊ตฌํ˜„์„ Go ์–ธ์–ด๋กœ ํ‘œํ˜„ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ ๋ณดํ†ต ๊ฒŒ์ŠคํŠธ ์–ธ์–ด์˜ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ํ˜ธ์ŠคํŠธ ์–ธ์–ด์— ๋งค์šฐ ์˜์กด์ ์ด๊ฒŒ ๋œ๋‹ค.

 

์ผ๋ก€๋กœ, Python์€ ์ธํ„ฐํ”„๋ฆฌํ„ฐ ์–ธ์–ด์ด๋‹ค. CPython ๊ตฌํ˜„์ฒด ๊ธฐ์ค€์œผ๋กœ, Python์ด๋ผ๋Š” ๊ฒŒ์ŠคํŠธ ์–ธ์–ด๋ฅผ ๊ฐœ๋ฐœํ•œ ํ˜ธ์ŠคํŠธ ์–ธ์–ด๋Š” C์–ธ์–ด์ด๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— Python์˜ ํƒ€์ž…์€ C์–ธ์–ด์˜ ํƒ€์ž…์— ๋งค์šฐ ์˜์กด์ ์ผ ์ˆ˜ ๋ฐ–์— ์—†๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. 

 

๋‹ค์‹œ ์šฐ๋ฆฌ์˜ ์ƒํ™ฉ์œผ๋กœ ๋Œ์•„์™€์„œ ์šฐ๋ฆฌ๋Š” Monkey ์–ธ์–ด๋ฅผ ํ‰๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด์„œ Monkey ์–ธ์–ด์˜ ๊ฐ์ฒด ์‹œ์Šคํ…œ์„ Go ์–ธ์–ด๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. ์ด ๊ตฌํ˜„์— ๋Œ€ํ•ด์„œ ํ•œ ๋ฐœ์”ฉ ๋‚ด๋”›์–ด๋ณด์ž.

3-1. ๊ฐ์ฒด ํ‘œํ˜„ํ•˜๊ธฐ: ์ •์ˆ˜(integer)

AST ๋…ธ๋“œ๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ฐ์ฒด๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ Go ์–ธ์–ด์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™œ์šฉํ•œ๋‹ค. ์†Œ์Šค์ฝ”๋“œ๋ฅผ ๋ณด์ž.

 

// object/object.go

package object

type ObjectType string

type Object interface {
	Type() ObjectType
	Inspect() string
}

 

Object ๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ–ˆ๊ณ , ์ด ์ธํ„ฐํŽ˜์ด๋Š” Type(), Inspect() ๋ผ๋Š” 2๊ฐ€์ง€ ๋ฉ”์†Œ๋“œ ๊ตฌํ˜„์„ ์ถฉ์กฑ์‹œ์ผœ์•ผ ํ•œ๋‹ค. Type() ๋ฉ”์†Œ๋“œ๋Š” ๋˜ ๋‹ค๋ฅธ ์ƒˆ๋กœ์šด ํƒ€์ž…์ธ ObjectType์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ด๋Š” ์†Œ์Šค์ฝ”๋“œ์—์„œ ์ •์˜๋œ ๊ฒƒ์ฒ˜๋Ÿผ ๋‹จ์ˆœํžˆ ๋ฌธ์ž์—ด ํƒ€์ž…์„ ์˜๋ฏธํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Inspect() ๋ฉ”์†Œ๋“œ๋Š” ๋ฐ˜ํ™˜ ํƒ€์ž…์ด ๋ฌธ์ž์—ด์ด๋‹ค.

 

์šฐ๋ฆฌ๋Š” ์œ„ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด์„œ Monkey ์–ธ์–ด์—์„œ ์ •์ˆ˜ ๊ฐ์ฒด ํƒ€์ž…์„ ๊ตฌํ˜„ํ•ด๋ณผ ๊ฒƒ์ด๋‹ค.

 

// object/object.go

const (
	INTEGER_OBJ = "INTEGER"
)

type Integer struct {
	Value int64
}

func (i *Integer) Inspect() string  { return fmt.Sprintf("%d", i.Value) }
func (i *Integer) Type() ObjectType { return INTEGER_OBJ }

 

Integer ๋ผ๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ์ •์˜ํ–ˆ๊ณ , ํ•ด๋‹น ๊ตฌ์กฐ์ฒด๋Š” Value ๋ผ๋Š” ๋ฉค๋ฒ„๋ฅผ ๊ฐ–๋Š”๋‹ค. ์ด Value ๋ฉค๋ฒ„์˜ ํƒ€์ž…์ด ์‹ค์งˆ์ ์œผ๋กœ integer๋ฅผ ๊ฐ–๋„๋ก ํ•œ๋‹ค. Integer ๋ผ๋Š” ๊ตฌ์กฐ์ฒด๊ฐ€ Object ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ์กฑ์‹œํ‚ค๊ธฐ ์œ„ํ•ด 2๊ฐ€์ง€ ๋ฉ”์†Œ๋“œ๋ฅผ ์ •์˜ํ–ˆ๋‹ค. Inspect ๋ฉ”์†Œ๋“œ๋Š” ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋ฏ€๋กœ, Integer ๊ตฌ์กฐ์ฒด์˜ Value ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋˜ Sprintf ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด์„œ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜์‹œํ‚ค๋„๋ก ํ•œ๋‹ค. ๋‹ค์Œ์€ Type ๋ฉ”์†Œ๋“œ๋Š” ObjectType์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค๊ณ  ํ–ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ObjectType ์ด๋ผ๋Š” ํƒ€์ž…์€ ๋ฌธ์ž์—ด์ด์—ˆ๋‹ค. ObjectType์ด ์•ž์œผ๋กœ ์—ฌ๋Ÿฌ๊ฐ€์ง€์ผ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์ˆ˜๋กœ ์ •์˜ํ•ด๋‘๋„๋ก ํ•˜์ž.

 

์ด๊ฒŒ ๋์ด๋‹ค. ์šฐ๋ฆฌ๋Š” Monkey ์–ธ์–ด์˜ ์ •์ˆ˜ ๊ฐ์ฒด๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค. 

3-2. ๊ฐ์ฒด ํ‘œํ˜„ํ•˜๊ธฐ: Booleans

๋‹ค์Œ์œผ๋กœ ํ‘œํ˜„ํ•ด๋ณผ ๊ฐ์ฒด๋Š” true ๋˜๋Š” false๋ฅผ ๊ฐ–๋Š” Bool ํƒ€์ž…์ด๋‹ค. ๋ฐฉ๊ธˆ ์ •์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฒƒ๊ณผ ๊ฑฐ์˜ ๋™์ผํ•˜๋‹ค. ์†Œ์Šค์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

// object/object.go

const (
	INTEGER_OBJ = "INTEGER"
	BOOLEAN_OBJ = "BOOLEAN"
)

type Boolean struct {
	Value bool
}

func (b *Boolean) Inspect() string  { return fmt.Sprintf("%t", b.Value) }
func (b *Boolean) Type() ObjectType { return BOOLEAN_OBJ }

3-3. ๊ฐ์ฒด ํ‘œํ˜„ํ•˜๊ธฐ: Null

๋‹ค์Œ์œผ๋กœ ํ‘œํ˜„ํ•ด๋ณผ ๊ฐ์ฒด๋Š” ๋ฐ”๋กœ ์•„๋ฌด๊ฒƒ๋„ ์—†์Œ์„ ์˜๋ฏธํ•˜๋Š” null ์ด๋‹ค. ํฌ์ธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋ฅผ ์กฐ๊ธˆ์ด๋ผ๋„ ์‚ฌ์šฉํ•ด๋ณธ ๊ฐœ๋ฐœ์ž๋ผ๋ฉด null ์ฐธ์กฐ๋ผ๋Š” ์ƒํ™ฉ์„ ํ•œ ๋ฒˆ ์ฏค์ด๋ผ๋„ ๊ฒฝํ—˜ํ•ด๋ณด์•˜์„ ๊ฒƒ์ด๋‹ค. null ์ฐธ์กฐ๋ž€, ๋ง ๊ทธ๋Œ€๋กœ ์•„๋ฌด๊ฒƒ๋„ ์—†๋Š” ๋Œ€์ƒ์„ ์ฐธ์กฐํ•˜๋ ค๊ณ  ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์šฐ๋ฆฌ์˜ Monkey ์–ธ์–ด๋Š” null ์ฐธ์กฐ๋„ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด null ๊ฐ์ฒด๋„ ๊ตฌํ˜„ํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค. 

 

// object/object.go

const (
	INTEGER_OBJ = "INTEGER"
	BOOLEAN_OBJ = "BOOLEAN"
	NULL_OBJ    = "NULL"
)

type Null struct{}

func (n *Null) Inspect() string  { return "null" }
func (n *Null) Type() ObjectType { return NULL_OBJ }

 

null๋Š” ์ด์ „์— ๊ตฌํ˜„ํ–ˆ๋˜ ์ •์ˆ˜, Boolean ๊ณผ ๋‹ค๋ฅด๊ฒŒ ๊ตฌ์กฐ์ฒด๋ฅผ ์ •์˜ํ•  ๋•Œ ๋ง ๊ทธ๋Œ€๋กœ ์ •๋ง ์•„๋ฌด๊ฒƒ๋„ ์—†๋Š” ๋นˆ ๊ตฌ์กฐ์ฒด๋ฅผ ์ •์˜ํ–ˆ๋‹ค. ์ด๊ฒƒ๋งŒ ์ฐจ์ด์ ์ด๊ณ  ๋‚˜๋จธ์ง€๋Š” ๋ชจ๋‘ ๋™์ผํ•˜๋‹ค.

3-4. ํ‘œํ˜„์‹ ํ‰๊ฐ€ํ•˜๊ธฐ

๋ฐฉ๊ธˆ๊นŒ์ง€ ์ •์ˆ˜, Booleans, Null ์ด๋ผ๋Š” 3๊ฐ€์ง€ ์ข…๋ฅ˜์˜ ๊ฐ์ฒด๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค. ์ด์ œ ์šฐ๋ฆฌ๋Š” ์ด 3๊ฐ€์ง€ ์ข…๋ฅ˜์˜ ๊ฐ์ฒด ์‹œ์Šคํ…œ์„ ์ด์šฉํ•ด์„œ ํ‰๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜ ์ฆ‰, Eval ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ด๋ณผ ์ฐจ๋ก€๋‹ค. ๋จผ์ € Eval ํ•จ์ˆ˜์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ๊ฒผ๋‹ค.

 

func Eval(node ast.node) object.Object

 

์ธ์ž๋Š” node๋กœ ์šฐ๋ฆฌ๊ฐ€ ์ด์ „์— ํŒŒ์„œ๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ ๋งŒ๋“  ast.Node ํƒ€์ž…์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ฐ˜ํ™˜ํ•˜๋Š” ํƒ€์ž…์€ ์šฐ๋ฆฌ๊ฐ€ ์ •์˜ํ•œ Object ํƒ€์ž…์ด๋‹ค. ์ฃผ๋ชฉํ•  ์ ์€ ์ธ์ž๋ฅผ ast.Node ๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ถฉ์กฑ์‹œํ‚ค๋Š” ํƒ€์ž…์ด์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋กœ ์ธํ•ด Eval ๋ฉ”์†Œ๋“œ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. ์ด๋Ÿฌํ•œ ์ ์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ช…๋ น๋ฌธ๋“ค์„ ์—ฌ๋Ÿฌ ๋ฒˆ์˜ ์žฌ๊ท€์ ์ธ ํ˜ธ์ถœ๋กœ ๋ชจ๋‘ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•œ๋‹ค.

3-4-1. ์ž์ฒดํ‰๊ฐ€ ํ‘œํ˜„์‹(1): ์ •์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด

๊ฐ€์žฅ ๋จผ์ € ํ•ด๋ณผ ํ‘œํ˜„์‹ ํ‰๊ฐ€๋Š” ์ž์ฒดํ‰๊ฐ€ ํ‘œํ˜„์‹์ด๋‹ค. ์ž์ฒดํ‰๊ฐ€ ํ‘œํ˜„์‹์ด๋ž€, ํ‰๊ฐ€๋ฅผ ํ•˜๋Š” ๊ด€์ ์—์„œ ๋ฆฌํ„ฐ๋Ÿด์„ ๋ถ€๋ฅด๋Š” ๊ฒƒ์ด๋‹ค. ์ •์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด, Bool ๋ฆฌํ„ฐ๋Ÿด ๋“ฑ ๋ง์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด, 5๋ฅผ ์ž…๋ ฅํ•˜๋ฉด 5๊ฐ€ ๋‚˜์˜ค๊ณ , true๋ฅผ ์ž…๋ ฅํ•˜๋ฉด true๊ฐ€ ๋‚˜์˜ค๋Š” ๊ฒƒ ๋ง์ด๋‹ค. 

 

๋จผ์ € ์ •์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์ด๋ผ๋Š” ์ž์ฒดํ‰๊ฐ€ ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•ด๋ณด์ž. ๊ทธ์— ์•ž์„œ ์ด๋ฅผ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ถ€ํ„ฐ ์‚ดํŽด๋ณด์ž.

 

// evaluator/evaluator_test.go

package evaluator

import (
	"monkey/lexer"
	"monkey/object"
	"monkey/parser"
	"testing"
)

func TestEvalIntegerExpression(t *testing.T) {
	tests := []struct {
		input    string
		expected int64
	}{
		{"5", 5},
		{"10", 10},
	}
	for _, tt := range tests {
		evaluated := testEval(tt.input)
		testIntegerObject(t, evaluated, tt.expected)
	}
}

func testEval(input string) object.Object {
	l := lexer.New(input)
	p := parser.New(l)
	program := p.ParseProgram()
	return Eval(program)
}

func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool {
	result, ok := obj.(*object.Integer)
	if !ok {
		t.Errorf("object is not Integer. got=%T (%+v)", obj, obj)
		return false
	}
	if result.Value != expected {
		t.Errorf("object has wrong value. got=%d, want=%d", result.Value, expected)
		return false
	}
	return true
}

 

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž์ฒด๋Š” ๊ฐ„๋‹จํ•˜๋‹ค. ๋ฌธ์ž 5๋ฅผ ์ž…๋ ฅํ–ˆ์„ ๋•Œ, 5๋กœ ํ‰๊ฐ€๋˜๋„๋ก ํ•˜๊ณ , ๋ฌธ์ž 10์„ ์ž…๋ ฅํ–ˆ์„ ๋•Œ, 10์œผ๋กœ ํ‰๊ฐ€๋˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.  ๊ทธ๋ฆฌ๊ณ  testEval ์ด๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š”๋ฐ, ์ด ๋ฉ”์†Œ๋“œ ์•ˆ์—์„œ๋Š” ๊ทธ๋™์•ˆ ์šฐ๋ฆฌ๊ฐ€ ๋ฐฐ์› ๋˜ ๋ฌธ์ž์—ด ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์ž…๋ ฅ ๋ฐ›์œผ๋ฉด ๋ ‰์„œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์ด ๋ ‰์„œ๋ฅผ ์ธ์ž๋กœ ์ฃผ์–ด ํŒŒ์‹ฑ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ํŒŒ์‹ฑ์ด ์ˆ˜ํ–‰๋œ ๊ฒฐ๊ณผ์ธ AST ๋…ธ๋“œ๋“ค์ด program ๋ณ€์ˆ˜์— ๋‹ด๊ฒจ์žˆ๊ณ , ์ด์— ๋Œ€ํ•œ ์‹ค์งˆ์ ์ธ ํ‰๊ฐ€๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” Eval ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ๋ฌผ๋ก  ์•„์ง ์šฐ๋ฆฌ๋Š” Eval ๋ฉ”์†Œ๋“œ๋ฅผ ์ •์˜ํ•˜์ง€ ์•Š์•˜๋‹ค. ์ด์ œ ์ •์˜ํ•ด๋ณผ ์ฐจ๋ก€๋‹ค.

 

// evaluator/evaluator.go

package evaluator

import (
	"monkey/ast"
	"monkey/object"
)

func Eval(node ast.Node) object.Object {
	// type switch
	switch node := node.(type) {
	
	// ๋ช…๋ น๋ฌธ
	case *ast.Program:
		return evalStatements(node.Statements)
		
	case *ast.ExpressionStatement:
		return Eval(node.Expression)
	
	// ํ‘œํ˜„์‹ 
	case *ast.IntegerLiteral:
		return &object.Integer{Value: node.Value}
	}
	return nil
}

func evalStatements(stmts []ast.Statement) object.Object {
	var result object.Object
	
	for _, statement := range stmts {
		result = Eval(statement)
	}
	return result
}

 

Eval ๋ฉ”์†Œ๋“œ์˜ ์ธ์ž๋Š” ํŒŒ์„œ์˜ ์ˆ˜ํ–‰ ๊ฒฐ๊ณผ๋ฌผ์ธ AST ๋…ธ๋“œ๋ผ๊ณ  ํ–ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ast.Node ์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž…์ด๋‹ค. switch ๊ตฌ๋ฌธ์—๋Š” ํƒ€์ž… ์Šค์œ„์น˜๋ฅผ ํ™œ์šฉํ•ด์„œ ast.Node์˜ ์‹ค์ œ ํƒ€์ž…์„ ์ถ”์ถœํ•ด ํ™•์ธํ•œ๋‹ค. ์‹ค์ œ ํƒ€์ž…์ด ๋งŒ์•ฝ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ช…๋ น๋ฌธ๋“ค๋กœ ๊ตฌ์„ฑ๋œ ast.Program ํƒ€์ž…์ด๋ผ๋ฉด evalStatements ๋ผ๋Š” ์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ๋ฅผ ๋˜ ํ˜ธ์ถœํ•œ๋‹ค. evalStatements ๋ฉ”์†Œ๋“œ์˜ ์ƒ๊น€์ƒˆ๋ฅผ ๋ณด๋ฉด Eval ๋ฉ”์†Œ๋“œ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋ณต์žกํ•œ ๊ฒƒ์€ ์•„๋‹ˆ๊ณ , ์—ฌ๋Ÿฌ ๋ช…๋ น๋ฌธ์œผ๋กœ ๊ตฌ์„ฑ๋œ ๊ฒƒ์„ loop๋ฅผ ๋Œ๋ฉด์„œ ๋ช…๋ น๋ฌธ ํ•˜๋‚˜์”ฉ ํ‰๊ฐ€๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์žฌ๊ท€์ ์œผ๋กœ ๊ณ„์† ํ˜ธ์ถœ๋˜๋ฉด์„œ ๊ฒฐ๊ตญ์—๋Š” object.Object ์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž…์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค. 

 

์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ช…๋ น๋ฌธ์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์ง€ ์•Š๊ณ , ๋‹จ์ผ ๋ช…๋ น๋ฌธ์ผ ์ˆ˜๋„ ์žˆ์„ ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿด ๊ฒฝ์šฐ๋Š” ์ฆ‰์‹œ Eval ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ ์ง€๊ธˆ ์šฐ๋ฆฌ๊ฐ€ ์ง‘์ค‘ํ•˜๊ณ  ์žˆ๋Š” ์ž์ฒดํ‰๊ฐ€ ํ‘œํ˜„์‹์ผ ๊ฒฝ์šฐ, ์ฆ‰์‹œ ์ •์ˆ˜๋ฉด object.Integer ๋ผ๋Š” object.Object ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ถฉ์กฑํ•˜๋Š” ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

์ด๋ ‡๊ฒŒ ํ•œ ๋’ค, ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋ฐฉ๊ธˆ ์ž‘์„ฑํ•œ ํ…Œ์Šคํฌ ์ฝ”๋“œ๋Š” ํ†ต๊ณผํ•œ๋‹ค.

 

go test ./evaluator

 

์šฐ๋ฆฌ๋Š” ์ •์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์ด๋ผ๋Š” ์ž์ฒดํ‰๊ฐ€ ํ‘œํ˜„์‹ ์ข…๋ฅ˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. ๋‹ค๋ฅธ ์ข…๋ฅ˜์˜ ์ž์ฒดํ‰๊ฐ€ ํ‘œํ˜„์‹๋„ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณด๊ธฐ์— ์•ž์„œ, ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“ค์–ด์˜จ REPL์˜ E(Evaluation) ๊ธฐ๋Šฅ์„ ๋งˆ์ € ์ถ”๊ฐ€ํ•ด๋ณด์ž. ์ด์— ๋Œ€ํ•œ ์ฝ”๋“œ ์„ค๋ช…์€ ์ƒ๋žตํ•˜๊ฒ ๋‹ค.

 

// repl/repl.go

package repl

import (
	"bufio"
	"fmt"
	"io"
	"monkey/evaluator"
	"monkey/lexer"
	"monkey/parser"
)

const PROMPT = ">> "

func Start(in io.Reader, out io.Writer) {
	scanner := bufio.NewScanner(in)

	for {
		fmt.Fprintf(out, PROMPT)
		scanned := scanner.Scan()
		if !scanned {
			return
		}
		line := scanner.Text()
		l := lexer.New(line)
		p := parser.New(l)

		program := p.ParseProgram()
		if len(p.Errors()) != 0 {
			printParserErrors(out, p.Errors())
			continue
		}

		evaluated := evaluator.Eval(program)
		if evaluated != nil {
			io.WriteString(out, evaluated.Inspect())
			io.WriteString(out, "\n")
		}
	}
}

const MONKEY_FACE = `            __,__
   .--.  .-"     "-.  .--.
  / .. \/  .-. .-.  \/ .. \
 | |  '|  /   Y   \  |'  | |
 | \   \  \ 0 | 0 /  /   / |
  \ '- ,\.-"""""""-./, -' /
   ''-' /_   ^ ^   _\ '-''
       |  \._   _./  |
       \   \ '~' /   /
        '._ '-=-' _.'
           '-----'
`

func printParserErrors(out io.Writer, errors []string) {
	io.WriteString(out, MONKEY_FACE)
	io.WriteString(out, "Woops! We ran into some monkey business here!\n")
	io.WriteString(out, " parser errors:\n")
	for _, msg := range errors {
		io.WriteString(out, "\t"+msg+"\n")
	}
}

3-4-2. ์ž์ฒดํ‰๊ฐ€ ํ‘œํ˜„์‹(2): Bool ๋ฆฌํ„ฐ๋Ÿด

๋‹ค์Œ์œผ๋กœ ์ถ”๊ฐ€ํ•ด๋ณผ ์ž์ฒดํ‰๊ฐ€ ํ‘œํ˜„์‹์€ true/false๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” Bool ๋ฆฌํ„ฐ๋Ÿด์ด๋‹ค. ๋จผ์ € ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ถ€ํ„ฐ ์‚ดํŽด๋ณด์ž. ์ •์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด ๋•Œ์™€ ํฌ๊ฒŒ ๋‹ฌ๋ผ์ง€๋Š” ์ ์€ ์—†๊ณ , Bool ํƒ€์ž…์„ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ๋กœ๋งŒ ์‚ด์ง ๋ณ€๊ฒฝ๋œ ๊ฒƒ ๋ฟ์ด๋‹ค.

 

// evaluator/evaluator_test.go

func TestEvalBooleanExpression(t *testing.T) {
	tests := []struct {
		input    string
		expected bool
	}{
		{"true", true},
		{"false", false},
	}
	for _, tt := range tests {
		evaluated := testEval(tt.input)
		testBooleanObject(t, evaluated, tt.expected)
	}
}

func testBooleanObject(t *testing.T, obj object.Object, expected bool) bool {
	result, ok := obj.(*object.Boolean)
	if !ok {
		t.Errorf("object is not Boolean. got=%T (%+v)", obj, obj)
		return false
	}
	if result.Value != expected {
		t.Errorf("object has wrong value. got=%t, want=%t", result.Value, expected)
		return false
	}
	return true
}

 

Bool ๋ฆฌํ„ฐ๋Ÿด ์ด๋ผ๋Š” ์ž์ฒดํ‰๊ฐ€ ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ์šฐ๋ฆฌ๋Š” Eval ํ•จ์ˆ˜์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

 

// evaluator/evaluator.go

func Eval(node ast.Node) object.Object {
	// type switch
	switch node := node.(type) {

	// ๋ช…๋ น๋ฌธ
	case *ast.Program:
		return evalStatements(node.Statements)

	case *ast.ExpressionStatement:
		return Eval(node.Expression)

	// ํ‘œํ˜„์‹
	case *ast.IntegerLiteral:
		return &object.Integer{Value: node.Value}
	case *ast.Boolean:  // Bool์— ๋Œ€ํ•œ case ๋ฌธ ์ถ”๊ฐ€
		return &object.Boolean{Value: node.Value}
	}
	return nil

 

๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์„œ ํ•œ ๊ฐ€์ง€ ๊ฐœ์„ ํ•  ํฌ์ธํŠธ๊ฐ€ ์žˆ๋‹ค. ๋ฐ”๋กœ Bool ๊ฐ’์ธ true/false๋Š” ์–ธ์ œ๋“  ๋™์ผํ•˜๊ฒŒ true/false ๋ผ๋Š” ์ ์ด๋‹ค. ์ด์ „์— ์ถ”๊ฐ€ํ–ˆ๋˜ ์ •์ˆ˜์˜ ๊ฒฝ์šฐ ์ •์ˆ˜๊ฐ€ 5์ผ๋•Œ๋„, 10์ผ๋•Œ๋„, 15์ผ ๋•Œ๋„, 999์ผ ๋•Œ๋„ ์กด์žฌํ•œ๋‹ค. ์ฆ‰, ๋งค๋ฒˆ ์ •์ˆ˜์˜ ๊ฐ’์ด ๋‹ฌ๋ผ์ง„๋‹ค๋Š” ์ ์ด๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๋Š” ๋งค๋ฒˆ object.Integer ๋ผ๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ true/false์˜ ๊ฒฝ์šฐ๋Š” ์–ธ์ œ๋‚˜ ๋™์ผํ•œ true/false์ด๋‹ค. ๋”ฐ๋ผ์„œ ์œ„ ์†Œ์Šค์ฝ”๋“œ์ฒ˜๋Ÿผ ๋งค๋ฒˆ object.Boolean ์ด๋ผ๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๋‹ค์‹œ ๋งํ•ด์„œ, ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ๊ตฌ์กฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด ์“ธ๋ฐ์—†๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„๋ฅผ ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

 

๊ฒฐ๊ณผ์ ์œผ๋กœ, true/false์— ๋Œ€ํ•œ ๊ตฌ์กฐ์ฒด๋Š” ๊ณ ์ •๋œ ๊ฐ’์ด๊ธฐ์— ์ „์—ญ ๋ณ€์ˆ˜๋กœ ์ •์˜ํ•ด๋†“๊ณ , ์ด๋ฅผ ๊ฐ€์ ธ๋‹ค ์‚ฌ์šฉํ•ด๋„ ๋  ๊ฒƒ์ด๋‹ค. ์ด๋กœ ์ธํ•ด ๊ฐœ์„ ๋œ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

// evaluator/evaluator.go

var (
	TRUE  = &object.Boolean{Value: true}
	FALSE = &object.Boolean{Value: false}
)

func Eval(node ast.Node) object.Object {
	// type switch
	switch node := node.(type) {

	// ๋ช…๋ น๋ฌธ
	case *ast.Program:
		return evalStatements(node.Statements)

	case *ast.ExpressionStatement:
		return Eval(node.Expression)

	// ํ‘œํ˜„์‹
	case *ast.IntegerLiteral:
		return &object.Integer{Value: node.Value}
	case *ast.Boolean:
		return nativeBoolToBooleanObject(node.Value) // ๊ฐœ์„ ๋œ ๋ฐฉ์‹
	}
	return nil
    
func nativeBoolToBooleanObject(input bool) *object.Boolean {
	if input {
		return TRUE
	}
	return FALSE
}

3-4-3. ์ž์ฒดํ‰๊ฐ€ ํ‘œํ˜„์‹(3): Null

๋‹ค์Œ์œผ๋กœ ์ถ”๊ฐ€ํ•ด๋ณผ ์ž์ฒดํ‰๊ฐ€ ํ‘œํ˜„์‹ ์ข…๋ฅ˜๋กœ๋Š” Null ์ด๋‹ค. Null๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Bool ๊ฐ’์ธ true/false ์ฒ˜๋Ÿผ ์ฐธ์กฐ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜๋งŒ ๋งŒ๋“ค์–ด๋„ ๋ฌด๋ฐฉํ•˜๋‹ค. ๋”ฐ๋ผ์„œ ์•„๋ž˜์ฒ˜๋Ÿผ ์ „์—ญ๋ณ€์ˆ˜์— Null ์šฉ ๊ตฌ์กฐ์ฒด๋ฅผ ์ „์—ญ์ ์œผ๋กœ ์ •์˜ํ•˜๊ณ  case ๊ตฌ๋ฌธ์— ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

 

// evaluator/evaluator.go

var (
	NULL  = &object.Null{}
	TRUE  = &object.Boolean{Value: true}
	FALSE = &object.Boolean{Value: false}
)

 

์ž, ์ด์ œ ์ง€๊ธˆ๊นŒ์ง€ ๋งŒ๋“  ํƒ€์ž… ๊ฐ์ฒด๋“ค์„ REPL ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•ด์„œ ๋ง˜๊ป ์ž…๋ ฅํ•ด๋ณด์ž. ์šฐ๋ฆฌ์˜ ์˜๋„๋Œ€๋กœ ํ‰๊ฐ€๊นŒ์ง€ ์ž˜ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ฐธ๊ณ ๋กœ null ์„ ์ž…๋ ฅํ•˜๋ฉด ์•„๋ฌด๊ฒƒ๋„ ์ถœ๋ ฅ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ๋งž๋‹ค. ์‹ค์ œ ํŒŒ์ด์ฌ ์ธํ„ฐํ”„๋ฆฌํ„ฐ์—์„œ๋„ null์„ ์˜๋ฏธํ•˜๋Š” None์„ ์ž…๋ ฅํ•˜๋ฉด ์•„๋ฌด๊ฒƒ๋„ ์ถœ๋ ฅ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

go run main.go

3-4-4. ์ „์œ„ ํ‘œํ˜„์‹

๋‹ค์Œ์€ ์ „์œ„ ํ‘œํ˜„์‹์— ๋Œ€ํ•ด์„œ ํ‰๊ฐ€ํ•ด๋ณด๋„๋ก ํ•˜์ž. Monkey ์–ธ์–ด์—์„œ ์ง€์›์ด ๊ฐ„์œผํ•œ ์ „์œ„ ํ‘œํ˜„์‹์€ ! ์™€ - ์ด๋‹ค. ์šฐ์„  ์ „์œ„ ํ‘œํ˜„์‹์ด๋ž€ ๋ฌด์—‡์ธ์ง€ ๋‹ค์‹œ ๋ณต์Šตํ•ด์กฐ๋ฉด ํ”ผ์—ฐ์‚ฐ์ž๊ฐ€ 1๊ฐœ์ธ ์—ฐ์‚ฐ์ž๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ „์œ„ ํ‘œํ˜„์‹์„ ์ ์ ˆํžˆ ํ‰๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ”ผ์—ฐ์‚ฐ์ž์— ๋Œ€ํ•ด์„œ ๋จผ์ € ํ‰๊ฐ€๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋‚œ ๋’ค, ์ „์œ„ ์—ฐ์‚ฐ์ž๋ฅผ ํ‰๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด ์ง์ „์— ํ”ผ์—ฐ์‚ฐ์ž๋ฅผ ํ‰๊ฐ€ํ•œ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.

 

๋จผ์ € ์ „์œ„ ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ถ€ํ„ฐ ์ž‘์„ฑํ•ด๋ณด์ž.

 

// evaluator/evaluator_test.go

func TestBangOperator(t *testing.T) {
	tests := []struct {
		input    string
		expected bool
	}{
		{"!true", false},
		{"!false", true},
		{"!5", false},
		{"!!true", true},
		{"!!false", false},
		{"!!5", true},
	}
	for _, tt := range tests {
		evaluated := testEval(tt.input)
		testBooleanObject(t, evaluated, tt.expected)
	}
}

 

๊ทธ๋Ÿฌ๋ฉด ์ด์ œ ์ „์œ„ ํ‘œํ˜„์‹์„ ์‹ค์งˆ์ ์œผ๋กœ ํ‰๊ฐ€ํ•˜๋„๋ก ํ•˜๋Š” ๋กœ์ง์„ ์ž‘์„ฑํ•  ์ฐจ๋ก€๋‹ค. ํ‰๊ฐ€ ๋กœ์ง์˜ ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ ๋ฉ”์†Œ๋“œ ๊ฒฉ์ธ Eval ๋ฉ”์†Œ๋“œ์— ์ „์œ„ ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•˜๋„๋ก switch-case ๊ตฌ๋ฌธ์„ ์ถ”๊ฐ€ํ•ด๋ณด๋„๋ก ํ•˜์ž.

 

// evaluator/evaluator.go

func Eval(node ast.Node) object.Object {
	// type switch
	switch node := node.(type) {

	(.. ์ƒ๋žต ...)

	// ํ‘œํ˜„์‹
	case *ast.IntegerLiteral:
		return &object.Integer{Value: node.Value}
	case *ast.Boolean:
		return nativeBoolToBooleanObject(node.Value)
	case *ast.PrefixExpression:
		right := Eval(node.Right)
		return evalPrefixExpression(node.Operator, right)
	}
	return nil

 

์ด์ „์— ํŒŒ์„œ๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ ์ •์˜ํ•œ AST ๋…ธ๋“œ ์ค‘ ์ „์œ„ ํ‘œํ˜„์‹์— ํ•ด๋‹นํ•˜๋Š” ํƒ€์ž…์ด๋ผ๋ฉด ํ•ด๋‹น ๋ถ„๊ธฐ๋ฅผ ํƒ€๋„๋ก ํ•œ๋‹ค. ๋ฐฉ๊ธˆ ์ „์œ„ ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด์„œ ํ”ผ์—ฐ์‚ฐ์ž๋ถ€ํ„ฐ ํ‰๊ฐ€ํ•œ๋‹ค๊ณ  ํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ right := Eval(node.Right) ๋ผ๋Š” ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์ „์œ„ ํ‘œํ˜„์‹์˜ ์˜ค๋ฅธ์ชฝ์— ์œ„์น˜ํ•œ ํ”ผ์—ฐ์‚ฐ์ž๋ฅผ ๋จผ์ € ํ‰๊ฐ€ํ•˜๋„๋ก ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚œ ๋’ค, evalPrefixExpression ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ „์œ„ ์—ฐ์‚ฐ์ž์— ๋Œ€ํ•œ ํ‰๊ฐ€๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ด์ œ evalPrefixExpression ๋ฉ”์†Œ๋“œ์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณผ ์ฐจ๋ก€๋‹ค.

 

// evaluator/evaluator.go

func evalPrefixExpression(operator string, right object.Object) object.Object {
	switch operator {
	case "!":
		return evalBangOperatorExpression(right)
	default:
		return NULL
	}
}

 

evalPrefixExpression ๋ฉ”์†Œ๋“œ์˜ ์ธ์ž ์ค‘ operator ์—๋Š” ์ „์œ„ ์—ฐ์‚ฐ์ž๊ฐ€ ๋‹ด๊ฒจ ๋„˜์–ด์˜จ๋‹ค. ! ๋‚˜ - ๊ฐ€ ๋˜๊ฒ ๋‹ค. ์šฐ์„  ! ์—ฐ์‚ฐ์ž๋ถ€ํ„ฐ ํ‰๊ฐ€ํ•ด๋ณด์ž. operator๊ฐ€ ! ์ผ ๊ฒฝ์šฐ, ๋‹ค์‹œ evalBangOperatorExpression ์ด๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ๋‹น์—ฐํžˆ evalBangOperatorExpression ๋ฉ”์†Œ๋“œ์˜ ์ƒ๊น€์ƒˆ๋ฅผ ๋ด๋ณด์ž.

 

// evaluator/evaluator.go

func evalBangOperatorExpression(right object.Object) object.Object {
	switch right {
	case TRUE:
		return FALSE
	case FALSE:
		return TRUE
	case NULL:
		return TRUE
	default:
		return FALSE
	}
}

 

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

 

// evaluator/evaluator_test.go

func TestEvalIntegerExpression(t *testing.T) {
	tests := []struct {
		input    string
		expected int64
	}{
		{"5", 5},
		{"10", 10},
		{"-5", -5},    // ์ „์œ„ ์—ฐ์‚ฐ์ž ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค(1) ์ถ”๊ฐ€
		{"-10", -10},  // ์ „์œ„ ์—ฐ์‚ฐ์ž ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค(2) ์ถ”๊ฐ€
	}
	for _, tt := range tests {
		evaluated := testEval(tt.input)
		testIntegerObject(t, evaluated, tt.expected)
	}
}

 

์ถ”๊ฐ€๋œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ํ†ต๊ณผ์‹œํ‚ค๋ ค๋ฉด ๋ฐฉ๊ธˆ ์ž‘์„ฑํ–ˆ๋˜ ๋ฉ”์†Œ๋“œ ์ค‘ evalPrefixExpression ๋ฉ”์†Œ๋“œ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ™•์žฅํ•ด์•ผ ํ•œ๋‹ค.

 

// evaluator/evaluator.go

func evalPrefixExpression(operator string, right object.Object) object.Object {
	switch operator {
	case "!":
		return evalBangOperatorExpression(right)
	case "-":
		return evalMinusPrefixOperatorExpression(right)
	default:
		return NULL
	}
}

 

๊ทธ๋ฆฌ๊ณ  evalMinusPrefixOperatorExpression ๋ฉ”์†Œ๋“œ๋ผ๋Š” ๊ฒƒ์ด ์ƒˆ๋กญ๊ฒŒ ๋“ฑ์žฅํ–ˆ๋‹ค. ํ•ด๋‹น ๋ฉ”์†Œ๋“œ์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋กœ ๊ฐ€๋ณด์ž.

 

// evaluator/evaluator.go

func evalMinusPrefixOperatorExpression(right object.Object) object.Object {
	if right.Type() != object.INTEGER_OBJ {
		return NULL
	}
	value := right.(*object.Integer).Value
	return &object.Integer{Value: -value}
}

 

๋กœ์ง ์ž์ฒด๋Š” ๊ฐ„๋‹จํ•˜๋‹ค. ๋จผ์ € Type ์ด๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋กœ ์ •์ˆ˜ ๊ฐ์ฒด์ธ์ง€ ํ™•์ธํ•œ๋‹ค. ์ •์ˆ˜ ๊ฐ์ฒด๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ ์ฆ‰์‹œ NULL์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•˜๊ณ , ์ •์ˆ˜ ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ์—๋Š” type assertion์„ ์ด์šฉํ•ด์„œ object.Integer ๊ตฌ์กฐ์ฒด์˜ Value ๋ฉค๋ฒ„๋ฅผ ์ถ”์ถœํ•œ ํ›„ -๋ฅผ ๋ถ™์—ฌ ๋‹ค์‹œ object.Integer ๊ตฌ์กฐ์ฒด๋ฅผ ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑํ•ด ๋ฆฌํ„ดํ•ด์ค€๋‹ค.

3-4-5. ์ค‘์œ„ ํ‘œํ˜„์‹

๋‹ค์Œ์€ ์ค‘์œ„ ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•ด๋ณด๋„๋ก ํ•˜์ž. ์ผ๋‹จ ์ง€๊ธˆ ๋‹น์žฅ Monkey ์–ธ์–ด์—์„œ ์ค‘์œ„ ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ์ค‘์œ„ ํ‘œํ˜„์‹ ์˜ˆ์ œ๋Š” ํฌ๊ฒŒ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

5 + 5;
5 - 5;
5 * 5;
5 / 5;

1 < 2;
1 > 2;
1 == 1;
1 != 1;

 

์œ„ 4๊ฐ€์ง€๋Š” ๊ฒฐ๊ณผ๊ฐ’์ด Boolean ๊ฐ’์ด ์•„๋‹Œ ๊ฒƒ, ์•„๋ž˜ 4๊ฐ€์ง€๋Š” ๊ฒฐ๊ณผ๊ฐ’์ด Boolean ๊ฐ’์œผ๋กœ ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค. ์šฐ์„  ์œ„๋Š” ๊ฒฐ๊ณผ๊ฐ’์ด Boolean ๊ฐ’์ด ์•„๋‹Œ ์ค‘์œ„ ํ‘œํ˜„์‹ ์ฆ‰, ์œ„์ชฝ 4๊ฐ€์ง€์˜ ์ค‘์œ„ ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณผ ๊ฒƒ์ด๋‹ค. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ๋ฐฉ๊ธˆ ์‚ฌ์šฉํ–ˆ๋˜ TestEvalIntegerExpression ์ด๋ผ๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๋ฉ”์†Œ๋“œ๋ฅผ ๋˜ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค.

 

// evaluator/evaluator.go

func TestEvalIntegerExpression(t *testing.T) {
	tests := []struct {
		input    string
		expected int64
	}{
		{"5", 5},
		{"10", 10},
		{"-5", -5},
		{"-10", -10},
		{"5 + 5 + 5 + 5 - 10", 10}, // ์ƒˆ๋กญ๊ฒŒ ์ถ”๊ฐ€๋œ ์ผ€์ด์Šค๋“ค
		{"2 * 2 * 2 * 2 * 2", 32},
		{"-50 + 100 + -50", 0},
		{"5 * 2 + 10", 20},
		{"5 + 2 * 10", 25},
		{"20 + 2 * -10", 0},
		{"50 / 2 * 2 + 10", 60},
		{"2 * (5 + 10)", 30},
		{"3 * 3 * 3 + 10", 37},
		{"3 * (3 * 3) + 10", 37},
		{"(5 + 10 * 2 + 15 / 3) * 2 + -10", 50},
	}
	for _, tt := range tests {
		evaluated := testEval(tt.input)
		testIntegerObject(t, evaluated, tt.expected)
	}

 

์œ„์— ์ถ”๊ฐ€๋œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ปค๋ฒ„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ๊ฐ€์žฅ ๋จผ์ € ํ•ด์•ผํ•  ๊ฒƒ์€ ๋‹น์—ฐํ•˜๊ฒŒ๋„ ํ‰๊ฐ€ ํ”„๋กœ์„ธ์Šค์˜ ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ ๋ฉ”์†Œ๋“œ์ธ Eval ๋ฉ”์†Œ๋“œ๋ฅผ ํ™•์žฅํ•ด์•ผ ํ•œ๋‹ค.

 

// evaluator/evaluator.go

func Eval(node ast.Node) object.Object {
	// type switch
	switch node := node.(type) {

	// ํ‘œํ˜„์‹
	(... ์ƒ๋žต ...)
	case *ast.PrefixExpression:
		right := Eval(node.Right)
		return evalPrefixExpression(node.Operator, right)
	case *ast.InfixExpression:
		left := Eval(node.Left)
		right := Eval(node.Right)
		return evalInfixExpression(node.Operator, left, right)
	}
	return nil
}

 

์ค‘์œ„ ํ‘œํ˜„์‹๋„ ์ „์œ„ ํ‘œํ˜„์‹ ๋•Œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ผ๋‹จ ๋จผ์ € ํ”ผ์—ฐ์‚ฐ์ž๋“ค์„ ํ‰๊ฐ€ํ•œ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— Eval ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ๊ฐ€ ํ˜ธ์ถœํ•ด ์—ฐ์‚ฐ์ž์˜ ์™ผ์ชฝ, ์˜ค๋ฅธ์ชฝ์— ์žˆ๋Š” ๊ฐ ํ”ผ์—ฐ์‚ฐ์ž๋“ค์„ ๋จผ์ € ํ‰๊ฐ€ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚œ ๋‹ค์Œ evalInfixExpression ์ด๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. evalInfixExpression ๋ฉ”์†Œ๋“œ๋กœ ์ด๋™ํ•ด๋ณด์ž.

 

// evaluator/evaluator.go

func evalInfixExpression(operator string, left, right object.Object) object.Object {
	switch {
	case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ:
		return evalIntegerInfixExpression(operator, left, right)
	default:
		return NULL
	}
}

 

๋จผ์ € ์™ผ์ชฝ, ์˜ค๋ฅธ์ชฝ ํ”ผ์—ฐ์‚ฐ์ž๋“ค์˜ ํƒ€์ž…์„ ๊ฐ๊ฐ ํ™•์ธํ•œ๋‹ค. ํ˜„์žฌ ์šฐ๋ฆฌ๋Š” ์ •์ˆ˜ ํƒ€์ž…๋“ค ๊ฐ„์˜ ์ค‘์œ„ ํ‘œํ˜„์‹์— ๋Œ€ํ•ด์„œ๋งŒ ํ‰๊ฐ€ํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๋‘ ํ”ผ์—ฐ์‚ฐ์ž์˜ ํƒ€์ž…์ด ๋ชจ๋‘ ์ •์ˆ˜์ผ ๋•Œ๋งŒ evalIntegerInfixExpression ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. evalIntegerInfixExpression ๋ฉ”์†Œ๋“œ๋กœ ๋˜ ์ด๋™ํ•ด๋ณด์ž.

 

// evaluator/evaluator.go

func evalIntegerInfixExpression(operator string, left, right object.Object) object.Object {
	leftVal := left.(*object.Integer).Value
	rightVal := right.(*object.Integer).Value
	switch operator {
	case "+":
		return &object.Integer{Value: leftVal + rightVal}
	case "-":
		return &object.Integer{Value: leftVal - rightVal}
	case "*":
		return &object.Integer{Value: leftVal * rightVal}
	case "/":
		return &object.Integer{Value: leftVal / rightVal}
	default:
		return NULL
	}
}

 

ํ•ด๋‹น ๋ฉ”์†Œ๋“œ์—์„œ ์‹ค์งˆ์ ์ธ ์ค‘์œ„ ํ‘œํ˜„์‹ ์—ฐ์‚ฐ์ด ์ด๋ฃจ์–ด์ง€๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋จผ์ € ํ”ผ์—ฐ์‚ฐ์ž 2๊ฐœ์— ์žˆ๋Š” ์‹ค์งˆ์ ์ธ ๊ฐ’์„ ์ถ”์ถœํ•œ ๋’ค, ์—ฐ์‚ฐ์ž ์ข…๋ฅ˜์— ๋งž๊ฒŒ ๊ณ„์‚ฐ ํ›„ ๋‹ค์‹œ object.Integer ๊ตฌ์กฐ์ฒด๋กœ ๋งŒ๋“ค์–ด ๋ฐ˜ํ™˜ํ•ด์ค€๋‹ค.

 

์ด์ œ ๋‚˜๋จธ์ง€ 4๊ฐœ์˜ ์ค‘์œ„ ํ‘œํ˜„์‹ ์ฆ‰, ๊ฒฐ๊ณผ๊ฐ’์ด Boolean์œผ๋กœ ๋‚˜์˜ค๋Š” ์ค‘์œ„ ํ‘œํ˜„์‹๋“ค์— ๋Œ€ํ•ด์„œ๋„ ํ‰๊ฐ€๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•ด๋ณด์ž. ์ง€๊ธˆ๊นŒ์ง€ ๊ตฌํ˜„ํ•œ ๋ฉ”์†Œ๋“œ์˜ ํ™•์žฅ ๋ฐ–์— ์—†๋‹ค. ์ผ๋‹จ ๊ธฐ์กด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๋ฉ”์†Œ๋“œ์ธ TestEvalBooleanExpression์— ์•„๋ž˜์™€ ๊ฐ™์ด ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

 

// evaluator/evaluator_test.go

func TestEvalBooleanExpression(t *testing.T) {
	tests := []struct {
		input    string
		expected bool
	}{
		{"true", true},
		{"false", false},
		{"1 < 2", true},  // ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ์ผ€์ด์Šค๋“ค
		{"1 > 2", false},
		{"1 < 1", false},
		{"1 > 1", false},
		{"1 == 1", true},
		{"1 != 1", false},
		{"1 == 2", false},
		{"1 != 2", true},
	}
	for _, tt := range tests {
		evaluated := testEval(tt.input)
		testBooleanObject(t, evaluated, tt.expected)
	}
}

 

์ถ”๊ฐ€๋œ ์ผ€์ด์Šค๋ฅผ ์ปค๋ฒ„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์šฐ๋ฆฌ๋Š” ๋ฐฉ๊ธˆ ๊ตฌํ˜„ํ–ˆ๋˜ evalIntegerInfixExpression์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ™•์žฅํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

 

// evaluator/evaluator.go

func evalIntegerInfixExpression(operator string, left, right object.Object) object.Object {
	leftVal := left.(*object.Integer).Value
	rightVal := right.(*object.Integer).Value
	switch operator {
	case "+":
		return &object.Integer{Value: leftVal + rightVal}
	case "-":
		return &object.Integer{Value: leftVal - rightVal}
	case "*":
		return &object.Integer{Value: leftVal * rightVal}
	case "/":
		return &object.Integer{Value: leftVal / rightVal}
	case "<":
		return nativeBoolToBooleanObject(leftVal < rightVal)
	case ">":
		return nativeBoolToBooleanObject(leftVal > rightVal)
	case "==":
		return nativeBoolToBooleanObject(leftVal == rightVal)
	case "!=":
		return nativeBoolToBooleanObject(leftVal != rightVal)
	default:
		return NULL
	}
}

 

๊ธฐ์กด์— Boolean ๋ฆฌํ„ฐ๋Ÿด์„ ํ‰๊ฐ€ํ•  ๋•Œ ๋งŒ๋“ค์–ด๋‘์—ˆ๋˜ nativeBoolToBooleanObject ๋ฉ”์†Œ๋“œ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ„ํŽธํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜๊ฐ€ ์žˆ์–ด์ง„๋‹ค.

์ง€๊ธˆ๊นŒ์ง€๋Š” ๋ชจ๋‘ ํ”ผ์—ฐ์‚ฐ์ž๊ฐ€ ์ •์ˆ˜์ผ ๋•Œ์˜ ์ค‘์œ„ ํ‘œํ˜„์‹์ด์˜€๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•  ๊ฒƒ์€ ๋ฐ”๋กœ Boolean์ด ํ”ผ์—ฌ์‚ฐ์ž์ผ ๋•Œ์˜ ์ค‘์œ„ ํ‘œํ˜„์‹์ด๋‹ค. ๋ฐ”๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ง์ด๋‹ค.

 

true == true
false == false
true != false
true != true

 

Monkey ์–ธ์–ด์—์„œ๋Š” Boolean์ด ํ”ผ์—ฐ์‚ฐ์ž์ผ ๋•Œ์˜ ์ค‘์œ„ ํ‘œํ˜„์‹์ผ ๊ฒฝ์šฐ, == ์™€ != ์—ฐ์‚ฐ์ž๋งŒ ์ง€์›์ด ๋œ๋‹ค. <, > ์™€ ๊ฐ™์€ ๋Œ€์†Œ๋น„๊ต ์—ฐ์‚ฐ์ž๋Š” ์ง€์›๋˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ์ง€์›์ด ๋˜๋Š” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๋จผ์ € ์ถ”๊ฐ€ํ•ด๋ณด์ž.

 

// evaluator/evaluator_test.go

func TestEvalBooleanExpression(t *testing.T) {
	tests := []struct {
		input    string
		expected bool
	}{
		{"true", true},
		{"false", false},
		{"1 < 2", true},
		{"1 > 2", false},
		{"1 < 1", false},
		{"1 > 1", false},
		{"1 == 1", true},
		{"1 != 1", false},
		{"1 == 2", false},
		{"1 != 2", true},
		{"true == true", true},
		{"false == false", true},
		{"true == false", false},
		{"true != false", true},
		{"false != true", true},
		{"(1 < 2) == true", true},
		{"(1 < 2) == false", false},
		{"(1 > 2) == true", false},
		{"(1 > 2) == false", true},
	}
	for _, tt := range tests {
		evaluated := testEval(tt.input)
		testBooleanObject(t, evaluated, tt.expected)
	}
}

 

 

์ด์ œ ์œ„ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ปค๋ฒ„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์šฐ๋ฆฌ๋Š” ๋‹จ ๋ช‡ ์ค„๋งŒ evalInfixExpression ๋ฉ”์†Œ๋“œ์— ์ถ”๊ฐ€๋งŒ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 

// evaluator/evaluator.go

func evalInfixExpression(operator string, left, right object.Object) object.Object {
	switch {
	case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ:
		return evalIntegerInfixExpression(operator, left, right)
	case operator == "==":
		return nativeBoolToBooleanObject(left == right)
	case operator == "!":
		return nativeBoolToBooleanObject(left != right)
	default:
		return NULL
	}
}

 

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

 

๊ทธ๋ž˜์„œ ์ •์ˆ˜์™€ ๊ฐ™์ด ํฌ์ธํ„ฐ ๋ณ€์ˆ˜๋กœ ๋™๋“ฑ์„ฑ ๋น„๊ต๊ฐ€ ์•ˆ๋˜๋Š” ๊ฒฝ์šฐ์— ๋Œ€๋น„ํ•˜๊ธฐ ์œ„ํ•ด์„œ switch-case ๊ตฌ๋ฌธ ์ค‘์— ๊ฐ€์žฅ ์ฒซ๋ฒˆ์งธ์˜ case ๊ตฌ๋ฌธ์— ์™ผ์ชฝ, ์˜ค๋ฅธ์ชฝ ํ”ผ์—ฐ์‚ฐ์ž์˜ ํƒ€์ž…์ด ๋ชจ๋‘ ์ •์ˆ˜์ธ ๊ฒƒ์„ ํ™•์ธํ•œ ๋’ค์— ํ”ผ์—ฐ์‚ฐ์ž๋“ค์ด ๋ชจ๋‘ ์ •์ˆ˜์ผ ๋•Œ์˜ ์ค‘์œ„ ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•˜๋Š” evalIntegerInfixExpression ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋„๋ก ํ•œ ๊ฒƒ์ด๋‹ค.

3-4-6. ์กฐ๊ฑด์‹

์ด๋ฒˆ์— ํ‰๊ฐ€ํ•ด๋ณผ ํ‘œํ˜„์‹์€ ์กฐ๊ฑด์‹์ด๋‹ค.์กฐ๊ฑด์‹์€ ๋ณดํ†ต if ~ else ๊ตฌ๋ฌธ์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค. ํ•ต์‹ฌ์€ ์˜ฌ๋ฐ”๋ฅธ ๋ถ„๊ธฐ๋ฅผ ์–ด๋–ป๊ฒŒ ํ‰๊ฐ€ํ•  ๊ฒƒ์ด๋ƒ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์†Œ์Šค์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•ด๋ณด์ž.

 

if (x > 10) {
    print 'A'
} else {
    print 'B'
}

 

์กฐ๊ฑด์‹์€ x > 10์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด if ๊ตฌ๋ฌธ์œผ๋กœ ๋ถ„๊ธฐ์‹œํ‚ฌ ๊ฒฝ์šฐ๋Š” ์กฐ๊ฑด์‹์ด ์ฐธ์ผ ๊ฒฝ์šฐ์—๋งŒ ํ•ด์•ผํ• ๊นŒ? ์•„๋‹ˆ๋ฉด ์กฐ๊ฑด์‹์ด ์ฐธ์ธ ๊ฒƒ '๊ฐ™์„' ๋•Œ์—๋„ ํ•ด์•ผํ• ๊นŒ? ์ฆ‰, ์ •๋ง ๋ง ๊ทธ๋Œ€๋กœ ์กฐ๊ฑด์‹ x > 10์˜ ๊ฒฐ๊ณผ๊ฐ€ true์ผ ๋•Œ๋งŒ ๋ถ„๊ธฐํ•ด์•ผ ํ•˜๋Š”์ง€, ์•„๋‹ˆ๋ฉด x > 10์˜ ๊ฒฐ๊ณผ๊ฐ€ true๊ฐ€ ์•„๋‹ˆ์ง€๋งŒ false๋„ ์•„๋‹ˆ๊ณ  null๋„ ์•„๋‹ ๊ฒฝ์šฐ(์ด๋ฅผ truthy ํ•˜๋‹ค๋ผ๊ณ  ํ•จ)์—๋„ ๋ถ„๊ธฐํ•ด์•ผ ํ• ๊นŒ? ๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜๋ฉด ์šฐ๋ฆฌ๊ฐ€ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ๋Š” Monkey ์–ธ์–ด์—์„œ๋Š” ์กฐ๊ฑด์‹์˜ ๊ฒฐ๊ณผ๊ฐ€ truthy ํ•œ ๊ฒฝ์šฐ๋ผ๋ฉด ๋ชจ๋‘ if ๋ถ„๊ธฐ๋ฌธ์„ ํƒ€๋„๋ก ํ•  ๊ฒƒ์ด๋‹ค.

 

์ด์ œ ์กฐ๊ฑด์‹์„ ํ‰๊ฐ€ํ•ด๋ณด๋„๋ก ํ•˜์ž. ๋‹ค์Œ ์ฝ”๋“œ๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์ด๋‹ค.

 

// evaluator/evaluator_test.go

func TestIfElseExpression(t *testing.T) {
	tests := []struct {
		input    string
		expected interface{}
	}{
		{"if (true) { 10 }", 10},
		{"if (false) { 10 }", nil},
		{"if (1) { 10 }", 10},
		{"if (1 < 2) { 10 }", 10},
		{"if (1 > 2) { 10 }", nil},
		{"if (1 > 2) { 10 } else { 20 }", 20},
		{"if (1 < 2) { 10 } else { 20 }", 10},
	}
	
	for _, tt := range tests {
		evaluated := testEval(tt.input)
		integer, ok := tt.expected.(int)
		if ok {
			testIntegerObject(t, evaluated, int64(integer))
		} else {
			testNullObject(t, evaluated)
		}
	}
}

func testNullObject(t *testing.T, obj object.Object) bool {
	if obj != NULL {
		t.Errorf("object is not NULL. got=%T (%+v)", obj, obj)
		return false
	}
	return true
}

 

๊ทธ๋Ÿฐ๋ฐ ์œ„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ๋ช‡ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๋ณด๋ฉด ์•ฝ๊ฐ„ ๋‹ค๋ฅธ ์กฐ๊ฑด์‹์ด ์žˆ๋‹ค. ๋ฐ”๋กœ else ๊ตฌ๋ฌธ์ด ์—†๋Š” ์ผ€์ด์Šค๋‹ค. ์กฐ๊ฑด์‹์ด ํ•ญ์ƒ if ์™€ else๋กœ๋งŒ ์ด๋ฃจ์–ด์ ธ ์žˆ์œผ๋ž€ ๋ฒ•์€ ์—†๋‹ค. Monkey ์–ธ์–ด์—์„œ๋Š” else๊ฐ€ ์—†์ด if ๊ตฌ๋ฌธ๋งŒ ์žˆ์–ด๋„ ์กฐ๊ฑด์‹์ด ํ‰๊ฐ€๋  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•  ๊ฒƒ์ด๋‹ค. 

 

์ด์ œ ์กฐ๊ฑด์‹์„ ์‹ค์งˆ์ ์œผ๋กœ ํ‰๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด๋ณด์ž. ๊ฐ€์žฅ ๋จผ์ € ํ‰๊ฐ€์˜ ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ ๋ฉ”์†Œ๋“œ๊ฒฉ์ธ Eval ๋ฉ”์†Œ๋“œ์— case ๊ตฌ๋ฌธ์„ ์ถ”๊ฐ€ํ•ด์ฃผ์ž. 

 

// evaluator/evaluator.go

func Eval(node ast.Node) object.Object {
	// type switch
	switch node := node.(type) {

	// ๋ช…๋ น๋ฌธ
	... (์ƒ๋žต) ...

	// ํ‘œํ˜„์‹
	... (์ƒ๋žต) ...
	case *ast.BlockStatement:
		return evalStatements(node.Statements)
	case *ast.IfExpression:
		return evalIfExpression(node)
	}
	return nil
}

 

๊ทธ๋Ÿฐ๋ฐ ๊ฐ‘์ž๊ธฐ BlockStatement๊ฐ€ ๋“ฑ์žฅํ•œ ๊ฒƒ์ด ์˜์•„ํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค. ์ด๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„œ ์šฐ๋ฆฌ๋Š” ์˜ˆ์ „์— ์กฐ๊ฑด์‹์„ AST ๋…ธ๋“œ๋กœ ํŒŒ์‹ฑํ•˜๊ธฐ ์œ„ํ•ด ์ •์˜ํ–ˆ๋˜ IfExpression ๊ตฌ์กฐ์ฒด์˜ ํ˜•ํƒœ๋ฅผ ๋‹ค์‹œ ํ•œ๋ฒˆ ๋ด์•ผ ํ•œ๋‹ค.

 

// ast/ast.go

type IfExpression struct {
	Token       token.Token
	Condition   Expression
	Consequence *BlockStatement
	Alternative *BlockStatement
}

type BlockStatement struct {
	Token      token.Token
	Statements []Statement
}

 

๊ทธ ๋•Œ ๋‹น์‹œ ์กฐ๊ฑด์‹์€ ์กฐ๊ฑด์‹๊ณผ if ๋ถ„๊ธฐ๋ฌธ์— ํ•ด๋‹นํ•˜๋Š” Consequence ๊ตฌ๋ฌธ, if ๋ถ„๊ธฐ๋ฌธ๋ฅผ ํƒ€์ง€ ์•Š๋Š” ๋‚˜๋จธ์ง€ ๊ตฌ๋ฌธ์€ Alternative ๊ตฌ๋ฌธ์ด๋ผ๊ณ  ๋ถˆ๋ €๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด Consequence, Alternative ๊ตฌ๋ฌธ์€ ํ•˜๋‚˜ ์ด์ƒ์˜ ๋ช…๋ น๋ฌธ์œผ๋กœ ๊ตฌ์„ฑ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๋Š” BlockStatement ๋ผ๋Š” ๊ตฌ์กฐ์ฒด๋กœ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ช…๋ น๋ฌธ์„ ๋“ค๊ณ  ์žˆ๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ์ •์˜ํ–ˆ์—ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ‰๊ฐ€ํ•˜๋Š” Eval ๋ฉ”์†Œ๋“œ์˜ case ๊ตฌ๋ฌธ์— BlockStatement์ด ์ถ”๊ฐ€๋œ ๊ฒƒ์ด๋‹ค.

 

์ด์ œ ๊ทธ๋Ÿฌ๋ฉด evalIfExpression ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ดํŽด๋ณผ ์ฐจ๋ก€๋‹ค.

 

// evaluator/evaluator.go

func evalIfExpression(ie *ast.IfExpression) object.Object {
	condition := Eval(ie.Condition)
	
	if isTruthy(condition) {
		return Eval(ie.Consequence)
	} else if ie.Alternative != nil {
		return Eval(ie.Alternative)
	} else {
		return NULL
	}
}

 

๊ฐ€์žฅ ๋จผ์ € ์กฐ๊ฑด์‹์„ ํ‰๊ฐ€ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ์กฐ๊ฑด์‹ ํ‰๊ฐ€ ๊ฒฐ๊ณผ๊ฐ€ Truthy ํ•œ์ง€ ์ฒดํฌํ•˜๊ณ  Truthy ํ•˜๋‹ค๊ณ  ํŒ๋‹จ๋˜๋ฉด if ๋ถ„๊ธฐ๋ฌธ์ธ Consequence ๊ตฌ๋ฌธ์„ ํ‰๊ฐ€ํ•œ๋‹ค. ์ด ๋•Œ, else ๊ตฌ๋ฌธ์ด ์กด์žฌํ•˜๋Š” ์ฆ‰, Alternative๊ฐ€ nil ์ด ์•„๋‹ˆ๋ผ๋ฉด else ๊ตฌ๋ฌธ์„ ํ‰๊ฐ€ํ•˜๋„๋ก ํ•œ๋‹ค. Alternative๊ฐ€ nil ์ด๋ผ๋Š” ๊ฒƒ์€ ๊ณง else ๊ตฌ๋ฌธ์ด ์ •์˜๋˜์ง€ ์•Š์€ if ๊ตฌ๋ฌธ๋งŒ ์žˆ๋Š” ์กฐ๊ฑด์‹์ด๋ผ๋Š” ๋œป์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋Ÿฌํ•œ ๊ฒฝ์šฐ ์ฆ‰์‹œ NULL์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

๋งˆ์ง€๋ง‰์œผ๋กœ ์กฐ๊ฑด์‹ ํ‰๊ฐ€ ๊ฒฐ๊ณผ๊ฐ€ Truthy์ธ์ง€ ์ฒดํฌํ•˜๋Š” isTruthy ๋ฉ”์†Œ๋“œ๋งŒ ์‚ดํŽด๋ณด๊ณ  ์กฐ๊ฑด์‹ ํ‰๊ฐ€๋ฅผ ๋งˆ๋ฌด๋ฆฌํ•˜์ž.

 

// evaluator/evaluator.go

func isTruthy(obj object.Object) bool {
	switch obj {
	case NULL:
		return false
	case TRUE:
		return true
	case FALSE:
		return false
	default:
		return true
	}
}

 

์•„๊นŒ ์œ„์—์„œ Truthy ํ•˜๋‹ค๋ผ๋Š” ๊ฒƒ์ด false๋„ null๋„ ์•„๋‹Œ ๊ฐ’์ด๋ผ๊ณ  ํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ true์ผ ๋•Œ๋ฅผ ํฌํ•จํ•ด์„œ false, null์ด ์•„๋‹Œ ๊ฒฝ์šฐ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

3-4-7. Return ๋ฌธ

๋‹ค์Œ์œผ๋กœ ํ‰๊ฐ€ํ•ด๋ณผ ํ‘œํ˜„์‹์„ Return ๋ฌธ์ด๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ํ”ํžˆ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•˜๊ฒŒ ๋˜๋ฉด ๋ฐ˜๋“œ์‹œ return ๋ฌธ์„ ์‚ฌ์šฉํ•ด์„œ ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ํŠน์ • ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•˜๊ณค ํ•œ๋‹ค. ๋ฐ”๋กœ ๊ทธ return ๋ฌธ์„ ์šฐ๋ฆฌ๋Š” ํ‰๊ฐ€ํ•ด๋ณผ ๊ฒƒ์ด๋‹ค. ์šฐ์„  return ๋ฌธ์€ ์ผ๋ จ์˜ ๋ช…๋ น๋ฌธ๋“ค์„ ํ‰๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ๋ฉˆ์ถ”๊ณ , ๋ช…๋ น๋ฌธ ์•ˆ์— ํฌํ•จ๋œ ํ‘œํ˜„์‹์„ ๋‚จ๊ฒจ๋‘๊ณ  ๋‚˜์™€๋ฒ„๋ฆฐ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์•„๋ž˜์™€ ๊ฐ™์€ ์†Œ์Šค์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•ด๋ณด์ž.

 

5 * 5;
return 10;
10 + 1;

 

์œ„ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ํ‰๊ฐ€ํ•  ๊ฒฝ์šฐ, 10 + 1 ์ด๋ผ๋Š” ์ฝ”๋“œ๋Š” ์ ˆ๋Œ€ ํ‰๊ฐ€๋˜์ง€ ์•Š๋Š”๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๊ทธ ์ „์— return ๋ฌธ์„ ๋งŒ๋‚˜์„œ ํ‰๊ฐ€๊ฐ€ ์ค‘๋‹จ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. return ๋ฌธ์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ์—๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์กด์žฌํ•œ๋‹ค. Golang์—์„œ์˜ goto ๊ตฌ๋ฌธ์ด๋‚˜ ์ผ๋ฐ˜์ ์ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์™ธ(Exception)๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ํ˜„์žฌ ์šฐ๋ฆฌ๋Š” ๊ฒŒ์ŠคํŠธ ์–ธ์–ด์ธ Monkey ์–ธ์–ด๋ฅผ ํ˜ธ์ŠคํŠธ ์–ธ์–ด์ธ Golang์œผ๋กœ ๊ตฌํ˜„ํ•˜๋ ค๊ณ  ํ•˜๋Š” ์ƒํ™ฉ์ด๋‹ค. Golang ์—์„œ๋Š” catch ์™€ ๊ฐ™์€ ํ‚ค์›Œ๋“œ๋ฅผ ์ง€์›ํ•˜์ง€๋„ ์•Š๊ณ , goto ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊น”๋”ํ•˜๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์•ˆ๋„ ๊ฐ–๊ณ  ์žˆ์ง€ ์•Š๋‹ค. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” return ๋ฌธ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ๋‹จ์ˆœํžˆ ๋ฐ˜ํ™˜๊ฐ’์„ ํ‰๊ฐ€๊ธฐ์— ๋„˜๊ธธ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ‰๊ฐ€ ๋„์ค‘ return ๋ฌธ์„ ๋งŒ๋‚ฌ์„ ๋•Œ ์‹ค์ œ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•  ๊ฐ’์„ return ๋ฌธ ์ „์šฉ ๊ฐ์ฒด ํ•˜๋‚˜๋กœ ๊ฐ์‹ธ์„œ ์ฒ˜๋ฆฌํ•  ๊ฒƒ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋Š” ์ด์œ ๋Š” ๊ทธ๋ž˜์•ผ ํ‰๊ฐ€๊ธฐ๊ฐ€ ์ด return ์ „์šฉ ๊ฐ์ฒด๋ฅผ ๊ณ„์† ์ถ”์ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๊ณ , ์ถ”์ฒ™์ด ๊ฐ€๋Šฅํ•˜๋ฉด ํ‰๊ฐ€ ๋„์ค‘์— ํ‰๊ฐ€๋ฅผ ๊ณ„์†ํ• ์ง€ ๋ง์ง€ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ์ค€์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

๊ทธ๋ž˜์„œ ๊ฐ€์žฅ ๋จผ์ € ํ•  ์ผ์€ ์‹ค์ œ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•  ๊ฐ’์„ return ๋ฌธ ์ „์šฉ ๊ฐ์ฒด๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž.

 

// object/object.go

package object

const (
	... (์ƒ๋žต) ...
    
	RETURN_VALUE_OBJ = "RETURN_VALUE"
)

type ReturnValue struct {
	Value Object
}

func (r *ReturnValue) Inspect() string  { return r.Value.Inspect() }
func (r *ReturnValue) Type() ObjectType { return RETURN_VALUE_OBJ }

 

ReturnValue ๋ผ๋Š” ์ƒˆ๋กœ์šด ๊ตฌ์กฐ์ฒด๋Š” Value ๋ผ๋Š” ๋ฉค๋ฒ„๋งŒ์„ ๊ฐ–๋Š”๋ฐ, ์ด Value ํƒ€์ž…์ด Object์ธ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๊ฒฐ๊ตญ, ReturnValue๋Š” ํŠน๋ณ„ํ•˜๊ฒŒ ์ƒˆ๋กญ๊ฒŒ ๋งŒ๋“ค์–ด์ง„ ๊ฐ์ฒด๋ผ๊ธฐ ๋ณด๋‹ค๋Š” ๊ธฐ์กด์— ์šฐ๋ฆฌ๊ฐ€ ์ •์˜ํ–ˆ๋˜ Integer, Boolean, Null ๋“ฑ๊ณผ ๊ฐ™์€ Object๋ฅผ ํ•œ๋ฒˆ ๊ฐ์‹ผ ๊ฐ์ฒด์— ๋ถˆ๊ณผํ•˜๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. 

 

์ด์ œ ๋‹ค์Œ์œผ๋กœ๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž.

 

// evaluator/evaluator_test.go

func TestReturnStatements(t *testing.T) {
	tests := []struct {
		input    string
		expected int64
	}{
		{"return 10;", 10},
		{"return 10; 9;", 10},
		{"return 2 * 5; 9;", 10},
		{"9; return 2 * 5; 9;", 10},
	}
	
	for _, tt := range tests {
		evaluated := testEval(tt.input)
		testIntegerObject(t, evaluated, tt.expected)
	}
}

 

์ฃผ๋กœ return ๋ฌธ์„ ๋งŒ๋‚ฌ์„ ๋•Œ return ๋’ค์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ž˜ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€ ๊ฒ€์ฆํ•˜๋Š” ๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋‹ค. ์ด์ œ ์ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ํ†ต๊ณผ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์ž. ๋จผ์ € Eval ๋ฉ”์†Œ๋“œ์— return ๋ฌธ์— ๋Œ€ํ•œ case ๋ฌธ์„ ์ถ”๊ฐ€ํ•˜์ž.

 

// evaluator/evaluator.go

func Eval(node ast.Node) object.Object {
	// type switch
	switch node := node.(type) {

	// ๋ช…๋ น๋ฌธ
	... (์ƒ๋žต) ...

	// ํ‘œํ˜„์‹
	... (์ƒ๋žต) ...
    
	case *ast.ReturnStatement:
		val := Eval(node.ReturnValue)
		return &object.ReturnValue{Value: val}
	}
	return nil

 

๋กœ์ง ์ž์ฒด๋Š” ๊ฐ„๋‹จํ•˜๋‹ค. return ๋ฌธ์ด ๋“ฑ์žฅํ•  ๊ฒฝ์šฐ, ReturnStatement ๊ตฌ์กฐ์ฒด์˜ ReturnValue ๋ฉค๋ฒ„๋ฅผ ํ‰๊ฐ€ํ•˜๋„๋ก ํ•œ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ฐฉ๊ธˆ ์ •์˜ํ–ˆ๋˜ object.ReturnValue ๋ผ๋Š” ์ƒˆ๋กœ์šด ๊ตฌ์กฐ์ฒด์— ๋‹ค์‹œ ํ‰๊ฐ€ํ•œ ๊ฒฐ๊ณผ ๊ฐ’์„ ๋‹ค์‹œ ๋‹ด๋Š”๋‹ค. ๋‹ค๋งŒ ์—ฌ๊ธฐ์„œ ๋์ด ์•„๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” evalStatements ๋ผ๋Š” ๋ฉ”์†Œ๋“œ์—๋„ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ํ˜„์žฌ ํ‰๊ฐ€๊ธฐ๋Š” AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ Eval ๋ฉ”์†Œ๋“œ๋ฅผ  ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•˜๊ณ  ์žˆ๋Š” ๊ตฌ์กฐ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋”ฐ๋ผ์„œ evalStatements ๋ฉ”์†Œ๋“œ์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•ด์ฃผ์ž.

 

// evaluator/evaluator.go

func evalStatements(stmts []ast.Statement) object.Object {
	var result object.Object

	for _, statement := range stmts {
		result = Eval(statement)

		if resultValue, ok := result.(*object.ReturnValue); ok {
			return resultValue.Value
		}
	}
	return result
}

 

๋ฐ”๋กœ, object.ReturnValue ๋กœ type assertion์„ ์ˆ˜ํ–‰ํ•ด์„œ ํ•ด๋‹น ๋ฉ”์†Œ๋“œ์—์„œ ์‹ค์งˆ์ ์œผ๋กœ ๋ฐ˜ํ™˜ํ•  ๋ฐ์ดํ„ฐ ์ฆ‰, object.ReturnValue ๊ตฌ์กฐ์ฒด์˜ Value ๋ฉค๋ฒ„์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋Ÿฐ ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ๋ฐ˜์˜๋จ์— ๋”ฐ๋ผ ์šฐ๋ฆฌ๋Š” ์ธํ„ฐํ”„๋ฆฌํ„ฐ์—์„œ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ํ‰๊ฐ€๋˜์–ด ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ Monkey ์–ธ์–ด์˜ ๊ตฌํ˜„์ฒด๊ฐ€ ์•„๋‹Œ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋งŒ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒƒ์ด๋‹ค. ์ผ๋ก€๋กœ, ์šฐ๋ฆฌ๊ฐ€ python ์ธํ„ฐํ”„๋ฆฌํ„ฐ์—๋‹ค๊ฐ€ 3 + 3์„ ์ž…๋ ฅํ–ˆ์„ ๋•Œ 6๋งŒ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด๋‹ค.

 

๊ทธ๋Ÿฐ๋ฐ ์œ„์™€ ๊ฐ™์€ evalStatements ๋ฉ”์†Œ๋“œ ๋กœ์ง์€ ํ•œ๊ณ„์ ์ด ์กด์žฌํ•œ๋‹ค. ๋ฐ”๋กœ ์ค‘์ฒฉ ๊ตฌ๋ฌธ์ด ์กด์žฌํ•  ๋•Œ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์•„๋ž˜์™€ ๊ฐ™์€ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

// evaluator/evaluator_test.go

func TestReturnStatements(t *testing.T) {
	tests := []struct {
		input    string
		expected int64
	}{
		... (์ƒ๋žต) ...
		{`if (10 > 1) {
			if (10 > 1) {
			  return 10;
		    }
		  return 1;
		 }`, 10},
	}

	for _, tt := range tests {
		evaluated := testEval(tt.input)
		testIntegerObject(t, evaluated, tt.expected)
	}
}

 

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜ค๋ฅ˜๋ฅผ ๋‚ด๋ฑ‰๋Š”๋‹ค.

 

--- FAIL: TestReturnStatements (0.00s)
    evaluator_test.go:51: object has wrong value. got=1, want=10
FAIL
FAIL    monkey/evaluator        0.444s
FAIL

 

๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋ฉด 10์„ return ํ•ด์•ผ ํ•˜๋Š”๋ฐ, 1์„ return ํ–ˆ๋‹ค. ์ฆ‰, ์ค‘์ฒฉ๋œ if ๋ถ„๊ธฐ๋ฌธ์„ ํƒ€์„œ 10์„ ๋ฐ˜ํ™˜ํ–ˆ์–ด์•ผ ํ–ˆ์Œ์—๋„ return 1; ์ด ํ‰๊ฐ€๋œ ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ evalStatements ๋ฉ”์†Œ๋“œ๋ฅผ 2๊ฐœ๋กœ ๋ถ„๋ฆฌํ•˜๋„๋ก ํ•˜์ž. ๋จผ์ € evalProgram ์ด๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์ƒˆ๋กญ๊ฒŒ ์ •์˜ํ•˜๋Š”๋ฐ, ์ด ๋ฉ”์†Œ๋“œ๋Š” evalStatements ๋ฉ”์†Œ๋“œ์™€ ๋กœ์ง์€ ๋™์ผํ•˜๋‹ค.

 

// evaluator/evaluator.go

func evalProgram(stmts []ast.Statement) object.Object {
	var result object.Object

	for _, statement := range stmts {
		result = Eval(statement)

		if resultValue, ok := result.(*object.ReturnValue); ok {
			return resultValue.Value
		}
	}
	return result
}

func evalBlockStatement(stmts []ast.Statement) object.Object {
	var result object.Object

	for _, statement := range stmts {
		result = Eval(statement)

		if result != nil && result.Type() == object.RETURN_VALUE_OBJ {
			return result
		}
	}
	return result
}

 

์šฐ๋ฆฌ๊ฐ€ ์ฃผ๋ชฉํ•  ๋ฉ”์†Œ๋“œ๋Š” evalBlockStatment ๋ฉ”์†Œ๋“œ๋‹ค. ๋ฐ”๋กœ ์ด ๋ฉ”์†Œ๋“œ๊ฐ€ ์ค‘์ฒฉ๋œ ๊ตฌ๋ฌธ์ด ๋“ฑ์žฅํ•ด๋„ return ๊ตฌ๋ฌธ์„ ์ž˜ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค. ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„์€ for-loop ๊ตฌ๋ฌธ ๋‚ด์˜ if ๋ถ„๊ธฐ๋ฌธ์ด๋‹ค. ๋ช…๋ น๋ฌธ์„ ํŒŒ์‹ฑํ•œ ๊ฒฐ๊ณผ์ธ result ๋ณ€์ˆ˜๊ฐ€ nil ์ด ์•„๋‹ˆ๋ฉด์„œ Type() ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ–ˆ์„ ๋•Œ ํƒ€์ž… ๊ฐ’์ด RETURN_VALUE_OBJ ๋ผ๋ฉด object.ReturnValue ๊ตฌ์กฐ์ฒด๋ฅผ ํ’€์–ด๋‚ด์ง€ ์•Š๊ณ  ํ•ด๋‹น ๊ตฌ์กฐ์ฒด ํ˜•ํƒœ ๊ทธ๋Œ€๋กœ ๋ฆฌํ„ดํ•˜๋„๋ก ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๊ฒŒ ๋˜๋ฉด ๋ฐ”๊นฅ์ชฝ ๋ธ”๋ก๋ฌธ ์–ด๋”˜๊ฐ€์—์„œ ์‹คํ–‰์„ ๋ฉˆ์ถ”๊ฒŒ ๋˜๊ณ , evalProgram ๋ฉ”์†Œ๋“œ๊นŒ์ง€ ์‹คํ–‰๋˜๋Š” ๋‹จ๊ณ„์— ๊ฐ€์„œ์•ผ object.ReturnValue ๊ตฌ์กฐ์ฒด์˜ Value ๊ฐ’์„ ํ’€์–ด๋‚ด์–ด ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋œ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ๋ถ„๋ฆฌ์‹œํ‚จ ๋ฉ”์†Œ๋“œ evalProgram, evalBlockStatement ๋ฉ”์†Œ๋“œ๋ฅผ Eval ๋ฉ”์†Œ๋“œ ๋‚ด์˜ case ๋ฌธ์— ์ ์ ˆํžˆ ๊ต์ฒดํ•ด์ค€๋‹ค.

 

func Eval(node ast.Node) object.Object {
	// type switch
	switch node := node.(type) {

	// ๋ช…๋ น๋ฌธ
	case *ast.Program:
		return evalProgram(node.Statements)
    
... (์ƒ๋žต) ...

	// ํ‘œํ˜„์‹
... (์ƒ๋žต) ...
	case *ast.BlockStatement:
		return evalBlockStatement(node.Statements)

... (์ƒ๋žต) ...
	}
	return nil
}

3-5. ์—๋Ÿฌ ์ฒ˜๋ฆฌ

๋‹ค์Œ์œผ๋กœ ํ•ด๋ณผ ๊ฒƒ์€ ์—๋Ÿฌ ์ฒ˜๋ฆฌ์ด๋‹ค. ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์˜ ์œ ํ˜•๋งˆ๋‹ค ๋‚ด๋ถ€์ ์œผ๋กœ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ตฌํ˜„์ฒด๊ฐ€ ์กด์žฌํ•œ๋‹ค. python์—์„œ๋Š” Exception ๊ฐ์ฒด๊ฐ€ ๋  ๊ฒƒ ๊ฐ™๋‹ค. python์—์„œ์˜ Exception ๊ฐ์ฒด์ฒ˜๋Ÿผ ์šฐ๋ฆฌ๋„ Monkey ์–ธ์–ด์—์„œ์˜ Exception ๊ฐ์ฒด์™€ ๊ฐ™์€ ๊ฒƒ์„ ๊ตฌํ˜„ํ•ด ๋ณผ ๊ฒƒ์ด๋‹ค. ๋ฐ”๋กœ ์ด๋ฅผ ๋‚ด๋ถ€ ์—๋Ÿฌ ์ฒ˜๋ฆฌ(internal error handling) ์ด๋ผ๊ณ ๋„ ํ•œ๋‹ค. ์ด๋Ÿฐ ๋‚ด๋ถ€ ์—๋Ÿฌ๋ž€, ์ž˜๋ชป๋œ ์—ฐ์‚ฐ์ž๋ฅผ ์“ฐ๊ฑฐ๋‚˜ ์ง€์›๋˜์ง€ ์•Š๋Š” ์—ฐ์‚ฐ์„ ํ•œ๋‹ค๊ฑฐ๋‚˜ ๋˜๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํ”„๋กœ๊ทธ๋žจ ์ค‘๊ฐ„์— Ctrl + c(ํ‚ค๋ณด๋“œ ์ธํ„ฐ๋ŸฝํŠธ)๋ฅผ ๋ˆŒ๋Ÿฌ ํ”„๋กœ๊ทธ๋žจ์„ ์ธ์œ„์ ์œผ๋กœ ์ค‘๋‹จ์‹œํ‚ค๊ฑฐ๋‚˜ ํ•˜๋Š” ๋“ฑ ๊ทธ ๋ฐ–์— ์‹คํ–‰ ์ค‘์— ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋Š” ์‚ฌ์šฉ์ž ๋˜๋Š” ๋‚ด๋ถ€ ์—๋Ÿฌ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

 

์—๋Ÿฌ ์ฒ˜๋ฆฌ๋Š” ์ง์ „์— ์‚ดํŽด๋ณธ return ๋ฌธ ์ฒ˜๋ฆฌ ๋ฐฉ์‹๊ณผ ๊ฑฐ์˜ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋„ return ๋ฌธ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ผ๋ จ์˜ ๋ช…๋ น๋ฌธ์„ ํ‰๊ฐ€ํ•˜๋‹ค๊ฐ€ ๋งˆ์ฃผํ•˜๋ฉด ๋„์ค‘์— ํ‰๊ฐ€๋ฅผ ๋ฉˆ์ถ”๊ฒŒ ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด ๊ณตํ†ต์ ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

์ผ๋‹จ ์šฐ๋ฆฌ๋งŒ์˜ ์—๋Ÿฌ ๊ตฌํ˜„์ฒด๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์—๋Ÿฌ ๊ฐ์ฒด๋ฅผ ์ •์˜ํ•ด๋ณด์ž.

 

// object/object.go

const (
	... (์ƒ๋žต) ...
    
	ERROR_OBJ = "ERROR"
)

type Error struct {
	Message string
}

func (e *Error) Inspect() string  { return "ERROR: " + e.Message }
func (e *Error) Type() ObjectType { return ERROR_OBJ }

 

์—๋Ÿฌ ๊ฐ์ฒด๋„ ๊ทธ๋™์•ˆ ์šฐ๋ฆฌ๊ฐ€ ์ •์˜ํ•ด์™”๋˜ ๋‹ค์–‘ํ•œ ํƒ€์ž…์˜ ๊ฐ์ฒด์™€ ๋น„์Šทํ•˜๊ฒŒ ์ •์˜ํ–ˆ๋‹ค. ๋™์ผํ•˜๊ฒŒ Inspect, Type ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ Object ๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ถฉ์กฑ์‹œํ‚ค๋„๋ก ํ•œ๋‹ค.

 

๋ฌผ๋ก  ์ƒ์šฉ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋ฅผ ๋ชฉํ‘œ๋กœ ํ•˜๋Š” ๊ฒฝ์šฐ, ์œ„์™€ ๊ฐ™์€ ์—๋Ÿฌ ์ฒ˜๋ฆฌ์—์„œ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋ฅผ ๋‹ฌ์•„๋ณผ ์ˆ˜๋„ ์žˆ๋‹ค. ์ฆ‰, ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ์ง€์ ์˜ ํ–‰๊ณผ ์—ด ๋ฒˆํ˜ธ๋ฅผ ๊ฐ™์ด ๋„ฃ์–ด์„œ ๋‹จ์ˆœํ•œ ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ณด๋‹ค ๋” ๋งŽ์€ ์ •๋ณด๋ฅผ ์ค„ ์ˆ˜ ์žˆ๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ‰๊ฐ€ ์ด์ „์— ์ž…๋ ฅ ์†Œ์Šค์ฝ”๋“œ ๋ฌธ์ž์—ด์„ ํ† ํฐํ™”์‹œํ‚ค๋Š” ๋ ‰์„œ๊ฐ€ ํ† ํฐ์— ํ–‰๊ณผ ์—ด ๋ฒˆํ˜ธ๋ฅผ ๋‹ฌ์•„๋†“์•˜์–ด์•ผ ๊ตฌํ˜„์ด ์‰ฌ์›Œ์ง„๋‹ค. ์‚ฌ์‹ค ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•ด์˜จ ๋ ‰์„œ๋Š” ํ† ํฐ์— ํ–‰๊ณผ ์—ด ๋ฒˆํ˜ธ๋ฅผ ๋‹ฌ์•„๋†“๋„๋ก ํ•˜์ง€๋Š” ์•Š์•˜๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์šฐ๋ฆฌ ์˜ˆ์ œ์—์„œ๋Š” ๊ตฌํ˜„ํ•˜์ง€๋Š” ์•Š์ง€๋งŒ, ์ผ๋ฐ˜ ์ƒ์šฉ ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ๊ฒฝ์šฐ ์ด๋Ÿฐ ๊ณผ์ •์ด ์ˆ˜๋ฐ˜๋˜์—ˆ์Œ์„ ์•Œ๊ณ  ์žˆ๋Š” ๊ฒƒ๋„ ์–ธ์  ๊ฐ€๋Š” ๋„์›€์ด ๋  ๋“ฏ ํ•˜๋‹ค.

 

์—๋Ÿฌ ์ฒ˜๋ฆฌ๋„ ๋˜‘๊ฐ™์ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ถ€ํ„ฐ ์ž‘์„ฑํ•ด๋ณด์ž.

 

// evaluator/evaluator_test.go

func TestErrorHandling(t *testing.T) {
	tests := []struct {
		input    string
		expectedMessage string
	}{
		{
			"5 + true;",
			"type mismatch: INTEGER + BOOLEAN",
		},
		{
			"5 + true; 5;",
			"type mismatch: INTEGER + BOOLEAN",
		},
		{
			"-true",
			"unknown operator: -BOOLEAN",
		},
		{
			"true + false;",
			"unknown operator: BOOLEAN + BOOLEAN",
		},
		{
			"5; true + false; 5",
			"unknown operator: BOOLEAN + BOOLEAN",
		},
		{
			"if (10 > 1) { true + false; }",
			"unknown operator: BOOLEAN + BOOLEAN",
		},
		{
			`
if (10 > 1) {
  if (10 > 1) {
    return true + false;
  }

  return 1;
}
`,
			"unknown operator: BOOLEAN + BOOLEAN",
		},
	}
	
	for _, tt := range tests {
		evaluated := testEval(tt.input)
		
		errObj, ok := evaluated.(*object.Error)
		if !ok {
			t.Errorf("no error object returned. got=%T (%+v)", evaluated, evaluated)
			continue
		}
		if errObj.Message != tt.expectedMessage {
			t.Errorf("error object has wrong message. got=%q, want=%q", errObj.Message, tt.expectedMessage)
		}
	}
}

 

์œ„ ์†Œ์Šค์ฝ”๋“œ ์† ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” ํฌ๊ฒŒ 2๊ฐ€์ง€ ๋™์ž‘์„ ์˜๋„ํ•˜๋Š”์ง€๋ฅผ ๊ฒ€์ฆํ•œ๋‹ค. ์ฒซ๋ฒˆ์งธ๋กœ ์—๋Ÿฌ๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๋Š” ๋™์ž‘, ๋‘๋ฒˆ์งธ๋Š” ํ‰๊ฐ€๋ฅผ ์ค‘๋‹จํ•˜๋Š” ๋™์ž‘์ด๋‹ค. 

 

์ด์ œ ์‹ค์ œ ํ‰๊ฐ€ ์ฝ”๋“œ ๋‚ด๋ถ€์—์„œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด๋ณผ ๊ฒƒ์ด๋‹ค. ๊ทธ ์ „์— ์•ž์„œ ์šฐ๋ฆฌ๋งŒ์˜ ์—๋Ÿฌ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž. ๊ตฌํ˜„์€ ๊ฐ„๋‹จํ•˜๋‹ค. ์œ„์—์„œ ์ •์˜ํ•œ ์šฐ๋ฆฌ๋งŒ์˜ ์—๋Ÿฌ ๊ฐ์ฒด๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋œ๋‹ค.

 

// evaluator/evaluator.go

func newError(format string, a ...interface{}) *object.Error {
	return &object.Error{Message: fmt.Sprintf(format, a...)}
}

 

์ฐธ๊ณ ๋กœ ์œ„ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ ์•Œ๊ฒŒ ๋œ Golang ๋ฌธ๋ฒ•์— ๋Œ€ํ•ด ์†Œ๊ฐœํ•œ๋‹ค. ์šฐ์„  ... ์ด๋ผ๋Š” ๊ฒƒ์€ ํฌ๊ฒŒ 2๊ฐ€์ง€ ๊ธฐ๋Šฅ์„ ํ•˜๋Š”๋ฐ, ํ•จ์ˆ˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜์—์„œ๋Š” ๊ฐ€๋ณ€ ์ธ์ž๋ฅผ ๋œปํ•œ๋‹ค. python์—์„œ *args, *kwargs ๋ผ๋Š” ํ‚ค์›Œ๋“œ๋กœ ์ฃผ๋กœ ์“ฐ์ด๋Š” ๊ฒฝ์šฐ๋ฅผ ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  return ๊ตฌ๋ฌธ์—์„œ ๋˜ ... ์ด ๋“ฑ์žฅํ•˜๋Š”๋ฐ, ์ด๋Š” ๊ฐ€๋ณ€์ธ์ž๋กœ ๋ฐ›์€ ๊ฒƒ์„ ๊ทธ๋Œ€๋กœ ๋ถ„ํ•ด ์ฆ‰, unpacking์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์œ„ ์˜ˆ์‹œ์—์„œ๋Š” a ์ธ์ž๊ฐ€ ๊ฐ€๋ณ€์ธ์ž๋กœ ๋“ค์–ด์˜ค๊ธด ํ–ˆ์ง€๋งŒ ๋งŒ์•ฝ a ์ธ์ž๊ฐ€ ์Šฌ๋ผ์ด์Šค๋กœ ๋ฐ›๋Š”๋‹ค๊ณ  ํ–ˆ์„ ๋•Œ, a... ๋ผ๊ณ  ๋ช…์‹œํ•˜๋ฉด ์Šฌ๋ผ์ด์Šค a๋Š” ์›์†Œ๋“ค์„ unpacking ํ•˜๊ฒŒ ๋œ๋‹ค.

 

๊ธฐ์กด๊นŒ์ง€ ์šฐ๋ฆฌ๋Š” ๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ์ง€์ ์— ๊ทธ๋ƒฅ ๋‹จ์ˆœํžˆ NULL์„ ๋ฆฌํ„ดํ•˜๋„๋ก ๋˜์–ด ์žˆ๋‹ค. ์ด์ œ ์ด๋ ‡๊ฒŒ ํ•˜์ง€ ๋ง๊ณ , ๋ฐฉ๊ธˆ ์ •์˜ํ•œ newError ๋ฉ”์†Œ๋“œ๋กœ ์—๋Ÿฌ๋ฅผ ํ•ธ๋“ค๋ง ํ•ด๋ณด๋„๋ก ํ•˜์ž.

 

// evaluator/evaluator.go

func evalPrefixExpression(operator string, right object.Object) object.Object {
	switch operator {
	case "!":
		return evalBangOperatorExpression(right)
	case "-":
		return evalMinusPrefixOperatorExpression(right)
	default:
		return newError("unknown operator: %s%s", operator, right.Type())
	}
}

func evalInfixExpression(operator string, left, right object.Object) object.Object {
	switch {
	case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ:
		return evalIntegerInfixExpression(operator, left, right)
        
	... (์ƒ๋žต) ...
    
	case left.Type() != right.Type():
		return newError("type mismatch: %s %s %s", left.Type(), operator, right.Type())
	default:
		return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
	}
}

func evalMinusPrefixOperatorExpression(right object.Object) object.Object {
	if right.Type() != object.INTEGER_OBJ {
		return newError("unknown operator: -%s", right.Type())
	}
	value := right.(*object.Integer).Value
	return &object.Integer{Value: -value}
}

func evalIntegerInfixExpression(operator string, left, right object.Object) object.Object {
	leftVal := left.(*object.Integer).Value
	rightVal := right.(*object.Integer).Value
	switch operator {
	case "+":
		return &object.Integer{Value: leftVal + rightVal}
        
	...(์ƒ๋žต)...
    
	default:
		return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
	}
}

 

๊ทธ๋Ÿฐ๋ฐ ์—ฌ์ „ํžˆ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค 2๊ฐœ๊ฐ€ ํ†ต๊ณผํ•˜์ง€ ๋ชปํ•˜๊ณ  ์žˆ๋‹ค. ์—ฌ์ „ํžˆ ํ‰๊ฐ€๋ฅผ ๋ฉˆ์ถ”๋Š” ๋™์ž‘์€ ์•„์ง ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์ด๋‹ค. 

 

์ด๋ฅผ ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์šฐ๋ฆฌ๋Š” evalProgram ๊ณผ evalBlockStatement ๋ฉ”์†Œ๋“œ์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

 

// evaluator/evaluator.go

func evalProgram(stmts []ast.Statement) object.Object {
	var result object.Object

	for _, statement := range stmts {
		result = Eval(statement)
		
		switch result := result.(type) {
		case *object.ReturnValue:
			return result.Value
		case *object.Error:
			return result
		}
	}
	return result
}

func evalBlockStatement(stmts []ast.Statement) object.Object {
	var result object.Object

	for _, statement := range stmts {
		result = Eval(statement)

		if result != nil {
			rt := result.Type()
			if rt == object.RETURN_VALUE_OBJ || rt == object.ERROR_OBJ {
				return result
			}
		}
	}
	return result
}

 

๋‘ ๋ฉ”์†Œ๋“œ ๋ณด๋‘ ์ค‘๊ฐ„์— object.Error ํƒ€์ž…์ธ์ง€๋ฅผ ์ฒดํฌํ•˜๊ณ  ํ•ด๋‹น ํƒ€์ž…์ด๋ผ๋ฉด ์ฆ‰์‹œ ํ‰๊ฐ€๋ฅผ ์ค‘๋‹จํ•˜๋„๋ก ์ถ”๊ฐ€ํ–ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ํ•  ์ผ์€ Eval ๋ฉ”์†Œ๋“œ ์•ˆ์—์„œ ์žฌ๊ท€์ ์œผ๋กœ Eval ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ์ฒดํฌํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ž˜์•ผ๋งŒ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ์ง€์ ์„ ๋ช…ํ™•ํžˆ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๊ณ , ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ ์˜ฎ๊ฒจ์ง€์ง€ ์•Š๋„๋ก ํ•ด์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ์ง€์ ์„ ์ž˜๋ชป๋œ ์ง€์ ์œผ๋กœ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋ฌธ์ œ๋ฅผ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค.

 

// evaluator/evaluator.go

func isError(obj object.Object) bool {
	if obj != nil {
		return obj.Type() == object.ERROR_OBJ
	}
	return false
}

 

์œ„ ํ•จ์ˆ˜๋ฅผ Eval ๋ฉ”์†Œ๋“œ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•˜๋Š” ๋ถ€๋ถ„์—๋‹ค๊ฐ€ ์ถ”๊ฐ€ํ•˜์—ฌ ๊ฒ€์ฆํ•˜๋Š” ๊ฒƒ์„ ๋ฐ˜์˜ํ•˜๋„๋ก ํ•˜์ž.

3-6. ๋ฐ”์ธ๋”ฉ๊ณผ ํ™˜๊ฒฝ(Environment)

๋‹ค์Œ์œผ๋กœ ์ถ”๊ฐ€ํ•ด๋ณผ ๊ธฐ๋Šฅ์€ ๋ฐ”์ธ๋”ฉ(Bindings)์ด๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ง€๊ธˆ๊นŒ์ง€๋Š” let ๋ฌธ, return ๋ฌธ, ์ค‘์œ„ ํ‘œํ˜„์‹, ์ „์œ„ ํ‘œํ˜„์‹ ๋“ฑ์„ ํ‰๊ฐ€ํ•˜๋Š” ๊ฒƒ๊นŒ์ง€๋งŒ ํ•ด๋ณด์•˜๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ํŠน์ • ์œ ํ˜•์˜ ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•œ ๋’ค์— ์–ด๋–ค ๋ณ€์ˆ˜์— ์ €์žฅ์„ ํ•  ์ˆ˜๊ฐ€ ์žˆ๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ let ๋ฌธ์ด ๊ทธ๋ ‡๋‹ค. ์•„๋ž˜์ฒ˜๋Ÿผ ๋ง์ด๋‹ค.

 

let x = 5 + 10;

 

์ผ๋ฐ˜์ ์ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ ์œ„์™€ ๊ฐ™์ด ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ ๋’ค, x๋ฅผ ์ถœ๋ ฅํ•˜๋ฉด ๋ชจ๋‘ 15๋ผ๋Š” ๊ฐ’์„ ์ถœ๋ ฅํ•  ๊ฒƒ์ด๋‹ค. ์ฆ‰, ์œ„์™€ ๊ฐ™์€ let ๋ฌธ ํ‘œํ˜„์‹์ด ํ‰๊ฐ€๋œ ๋’ค, x ๋ผ๋Š” ๊ฐ’์— ํ‘œํ˜„์‹ ํ‰๊ฐ€์˜ ๊ฒฐ๊ณผ๋กœ ์–ป์–ด์ง€๋Š” ๊ฐ’(15)์ด ํ• ๋‹น๋˜๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋ฅผ ๋ฐ”๋กœ '๋ฐ”์ธ๋”ฉ'์ด๋ผ๊ณ  ํ•œ๋‹ค. 

 

์ด๋Ÿฌํ•œ ๋ฐ”์ธ๋”ฉ ๊ธฐ๋Šฅ์€ ์•„์ง ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์ธํ„ฐํ”„๋ฆฌํ„ฐ์— ์ถ”๊ฐ€๋˜์ง€ ์•Š์€ ์ƒํƒœ๋‹ค. ์•ž์œผ๋กœ ์ถ”๊ฐ€ํ•ด๋ณด๋„๋ก ํ•˜์ž. ์šฐ๋ฆฌ๋Š” let ๋ฌธ ํ‘œํ˜„์‹์— ๋Œ€ํ•œ ๋ฐ”์ธ๋”ฉ์„ ์ถ”๊ฐ€ํ•ด๋ณผ ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

// evaluater/evaluator_test.go

func TestLetStatements(t *testing.T) {
	tests := []struct {
		input    string
		expected int64
	}{
		{"let a = 5; a;", 5},
		{"let a = 5 * 5; a;", 25},
		{"let a = 5; let b = a; b;", 5},
		{"let a = 5; let b = a; let c = a + b + 5; c;", 15},,
	}
	
	for _, tt := range tests {
		testIntegerObject(t, testEval(tt.input), tt.expected)
	}
}

func TestErrorHandling(t *testing.T) {
	tests := []struct {
		input           string
		expectedMessage string
	}{
		{
			"foobar",
			"identifier not found: foobar",
		},
	}
    ... ์ƒ๋žต ...
}

 

ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” ๊ฐ„๋‹จํ•˜๋‹ค. ํฌ๊ฒŒ 2๊ฐ€์ง€ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•œ๋‹ค. ์ฒซ ๋ฒˆ์งธ๋Š” let ๋ฌธ ์•ˆ์— ๊ฐ’์„ ๋งŒ๋“ค์–ด๋‚ด๋Š” ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด๊ณ , ๋‘ ๋ฒˆ์งธ๋Š” ์ด๋ฆ„์— ๋ฐ”์ธ๋”ฉ๋œ ์‹๋ณ„์ž๋ฅผ ํ‰๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์—ฌ๊ธฐ์— ํ•œ ๊ฐ€์ง€ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด๋ณธ๋‹ค๋ฉด ๋งŒ์•ฝ ๋ฐ”์ธ๋”ฉ๋˜์–ด ์žˆ์ง€ ์•Š์€ ์‹๋ณ„์ž๋ฅผ ํ‰๊ฐ€ํ•˜๋ ค ํ•  ๋•Œ ์—๋Ÿฌ๋ฅผ ๋‚ด๋ฑ‰๋„๋ก ํ•˜๋Š” ๊ฒƒ๋„ ์ถ”๊ฐ€ํ•ด ๋ณผ ๊ฒƒ์ด๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ๊ธฐ์กด์— ์ž‘์„ฑํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์ธ TestErrorHandling ๋ฉ”์†Œ๋“œ์— ์œ„์™€ ๊ฐ™์€ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

 

์ด์ œ ์œ„ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋“ค์„ ํ†ต๊ณผํ•˜๋„๋ก ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ํ‰๊ฐ€๊ธฐ์˜ ์—”ํŠธ๋ฆฌํฌ์ธํŠธ ๋ฉ”์†Œ๋“œ์ธ Eval ๋ฉ”์†Œ๋“œ์— ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

 

// evaluator/evalutor.go

func Eval(node ast.Node) object.Object {
	// type switch
	switch node := node.(type) {

	... ์ƒ๋žต ...
    
	case *ast.LetStatement:
		val := Eval(node.Value)
		if isError(val) {
			return val
		}
	}
	return nil

 

์œ„ ์†Œ์Šค์ฝ”๋“œ๋Š” let ๋ฌธ ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•˜๋Š” ๊ฒƒ๋งŒ์—์„œ ๋๋‚œ๋‹ค. ์ฆ‰, ๋ฐ”์ธ๋”ฉ ๊ธฐ๋Šฅ์€ ์ถ”๊ฐ€๋˜์ง€ ์•Š์€ ๊ฒƒ์ด๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด์ œ ๋ฐ”์ธ๋”ฉ์„ ์‹ค์งˆ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ๋กœ์ง์„ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

 

๊ทธ์— ์•ž์„œ ์‚ฌ์ „์— ์•Œ์•„๋‘˜ ๊ฐœ๋…์ด ํ•˜๋‚˜ ์žˆ๋‹ค. ๋ฐ”๋กœ 'ํ™˜๊ฒฝ(Environment)' ์ด๋ผ๋Š” ๊ฒƒ์ด๋‹ค. ํ™˜๊ฒฝ์ด๋ผ๋Š” ๊ฐœ๋…์€ ์ธํ„ฐํ”„๋ฆฌํ„ฐ ๋ถ„์•ผ์—์„œ ๋งค์šฐ ํ”ํ•˜๊ฒŒ ์“ฐ์ด๋Š” ์šฉ์–ด์ธ๋ฐ, ํ™˜๊ฒฝ์ด๋ž€ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๊ฐ€ ๊ฐ’์„ ์ถ”์ ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ์ฒด๋กœ, ๊ฐ’(value)์„ ์ด๋ฆ„(name)๊ณผ ์—ฐ๊ด€์‹œํ‚ค๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ๋งํ•˜๋ฉด ํ™˜๊ฒฝ์ด๋ผ๋Š” ๊ฐœ๋…์ด ๋˜๊ฒŒ ๋‚ฏ์„ค๊ณ  ์ถ”์ƒ์ ์œผ๋กœ ๋“ค๋ฆด ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ฒฐ๊ตญ "๋ฌธ์ž์—ด๊ณผ ๊ฐ์ฒด๋ฅผ ์—ฐ๊ด€์‹œํ‚ค๋Š” ํ•ด์‹œ ๋งต ์ž๋ฃŒ๊ตฌ์กฐ"์— ๋ถˆ๊ณผํ•˜๋‹ค. ์ด๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด ์†Œ์Šค์ฝ”๋“œ๋กœ ์ดํ•ดํ•ด๋ณด์ž. ์šฐ๋ฆฌ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด Environemnt ๋ผ๋Š” ์ด๋ฆ„์˜ ์ƒˆ๋กœ์šด ๊ตฌ์กฐ์ฒด๋ฅผ ์ •์˜ํ•ด๋ณด์ž.

 

// object/environment.go

package object 

type Environment struct {
	store map[string]Object
}

func NewEnvironment() *Environment {
	s := make(map[string]Object)
	return &Environment{s}
}

func (e *Environment) Get(name string) (Object, bool) {
	obj, ok := e.store[name]
	return obj, ok
}

func (e *Environment) Set(name string, val Object) Object {
	e.store[name] = val
	return val
}

 

Environment ๊ตฌ์กฐ์ฒด์˜ store ๋ฉค๋ฒ„ ํƒ€์ž…์„ ๋ณด๋ฉด ๋‹จ์ˆœํ•œ map ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๋ž˜ํ•‘ํ•œ ๊ฒƒ์— ๋ถˆ๊ณผํ•œ ๊ฒƒ์ž„์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋ž˜ํ•‘์„ ํ•œ ์ด์œ ๋Š” ์ถ”ํ›„์— ์šฐ๋ฆฌ๊ฐ€ ์ฝ”๋“œ๋ฅผ ํ™•์žฅํ•˜๊ธฐ ์œ„ํ•œ ์ค€๋น„๋ผ๊ณ  ์ƒ๊ฐํ•ด๋‘๊ณ  ๋„˜์–ด๊ฐ€์ž. NewEnvironment ๋ฉ”์†Œ๋“œ๋Š” ๋ฐฉ๊ธˆ ์†Œ๊ฐœํ•œ Environment ๊ตฌ์กฐ์ฒด๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. ์ด๊ฒŒ ํ•„์š”ํ•œ ์ด์œ ๋Š” ์ถ”ํ›„์— ์†Œ๊ฐœํ•˜๊ฒ ์ง€๋งŒ ํ…Œ์ŠคํŠธํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ํ™˜๊ฒฝ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  Get ์ด๋ผ๋Š” Pointer Receiver ๋ฉ”์†Œ๋“œ๋Š” map ์ž๋ฃŒ๊ตฌ์กฐ ์ฆ‰, ํ™˜๊ฒฝ์— ์ €์žฅ๋˜์–ด ์žˆ๋Š” ํŠน์ • ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” ์—ญํ• ์„, Set ๋ฉ”์†Œ๋“œ๋Š” map ์ž๋ฃŒ๊ตฌ์กฐ์— ํŠน์ • ๊ฐ’์„ ์ €์žฅํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

 

์ด๋ ‡๊ฒŒ ๊ตฌํ˜„๋œ Environment ๊ตฌ์กฐ์ฒด๋ฅผ ์šฐ๋ฆฌ๋Š” ์–ด๋””์—์„œ ํ™œ์šฉํ• ๊นŒ? ๋ฐ”๋กœ Eval ์ด๋ผ๋Š” ์—”ํŠธ๋ฆฌํฌ์ธํŠธ ๋ฉ”์†Œ๋“œ์— ์ถ”๊ฐ€ ์ธ์ž๋กœ ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ Eval ์ด๋ผ๋Š” ๋ฉ”์†Œ๋“œ์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝ๋œ๋‹ค.

 

// evaluator/evaluator.go ์˜ Eval ๋ฉ”์†Œ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜ ๋ณ€๊ฒฝ ์ „/ํ›„
// as-is
func Eval(node ast.Node) object.Object { 
    ...
}

// to-be
func Eval(node ast.Node, env *object.Environment) object.Object {
    ...
}

 

์ด์ œ ๊ทธ๋™์•ˆ ์ž‘์„ฑ๋œ ์†Œ์Šค์ฝ”๋“œ์—์„œ Eval ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์†Œ๋“œ์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๋ชจ๋‘ to-be ํ˜•ํƒœ๋กœ ๋ฐ”๊พธ์–ด์ฃผ๋„๋ก ํ•˜์ž. ๊ทธ๋ฆฌ๊ณ  testEval ์ด๋ผ๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ํ•จ์ˆ˜์—์„œ๋Š” ๋งค๋ฒˆ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊ฐ€ ์ˆ˜ํ–‰๋  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ํ™˜๊ฒฝ(Environment) ์ฆ‰, ์ƒˆ๋กœ์šด map ์ž๋ฃŒ๊ตฌ์กฐ๊ฐ€ ์ƒ์„ฑ๋˜๋„๋ก ์•„๋ž˜์ฒ˜๋Ÿผ ๋ณ€๊ฒฝํ•ด์ฃผ์ž.

 

// evaluator/evaluator_test.go

func testEval(input string) object.Object {
	l := lexer.New(input)
	p := parser.New(l)
	program := p.ParseProgram()
	env := object.NewEnvironment()
	return Eval(program, env)
}

 

์ด์ œ ๊ทธ๋Ÿฌ๋ฉด Eval ๋ฉ”์†Œ๋“œ ๋‚ด๋ถ€์—์„œ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๋กœ์ง์„ ์ž‘์„ฑํ•ด๋ณด์ž. ์šฐ๋ฆฌ๊ฐ€ ์œ„์—์„œ ์ •์˜ํ–ˆ๋˜ Environment์˜ Pointer Receiver ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. 

 

// evaluator/evalutor.go

func Eval(node ast.Node) object.Object {
	// type switch
	switch node := node.(type) {

	... ์ƒ๋žต ...
    
	case *ast.LetStatement:
		val := Eval(node.Value)
		if isError(val) {
			return val
		}
        env.Set(node.Name.Value, val)
	}
	return nil

 

์ด์ œ env.Set ๋ฉ”์†Œ๋“œ๋กœ ํ™˜๊ฒฝ์— let ๋ฌธ ํ‘œํ˜„์‹์˜ ๊ฒฐ๊ณผ๋ฅผ ๋‹ด์•„๋†“์•˜๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ํ•  ์ผ์€ ์‹๋ณ„์ž๋ฅผ ํ‰๊ฐ€ํ–ˆ์„ ๋•Œ, ํ•ด๋‹น map ์ž๋ฃŒ๊ตฌ์กฐ์—์„œ ์‹๋ณ„์ž๋ฅผ key๋กœ ํ•˜๋Š” ๊ฐ’์„ ์ฐพ์•„์™€์„œ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, let x = 5 + 5; ํ‰๊ฐ€ํ•œ ๋’ค, x; ๋ผ๋Š” ์‹๋ณ„์ž ํ‘œํ˜„์‹๋„ ํ‰๊ฐ€๋ฅผ ํ•ด์„œ 10 ์ด๋ผ๋Š” ๊ฒฐ๊ณผ๊ฐ€ ์ถœ๋ ฅ๋˜๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค. ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด Eval ๋ฉ”์†Œ๋“œ์— ์‹๋ณ„์ž ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•˜๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

 

// evaluator/evaluator.go

func Eval(node ast.Node, env *object.Environment) object.Object {
	// type switch
	switch node := node.(type) {

	... ์ƒ๋žต ...
    
	case *ast.Identifier:
		return evalIdentifier(node, env)
	}
	return nil
}

func evalIdentifier(node *ast.Identifier, env *object.Environment) object.Object {
	val, ok := env.Get(node.Value)
	if !ok {
		return newError("identifier not found: %s", node.Value)
	}
	return val
}

 

๊ทธ๋ฆฌ๊ณ  ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉด ์ž˜ ์„ฑ๊ณตํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. REPL๋„ ์ˆ˜ํ–‰ํ•ด์„œ ๋ฐ”์ธ๋”ฉ์ด ์ž˜ ํ‰๊ฐ€๋˜๋Š”์ง€๋„ ์ˆ˜ํ–‰ํ•ด๋ณด๋„๋ก ํ•˜์ž.

3-7. ํ•จ์ˆ˜์™€ ํ•จ์ˆ˜ ํ˜ธ์ถœ

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

 

>>> let add = fn(a, b, c, d) { return a + b + c + d };
>>> add(1, 2, 3, 4);
10

>>> let max = fn(x, y) { if (x > y) { x } else { y } };
>>> max(5, 10)
10

>>> let callTwoTimes = fn(x, func) { func(func(x)) };
>>> callTwoTimes(3, fn(x) { x + 1 });
5

>>> let newAdder = fn(x) { fn(n) { x + n } };
>>> let addTwo = newAdder(2);
>>> addTwo(2);
4

 

์œ„ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํฌ๊ฒŒ 2๊ฐ€์ง€ ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค. ์ฒซ ๋ฒˆ์งธ๋Š” ์šฐ๋ฆฌ๊ฐ€ ๊ทธ๋™์•ˆ ๊ตฌํ˜„ํ•ด์˜จ ๊ฐ์ฒด ์‹œ์Šคํ…œ(object.go)์— ํ•จ์ˆ˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋‚ด๋ถ€ ํ‘œํ˜„์„ ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค. ๋งˆ์น˜ ์ •์ˆ˜๋ฅผ ์šฐ๋ฆฌ๊ฐ€ object.Integer ๊ตฌ์กฐ์ฒด๋กœ ์ •์˜ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด๋‹ค. ๋‘ ๋ฒˆ์งธ๋Š” ํ‰๊ฐ€๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ์—”ํŠธ๋ฆฌํฌ์ธํŠธ ํ•จ์ˆ˜๊ฒฉ์ธ Eval ๋ฉ”์†Œ๋“œ๊ฐ€ 'ํ•จ์ˆ˜ ํ˜ธ์ถœ'์„ ์ œ๋Œ€๋กœ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

3-7-1. ํ•จ์ˆ˜ ํ‘œํ˜„ ์ •์˜ํ•˜๊ธฐ

๋จผ์ € ํ•ด๋ณผ ๊ฒƒ์€ ํ•จ์ˆ˜ ํ‘œํ˜„์„ ๊ฐ์ฒด ์‹œ์Šคํ…œ์— ์ •์˜ํ•ด๋ณด๋Š” ๊ฒƒ์ด๋‹ค. ๊ฐ์ฒด ์‹œ์Šคํ…œ์— ์ •์˜ํ•ด์•ผ๋งŒ ํ•˜๋Š” ์ด์œ ๋Š” ๊ทธ๋ž˜์•ผ ํ•จ์ˆ˜๋ฅผ ๋ณ€์ˆ˜ ์ด๋ฆ„์— ๋ฐ”์ธ๋”ฉํ•˜๊ณ , ํ‘œํ˜„์‹์— ์‚ฌ์šฉํ•˜๊ธฐ๋„ ํ•˜๋ฉฐ, ๋‹ค๋ฅธ ํ•จ์ˆ˜์— ํŠน์ • ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋„˜๊ธธ ์ˆ˜๋„ ์žˆ๊ณ  ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค. 

 

ํ•จ์ˆ˜ ํ‘œํ˜„์„ ์ •์˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์šฐ๋ฆฌ๋Š” ์ด์ „ Parser ๊ด€๋ จ ํฌ์ŠคํŒ…์—์„œ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์— ๋Œ€ํ•ด์„œ AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค ๋•Œ ๋ฐฐ์› ๋˜ FunctionLiteral ๊ตฌ์กฐ์ฒด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ผ์•„๋ณผ ์ˆ˜ ์žˆ๋‹ค. FunctionLiteral ๊ตฌ์กฐ์ฒด์— ๋Œ€ํ•œ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด์ž.

 

// ast/ast.go

type FunctionLiteral struct {
	Token      token.Token
	Parameters []*Identifier
	Body       *BlockStatement
}

 

์œ„ ๊ตฌ์กฐ์ฒด๋ฅผ ๋ณด๊ณ  ์šฐ๋ฆฌ๋Š” object.go ํŒŒ์ผ ์ฆ‰, ์šฐ๋ฆฌ๋งŒ์˜ ๊ฐ์ฒด ์‹œ์Šคํ…œ์— Function ์ด๋ผ๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ์ •์˜ํ•ด๋ณด๋„๋ก ํ•˜์ž.

 

// object/object.go

const (
	... ์ƒ๋žต ...
    
	FUNCTION_OBJ     = "FUNCTION"
)

type Function struct {
	Parameters []*ast.Identifier
	Body       *ast.BlockStatement
	Env        *Environment
}

func (f *Function) Type() ObjectType { return FUNCTION_OBJ }
func (f *Function) Inspect() string {
	var out bytes.Buffer

	params := []string{}
	for _, p := range f.Parameters {
		params = append(params, p.String())
	}

	out.WriteString("fn")
	out.WriteString("(")
	out.WriteString(strings.Join(params, ", "))
	out.WriteString(") {\n")
	out.WriteString(f.Body.String())
	out.WriteString("\n}")

	return out.String()
}

 

์šฐ์„  ๊ฐ์ฒด ์‹œ์Šคํ…œ์—์„œ์˜ ํ•จ์ˆ˜๋„ ์—ญ์‹œ ํ† ํฐ์€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค. ๋‹ค๋งŒ, Prameters ์™€ Body ๋ผ๋Š” ๋ฉค๋ฒ„๋“ค์ด ํ•„์š”ํ•˜๋‹ค. Parameters๋Š” 0๊ฐœ ์ด์ƒ์˜ ์ธ์ž๊ฐ€ ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์Šฌ๋ผ์ด์‹ฑ ํ˜•ํƒœ์˜ ์‹๋ณ„์ž๊ฐ€ ํƒ€์ž…์œผ๋กœ ์ง€์ •๋˜์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Body๋Š” ํ•จ์ˆ˜์˜ ๋ฐ”๋”” ์Šค์ฝ”ํ”„๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ์ด ๋ฐ”๋”” ์Šค์ฝ”ํ”„๋Š” ์ด๋ฏธ ์šฐ๋ฆฌ๊ฐ€ ๋ฐฐ์šด BlockStatement ํƒ€์ž…์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ Env ๋ผ๋Š” ํ•„๋“œ๊ฐ€ ์กด์žฌํ•˜๋Š”๋ฐ, ์ด ํ•„๋“œ์— ๋Œ€ํ•ด์„œ๋Š” ์ถ”ํ›„์— ์ž์„ธํžˆ ๋ฐฐ์šธ ์˜ˆ์ •์ด๋‹ค. ์šฐ์„  ์—ฌ๊ธฐ์„œ๋Š” ํ•จ์ˆ˜๋ผ๋Š” ๊ฒƒ์ด ์ž๊ธฐ ํ™˜๊ฒฝ๊ณผ ๊ฐ™์ด ์›€์ง์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ ํŠน์„ฑ์„ ํ™œ์šฉํ•ด ํด๋กœ์ €(Closure)๊ฐ€ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ๊ทผ๊ฑฐ๋ฅผ ๋งˆ๋ จํ•˜๋„๋ก ํ•ด์ค€๋‹ค๊ณ  ์•Œ์•„๋‘๋„๋ก ํ•˜์ž.

 

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

 

// evaluator/evaluator_test.go

func TestFunctionObject(t *testing.T) {
	input := "fn(x) { x + 2; };"
	evaluated := testEval(input)
	fn, ok := evaluated.(*object.Function)
	if !ok {
		t.Fatalf("object is not Function. got=%T (%+v)", evaluated, evaluated)
	}

	if len(fn.Parameters) != 1 {
		t.Fatalf("function has wrong number of parameters. got=%d, want=1", len(fn.Parameters))
	}

	if fn.Parameters[0].String() != "x" {
		t.Fatalf("parameter is not 'x'. got=%q, want='x'", fn.Parameters[0])
	}

	expectedBody := "(x + 2)"
	if fn.Body.String() != expectedBody {
		t.Fatalf("body is not %q. got=%q", expectedBody, fn.Body.String())
	}
}

 

์ด์ œ Eval ๋ฉ”์†Œ๋“œ์— ํŒŒ์„œ๊ฐ€ ๋งŒ๋“ค์–ด๋‚ธ AST ์ž๋ฃŒ๊ตฌ์กฐ ์ค‘ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด ์ฆ‰, FunctionLiteral์ด ๋“ฑ์žฅํ•  ๊ฒฝ์šฐ, Function ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

 

// evaluator/evaluator.go

func Eval(node ast.Node, env *object.Environment) object.Object {
	// type switch
	switch node := node.(type) {

	... ์ƒ๋žต ...
    
	case *ast.FunctionLiteral:
		params := node.Parameters
		body := node.Body
		return &object.Function{Parameters: params, Body: body, Env: env}
	}
	return nil
}

 

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

3-7-2. ํ•จ์ˆ˜ ์ ์šฉ(Function Application)

ํ•จ์ˆ˜ ์ ์šฉ์ด๋ž€ ๋ง์€ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋ฅผ ํ™•์žฅํ•ด์„œ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค. [๋ชฉ์ฐจ 3-7-1]์—์„œ๋Š” ๋‹จ์ˆœํžˆ ํ‰๊ฐ€๊ธฐ๊ฐ€ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์„ ํ‰๊ฐ€ํ•˜๋Š” ๊ฒƒ์— ๊ทธ์ณค๋‹ค. ํ•˜์ง€๋งŒ ๋งŒ์•ฝ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๊ฒฝ์šฐ์—๋Š” ์–ด๋–ป๊ฒŒ ํ•  ๊ฒƒ์ธ๊ฐ€? ๋ฐ”๋กœ ์ด ๋ถ€๋ถ„์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๊ณ ์ž ํ•œ๋‹ค. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๋‚ด์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋กœ ํ‘œํ˜„ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์ผ€์ด์Šค๋“ค์ด ํ•ด๋‹น๋œ๋‹ค.

 

// evaluator/evaluator_test.go

func TestFunctionApplication(t *testing.T) {
	tests := []struct {
		input    string
		expected int64
	}{
		{"let identity = fn(x) { x; }; identity(5);", 5},
		{"let identity = fn(x) { return x; }; identity(5);", 5},
		{"let double = fn(x) { x * 2; }; double(5);", 10},
		{"let add = fn(x, y) { x + y; }; add(5, 5);", 10},
		{"let add = fn(x, y) { x + y; }; add(5 + 5, add(5, 5));", 20},
		{"fn(x) { x; }(5)", 5},
	}

	for _, tt := range tests {
		testIntegerObject(t, testEval(tt.input), tt.expected)
	}
}

 

ํ•จ์ˆ˜ ์ ์šฉ์„ ํ•ด๋ณด๊ธฐ์— ์•ž์„œ ์šฐ๋ฆฌ๋Š” ๊ณผ๊ฑฐ์— ๋ฐฐ์› ๋˜ AST ์ž๋ฃŒ๊ตฌ์กฐ ์ค‘ ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ–ˆ๋˜ ๋‚ด์šฉ์„ ํšŒ๊ณ ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค. ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์€ ast.CallExpression ์ด๋ผ๋Š” ๊ตฌ์กฐ์ฒด๋กœ ์ •์˜ํ–ˆ๊ณ , ์ด ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์€ ํฌ๊ฒŒ 2๊ฐ€์ง€ ํ˜•ํƒœ๋กœ ์ผ€์ด์Šค๊ฐ€ ๋‚˜๋‰œ๋‹ค๊ณ  ํ–ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ์ผ€์ด์Šค๋Š” ์‹๋ณ„์ž์ผ ๋•Œ์ด๋‹ค. ์ด ์‹๋ณ„์ž์ผ ๋•Œ์˜ ์˜ˆ์‹œ ์†Œ์Šค์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

>>> add = fn(x, y) { return x + y };
>>> add(1, 2)
3

 

๋‘ ๋ฒˆ์งธ ์ผ€์ด์Šค๋Š” ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด ์ž์ฒด์ผ ๊ฒฝ์šฐ์ด๋‹ค.

 

>>> fn(x, y) { return x + y }(1, 2);
3

 

๋ฌผ๋ก  Eval ๋ฉ”์†Œ๋“œ์—์„œ ์ด 2๊ฐ€์ง€ ์ผ€์ด์Šค์— ๋”ฐ๋ผ ๋กœ์ง์ด ๋ถ„๊ธฐ๋˜๊ฑฐ๋‚˜ ํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค. ์™„๋ฒฝํ•˜๊ฒŒ ์ž‘์„ฑ๋œ Eval ๋ฉ”์†Œ๋“œ๋Š” ์•„๋‹ˆ์ง€๋งŒ ์ง€๊ธˆ ๋‹น์žฅ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋กœ์ง์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

// evaluator/evaluator.go

func Eval(node ast.Node, env *object.Environment) object.Object {
	// type switch
	switch node := node.(type) {

	... ์ƒ๋žต ...
    
	case *ast.FunctionLiteral:
		params := node.Parameters
		body := node.Body
		return &object.Function{Parameters: params, Body: body, Env: env}
	case *ast.CallExpression:
		function := Eval(node.Function, env)
		if isError(function) {
			return function
		}
		// TODO: ์ž‘์„ฑ ์˜ˆ์ •
	}
	return nil
}

 

์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์„ ํ‰๊ฐ€ํ•ด๋ณผ ์ฐจ๋ก€๋‹ค. ๊ฐ€์žฅ ๋จผ์ € ํ•  ์ผ์€ 'ํ˜ธ์ถœ ํ‘œํ˜„์‹์˜ ์ธ์ˆ˜๋ฅผ ํ‰๊ฐ€'ํ•˜๋Š” ๋™์ž‘์ด๋‹ค. ๋จผ์ € ์˜ˆ์‹œ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ๋ณด์ž.

 

>>> let add = fn(x, y) { x + y };
>>> add(2 + 2, 5 + 5);

 

์šฐ๋ฆฌ๊ฐ€ ํ‰๊ฐ€ํ•  ๋ถ€๋ถ„์€ add ๋’ค์— ์†Œ๊ด„ํ˜ธ๊ฐ€ ๋‚˜์˜ค๋ฉด์„œ ์ธ์ž๋กœ ๋“ฑ์žฅํ•˜๋Š” 2 + 2 ์™€ 5 + 5 ์ด๋‹ค. ์šฐ๋ฆฌ๋Š” 2 + 2 ์™€ 5 + 5๋ฅผ ์ž์ฒด ํ‘œํ˜„์‹์ด ์•„๋‹Œ ๊ฐ ํ‘œํ˜„์‹์˜ ๊ฒฐ๊ณผ๋ฌผ๋“ค ์ฆ‰, 4์™€ 10์œผ๋กœ ํ‰๊ฐ„ํ•œ ๋’ค ๋„˜๊ธฐ๊ณ  ์‹ถ์€ ๊ฒƒ์ด๋‹ค. ์ด๊ฒƒ์„ ๋ฐ”๋กœ ํ˜ธ์ถœ ํ‘œํ˜„์‹์˜ ์ธ์ˆ˜๋ฅผ ํ‰๊ฐ€ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค. ์ธ์ˆ˜๋ฅผ ํ‰๊ฐ€ํ•˜๋Š” ๋™์ž‘์€ ์‚ฌ์‹ค ์—ฌ๋Ÿฌ๊ฐœ์˜ ํ‘œํ˜„์‹์œผ๋กœ ๊ตฌ์„ฑ๋œ ์ฆ‰, ํ‘œํ˜„์‹ ๋ฆฌ์ŠคํŠธ๋ฅผ ํ‰๊ฐ€ํ•˜๋Š” ๋™์ž‘๊ณผ ๋™์ผํ•˜๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๋ฐฉ๊ธˆ ์ด์•ผ๊ธฐ ํ–ˆ๋“ฏ์ด 2 + 2 ์™€ 5 + 5๋Š” ๊ฐ๊ฐ ํ‘œํ˜„์‹์„ ์˜๋ฏธํ•˜๊ณ , 2๊ฐœ์˜ ํ‘œํ˜„์‹์œผ๋กœ ๊ตฌ์„ฑ๋œ ์…ˆ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์•„๋ž˜ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ๋ณด์ž.

 

// evaluator/evaluator.go

func Eval(node ast.Node, env *object.Environment) object.Object {
	// type switch
	switch node := node.(type) {

	... ์ƒ๋žต ...
    
	case *ast.CallExpression:
		function := Eval(node.Function, env)
		if isError(function) {
			return function
		}
		args := evalExpressions(node.Arguments, env)
		if len(args) == 1 && isError(args[0]) {
			return args[0]
		}
	}
	return nil
}

func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Object {
	var result []object.Object

	for _, e := range exps {
		evaluated := Eval(e, env)
		if isError(evaluated) {
			return []object.Object{evaluated}
		}
		result = append(result, evaluated)
	}
	return result
}

 

Eval ๋ฉ”์†Œ๋“œ๋ฅผ ๋ณด๋ฉด evalExpressions ๋ผ๋Š” ์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ๊ฐ€ ๋“ฑ์žฅํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ๋ฉ”์†Œ๋“œ์˜ ์ธ์ž๋กœ node.Arguments๊ฐ€ ๋“ฑ์žฅํ•œ๋‹ค. ์ด๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ด์ „์— ๋ฐฐ์› ๋‹ค๊ณ  ํ•œ AST ์ž๋ฃŒ๊ตฌ์กฐ ์ค‘ ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์„ ์˜๋ฏธํ•˜๋Š” CallExpression ๊ตฌ์กฐ์ฒด์˜ ๋ฉค๋ฒ„๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ์ž ์‹œ ์ƒ๊ธฐ์‹œํ‚ค๊ธฐ ์œ„ํ•ด CallExpression ๊ตฌ์กฐ์ฒด ์†Œ์Šค์ฝ”๋“œ ๋ถ€๋ถ„๋งŒ ๋ฐœ์ทŒํ•ด์™”๋‹ค.

 

// ast/ast.go

type CallExpression struct {
	Token     token.Token
	Function  Expression
	Arguments []Expression
}

 

Arguments๋ฅผ ๋ณด๋ฉด ์Šฌ๋ผ์ด์‹ฑ ํ˜•ํƒœ์˜ ํ‘œํ˜„์‹ ์ฆ‰, ํ‘œํ˜„์‹ ๋ฆฌ์ŠคํŠธ์ž„์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋‹ค์Œ์œผ๋กœ evalExpressions ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž. ํŠน๋ณ„ํ•œ ๊ฒƒ์ด ์—†๋‹ค. ํ‘œํ˜„์‹ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋“ค์–ด์™”๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ฆฌ์ŠคํŠธ์— ๋Œ€ํ•ด์„œ ๋ฃจํ”„๋ฅผ ๋Œ๋ฉด์„œ ํ‘œํ˜„์‹ 1๊ฐœ์”ฉ Eval ๋ฉ”์†Œ๋“œ๋ฅผ ๋˜ ํ•œ๋ฒˆ ํ˜ธ์ถœํ•ด์ฃผ๋ฉด์„œ 1๊ฐœ์˜ ํ‘œํ˜„์‹์— ๋Œ€ํ•ด์„œ ํ‰๊ฐ€๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  result ๋ณ€์ˆ˜์— append ํ•˜๋Š” ๋กœ์ง์ด๋‹ค. ์—ฌ๊ธฐ์„œ ์šฐ๋ฆฌ๊ฐ€ ์ฃผ๋ชฉํ•  ๋ถ€๋ถ„์€ ๋ฐ”๋กœ ์ด ํ‘œํ˜„์‹ ๋ฆฌ์ŠคํŠธ์— ๋Œ€ํ•ด ๋ฃจํ”„๋ฅผ ๋Œ๋ฉด์„œ ํ‰๊ฐ€๋ฅผ ํ•˜๋Š” ์ˆœ์„œ๊ฐ€ ๋ฐ”๋กœ ํ•จ์ˆ˜ ํ˜ธ์ถœ์˜ ์ธ์ž๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ, ์–ด๋–ค ์ชฝ์˜ ์ธ์ž๋ถ€ํ„ฐ ํ‰๊ฐ€๋˜๋Š”์ง€ ์ˆœ์„œ๋ฅผ ๋ˆˆ์น˜์ฑŒ ์ˆ˜ ์žˆ๋Š” ํžŒํŠธ๊ฐ€ ๋œ๋‹ค. ํ•œ ๋ฒˆ ์ž์‹ ์—๊ฒŒ ๊ฐ€์žฅ ์ต์ˆ™ํ•œ ์ธํ„ฐํ”„๋ฆฌํ„ฐ ์–ธ์–ด์˜ ํ‰๊ฐ€๊ธฐ ๊ตฌํ˜„์ฒด๋ฅผ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ๋„ ์žฌ๋ฐŒ์„ ๊ฒƒ ๊ฐ™๋‹ค.

 

ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์˜ ์ธ์ž๋ฅผ ํ‰๊ฐ€ํ•˜๋Š” ๊ฒƒ์€ ์ด์ œ ๋งˆ์ณค๋‹ค. ๋‹ค์Œ์œผ๋กœ๋Š” ์‹ค์งˆ์ ์œผ๋กœ ํ•จ์ˆ˜์˜ ๋กœ์ง์„ ๋‹ด๋‹นํ•œ๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜ Body๋ฅผ ํ‰๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค. ํ•จ์ˆ˜ Body๋Š” ์•ž์„œ ๋ฐฐ์›Œ์™”๋˜ BlockStatement์— ํ•ด๋‹นํ•˜๊ณ  ์ด BlockStatement๋ฅผ ํ‰๊ฐ€ํ•˜๋Š” ๋กœ์ง์€ evalBlockStatement๋กœ ์ด๋ฏธ ๊ตฌํ˜„๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹จ์ˆœํ•˜๊ฒŒ ์ƒ๊ฐํ•˜๋ฉด Eval ๋ฉ”์†Œ๋“œ์˜ ์ธ์ž์— ํ•จ์ˆ˜ Body๋งŒ ์ธ์ž๋กœ ์ง‘์–ด๋„ฃ์œผ๋ฉด ๋˜์ง€ ์•Š์„๊นŒ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋˜๋ฉด ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์˜ ์ธ์ž ๋ถ€๋ถ„ ๋•Œ๋ฌธ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ฆ‰, ํ•จ์ˆ˜ Body์—์„œ ์ธ์ž๋“ค์„ ์ฐธ์กฐํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋˜๋ฉด ํ•จ์ˆ˜ Body๋ฅผ ํ‰๊ฐ€ํ•  ๋•Œ ์ธ์ž๋“ค์„ ์•Œ ์ˆ˜ ์—†๋Š” ์ด๋ฆ„์„์œผ๋กœ ๊ฐ„์ฃผํ•˜๊ฒŒ ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๋Š” ํ•จ์ˆ˜๊ฐ€ ํ‰๊ฐ€๋  ํ™˜๊ฒฝ์„ ๋ฐ”๊พธ๋Š” ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•จ์œผ๋กœ์จ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ž˜์•ผ ํ•จ์ˆ˜ Body์•ˆ์—์„œ ์ฐธ์กฐํ•˜๋Š” ์ธ์ž๋“ค์ด ์ œ๋Œ€๋กœ ๋œ ๊ฐ’์œผ๋กœ ๋ณ€ํ™˜๋œ๋‹ค. 

 

๊ทธ๋ ‡๋‹ค๊ณ  ํ•ด์„œ ํ˜„์žฌ ํ™˜๊ฒฝ์ด ์ „๋‹ฌํ•œ ์ธ์ž๋ฅผ ํ•จ์ˆ˜ Body์— ๊ทธ๋Œ€๋กœ ์ถ”๊ฐ€ํ•˜๋ฉด ๋˜ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๋ฐ”๋กœ ์ด์ „์— ์žˆ๋˜ ๋ฐ”์ธ๋”ฉ์„ ๋ฎ์–ด์“ฐ๊ฒŒ ๋œ๋‹ค. ์ด๊ฒŒ ๋Œ€์ฒด ๋ฌด์Šจ ๋ง์ธ๊ฐ€? ์•„๋ž˜ ์˜ˆ์ œ ์†Œ์Šค์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•ด๋ณด์ž.

 

let i = 5;
let printNum = fn(i) {
    puts(i);
};

printNum(10);
puts(i);

 

puts ํ•จ์ˆ˜๋Š” C์–ธ์–ด์—์„œ์™€ ๋™์ผํ•˜๊ฒŒ ํ–‰์„ ์ถœ๋ ฅํ•ด์ฃผ๋Š” ํ•จ์ˆ˜๋ผ๊ณ  ์ƒ๊ฐํ•˜์ž. ์šฐ๋ฆฌ๊ฐ€ ํ”ํžˆ ์‚ฌ์šฉํ•˜๋Š” ์ธํ„ฐํ”„๋ฆฌํ„ฐ ์–ธ์–ด์˜ ๊ฒฝ์šฐ ์œ„ ์†Œ์Šค์ฝ”๋“œ ๋กœ์ง์ด๋ผ๋ฉด ๊ฒฐ๊ณผ๋ฌผ์€ ๊ฐ๊ฐ 10๊ณผ 5๊ฐ€ ๋‚˜์˜ฌ ๊ฒƒ์ด๋ผ๊ณ  ์˜ˆ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฐฉ๊ธˆ ์ด์•ผ๊ธฐํ•œ '๋ฎ์–ด์“ฐ๊ธฐ ๋ฌธ์ œ'๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋ฉด ๊ฒฐ๊ณผ๋Š” 10๊ณผ 10์„ ์ถœ๋ ฅํ•˜๊ฒŒ ๋œ๋‹ค. ๋‹ค์‹œ ๋งํ•ด, printNum(10); ์ด๋ผ๋Š” ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์ด ํ‰๊ฐ€๋˜๋ฉด์„œ 10์ด ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋œ๋‹ค. ์ฆ‰, ํ˜„์žฌ ํ™˜๊ฒฝ์—์„œ 10์„ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด ๋•Œ printNum ์ด๋ผ๋Š” ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์„ ํ‰๊ฐ€ํ•˜๋ฉด์„œ ํ˜„์žฌ ํ™˜๊ฒฝ์—์„œ ์ „๋‹ฌ๋œ 10์ด ์ „์—ญ์ ์œผ๋กœ ์„ ์–ธ๋œ i = 5 ๋ผ๊ณ  ๋ฐ”์ธ๋”ฉ๋œ ๊ฐ’์„ ๋ฎ์–ด์“ฐ๊ธฐ ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.

 

๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๋Š” ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด์„œ ํ˜„์žฌ ํ™˜๊ฒฝ์„ ๋ณด์กดํ•˜๋ฉด์„œ ๋™์‹œ์— ์ƒˆ๋กœ์šด ํ™˜๊ฒฝ์„ ๋งŒ๋“ค์–ด์•ผ ํ•˜๊ณ , ์ด๋Ÿฌํ•œ ๊ฒƒ์„ 'ํ™˜๊ฒฝ ํ™•์žฅ'์ด๋ผ๊ณ  ๋ถ€๋ฅด๋„๋ก ํ•˜์ž.  ํ™˜๊ฒฝ ํ™•์žฅ์„ ์šฐ๋ฆฌ๊ฐ€ ๊ทธ๋™์•ˆ ๋งŒ๋“  ์†Œ์Šค์ฝ”๋“œ ๋ ˆ๋ฒจ๋กœ ์ดํ•ดํ•œ๋‹ค๋ฉด [๋ชฉ์ฐจ 3-6]์—์„œ ๋ฐฐ์šด object.Environment ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“œ๋Š”๋ฐ, ํ•ด๋‹น ์ธ์Šคํ„ด์Šค์— ํ™•์žฅํ•  ํ™˜๊ฒฝ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ํฌ์ธํ„ฐ ๋ณ€์ˆ˜๋ฅผ ๊ฐ–๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ ์šฐ๋ฆฌ๋Š” ํ˜„์žฌ ํ™˜๊ฒฝ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์ƒˆ๋กœ์šด ํ™˜๊ฒฝ์„ ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑํ•˜๊ฒŒ ๋œ๋‹ค.

 

์ด๋ ‡๊ฒŒ ํ•˜๋Š” ์ด์œ ๋Š” ์ƒˆ๋กœ์šด ํ™˜๊ฒฝ์—์„œ Environment ๊ตฌ์กฐ์ฒด์˜ Get ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ–ˆ์„ ๋•Œ ํ•ด๋‹น ํ™˜๊ฒฝ์— ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์ด๋ฆ„๊ณผ ๊ฐ’์ด๋ผ๋ฉด ํ•ด๋‹น ํ™˜๊ฒฝ์„ ๊ฐ์‹ธํ•˜๊ณ  ์žˆ๋Š” ํ™˜๊ฒฝ์—์„œ ์ฐพ๊ณ , ๋˜ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด ๊ฐ์‹ธ๊ณ  ์žˆ๋Š” ํ™˜๊ฒฝ์—์„œ ์ฐพ๊ณ ๋ฅผ ๋ฐ˜๋ณตํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‹ค๊ฐ€ ๊ฒฐ๊ตญ์€ ์ฐพ์„ ์ˆ˜ ์—†์„ ๋•Œ ์šฐ๋ฆฌ๋Š” Undefined ๋ผ๋Š” ํ‚ค์›Œ๋“œ๋ฅผ ๊ฐ€์ง€๋ฉด์„œ ์„ ์–ธ๋œ ๋ณ€์ˆ˜๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค๋Š” ์—๋Ÿฌ๊ฐ€ ๋“ฑ์žฅํ•˜๊ฒŒ ๋œ๋‹ค. ํ™˜๊ฒฝ์„ ๊ฐ์‹ธ๋Š” ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

// environment/environment.go

package object

type Environment struct {
	store map[string]Object
	outer *Environment
}

func NewEnclosedEnvironment(outer *Environment) *Environment {
	env := NewEnvironment()
	env.outer = outer
	return env
}

func NewEnvironment() *Environment {
	s := make(map[string]Object)
	return &Environment{store: s, outer: nil}
}

func (e *Environment) Get(name string) (Object, bool) {
	obj, ok := e.store[name]
	if !ok && e.outer != nil {
		obj, ok = e.outer.Get(name)
	}
	return obj, ok
}

func (e *Environment) Set(name string, val Object) Object {
	e.store[name] = val
	return val
}

 

์œ„ ์†Œ์Šค์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•œ ๋งค์ปค๋‹ˆ์ฆ˜์€ ํ”ํžˆ ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ์–ธ์–ด์—์„œ ๋ณ€์ˆ˜ ์Šค์ฝ”ํ”„์˜ ๊ฐœ๋…์„ ๋ฐ˜์˜ํ•˜๊ณ  ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ค‘์ฒฉ๋œ ์Šค์ฝ”ํ”„๊ฐ€ ์žˆ๋‹ค๊ณ  ํ–ˆ์„ ๋•Œ, ์•ˆ์ชฝ๊ณผ ๋ฐ”๊นฅ์ชฝ ์Šค์ฝ”ํ”„๊ฐ€ ์กด์žฌํ•œ๋‹ค. ์ตœ์ดˆ์—๋Š” ์•ˆ์ชฝ ์Šค์ฝ”ํ”„์—์„œ ๊ฐ’์„ ์ฐพ์œผ๋ ค๊ณ  ์‹œ๋„ํ•˜๊ณ  ์ฐพ์ง€ ๋ชปํ•œ๋‹ค๋ฉด ๋ฐ”๊นฅ์ชฝ ์Šค์ฝ”ํ”„์—์„œ ์ฐพ๋Š” ์‚ฌ๋ก€์ด๋‹ค.

 

์ด์ œ ์œ„์—์„œ ๊ตฌํ˜„ํ•œ ํ™˜๊ฒฝ ํ™•์žฅ์ด๋ผ๋Š” ๊ฒƒ์„ Eval ๋ฉ”์†Œ๋“œ์— ์ ์šฉํ•ด๋ณผ ์ฐจ๋ก€๋‹ค. ์ˆ˜์ •๋œ Eval ๋ฉ”์†Œ๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

// evaluator/evaluator.go

func Eval(node ast.Node, env *object.Environment) object.Object {
	// type switch
	switch node := node.(type) {

	... ์ƒ๋žต ...
    
	case *ast.CallExpression:
		function := Eval(node.Function, env)
		if isError(function) {
			return function
		}
		args := evalExpressions(node.Arguments, env)
		if len(args) == 1 && isError(args[0]) {
			return args[0]
		}
		return applyFunction(function, args)
	}
	return nil
}

 

applyFunction ๋ฉ”์†Œ๋“œ๊ฐ€ ์ƒˆ๋กญ๊ฒŒ ๋“ฑ์žฅํ–ˆ๋‹ค. ์ด ๋ฉ”์†Œ๋“œ์˜ ์ƒ๊น€์ƒˆ๋ฅผ ๋ณด์ž.

 

// evaluator/evaluator.go

func applyFunction(fn object.Object, args []object.Object) object.Object {
	function, ok := fn.(*object.Function)
	if !ok {
		return newError("not a function: %s", fn.Type())
	}
	extendedEnv := extendedFunctionEnv(function, args)
	evaluated := Eval(function.Body, extendedEnv)
	return unwrapReturnValue(evaluated)
}

 

๋จผ์ € type assertion์„ ํ†ตํ•ด์„œ Object ํƒ€์ž…์˜ ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์ธ Function ํƒ€์ž…์œผ๋กœ ๋ฐ”๊พธ์–ด์ค€๋‹ค. ๊ทธ๋Ÿฐ๋‹ค์Œ ๋˜ ์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ์ธ extendedFunctionEnv๊ฐ€ ๋“ฑ์žฅํ•œ๋‹ค. ์ด๋ฆ„์—์„œ ์œ ์ถ”ํ•  ์ˆ˜ ์žˆ๋‹ค์‹œํ”ผ ์ด ๋ฉ”์†Œ๋“œ๊ฐ€ ๋ฐ”๋กœ ํ™•์žฅ ํ™˜๊ฒฝ์„ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค. extendedFunctionEnv ๋ฉ”์†Œ๋“œ๋กœ ๊ฐ€๋ณด์ž.

 

// evaluator/evaluator.go

func extendedFunctionEnv(fn *object.Function, args []object.Object) *object.Environment {
	env := object.NewEnclosedEnvironment(fn.Env)
	
	for paramIdx, param := range fn.Parameters {
		env.Set(param.Value, args[paramIdx])
	}
	return env
}

 

์ฃผ๋ชฉํ•  ๋ถ€๋ถ„์€ ํ™•์žฅ ํ™˜๊ฒฝ์„ ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑํ•œ ๋’ค, ํ•ด๋‹น ํ™˜๊ฒฝ์— ์ธ์ž๋กœ ๋ฐ›์€ Function์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋“ค๊ณผ ํ•ด๋‹น ํŒŒ๋ผ๋ฏธํ„ฐ๋“ค์„ ํ‰๊ฐ€ํ•œ ๊ฒฐ๊ณผ๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ‰๊ฐ€ํ•œ ๊ฒฐ๊ณผ๊ฐ’๋“ค์€ args ๋ผ๋Š” ๋ณ€์ˆ˜์— ๋‹ด๊ฒจ์žˆ๊ณ , ์ด args ๋ผ๋Š” ๋ณ€์ˆ˜๋Š” Eval ๋ฉ”์†Œ๋“œ ๋‚ด์˜ evalExpressions ๋ฉ”์†Œ๋“œ์— ์˜ํ•ด ํ‰๊ฐ€๋˜์—ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ unwrapReturnValue ๋ผ๋Š” ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ๋‹ค. ํ•ด๋‹น ๋ฉ”์†Œ๋“œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ด์ „์— evalProgram ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ–ˆ์„ ๋•Œ์™€ ์œ ์‚ฌํ•˜๋‹ค. ์ž˜ ๊ธฐ์–ต์ด ๋‚˜์ง€ ์•Š๋Š”๋‹ค๋ฉด evalProgram ๋ฉ”์†Œ๋“œ๋กœ ์ž ์‹œ ๋Œ์•„๊ฐ€๋ณด์ž.

 

// evaluator/evaluator.go

func unwrapReturnValue(obj object.Object) object.Object {
	if returnValue, ok := obj.(*object.ReturnValue); ok {
		return returnValue.Value
	}
	return obj
}

 

์ด๋ ‡๊ฒŒ ํ™•์žฅ ํ™˜๊ฒฝ์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” ๋ฐ”๋กœ ํด๋กœ์ € ๋งค์ปค๋‹ˆ์ฆ˜์„ ์‚ฌ์šฉํ•  ๋•Œ์˜ ์ผ€์ด์Šค ๋•Œ๋ฌธ์ด๋‹ค. ์ด๋ฅผ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

// evaluator/evaluator_test.go

func TestClosures(t *testing.T) {
	input := `
let newAdder = fn(x) {
  fn(y) { x + y };
};}

let addTwo = newAdder(2);
addTwo(2);`

	testIntegerObject(t, testEval(input), 4)
}

 

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅด ๋Œ๋ ค๋ณด๋ฉด ์œ„์™€ ๊ฐ™์€ ํด๋กœ์ € ๋งค์ปค๋‹ˆ์ฆ˜๋„ ์ž˜ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์•„์ง ํด๋กœ์ €๋ผ๋Š” ๊ฒŒ ๋ฌด์—‡์ด๊ณ , ์™œ ์ด๊ฒƒ ๋•Œ๋ฌธ์— ํ™•์žฅ ํ™˜๊ฒฝ์„ ์‚ฌ์šฉํ–ˆ๋Š”์ง€๊ฐ€ ์ž˜ ์™€๋‹ฟ์ง€ ์•Š๋Š”๋‹ค. ๋ฐฉ๊ธˆ ์‚ฌ์šฉํ–ˆ๋˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๋ณด์ž.

 

let newAdder = fn(x) { fn(y) { x + y } };
let addTwo = newAdder(2);

 

ํด๋กœ์ €๋Š” ๊ทธ ํ•จ์ˆ˜๊ฐ€ ์ •์˜๋œ ํ™˜๊ฒฝ์„ '๋‹ด์•„ ๋‚ด๋Š”' ํ•จ์ˆ˜์ด๋‹ค. ๊ทธ๋ž˜์„œ ํด๋กœ์ €๋Š” ์ž์‹ ์˜ ํ™˜๊ฒฝ์„ ๋‹ด๊ณ  ์›€์ง์ด๋‹ค๊ฐ€ ํ˜ธ์ถœ๋˜์—ˆ์„ ๋•Œ ๊ทธ ํ™˜๊ฒฝ์— ์ ‘๊ทผํ•œ๋‹ค. ์œ„ ์˜ˆ์ œ ์ฝ”๋“œ์—์„œ newAdder ์ด๋ผ๋Š” ๋ณ€์ˆ˜๋Š” ๊ณ ์ฐจ(High-order) ํ•จ์ˆ˜์ด๋‹ค. ๊ณ ์ฐจ ํ•จ์ˆ˜๋ž€, ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š” ํ•จ์ˆ˜๋ฅผ ๋งํ•œ๋‹ค. ์œ„ ์˜ˆ์ œ ์ฝ”๋“œ์˜ ๊ฒฝ์šฐ, ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์œ ํ˜•์˜ ๊ณ ์ฐจ ํ•จ์ˆ˜์ด๋‹ค. 

 

ํ•˜์ง€๋งŒ ํด๋กœ์ €๋Š” ๋‹จ์ˆœํ•œ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋‹ค. addTwo์— ์ธ์ž 2๋ฅผ ๋„˜๊ฒจ์„œ ํ˜ธ์ถœํ–ˆ์„ ๋•Œ ์ฆ‰, "addTwo(2)" ๋ผ๊ณ  ํ–ˆ์„ ๋•Œ addTwo์—๋Š” ๋ฐ˜ํ™˜๋œ ํด๋กœ์ €๊ฐ€ ๋ฐ”์ธ๋”ฉ๋˜์–ด ์žˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด "addTwo(2)" ๋ผ๊ณ  ํ•ด์•ผ addTwo ํ•จ์ˆ˜์˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์ด ํ‰๊ฐ€๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด addTwo๋ฅผ ํด๋กœ์ €๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ๋ฌด์—‡์ผ๊นŒ? ๋ฐ”๋กœ addTwo๊ฐ€ ํ˜ธ์ถœ๋˜์—ˆ์„ ๋•Œ ์ฆ‰, "addTwo(2)" ๋ผ๊ณ  ํ–ˆ์„ ๋•Œ ๊ฐ–๊ณ  ์žˆ๋Š” ๋ฐ”์ธ๋”ฉ์ด addTwo๋ฅผ ํด๋กœ์ €๋กœ ๋งŒ๋“ค๊ฒŒ ๋œ๋‹ค. addTwo๊ฐ€ ํ˜ธ์ถœ๋˜์—ˆ์„ ๋•Œ addTwo ํ•จ์ˆ˜๋Š” ํ˜ธ์ถœ ์ธ์ˆ˜์ธ ํŒŒ๋ผ๋ฏธํ„ฐ y์— ์ ‘๊ทผํ•  ์ˆ˜๋„ ์žˆ๊ณ , ์‹ฌ์ง€์–ด newAdder(2)์— ํ˜ธ์ถœ๋œ ์‹œ์ ์— ๋ฐ”์ธ๋”ฉ๋œ x์—๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฒฐ๊ตญ, addTwo ํ•จ์ˆ˜๋Š” Body ๋‚ด๋ถ€์—์„œ x, y๋ฅผ ์ถœ๋ ฅํ•˜๋ฉด ๋ชจ๋‘ ๊ฐ’์ด ์ถœ๋ ฅ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. 

 

๋‹ค์‹œ ๋งํ•ด์„œ, ํด๋กœ์ € ํ•จ์ˆ˜์ธ addTwo๋Š” ์—ฌ์ „ํžˆ ์ฝ”๋“œ๊ฐ€ ์ •์˜๋  ๋‹น์‹œ์˜ ํ™˜๊ฒฝ ์ฆ‰, ์œ„ ์˜ˆ์ œ ์†Œ์Šค์ฝ”๋“œ๋กœ ์น˜๋ฉด "let addTwo = newAdder(2)" ๋ผ๊ณ  ์ •์˜๋˜๋Š” ํ™˜๊ฒฝ์—๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋ง์ด๋‹ค.

 

์ด๋Ÿฌํ•œ ํด๋กœ์ €์˜ ํŠน์„ฑ ๋•Œ๋ฌธ์— ์ด๋ฅผ ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์šฐ๋ฆฌ๋Š” ํ™•์žฅ ํ™˜๊ฒฝ์ด๋ผ๋Š” ๊ฐœ๋…์„ ๋„์ž…ํ•˜๊ณ  ์ ์šฉํ•œ ๊ฒƒ์ด๋‹ค. ์ฐธ๊ณ ๋กœ ๊ณ ์ฐจ ํ•จ์ˆ˜์˜ ๋‹ค๋ฅธ ์ข…๋ฅ˜๋กœ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š” ํ•จ์ˆ˜๋„ ์šฐ๋ฆฌ์˜ ์ธํ„ฐํ”„๋ฆฌํ„ฐ์—์„œ๋Š” ๋™์ž‘ํ•œ๋‹ค.

 

>> let add = fn(a,b) {a+b};
>> let sub = fn(a,b) {a-b};
>> let applyFunc = fn(a, b, func) { func(a, b) };
>> applyFunc(2, 2, add)
4
>> applyFunc(10, 2, sub);
8
๋ฐ˜์‘ํ˜•