1 + 2 > 10;
대략 이런 구문이 있다고 칩시다. 이 구문은 아래와 같이 쪼갤 수 있습니다.
{ NUMBER, 1}
{ PLUS }
{ NUMBER, 2}
{ GREATE_THAN }
{ NUMBER, 10}
{ SEMICOLON }
이렇게 인지할 수 있는 최소 단위로 쪼갠 것을 Token 이라고 합니다.
이 토큰을 가지고 Parser 를 연결해서 실제 동작할 것 같은 의미로 구조화 시킵니다.
[>]
[+] [10]
[1] [2]
이렇게 구조화 된 형태로 만든 것이 AST(추상 구문 트리)입니다.
그래서 어떤 언어의 기본적인 의미를 가지게 하려면 AST 까지는 만들어야 어떤 언어를 구축할 것인지 알 수 있습니다.
지금부터 해볼 것은 이 AST 까지 만들어내는 Parser(Lexer 를 포함한) 입니다.
Parser 를 만들 때는 ebnf 포맷을 통한 Parser Genenator 를 사용해도 되지만 그냥 한번 해봅시다. 어떤 것인지 맛은 보고 그 다음으로 넘어가면 좋으니깐요.
자, 이제 시작합니다.
먼저 Token 을 분리할 수 있는 Lexer 를 만들어 봅시다.
Token 이 의미를 가질려면 최소한의 의미있는 형태로 먼저 정해진 키워드나 기호를 만들어야 합니다.
그러기 위해서는 초기 언어를 설계해봐야겠죠?
어떤 것을 해보면 좋을까요? 계산기 로직을 한번 만들어 볼까요?
1 + 2 + 3 + (4 * 5) / 2 + 10 - 30
대략 이정도 나열해보면 어떤 것을 구분 지어야 할지 조금 보일까요? 형태로 나열해봅시다.
NUMBER
PLUS : +
MULTIPLE : *
DIVIDE : /
MINUS : -
ParenStart : (
ParenEnd: )
대략 이정도 Token 으로 쪼개야 합니다.
보통 여기서 질문이 올 수 있습니다.
공백은 토큰으로 안 만드나요?공백을 토큰으로 만들 수 있지만 특별한 의미를 가지지 않기 때문에 굳이 토큰으로 취급하지 않고 그냥 넘깁니다.
형태가 나왔으니 하나의 문자열을 토큰으로 쪼개면 토큰의 배열이 될 것입니다.
const tokens = [
['NUMBER', 1],
['PLUS'],
['NUMBER', 2],
['PLUS'],
['NUMBER', 3],
['PLUS'],
['PARENSTART'],
['NUMBER', 4],
['MULTIPLE'],
['NUMBER', 5],
['PARENEND'],
['DIVIDE']
['NUMBER', 2],
['PLUS'],
['NUMBER', 10],
['MINUS'],
['NUMBER', 30],
]
그래서 최종적으로 Lexer 가 만드는 결과물은 tokens 처럼 단순한 Token 의 나열이 됩니다.
이렇게 나열된 Token 을 가지고 Parser 는 하나씩 의미를 쌓아서 AST 를 만들게 됩니다.