[CS] ๋๋ง์ ์ธํฐํ๋ฆฌํฐ๋ฅผ ๋ง๋ค์ด๋ณด์! (2): Parser ๋ง๋ค๊ธฐ - ์ฒซ๋ฒ์งธ
๐ ํด๋น ํฌ์คํ
์ ๋ฐ๋ฐ๋ฅ๋ถํฐ ๋ง๋๋ ์ธํฐํ๋ฆฌํฐ in go ์ฑ
์ ์ฝ๊ณ ๊ฐ์ธ์ ์ธ ์ ๋ฆฌ ๋ชฉ์ ํ์ ์์ฑ๋ ๊ธ์
๋๋ค. ๋ณธ ํฌ์คํ
์ ์ฌ์ฉ๋ ์๋ฃ๋ ๋ชจ๋ ๋ณธ์ธ์ด ์ง์ ์ฌ๊ตฌ์ฑํ์ฌ ์์ฑํ์์์ ์๋ฆฝ๋๋ค.
์ง์ ํฌ์คํ ์์ ์ฐ๋ฆฌ๋ ์ ๋ ฅ๋ ์์ค์ฝ๋ ๋ฌธ์์ด์ ํ ํฐํ์ํค๋ ๋ ์๋ฅผ ์ง์ ๋ง๋ค๊ณ ํ ์คํธ๋ฅผ ํด๋ณด์๋ค. ์ด๋ฒ ํฌ์คํ ์์๋ ์ด ๋ ์๊ฐ ๋ง๋ค์ด๋ธ ํ ํฐ๋ค์ ๊ฐ์ง๊ณ AST(์ถ์ ๊ตฌ๋ฌธ ํธ๋ฆฌ)์ ๊ฐ์ ์๋ฃ๊ตฌ์กฐ๋ก ๋ณํ์ ์ํํ๋ ํ์(Parser)๋ฅผ ๋ง๋ค์ด๋ณด๋๋ก ํ์. ํด๋น ์ฑํฐ๋ ๋ด์ฉ์ด ๊ธธ์ด์ง๊ธฐ ๋๋ฌธ์ ๋ช ๊ฐ์ ํฌ์คํ ์ผ๋ก ๋๋์ด์ ๊ฒ์ํ ์์ ์ด๋ค. ์ด์ ํ์๋ฅผ ๋ง๋ค์ด๋ณด๋ ์ฒซ ๊ฑธ์์ ๋ด๋์ด๋ณด์.
1. Parser๋ ๋ฌด์์ผ๊น?
ํ์๋ฅผ ๋ง๋ค์ด๋ณด๊ธฐ ์ , ์ฐ๋ฆฌ๋ ํ์๋ผ๋ ๊ฒ์ด ๋ฌด์์ด๊ณ ์ด๋ค ์ญํ ์ ํ๋์ง ์์์ผ ๋ง๋ค์ด๊ฐ๋ฉด์ ๊ทธ ์๋ฏธ๋ฅผ ์ ๋๋ก ์ดํดํด๋ณผ ์ ์์ ๊ฒ์ด๋ค. ํ๋ก๊ทธ๋๋ฐ์ ํด๋ณธ ๊ฒฝํ์ด ์๊ฑฐ๋ SQL ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๊ฑฐ๋ ํ๊ฒ ๋๋ฉด ํ ๋ฒ์ฏค์ ํ์ฑ ์๋ฌ(Parsing Error)๋ผ๋ ๋ฉ์ธ์ง๋ฅผ ์ ํด๋ณธ ์ ์ด ์์ ๊ฒ์ด๋ค. ํ์์ ์ฌ์ ์ ์ธ ์ ์๋ "(์ฃผ๋ก ๋ฌธ์๋ก ๋) ์ ๋ ฅ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ํ์ค ํธ๋ฆฌ, ์ถ์ ๊ตฌ๋ฌธํธ๋ฆฌ ๋ฑ๊ณผ ๊ฐ์ ๊ณ์ธต์ ๊ฐ๋ ์๋ฃ๊ตฌ์กฐ๋ก ๋ง๋ค์ด๋ด๋ ์ํํธ์จ์ด ์ปดํฌ๋ํธ"์ด๋ค. ์ฝ๊ฒ ๋งํ๋ฉด ์ฐ๋ฆฌ๊ฐ ์ด์ ํฌ์คํ ์์ ์ดํด๋ณธ ๋ ์๊ฐ ๋ง๋ค์ด๋ธ ํ ํฐ๋ค์ ์๋ฃ๊ตฌ์กฐ๋ก ๋ณํํ๋ ์ ์ด๋ค. ์ด ๋ณํ ๊ณผ์ ์ ๊ฑฐ์น๋ฉด์ ํ์๋ ๊ตฌ์กฐํ๋ ํํ์ ๋ํ๊ธฐ๋ ํ๊ณ ๊ตฌ๋ฌธ์ด ์ฌ๋ฐ๋ฅธ์ง ๊ฒ์ฌํ๊ธฐ๋ ํ๋ค. ์ ์ ์ด์ ํฌ์คํ ์์ ์ฌ์ฉํ๋ ์ฌ์ง ์๋ฃ๋ฅผ ๋ค์ ๊ฐ์ ธ์๋ณด์.
์ ๊ทธ๋ฆผ์ ๋ณด๋ฉด 'ํ์ฑ'์ด๋ผ๋ ๋์์ด ์ํ๋๋๋ฐ, ์ด 'ํ์ฑ'์ ์ํํ๋ ์ฃผ์ฒด๊ฐ ํ์์์ ์ ์ ์๋ค. ๋ณดํธ์ ์ผ๋ก ์ฌ์ฉ๋๊ณ ์๋ ์ธํฐํ๋ฆฌํฐ, ๋จ์ ์ธ ์๋ก, Python ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ ์ฌ์ฉ๋๋ ์ธํฐํ๋ฆฌํฐ๋ฅผ ๊ฐ๋ฐ์๋ค์ด ์ฌ์ฉํ๋ฉด์ ์์ ๋ค์ด ์ ๋ ฅํ ์์ค์ฝ๋๊ฐ ์๋ฃ๊ตฌ์กฐ๋ก ๋ณํํ๋ ๊ฒ์ ํผ๋ถ์ ์๋ฟ๊ฒ ๋๋ผ์ง๋ ๋ชปํ๋ค. ๋์ฒด ์์ผ๊น? ๊ทธ๊ฒ์ ๋ฐ๋ก ์ธ๊ฐ์ธ ๊ฐ๋ฐ์๋ค์ ํ์๊ฐ ๋ง๋ค์ด๋ธ ์๋ฃ๊ตฌ์กฐ์ ์ต์ํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
ํ์๊ฐ ๋ง๋ค์ด๋ด๋ ์๋ฃ๊ตฌ์กฐ ์ค ์ธ๊ฐ์ธ ๊ฐ๋ฐ์์๊ฒ ์ต์ํ ๋ํ์ ์ธ ์์๊ฐ ์๋ค. ๋ฐ๋ก JSON ํ์์ด๋ค. ๋๋ถ๋ถ์ ๊ฐ๋ฐ์๋ค์ JSON ์ด๋ผ๋ ์๋ฃ๊ตฌ์กฐ๋ก ํ์ฑํด๋ณธ ์ ์ด ์์ ๊ฒ์ด๋ค. Python์์๋ ์๋์ ๊ฐ์ด JSON String์ JSON ์๋ฃ๊ตฌ์กฐ, ํ์ด์ฌ์์๋ ๋์ ๋๋ฆฌ ์๋ฃ๊ตฌ์กฐ๋ก ํ์ฑ์ ํด๋ณธ์ ์ด ์์ ๊ฒ์ด๋ค.
# https://www.w3schools.com/python/python_json.asp
import json
# some JSON:
x = '{ "name":"John", "age":30, "city":"New York"}'
# parse x:
y = json.loads(x)
# the result is a Python dictionary:
print(y["age"])
์ ์์ค์ฝ๋๋ฅผ ํ ๋ฒ์ด๋ผ๋ ์ฌ์ฉํด๋ณธ ์ ์ด ์์ ๊ฒ์ด๋ค. ์ด๋ฌํ JSON ํ์๋ ์์ค์ฝ๋์ ์ฌ์ฉ๋๋ ํ์์ ๋ณธ์ง์ ์ผ๋ก ๊ฐ๋ ์ ๊ฐ๋ค. ์ด '๋ณธ์ง์ ์ผ๋ก ๊ฐ์ ๊ฐ๋ '์ด๋ผ ํจ์ ๋ฌธ์์ด์ ์ ๋ ฅ๋ฐ์์ ์ด๋ ํ ์๋ฃ๊ตฌ์กฐ๋ก ํ์ฑํ๋ค๋ ์ ์ด๋ค. ํ์ง๋ง JSON ํ์๋ ์ฐ๋ฆฌ์๊ฒ ์ต์ํ์ง๋ง, ์์ค์ฝ๋์ ํ์๋ ์ฐ๋ฆฌ์๊ฒ ์ต์ํ์ง ์์ ์ด์ ๋ ๋จ์ํ ์์ค์ฝ๋์ ํ์๊ฐ ๋ง๋ค์ด๋ด๋ ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ฐ๋ฆฌ๊ฐ ์์ฃผ ๋ณธ์ ์ด ์์ ๋ฟ์ด๋ค.
๋ฐ๋ผ์, ์ฐ๋ฆฌ๊ฐ JSON ์ด๋ผ๋ ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ดํดํ๋ฉด JSON ํ์๋ฅผ ์ดํดํ ์ ์๋ฏ์ด, ์์ค์ฝ๋ ํ์๊ฐ ๋ง๋ค์ด๋ด๋ ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ดํดํ ์ ์๊ธฐ๋ง ํ๋ค๋ฉด ์ฐ๋ฆฌ๋ ์์ค์ฝ๋ ํ์๊ฐ ์ด๋ค ์๋ฆฌ๋ก ๋์ํ๋์ง ์ดํดํ ์ ์๊ฒ ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก, ์์ค์ฝ๋์ ํ์๋ค์ด ๋ง๋ค์ด๋ด๋ ์๋ฃ๊ตฌ์กฐ๋ก๋ ๊ตฌ๋ฌธํธ๋ฆฌ(Syntax Tree) ๋๋ ์ถ์๊ตฌ๋ฌธํธ๋ฆฌ(Abstract Syntax Tree, AST)๋ผ๊ณ ๋ถ๋ฅธ๋ค. '์ถ์' ์ด๋ผ๋ ํค์๋๊ฐ ๋ถ์ ์ด์ ๋ ์์ค์ฝ๋์์ ๋ณด์ด๋ ์ธ๋ถ ์ ๋ณด(์๋ฅผ ๋ค์ด, ์ธ๋ฏธ์ฝ๋ก , ์ค ๋ฐ๊ฟ, ๊ณต๋ฐฑ, ์ฃผ์, ๋๊ดํธ, ์ค๊ดํธ, ์๊ดํธ, ...๋ค์ ๊ตฌ๋ฌธํธ๋ฆฌ์์ ์๋ต๋๊ธฐ ๋๋ฌธ์ด๋ค. ์ด๋ฌํ ์ธ๋ถ์ ๋ณด๋ ๋จ์ํ ํ์๊ฐ AST๋ฅผ ๊ตฌ์ฑํ ๋, ๊ตฌ์ฑ ํํ๋ฅผ ์๋ ค์ฃผ๋ ์ฉ๋๋ก๋ง ์ฌ์ฉ๋ ๋ฟ์ด๋ค. ๋ํ ์ด AST ํ์์ ๋ชจ๋ ํ์๊ฐ ์ฌ์ฉํ๋ ๊ณตํต์ ์ธ ํ์์ด ์๋ ๊ฒ์ ์๋๋ค. ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ง๋ค ๋๋ ํ์๋ง๋ค ๊ตฌํ์ฒด๊ฐ ์ฝ๊ฐ ์ฉ์ ๋ฌ๋ผ์ง๋ค.
์ด์ ํ์์ ๋ํ ๋ด์ฉ์ ์ ๋ฆฌํด๋ณด์๋ฉด, ํ์๋ ์์ค์ฝ๋ ๋ฌธ์์ด์ ์ ๋ ฅ์ผ๋ก ๋ฐ์์ ์์ค์ฝ๋๋ฅผ ํํํ๋ ์๋ฃ๊ตฌ์กฐ(ex. AST, ..)๋ฅผ ๋ง๋ค์ด๋ธ๋ค. ์ด ๋, ์๋ฃ๊ตฌ์กฐ๋ฅผ ๋ง๋ค์ด๋ด๋ ๋์ ํ์๋ ์ ๋ ฅ์ ๋ถ์ํ๊ฒ ๋๋๋ฐ ์ด '๋ถ์'์ด๋ผ ํจ์ ์ ๋ ฅ์ด ์์ ๊ตฌ์กฐ์ ๋ง๋์ง ๊ฒ์ฌํ๋ ๊ฒ์ด๋ค. ์ด ๊ฒ์ฌํ๋ ๊ณผ์ ์ '๊ตฌ๋ฌธ ๋ถ์(Syntactic Analysis)'์ด๋ผ๊ณ ๋ ํ๋ค.
์ฐธ๊ณ ๋ก, ์์ฆ์๋ ํ์๋ฅผ ์ฐ๋ฆฌ์ฒ๋ผ ์ง์ ์์ฑํ๋ ๊ฒ๋ณด๋ค๋ ์ด ํ์ ์์ฒด๋ฅผ ์๋์ผ๋ก ์์ฑํด์ฃผ๋ ํ์ ์ ๋ค๋ ์ดํฐ๋ฅผ ์ด์ฉํ๊ธฐ๋ ํ๋ค. ๋ํ์ ์ธ ํ์ ์ ๋ค๋ ์ดํฐ๋ก๋ yacc, bison, ANTLR ๋ฑ์ด ์๋ค. ์ด ํ์ ์ ๋ค๋ ์ดํฐ๋ ํน์ ์ธ์ด์ ๋ํ ๊ณต์์ ์ธ ์ค๋ช ์ด ์ฃผ์ด์ก์ ๋, ์ถ๋ ฅ์ผ๋ก ํ์๋ฅผ ์์ฑํ๋ ๋๊ตฌ์ด๋ค. ์ด '์ถ๋ ฅ'๋ฌผ์ ์์ค์ฝ๋์ธ๋ฐ, ์ปดํ์ผ ๋๋ ์ธํฐํ๋ฆฌํ ๋๋ ์ฝ๋์ด๋ค. ์ด ๋ถ๋ถ์ ๋ํ ๊น์ ๋ด์ฉ์ ๋ค๋ฃจ์ง ์๋๋ค. ๋จ์ง ํ์๋ฅผ ์ง์ ์์ฑํ๋ ๋ฐฉ๋ฒ๋ ์์ง๋ง ํ์ ์ ๋ค๋ ์ดํฐ๋ผ๋ ๋ณ๋์ ๋๊ตฌ๋ฅผ ์ด์ฉํด์ ํ์๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ๋ ์๋ค๋ ์ ๋ง ์์๋๋๋ก ํ์.
๋ง์ง๋ง์ผ๋ก, ํ์์ ํ์ฑ ์ ๋ต์ ๋ํด์๋ ๊ฐ๋จํ ์์๋์. ํ์ฑ ์ ๋ต์๋ ํฌ๊ฒ ์๋์ ๊ฐ์ ์ข ๋ฅ๋ก ๊ตฌ๋ถ ๋๋ค.
์ฐ๋ฆฌ๊ฐ ๋ง๋ค ํ์์ ํ์ฑ ์ ๋ต์ ํํฅ์์ ์ฌ๊ท์ ํํฅ ํ์ฑ์์๋ ํํฅ์ ์ฐ์ฐ์ ์ฐ์ ์์ ํ์๋ฅผ ๋ง๋ค ๊ฒ์ด๋ค. ์ด ํ์๋ฅผ ํ๋ซ ํ์๋ผ๊ณ ๋ ๋ถ๋ฅธ๋ค. ํํฅ์์ ์ฌ์ฉํ๋ ์ด์ ๋ AST์ ๋ฃจํธ ๋ ธ๋๋ฅผ ์์ฑํ๋ ๊ฒ์ผ๋ก ์์ํด์ ์ ์ ์๋์ชฝ์ผ๋ก ํ์ฑํด ๋๊ฐ๋ค. ์ด๋ฌํ ์ ๋ต์ ์ฒ์ ํ์๋ฅผ ๊ตฌํํ๋ ์ฌ๋๋ค์๊ฒ ์์ฃผ ๊ถ์ฅ๋๋ ์ ๋ต์ด๋ค. ์๋ํ๋ฉด ์ฌ๊ท์ ํํฅ ํ์๊ฐ ๊ตฌ์ฑํ๋ AST์ ๊ทธ ์์ฑ ๋ฐฉ์์ด ์ฌ๋์ด ์๊ฐํ๋ ๋ฐฉ์๊ณผ ์ ์ฌํ๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ผ์ ์ฐ๋ฆฌ๋ ์ฌ๊ท์ ํํฅ ํ์ฑ์ ํํฅ์ ์ฐ์ฐ์ ์ฐ์ ์์ ํ์๋ฅผ ๋ง๋ค์ด๋ณผ ๊ฒ์ด๋ค.
์ด์ ๊ฐ๋จํ๊ฒ ํ์๊ฐ ๋ฌด์์ด๊ณ , ์ฐ๋ฆฌ๊ฐ ๋ง๋ค ํ์๊ฐ ์ด๋ค ํ์ฑ ์ ๋ต์ ๊ฐ์ ธ๊ฐ์ง๋ ์์๋ณด์๋ค. ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ํ์์ ๊ธฐ๋ฅ์ ํ๋์ฉ ๊ตฌํํด๋ณด์.
2. let ๋ฌธ ํ์ฑํ๊ธฐ
์ฐ๋ฆฌ๋ง์ ํ์์ ์ฒซ๋ฒ์งธ๋ก ๋์ ํด๋ณผ ๊ธฐ๋ฅ์ Monkey ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ let ์ ์ธ๋ฌธ์ ํ์ฑํ ์ ์๋๋ก ํ๋ ๊ฒ์ด๋ค. ์ฆ, ์๋์ ๊ฐ์ ์์ค์ฝ๋๋ฅผ ์ ๋ ฅ์ผ๋ก ๋ฐ์์ ๋, ํ์๊ฐ ์ ํํ ํ์ฑ์ ์ ํ ์ ์๋๋ก ํ๋ ๊ฒ์ด ๋ชฉํ์ด๋ค.
let x = 5;
let y = 10;
let add = fn(a, b) {
return a + b;
};
let ๋ฌธ์ ์ ํํ ํ์ฑํ๋ค๋ ๊ฒ์ ํ์๊ฐ let ๋ฌธ์ด ๋ด๊ณ ์๋ ์ ๋ณด๋ฅผ ์ ํํ ํํํ๋ AST ์๋ฃ๊ตฌ์กฐ๋ฅผ ๋ง๋ ๋ค๋ ๋ป์ด๋ค. let ๋ฌธ์ ํ์ฑํ๊ธฐ ์ ์ ์์ ๊ฐ์ let ๋ฌธ์ด ์ด๋ค ์์๋ค๋ก ๊ตฌ์ฑ๋์ด ์๋์ง ํ์ ํ ํ์๊ฐ ์๋ค. ๊ทธ๋์ผ '์ด ์์๋ค'์ ์ด์ฉํด์ AST ์๋ฃ๊ตฌ์กฐ๋ฅผ ๊ตฌ์ฑํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ ์์ค์ฝ๋๋ let ๋ฌธ 3๊ฐ๋ก ๊ตฌ์ฑ๋ ์ผ๋ จ์ ๋ช ๋ น๋ฌธ์ด๋ค. ๊ทธ๋ฆฌ๊ณ let ๋ฌธ์ 2๊ฐ์ ๊ตฌ์ฑ ์์๋ก ๊ตฌ์ฑ๋๋ค. ํ๋๋ ์๋ณ์์ด๊ณ , ๋ค๋ฅธ ํ๋๋ ํํ์์ด๋ค.
ํ ์ค์ let ๋ช ๋ น๋ฌธ์ ์๋ณ์์ ํํ์์ผ๋ก ๊ตฌ์ฑ๋๋ค. ์๋ณ์๋ ๋นจ๊ฐ์, ํํ์์ ํ๋์ ๊ธ์จ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ์ ์์ค์ฝ๋๋ฅผ ๋ 2๊ฐ์ง ๊ตฌ์ฑ์์๋ก๋ ๊ตฌ๋ถํ ์ ์๋ค.
์ ์๋ฃ์ ํ์ ์ ๋๋ ๋ค๋ชจ์นธ์ ์ฐ๋ฆฌ๋ 1๊ฐ์ ๋ช ๋ น๋ฌธ์ด๋ผ๊ณ ํ๋ค. ๊ทธ๋ฆฌ๊ณ ํ๋์ ๋ถ๋ถ์ ํํ์์ด๋ผ๊ณ ํ๋ค. ์ด ๋ ๊ฐ ๊ฐ์ ์ฐจ์ด์ ์ด ์๋๋ฐ, ๋ช ๋ น๋ฌธ์ ๊ฐ์ ๋ง๋ค์ง ์์ง๋ง, ํํ์์ ๊ฐ์ ๋ง๋ ๋ค๋ ๊ฒ์ด๋ค. ๋ฌผ๋ก ์์ ๊ฒฝ์ฐ์์ ํจ์ ๋ฆฌํฐ๋ด ๋ถ๋ถ๋ ํํ์์ผ๋ก ์ ์ํ๊ธด ํ์ง๋ง ์ด๋ฅผ ํํ์์ผ๋ก ์ ์ํ ์ง ๋ช ๋ น๋ฌธ์ผ๋ก ์ ์ํ ์ง๋ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ง๋ค ๋ฌ๋ผ์ง๋ค. ๋ค๋ฅธ ์๋ก, if ๊ตฌ๋ฌธ๋ if ๊ตฌ๋ฌธ ๋ด๋ถ์์ ๊ฐ์ ๋ง๋ค ์ ์๋๋ก ์ง์ํ๋ ์ธ์ด๋ผ๋ฉด if ๊ตฌ๋ฌธ์ ํํ์์ผ๋ก ์ ์ํ๊ฒ ๋๋ค.
์ด์ let๋ฌธ์ ๊ตฌ์ฑ์์๋ฅผ ์ดํด๋ณด์์ผ๋, ์ฐ๋ฆฌ๋ ์์ผ๋ก AST ์๋ฃ๊ตฌ์กฐ๋ฅผ ๋ง๋ค๋ ๋ ๊ฐ์ง ์ ํ์ ๋ ธ๋๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ ๊ฒ์ด๋ค. ํ๋๋ ํํ์ ํ์ ๊ณผ ํ๋๋ ๋ช ๋ น๋ฌธ ํ์ ์ด๋ค. ํด๋น ์ ํ์ ๋ ธ๋๋ฅผ ๋ง๋ค๊ธฐ ์ํด go ์ธ์ด์์ interface ๋ผ๋ ๋ฌธ๋ฒ์ ์ฌ์ฉํ ๊ฒ์ด๋ค. ์์ค์ฝ๋๋ฅผ ์ดํด๋ณด์.
// ast/ast.go
package ast
type Node interface {
TokenLiteral() string
}
type Statement interface {
Node
statementNode()
}
type Expression interface {
Node
expressionNode()
}
๋จผ์ Node ๋ผ๋ ํ์ ์ ์ ์ํ๊ณ ํด๋น ํ์ ์ TokenLiteral ์ด๋ผ๋ ๋ฉ์๋๋ฅผ ์ธํฐํ์ด์ค ์ค ํ๋๋ก ์ค์ ํ๋ค. ์ด์ Node ํ์ ์ ๊ฐ๋ ๊ฒ์ TokenLiteral ์ด๋ผ๋ ๋ฉ์๋๋ฅผ ๋ฐ๋์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ผ ํ๋ค. ์ด๋ ๊ฒ ๋๋จธ์ง๋ Statement, Expression ํ์ ์ ์ธํฐํ์ด์ค๋ ๊ตฌํํ๋ค. Statement๋ ๋ช ๋ น๋ฌธ์, Expression์ ํํ์์ ๋ํ ์ธํฐํ์ด์ค๋ฅผ ์๋ฏธํ๋ค. ์ด 2๊ฐ์ ์ธํฐํ์ด์ค๊ฐ ๊ฐ๋ statementNode() ์ expressionNode() ๋ฉ์๋์ ๊ธฐ๋ฅ์ ์ถํ์ ์๊ฐํ๊ธฐ๋ก ํ์.
์ด์ AST ์๋ฃ๊ตฌ์กฐ๋ฅผ ์์ฑํ๋ ์์์ ์ฆ, ๋ฃจํธ๋ ธ๋๋ฅผ ์์ฑํด๋ณด๋๋ก ํ์.
// ast/ast.go
type Program struct {
Statements []Statement
}
func (p *Program) TokenLiteral() string {
if len(p.Statements) > 0 {
return p.Statements[0].TokenLiteral()
} else {
return ""
}
}
Program ์ด๋ผ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ์ ์ํ๊ณ , ํด๋น ๊ตฌ์กฐ์ฒด์ ๋ฉค๋ฒ๋ก ์ฌ๋ผ์ด์ค ํํ์ Statement ํ์ ์ ์ธํฐํ์ด์ค๋ฅผ ์ ์ํ๋ค. ์ฆ, ์ฌ๊ธฐ์๋ ์ด์ ์ฌ๋ฌ ๊ฐ์ ๋ช ๋ น๋ฌธ๋ค์ด ๋ค์ด๊ฐ๋ค. ์ผ๋ก๋ก, ์ฐ๋ฆฌ๊ฐ ์์์ ์ดํด๋ณด์๋ ์๋์ let ๋ฌธ๋ค์ด ๋ค์ด๊ฐ๋ ์ ์ด๋ค.
let x = 5;
let y = 10;
let add = fn(a, b) {
return a + b;
};
๋ค์์ Program ์ด๋ผ๋ ๊ตฌ์กฐ์ฒด๋ฅผ pointer recevier๋ก ํ๋ ๋ฉ์๋์ธ TokenLiteral์ ์ ์ํ๋ค. ์ด ๋ฉ์๋์ ์ญํ ์ ๊ฐ๋จํ๋ค. Program ์ด๋ผ๋ ๊ตฌ์กฐ์ฒด์ ๋ช ๋ น๋ฌธ๋ค์ด ๋ค์ด์์ผ๋ฉด ๊ฐ์ฅ ์ฒซ๋ฒ์งธ ์์์ ์๋ Statement ํ์ ์ธํฐํ์ด์ค์ TokenLiteral ๋ฉ์๋๋ฅผ ์ฌ๊ท์ ์ผ๋ก ํธ์ถํ๋ฉด ๋๋ค.(์ฌ๊ธฐ์ ๊ฐ์ฅ ์ฒซ๋ฒ์งธ ์์๋ก ๊ณ ์ ๋์ด ์๋ ์ด์ ๋ ์ถํ์ ์๊ฒ ๋๋ค)
AST ์์ฑ์ ํ์ํ ๊ธฐ์ด์ ์ธ ๋ธ๋ก์ธ Program ๊ตฌ์กฐ์ฒด๋ฅผ ์์๋ณด์๋ค. ์ด์ ๋ ์ ๋ง let ๋ฌธ์ ํ์ฑํด๋ณด์. ์๋ฅผ ๋ค์ด, let x = 5; ๋ผ๋ ๊ตฌ๋ฌธ์ด ์์ ๋, ์ด๋ค ํ๋๊ฐ ํ์ํ ์ง ์๊ฐํด๋ณด์. ๊ฐ์ฅ ๋จผ์ x ๋ผ๋ ๋ณ์ ์ด๋ฆ ์ฆ, ์๋ณ์๋ฅผ ๊ฐ๋ฆฌํค๋ ํ๋๊ฐ ํ์ํ๋ค. ๋ ๋ฒ์งธ๋ก๋ ๋ฑํธ(=) ์ค๋ฅธ์ชฝ์ ์๋ ํํ์ ์ฆ, 5 ๋ผ๋ ๊ฒ์ ๊ฐ๋ฆฌํค๋ ํ๋๊ฐ ํ์ํ๋ค. ์ด ๋ ์ฃผ์ํด์ผ ํ ์ ์ 5 ๋ผ๋ ๋ฆฌํฐ๋ด ๊ฐ ๋ง๊ณ ๋ ๊ทธ ๋ค์ ๋ฌด์์ธ๊ฐ ๋ ์๋ ๊ฒฝ์ฐ๋ ์ปค๋ฒํด์ผ ํ๋ค. ์๋ฅผ ๋ค์ด, let x = 5 * 10 + 12; ์ด๋ฐ ์์ผ๋ก๋ ํํ์์ด ํํ๋ ์ ์๋ค. let x = 5 + add(5, 4); ์ด๋ฐ ๊ฒ๋ ๋ง์ฐฌ๊ฐ์ง์ด๋ค. ์ด๋ฐ ๊ฒฝ์ฐ๋ ์ปค๋ฒํ๊ธฐ ์ํด์ ์ฐ๋ฆฌ๋ (์์ ๋ ์ ๊ตฌํํ ๋ ์์ฃผ ์ดํด๋ณด์๋) ํ ํฐ ํ๋๋ ํ์ํ๋ค.
๋ฐ๋ผ์, let x = 5; ๋ผ๋ let ๋ฌธ์ ํ์ฑํ๊ธฐ ์ํด์๋ [1. ์๋ณ์ ํ๋], [2. ํํ์ ํ๋], [3. ํ ํฐ ํ๋] ์ด 3๊ฐ์ง๊ฐ ํ์ํ๋ค. ์ด๋ฅผ ์ ์ํ ์์ค์ฝ๋๋ฅผ ์ดํด๋ณด์.
// ast/ast.go
package ast
import (
"monkey/token"
)
type LetStatement struct {
Token token.Token
Name *Identifier
Value Expression
}
func (ls *LetStatement) statementNode() {}
func (ls *LetStatement) TokenLiteral() string { return ls.Token.Literal }
type Identifier struct {
Token token.Token
Value string
}
func (i *Identifier) expressionNode() {}
func (i *Identifier) TokenLiteral() string { return i.Token.Literal }
๊ฐ์ฅ ๋จผ์ LetStatement ๋ผ๋ ์ด๋ฆ์ ๊ตฌ์กฐ์ฒด๋ฅผ ์ดํด๋ณด์. ์ด๋ฆ์์ ์ ์ ์๋ฏ์ด ์ด ๊ตฌ์กฐ์ฒด๊ฐ let ๋ฌธ 1๊ฐ๋ฅผ ์๋ฏธํ๋ค. ์ด ๊ตฌ์กฐ์ฒด๊ฐ ๊ฐ๋ ๋ฉค๋ฒ๋ค์ ์ด 3๊ฐ์ง์ด๋ค.
- Token: [3. ํ ํฐ ํ๋]๋ฅผ ๊ฐ๋ฆฌํค๋ ํ๋(๋ณต์กํ ํํ์์ ์ถ์ ํ๊ธฐ ์ํด ํ์)
- Name: [1. ์๋ณ์]๋ฅผ ๊ฐ๋ฆฌํค๋ ํ๋(์ ํฌ์ธํฐ ๋ณ์)
- Value: [2. ํํ์]์ ๊ฐ๋ฆฌํค๋ ํ๋
์ด LetStatement ๊ตฌ์กฐ์ฒด๋ AST๋ฅผ ๊ตฌ์ฑํ๋ ๋ ธ๋๋ก ๋ค์ด๊ฐ๋ฉด์ ๋ช ๋ น๋ฌธ์ ์ํ๋ ๋ ธ๋์ด๊ธฐ ๋๋ฌธ์ Statement ์ธํฐํ์ด์ค๋ฅผ ๋ง์กฑํด์ผ ํ๋ค. ๋ฐ๋ผ์ statementNode()์ TokenLiteral() ๋ฉ์๋๋ฅผ ์ธํฐํ์ด์ค๋ก ๊ตฌํํ์๋ค.
LetStatement ๊ตฌ์กฐ์ฒด ๋ฉค๋ฒ ์ค Name ์ด๋ผ๋ ๋ฉค๋ฒ๋ Identifier ๋ผ๋ ๊ตฌ์กฐ์ฒด์ ํฌ์ธํฐ ๋ณ์๋ก ์ ์๋์๋ค. ์ด Identifier ๊ตฌ์กฐ์ฒด ๋ณ์๋ ๋ฌด์์ธ์ง ์ ์์ค์ฝ๋์์ ์ดํด๋ณด์. ํด๋น ๊ตฌ์กฐ์ฒด๋ Token ๋ฉค๋ฒ์ Value ๋ฉค๋ฒ๋ฅผ ๊ฐ๋๋ค. ์ด ๊ตฌ์กฐ์ฒด์์ ์ฃผ๋ชฉํ ์ ์ Expression ์ธํฐํ์ด์ค๋ฅผ ๋ง์กฑ์ํค๋๋ก expressionNode()์ TokenLiteral() ๋ฉ์๋๋ฅผ ๊ตฌํ์์ผฐ๋ค๋ ๊ฒ์ด๋ค. Identifier ๊ตฌ์กฐ์ฒด๋ '์๋ณ์'๋ฅผ ๋ํ๋ด๋ ๊ฒ์ด๊ณ , ์ด๋ ํํ์์ด ์๋์๋ ์ ํํ์์ด ๊ฐ๋ ๋ ธ๋๊ฐ ๋ง์กฑ์์ผ์ผํ๋ Expression ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ์๊น? ์ด๋ Monkey ์ธ์ด๊ฐ ๋ฐ๋ก ์๋ณ์๋ฅผ ์ ์ธํ๊ณ ์ ์ํ๋ ๊ฒ์ ํ๋ฒ์ ํ์ฌ ๊ฐ์ ์์ฑํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ฐ๋ฆฌ๊ฐ ๊ณ์ ์์๋ฅผ ๋ค๊ณ ์๋ let x = 5; ๊ตฌ๋ฌธ๋ ์๋ณ์๋ฅผ ์ ์ธํ๊ณ 5๋ผ๋ ๊ฐ์ ์ ์ํ์ฌ ๊ฐ์ ์์ฑํ ๊ฒ์ด๋ค.
์ง๊ธ๊น์ง go ์ธ์ด๋ก let ๋ฌธ์ ํ์ฑํ ๊ฒฐ๊ณผ๋ก ์์ฑ๋๋ AST ์๋ฃ๊ตฌ์กฐ๋ฅผ ๋์ํํ๋ฉด ์๋์ ๊ฐ๋ค.
์ด์ ํ์๋ฅผ ์ ์ํ๋ ์์ค์ฝ๋๋ฅผ ์ดํด๋ณด์.
// parser/parser.go
package parser
import (
"monkey/ast"
"monkey/lexer"
"monkey/token"
)
type Parser struct {
l *lexer.Lexer
curToken token.Token
peekToken token.Token
}
func New(l *lexer.Lexer) *Parser {
p := &Parser{l: l}
// ํ์ฌ ์ด๊ธฐ token ๊ฐ์ ๋น ๋ฌธ์์ด๊ธฐ ๋๋ฌธ์, curToken, peekToken์ ํ ํฐ์ ๋ด์ผ๋ ค๋ฉด 2๋ฒ ์ํ
p.nextToken()
p.nextToken()
return p
}
func (p *Parser) nextToken() {
p.curToken = p.peekToken
p.peekToken = p.l.NextToken()
}
func (p *Parser) ParseProgram() *ast.Program {
return nil
}
์ ์์ค์ฝ๋๋ ๋ ์๋ฅผ ์ ๋ ฅ์ผ๋ก ๋ฐ์์ ๋ ์๋ฅผ ๋ฉค๋ฒ๋ก๋ Parser ๊ตฌ์กฐ์ฒด ๋ณ์๋ฅผ ์ ์ํ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ์ฃผ์์๋ ํ์ํด๋์์ง๋ง ๋ ์๊ฐ ํ ํฐํ์ํจ ํ ํฐ๋ค์ ํ์ ๊ตฌ์กฐ์ฒด ๋ฉค๋ฒ์ธ curToken, peekToken ๋ณ์์ ์ต์ด๋ก ๋ด์๋ด๊ธฐ ์ํด 2๋ฒ nextToken ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค. ์ด nextToken ๋ฉ์๋๋ ๋ณ ๊ฒ์ ์๋๊ณ , ์ฐ๋ฆฌ๊ฐ ์ด์ ํฌ์คํ ์์ ๊ตฌํํ ๋ ์์ NextToken() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒ์ด๋ค.(๊ธฐ์ต์ด ๋์ง ์๋๋ค๋ฉด ์ด์ ํฌ์คํ ์ฐธ์กฐ)
๊ทธ๋ฆฌ๊ณ ParseProgram() ์ด๋ผ๋ Parser ๊ตฌ์กฐ์ฒด์ pointer ๋ณ์๋ฅผ receive ํ๋ ๋ฉ์๋๋ ํ์ฑ ํ์ ๊ฒฐ๊ณผ๋ฌผ์ธ AST ์๋ฃ๊ตฌ์กฐ๋ฅผ ๋ฐํํ๋ ๊ธฐ๋ฅ์ ํ๋ค. ์ง๊ธ์ ๋ฐ๋ก ๊ตฌํํ ๊ฒ์ด ์๊ธฐ์ ๊ณง๋ฐ๋ก nil ์ ๋ฐํํ๋๋ก ์ค์ ํ๋ค.
๋ ์๋ฅผ ๊ตฌํํ๊ณ ํ ์คํธํ์ ๋์ฒ๋ผ ํ์๋ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๊ณ , ์ฐ๋ฆฌ๊ฐ ๊ฐ๋ฐํ๊ณ ์๋ ํ์๊ฐ ํ ์คํธ ์ฝ๋๋ฅผ ํต๊ณผํ๋์ง ์ดํด๋ณด์์ผ ํ๋ค. ํ ์คํธ ์ฝ๋๋ถํฐ ์ดํด๋ณด์.
// parser/parser_test.go
package parser
import (
"monkey/ast"
"monkey/lexer"
"testing"
)
func TestLetStatements(t *testing.T) {
input := `
let x = 5;
let y = 10;
let foobar = 838383;
`
// lexer(lexing is not run yet)
l := lexer.New(input)
// parser(parsing is not run yet)
p := New(l)
program := p.ParseProgram()
if program == nil {
t.Fatalf("ParseProgram() returned nill")
}
if len(program.Statements) != 3 {
t.Fatalf("program.Statements does not contain 3 statements. got=%d",
len(program.Statements))
}
tests := []struct {
expectedIdentifier string
}{
{"x"},
{"y"},
{"foobar"},
}
for i, tt := range tests {
stmt := program.Statements[i]
if !testLetStatement(t, stmt, tt.expectedIdentifier) {
return
}
}
}
func testLetStatement(t *testing.T, s ast.Statement, name string) bool {
if s.TokenLiteral() != "let" {
t.Errorf("s.TokenLiteral not 'let'. got=%q", s.TokenLiteral())
return false
}
// type assertion(downcast from interface to struct)
letStmt, ok := s.(*ast.LetStatement)
if !ok {
t.Errorf("s not *ast.LetStatement. got=%T", s)
return false
}
if letStmt.Name.Value != name {
t.Errorf("letStmt.Name.Value not '%s'. got=%s", name, letStmt.Name.Value)
return false
}
if letStmt.Name.TokenLiteral() != name {
t.Errorf("letStmt.Name.TokenLiteral() not '%s'. got=%s", name, letStmt.Name.TokenLiteral())
return false
}
return true
}
input ๋ณ์์๋ ๋ ์ฑ ๋ฐ ํ์ฑ์ ๋์์ด ์์ค์ฝ๋ ๋ฌธ์์ด์ ์ ์ํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ์์ ํ์๋ฅผ ์ค๋นํ๋ค. tests ๋ณ์๋ฅผ ๋ณด๋ฉด input์ ๋ช ์ํ ์์ค์ฝ๋ ๋ฌธ์์ด์ ๋ง๊ฒ๋ x, y , foobar ๋ผ๋ ๋ฌธ์์ด์ด ๋ด๊ฒจ์๋ ๊ตฌ์กฐ์ฒด๋ฅผ ์ ์ํ๋ค. ์ฆ, ์ผ์ข ์ ํ ์คํธ ๊ธฐ๋๊ฐ์ ๋ฏธ๋ฆฌ ์ฌ์ ์ ์ ์ํ๊ณ ์ฐ๋ฆฌ์ ํ์๊ฐ ๋์ํ์ ๋ ๊ธฐ๋๊ฐ๊ณผ ์ผ์นํ๋์ง ๋ณด๊ธฐ ์ํจ์ด๋ค.
tests ๊ตฌ์กฐ์ฒด ๊ธธ์ด๋งํผ ๋ฃจํ๋ฅผ ๋๋๋ฐ, program ์ด๋ผ๋ ๋ณ์์ ์ ์๋ AST ์๋ฃ๊ตฌ์กฐ์ ๋ด๊ฒจ์ง ๋ช ๋ น๋ฌธ ํ๋์ฉ์ ๋น๊ตํ๋๋ฐ ์ด ๋ testLetStatement ํจ์๋ฅผ ์ด์ฉํด ํ ์คํธํ๋ค. ์ด testLetStatement ํจ์์ ๊ธฐ๋ฅ์ ์ดํด๋ณด์.
๊ฒ์ฌํ๊ณ ์๋ ๋ช ๋ น๋ฌธ์ Statement ํ์ ์ ์ํ๊ธฐ ๋๋ฌธ์ TokenLiteral ๋ฉ์๋๊ฐ ์ธํฐํ์ด์ค๋ก ๊ตฌํ๋์ด ์์ ๊ฒ์ด๊ณ , ์ด๋ฅผ ํธ์ถํจ์ผ๋ก์จ ๊ฒ์ฌํ๊ณ ์๋ ๋ช ๋ น๋ฌธ์ด 'let' ๋ฌธ์ธ์ง๋ฅผ ๋จผ์ ํ์ธํ๋ค.
๋ค์์ผ๋ก go ์ธ์ด์์ Type Assertion ์ด๋ผ๋ ๋ฌธ๋ฒ์ด ๋ฑ์ฅํ๋ค. ์ฆ, ๋ณ์ s๋ Statement ๋ผ๋ ์ธํฐํ์ด์ค์ธ๋ฐ, ํด๋น ์ธํฐํ์ด์ค๋ฅผ LetStatement ๋ผ๋ ์ด๋ฆ์ ๊ตฌ์กฐ์ฒด๋ก downcasting ํ๋ ๊ฒ์ด๋ค. ์๋ํ๋ฉด ๋ฐ์์ LetStatement ๊ตฌ์กฐ์ฒด ๋ฉค๋ฒ ๋ณ์์ธ Name์ ์ ๊ทผํ๊ธฐ ์ํด์๋ค.
LetStatement์ Name ๋ฉค๋ฒ๋ Identifier ๊ตฌ์กฐ์ฒด๋ฅผ ๋ด๊ณ ์๋๋ฐ, ๋จผ์ Identifier ๊ตฌ์กฐ์ฒด์ Value ๋ฉค๋ฒ์ ๋ค์ด์๋ ๊ฐ์ ํ์ธํ๋ค. ์ด ๊ฐ์๋ ์๋ณ์(ex. x, y, foobar)๊ฐ ๋ค์ด์์ด์ผ๋ง ํ๋ค. ๊ทธ๋ฆฌ๊ณ ํด๋น Identifer ๊ตฌ์กฐ์ฒด์ TokenLiteral() ๋ฉ์๋๋ฅผ ํธ์ถํด์ ํน์๋ผ๋ ์๋ณ์์ ํํ์์ด ์ฐ๊ฒฐ๋์ด ์๋์ง ์ฆ, ํ ํฐ์ด ์๋์ง ํ์ธํ๋ค.
์ฐธ๊ณ ๋ก, ์ฌ๊ธฐ์๋ LetStatement ๊ตฌ์กฐ์ฒด์ Value ๋ฉค๋ฒ๋ฅผ ๊ฒ์ฌํ์ง๋ ์๋๋ฐ, ์ถํ์ ์ถ๊ฐ๋ ์์ ์ด๋ค. ์ง๊ธ ๋น์ฅ์ let ๋ฌธ ํ์ฑ์ด ๋์ํ๋์ง ์ฌ๋ถ๋ง ํ์ธํ๊ธฐ ์ํจ์ด๋ผ ์คํตํ์๋ค.
์ด์ ์ฐ๋ฆฌ๋ง์ ํ์์ ๊ธฐ๋ฅ์ ๋ง๋ค๊ธฐ ์ํด์ ParseProgram() ์ด๋ผ๋ ์ํธ๋ฆฌํฌ์ธํธ ๋ฉ์๋์ ๋ก์ง์ ์ด์ ๋ถ์ฌ๋ณด์. ๋จผ์ ์ํธ๋ฆฌํฌ์ธํธ ํจ์์ ์๊น์๋ฅผ ๋ณด๊ณ , ๊ทธ ์์์ ์ฌ์ฉํ๋ ๋ ๋ค๋ฅธ ํจ์๊ฐ ์ด๋ค ๊ธฐ๋ฅ์ ํ๋์ง ์ฌ๊ท์ ์ผ๋ก ์ดํด๋ณด๋๋ก ํ์.
// parser/parser.go
func (p *Parser) ParseProgram() *ast.Program {
program := &ast.Program{}
program.Statements = []ast.Statement{}
for p.curToken.Type != token.EOF {
stmt := p.parseStatement()
if stmt != nil {
program.Statements = append(program.Statements, stmt)
}
p.nextToken()
}
return program
}
๋จผ์ Program ์ด๋ผ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ๋น ๊ตฌ์กฐ์ฒด๋ก ์ ์ธ ๋ฐ ์ ์ํ๋ค. ์์์ ์ด์ผ๊ธฐํ๋ค์ํผ Program ์ด๋ผ๋ ๊ตฌ์กฐ์ฒด๋ ํ์๊ฐ ๋์ํ๋ ๊ฒฐ๊ณผ๋ฌผ์ธ AST ์๋ฃ๊ตฌ์กฐ๋ฅผ ์๋ฏธํ๋ค. ๊ทธ๋ฆฌ๊ณ Program ๊ตฌ์กฐ์ฒด์ Statments ๋ผ๋ ๋ฉค๋ฒ๊ฐ ์๋๋ฐ, ์ด ๋ฉค๋ฒ์๋ Statement ๋ผ๋ ์ธํฐํ์ด์ค๊ฐ ์ฌ๋ผ์ด์ค ํํ๋ก ์ ์๋๋๋ฐ, ์ฌ๊ธฐ์ ์ฌ๋ฌ ๊ฐ์ ๋ช ๋ น์ด๊ฐ ์ ์ฅ๋๋ค. ์ด Statements ๋ฉค๋ฒ๋ ๋น ์ฌ๋ผ์ด์ค๋ก ์ด๊ธฐํ๋ฅผ ์ํํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ๋ค, ํ์ฌ ํ ํฐ์ด ์์ค์ฝ๋์ ๋(EOF)์ด ๋์ค๊ธฐ ์ ๊น์ง ํ์ฑ์ ์ํํ๊ณ , ํ์ฑ์ด ์๋ฃ๋ ๋ช ๋ น๋ฌธ ๋ ธ๋๋ Statements ๋ฉค๋ฒ ์ฆ, ์ฌ๋ผ์ด์ค์ ์ถ๊ฐ๋์ด AST ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ ์ ํ์ฑํด ๋๊ฐ๊ฒ ๋๋ค.
์ด์ ์ ์์ค์ฝ๋์ p.parseStatement() ํจ์์ ์์ค์ฝ๋๋ฅผ ์ดํด๋ณผ ์ฐจ๋ก๋ค.
// parser/parser.go
func (p *Parser) parseStatement() ast.Statement {
switch p.curToken.Type {
case token.LET:
return p.parseLetStatement()
default:
return nil
}
}
ํ์ฌ ๊ฐ๋ฆฌํค๊ณ ์๋ ํ ํฐ์ด ๋ฌด์์ธ์ง๋ฅผ ๋ณด๊ณ , ๊ทธ ํ ํฐ์ ๋ง๋ ํ์ฑ ํจ์๋ฅผ ๋ ํ๋ฒ ํธ์ถํ๋ค. ํ์ฌ ์ฐ๋ฆฌ๋ let ๋ฌธ๋ง์ ํ์ฑํ๋ ๊ฒ์ ๊ธฐ๋ฅ์ผ๋ก ์ถ๊ฐํ๊ณ ์๊ธฐ ๋๋ฌธ์ case ๊ตฌ๋ฌธ์ LET์ ๋ํ ์กฐ๊ฑด๋ฌธ๋ง ์กด์ฌํ๋ค. ์ด์ let ๋ฌธ์ ์ค์ง์ ์ผ๋ก ํ์ฑํ๋ ์ญํ ์ ํ๋ p.parseLetStatment() ํจ์์ ์๊น์๋ฅผ ์ดํด๋ณผ ์ฐจ๋ก๋ค.
// parser/parser.go
func (p *Parser) parseLetStatement() *ast.LetStatement {
stmt := &ast.LetStatement{Token: p.curToken}
if !p.expectPeek(token.IDENT) {
return nil
}
stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
if !p.expectPeek(token.ASSIGN) {
return nil
}
// This time, it is skipped !
for !p.curTokenIs(token.SEMICOLON) {
p.nextToken()
}
return stmt
}
let ๋ฌธ์ ํ์ฑํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๊ฐ์ฅ ๋จผ์ ์ฌ์ ์ ์ ์ํด๋์ let ๋ฌธ ๋ ธ๋๋ฅผ ์์ฑํ LetStatment ๋ผ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ์ ์ํ๋ค. ์ด ๋ LetStatement ๊ตฌ์กฐ์ฒด์ Token ๋ฉค๋ฒ์๋ ํ์ฌ ํ ํฐ์ธ LET ํ ํฐ์ ํ ๋นํ๋ฉฐ ์ ์ธ ๋ฐ ์ ์ํ๋ค.
๊ทธ๋ฆฌ๊ณ p.expectPeek() ๋ผ๋ ํจ์๋ฅผ ์ด์ฉํด์ IDENT ํ ํฐ ์ฆ, ์๋ณ์ ํ ํฐ์ด let ํ ํฐ ๋ค์์ ์์ ๊ฒ์ ๊ธฐ๋ํ๋ค. ๋ง์ฝ ์๋ณ์ ํ ํฐ์ด ๋ฑ์ฅํ์ง ์๋๋ค๋ฉด nil ์ ๋ฐํํ๋ค. ๊ทธ๋ฌ๋ฉด expectPeek ํจ์๊ฐ ๋ฌด์จ ์ญํ ์ ํ๋์ง ํ ๋ฒ ์ดํด๋ณด์.
// parser/parser.go
func (p *Parser) expectPeek(t token.TokenType) bool {
if p.peekTokenIs(t) {
p.nextToken()
return true
} else {
return false
}
}
์ด๋ฒ์ ๋ peekTokenIs ๋ผ๋ ํจ์๊ฐ ๋ฑ์ฅํ๋ค. ๊ทธ๋ฌ๋ฉด ๋ peekTokenIs ํจ์ ์๊น์๋ฅผ ๋ณด์.
// parser/parser.go
func (p *Parser) peekTokenIs(t token.TokenType) bool {
return p.peekToken.Type == t
}
peekTokenIs ํจ์๋ ํ์ฌ ํ ํฐ์ ๋ค์ ์์น์ ์๋ ํ ํฐ์ด ํด๋น ํจ์์ ์ธ์๋ก ๋ค์ด์จ ํ ํฐ๊ณผ ๋์ผํ๋ค๋ฉด true๋ฅผ ๋ฐํํ๋ค. ์ด์ ๋ค์ expectPeek ํจ์๋ก ๋์๊ฐ๋ณด๋ฉด, expectPeek ํจ์์ ์ธ์๋ก ๋ค์ด์จ ํ ํฐ์ด ๋ค์ ์์น์ ์๋ ํ ํฐ๊ณผ ์ผ์นํ๋ค๋ฉด ํ์ฌ ์์น๋ฅผ ๋ค์ ํ ํฐ์ด ์๋ ์์น๋ก ์ฎ๊ธด ํ true๋ฅผ ๋ฆฌํดํ๋ค.(์ฃผ๋ชฉํ ์ ์ ์ด 'ํ์ฌ ์์น๋ฅผ ๋ค์ ํ ํฐ์ด ์๋ ์์น๋ก ์ฎ๊ธฐ๋' ํจ์์ธ p.nextToken() ํจ์๋ฅผ ํธ์ถํ๋ ์๊ฐ ๋ ์์ NextToken() ํจ์๋ฅผ ํธ์ถํด์ ๋ ์ฑ์ ์ํํ๋ค!!) ์ด์ ๋ค์ ์๋ก ์ฌ๋ผ๊ฐ์ parseLetStatement() ํจ์์์ p.expectPeek(token.IDENT) ํจ์๋ฅผ ํด์ํด๋ณด์. ๊ฒฐ๊ตญ, ์ด ํจ์๋ ๋ค์ ์์น์ ์๋ ํ ํฐ์ด ์๋ณ์ ํ ํฐ์ด๋ผ๋ฉด true๋ฅผ, ๊ทธ๋ ์ง ์์ผ๋ฉด false๋ฅผ ๋ฐํํ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฐ๋ฐ ์์ not ์ฐ์ฐ(!)์ด ์์ผ๋ฏ๋ก if ๊ตฌ๋ฌธ์ ํ๋ ๊ฒฝ์ฐ๋ ๋ค์ ์์น์ ์๋ ํ ํฐ์ด ์๋ณ์ ํ ํฐ์ด ์๋ ๊ฒฝ์ฐ์ด๋ค. ๊ฒฐ๊ตญ, ์ด ํจ์๊ฐ ์๋ฏธํ๋ ๋ฐ๋ let ๋ฌธ ๋ค์์ ์๋ณ์๊ฐ ๋ฐ๋์ ์์ผ ํ ๊ฒ์์ ์์ํ๊ณ , ๋ง์ฝ ์๋ณ์๊ฐ ๋ฑ์ฅํ์ง ์๋๋ค๋ฉด nil์ ๋ฐํํ๋๋ก ํ ๊ฒ์ด๋ค.
๊ณ์ parseLetStatement() ํจ์ ๋ก์ง์ ์ดํด๋ณด์. ๋ฐฉ๊ธ ์ค๋ช ํ if ๊ตฌ๋ฌธ์ ํ์ง ์์์ผ๋ฉด ํ์ฌ ์์น์๋ ์๋ณ์ ํ ํฐ์ด ๋ด๊ฒจ ์๋ ์ํ์ด๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ด์ LetStatement ๊ตฌ์กฐ์ฒด์ Name ๋ฉค๋ฒ๊ฐ Identifer ๊ตฌ์กฐ์ฒด๋ฅผ ์ ์ํ๋ค. ์ด Identifer ๊ตฌ์กฐ์ฒด๋ ์๋ณ์๋ฅผ ์๋ฏธํ๋ฏ๋ก, ํ์ฌ ์์น์ ํ ํฐ๊ณผ ๋ฆฌํฐ๋ด ๊ฐ์ ํ ๋นํ๋ค.
๋ค์์ผ๋ก !p.expectPeek(token.ASSIGN) ๊ตฌ๋ฌธ์ ๋ณด์. ์ฐ๋ฆฐ ์ด๋ฏธ expectPeek ํจ์์ ์ญํ ์ ์ดํด๋ณด์์ผ๋ฏ๋ก, ๋ฐ๋ก ์์ฐ์ค๋ฝ๊ฒ ์ดํดํ ์ ์๋ค. not ์ฐ์ฐ๊น์ง ํฌํจํด์ !p.expectPeek(token.ASSIGN) ์ ํด์ํ๋ค๊ณ ํ๋ฉด, ํด๋น if ๊ตฌ๋ฌธ์ ํ๋ ๊ฒฝ์ฐ๋ ๋ค์ ํ ํฐ์ด ๋ฑํธ(=) ํ ํฐ์ด ๋ฑ์ฅํ์ง ์์ ๊ฒฝ์ฐ์ด๋ค.
๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง์ผ๋ก p.curTokenIs ๋ผ๋ ํจ์๊ฐ ๋ฑ์ฅํ๋ค. ์ด ํจ์์ ์๊น์๋ ์ดํด๋ณด์.
// parser/parser.go
func (p *Parser) curTokenIs(t token.TokenType) bool {
return p.curToken.Type == t
}
curTokenIs ํจ์๋ peekTokenIs ํจ์์ ๊ฑฐ์ ๋์ผํ๋ค. ๋ค๋ง, ํ์ฌ ํ ํฐ์ ์ดํด๋ณด๋์ง, ๋ค์ ํ ํฐ์ ์ดํด๋ณด๋์ง์ ์ฐจ์ด์ผ ๋ฟ์ด๋ค. ์ด curTokenIs ํจ์๋ฅผ ์ฌ์ฉํ๋ ๊ตฌ๋ฌธ์ธ !p.curTokenIs(token.SEMICOLON) ์ ๋ณด๋ฉด ์ธ๋ฏธ์ฝ๋ก ์ ๋ง๋ ๋ ๊น์ง for ๋ฃจํ๋ฅผ ๋๋ฉด์ ๊ณ์ nextToken ํจ์๋ฅผ ํธ์ถํ๋ค .
๊ทธ๋ฐ๋ฐ ์ ์์ค์ฝ๋๋ฅผ ๋ณด๋ฉด ํ์๊ฐ ์์์น ๋ชปํ ํ ํฐ์ด ๋ฑ์ฅํ๊ฑฐ๋ ํ์ ๋ ํ์ฌ๋ ์๋ฌ๋ฅผ ๋ฐ๋ก ๋ด๋ฑ์ง ์๊ณ ์๋ค. ๊ทธ๋ฌ๋ฏ๋ก ์๋ฌ๋ฅผ ๋ฐํํ๋๋ก ์ถ๊ฐํด๋ณด์.
// parser/parser.go
type Parser struct {
l *lexer.Lexer
curToken token.Token
peekToken token.Token
errors []string
}
func New(l *lexer.Lexer) *Parser {
p := &Parser{
l: l,
errors: []string{},
}
// ํ์ฌ ์ด๊ธฐ token ๊ฐ์ ๋น ๋ฌธ์์ด๊ธฐ ๋๋ฌธ์, curToken, peekToken์ ํ ํฐ์ ๋ด์ผ๋ ค๋ฉด 2๋ฒ ์ํ
p.nextToken()
p.nextToken()
return p
}
func (p *Parser) Errors() []string {
return p.errors
}
func (p *Parser) peekError(t token.TokenType) {
msg := fmt.Sprintf("expected next token to be %s, got %s instead", t, p.peekToken.Type)
p.errors = append(p.errors, msg)
}
func (p *Parser) expectPeek(t token.TokenType) bool {
if p.peekTokenIs(t) {
p.nextToken()
return true
} else {
p.peekError(t)
return false
}
}
์ง๊ธ๊น์ง ์์ฑํ๋ ์ฝ๋์ ์๋ฌ ๋ฉ์ธ์ง๋ฅผ ๋ด๋๋ก ๊ตฌ์กฐ์ฒด ๋ฉค๋ฒ์ ์ถ๊ฐํ๋ค. ์ด์ ํ ์คํธ ์ฝ๋์ ํด๋น ์๋ฌ๋ฅผ ๋ด๋ฑ๋ ๋ก์ง์ ์๋์ฒ๋ผ ๋ฐ์ํด๋ณด์. ์๋ ์ฝ๋๋ ๋ฐ์๋ ์ฝ๋ ์ผ๋ถ๋ง ์์ฑํ๋ค.
// parser/parser_test.go
func TestLetStatements(t *testing.T) {
input := `
let x = 5;
let y = 10;
let foobar = 838383;
`
// lexer(lexing is not run yet)
l := lexer.New(input)
// parser(parsing is not run yet)
p := New(l)
program := p.ParseProgram()
checkParserErrors(t, p)
if program == nil {
t.Fatalf("ParseProgram() returned nill")
}
...(์๋ต)...
}
func checkParserErrors(t *testing.T, p *Parser) {
errors := p.Errors()
if len(errors) == 0 {
return
}
t.Errorf("parser has %d errors", len(errors))
for _, msg := range errors {
t.Errorf("parser error: %q", msg)
}
t.FailNow()
}
์ด์ ์๋์ฒ๋ผ ์ผ๋ถ๋ฌ ์๋ฌ๋ฅผ ๋ด๋ฑ๋๋ก ์ธํ ์์ค์ฝ๋ ๋ฌธ์์ด์ ์๋์ฒ๋ผ ์์ ํ ํ, ํ ์คํธ ์ฝ๋๋ฅผ ์ํํด๋ณด์.
// parser/parser_test.go
func TestLetStatements(t *testing.T) {
input := `
let x 5;
let = 10;
let 838383;
`
(...์๋ต...)
go test ./parser
๊ทธ๋ฌ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ฝ์์ ์ถ๋ ฅ์ด ๋๋ค.
๋ฉ์ธ์ง๋ฅผ ๋ณด๋ฉด ๋ฑํธ(=) ํ ํฐ์ ์์ํ์ง๋ง INT ํ ํฐ์ด ๋ฑ์ฅํ๋ค๊ณ ๋ฉ์ธ์ง๊ฐ ๋์๋ค. ์ด๋ ์ ์ธํ์์ let x 5; ์ด ๋ถ๋ถ์์ ํ์ฑ ์๋ฌ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋ค. ๋๋จธ์ง ๋ฉ์ธ์ง๋ ์ค์ค๋ก ํ ๋ฒ ์๊ฐํด๋ณด์.
3. return ๋ฌธ ํ์ฑํ๊ธฐ
์ง์ ๋ชฉ์ฐจ๊น์ง์ ๋ด์ฉ์ ํ์ตํ๋ฉด์ ์ฐ๋ฆฌ๋ง์ ํ์๊ฐ let ๋ฌธ์ ํ์ฑํ ์ ์๋๋ก ๊ธฐ๋ฅ์ ์์ฑํ๋ค. ์์์ ๊ตฌํํ๋ ์ฃผ์ ๋ก์ง์ ํ ๋๋ก return ๋ฌธ์ ํ์ฑํด๋ณด๋๋ก ํ์. ์ฐ๋ฆฌ์ ํ์๊ฐ ํ์ฑํ ๋์์ธ Monkey ์ธ์ด์ return ๋ฌธ ์์๋ ๋ค์๊ณผ ๊ฐ๋ค.
return 5;
return 10;
return add(15);
์ ์์์ฝ๋๋ฅผ ๋ณด๋ ๊ฒ์ฒ๋ผ return ๋ฌธ์ ๊ท์น์ return ์ด๋ผ๋ ํ ํฐ์ ํํ์, ๊ทธ๋ฆฌ๊ณ ์ธ๋ฏธ์ฝ๋ก ํ์์ผ๋ก ์ด๋ฃจ์ด์ ธ ์๋ค.
๋จผ์ return ๋ฌธ์ ํ์ฑํด์ ๋ ธ๋๋ก ์์ฑํ๊ธฐ ์ํด return ๊ตฌ๋ฌธ ๋ ธ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ์ ์ํด๋ณด์.
// ast/ast.go
type ReturnStatement struct {
Token token.Token
ReturnValue Expression
}
func (rs *ReturnStatement) statementNode() {}
func (rs *ReturnStatement) TokenLiteral() string { return rs.Token.Literal }
ReturnStatement ๋ผ๋ ์ด๋ฆ์ ๊ตฌ์กฐ์ฒด๋ก ์ ์ํ๊ณ , ๋ฉค๋ฒ๋ return ์ด๋ผ๋ ํ ํฐ์ ๋ด์ Token ์ด๋ผ๋ ๋ฉค๋ฒ์ ํํ์์ ๋ด์ ReturnValue ๋ฉค๋ฒ๋ฅผ ๊ฐ์ง๋ค. ๊ทธ๋ฆฌ๊ณ ReturnStatement๋ ๋ ธ๋์ ์ผ์ข ์ด๊ธฐ ๋๋ฌธ์ statementNode() ๋ผ๋ ๋ฉ์๋์ TokenLiteral() ๋ฉ์๋๋ฅผ ์ ์ํจ์ผ๋ก์จ ์ธํฐํ์ด์ค๋ฅผ ์ถฉ์กฑ์์ผฐ๋ค.
์ด์ ๋ค์์ผ๋ก๋ return ๋ฌธ์ ํ์ฑํ๊ธฐ ์ํ ํ ์คํธ ์ฝ๋๋ถํฐ ์์ฑํด๋ณด์. ๊ทธ๋ฆฌ๊ณ ๋ ๋ค์๋ ํ ์คํธ ์ฝ๋์ ์ ์๋์ด ์๋ return ๋ฌธ์ ํ์ฑํ๋ ํจ์๋ฅผ ํ๋์ฉ ์ดํด๋ณด์.
// parser/parset_test.go
func TestReturnStatements(t *testing.T) {
input := `
return 5;
return 10;
return add(15);
`
l := lexer.New(input)
p := New(l)
program := p.ParseProgram()
checkParserErrors(t, p)
if len(program.Statements) != 3 {
t.Fatalf("program.Statements does not contain 3 statements. got=%d",
len(program.Statements))
}
for _, stmt := range program.Statements {
returnStmt, ok := stmt.(*ast.ReturnStatement) // type assertion: downcast from interface to struct
if !ok {
t.Errorf("stmt not *ast.ReturnStatement. got=%T", stmt)
continue
}
if returnStmt.TokenLiteral() != "return" {
t.Errorf("returnStmt.TokenLiteral not 'return'. got=%s", returnStmt.TokenLiteral())
}
}
}
๋ก์ง ์์ฒด๋ ์ด์ ์ let ๋ฌธ์ ํ ์คํธํ๋ ์ฝ๋์ ๋์ผํ๋ค. ํ์ฑ์ ์ํํ๋ ํธ๋ฆฌ๊ฑฐ๋ program := p.ParseProgram() ๋ผ์ธ์์ ์คํ๋๋ค. ๊ทธ๋ฌ๋ฉด ์ฐ๋ฆฌ๋ ์ด์ ์ ์ ์ํ๋ p.ParseProgram() ํจ์์๋ค๊ฐ๋ return ๋ฌธ์ ํ์ฑํ ์ ์๋๋ก ์์ ํด์ฃผ์ด์ผ ํ๋ค. ๋ค์ p.ParseProgram() ํจ์ ์๊น์๋ฅผ ๋ณด์.
// parser/parser.go
func (p *Parser) ParseProgram() *ast.Program {
program := &ast.Program{}
program.Statements = []ast.Statement{}
for p.curToken.Type != token.EOF {
stmt := p.parseStatement()
if stmt != nil {
program.Statements = append(program.Statements, stmt)
}
p.nextToken()
}
return program
}
์ด์ ์ ๋ดค๋ ๊ฒ๊ณผ ์๊น์๊ฐ ๋์ผํ๋ค. ์ ์์ค์ฝ๋์์ p.parseStatement() ๋ผ๋ ํจ์๊ฐ ํธ์ถ๋๋ค. ๊ทธ๋ฌ๋ฉด ๋ ์ฐ๋ฆฌ๋ ์ด ํจ์๋ฅผ ๋ฐ๋ผ๊ฐ๋ณด์.
// parser/parser.go
func (p *Parser) parseStatement() ast.Statement {
switch p.curToken.Type {
case token.LET:
return p.parseLetStatement()
default:
return nil
}
}
parseStatement() ํจ์๋ ํ์ฌ ์์ ๊ฐ์ ์๊น์๋ฅผ ๊ฐ๊ณ ์๋ค. ์ด์ ์ ์ฐ๋ฆฌ๊ฐ ์์ฑํ๋ ๊ฒ์ฒ๋ผ ์์ง์ let ๋ฌธ๋ง ํ์ฑํ ์ ์๋๋ก ๋์ด ์๋ค. ์ฌ๊ธฐ์๋ค๊ฐ ์ฐ๋ฆฌ๋ return ๋ฌธ๋ ํ์ฑํ ์ ์๋๋ก ์ถ๊ฐํด์ฃผ๋๋ก ํ์.
// 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 nil
}
}
parseReturnStatement() ๋ผ๋ ํจ์๊ฐ ์ถ๊ฐ๋์๋ค. ์ด๋ฒ์๋ parseReturnStatement() ํจ์์ ์๊น์๋ฅผ ๋ณผ ์ฐจ๋ก๋ค.
// parser/parser.go
func (p *Parser) parseReturnStatement() *ast.ReturnStatement {
stmt := &ast.ReturnStatement{Token: p.curToken}
p.nextToken()
// This time, it is skipped !
for !p.curTokenIs(token.SEMICOLON) {
p.nextToken()
}
return stmt
}
ReturnStatement ๊ตฌ์กฐ์ฒด๋ฅผ ์ ์ํ๋๋ฐ, ํด๋น ๊ตฌ์กฐ์ฒด์ Token ๋ฉค๋ฒ์ RETURN ํ ํฐ(p.curToken)์ด ๋ด์์ค๋ค. ๊ทธ๋ฆฌ๊ณ parseLetStatement() ํจ์์๋ ๋ค๋ฅด๊ฒ ๋จผ์ p.nextToken() ํจ์๋ฅผ ํธ์ถํด์ฃผ์ด์ ํ์ฌ ์์น๋ฅผ ๋ค์ ํ ํฐ์ผ๋ก ์ฎ๊ฒจ์ค๋ค. ์ฆ, ํ์ฌ ์์น๋ return ํ ํฐ์ ์๋๋ฐ, ์ด ์์น๋ฅผ return ๋ค์์ ์๋ ํํ์์ผ๋ก ์ฎ๊ธด๋ค๋ ์๋ฏธ์ด๋ค.
๊ทธ๋ฆฌ๊ณ parseLetStatement() ํจ์์ ๋ง์ฐฌ๊ฐ์ง๋ก ์์ง์ ํํ์์ ํ์ฑํ๋ ๋ก์ง์ ์ถ๊ฐํ๋ ๋จ๊ณ๋ ์๋๊ธฐ ๋๋ฌธ์ parseReturnStatement() ํจ์์์๋ ์ธ๋ฏธ์ฝ๋ก (;) ํ ํฐ์ ๋ง์ฃผํ ๋๊น์ง ๊ณ์ ํ ํฐ์ ๋ค์ ์์น๋ก ์ฎ๊ธด๋ค. ์ฌ๊ธฐ๊น์ง์ ๋ก์ง์ ํ ์คํธํ๊ธฐ ์ํด์ ํ ์คํธ ์ฝ๋๋ฅผ ์คํ์์ผ๋ณด์.
go test ./parser
์ฝ์์ ํ ์คํธ ๊ฒฐ๊ณผ๊ฐ ok๋ก ๋์ค๋ฉด ์์ฑ๋ ๊ฒ์ด๋ค. ์์ง ์ฐ๋ฆฌ๊ฐ ๋ง๋ ํ์๊ฐ ํํ์์ ํ์ฑํ ์ ์๋ ๊ธฐ๋ฅ์ ์๋ ์ํ์ง๋ง, let ๋ฌธ๊ณผ return ๋ฌธ์ ํ์ฑํ ์๋ ์๋ ๊ธฐ๋ฅ๊น์ง ๊ฐ์ถ์๋ค. ์ด์ ๋ค์ ํฌ์คํ ์์ ํํ์๋ ํ์ฑํ ์ ์๋ ๊ธฐ๋ฅ์ ํ์ฌํด๋ณด๋๋ก ํ์.