파서를 구현할 때 가장 먼저 마주하게 되는 설계 결정 중 하나는 식별자와 예약어를 어떻게 분리하고 처리할 것인지에 대한 부분이에요. 프로그래밍 언어에서 `if`, `while`, `for`와 같은 단어들은 문법 구조를 결정하는 중요한 역할을 수행하기 때문에, 이를 일반적인 변수나 함수 이름으로 사용할 수 있는 식별자(Identifier)와 엄격하게 구분해야 하거든요. 만약 파싱 단계에서 이 둘을 명확히 구분하지 못하면 문법 분석기가 모호한 상태에 빠지게 되어 정확한 구문 분석이 불가능해지기도 해요.
실무에서는 보통 어휘 분석(Lexing) 단계나 그 직후의 파싱 초기 단계에서 예약어 목록을 정의하여 처리해요. Lexer가 문자열을 읽어 들였을 때 그것이 식별자의 규칙에 부합한다면, 다시 한번 미리 정의된 키워드 리스트와 대조하는 과정을 거치는 방식이에요. 예를 들어 `let`이라는 단어가 입력되었을 때, 이것이 단순히 변수 선언을 위한 이름인지 아니면 변수 선언 키워드인지를 확인하여 각각 다른 토큰 타입으로 할당하게 돼요. 이렇게 타입을 분리해두면 파서가 구문 규칙을 적용할 때 훨씬 명확하게 동작할 수 있거든요.
public enum TokenType {
KEYWORD_IF, KEYWORD_WHILE,
IDENTIFIER,
EOF
}
public class Lexer {
private static final Set<String> RESERVED_WORDS = Set.of("if", "while");
public TokenType identify(String input) {
if (RESERVED_WORDS.contains(input)) {
if (input.equals("if")) return TokenType.KEYWORD_IF;
if (input.equals("while")) return TokenType.KEYWORD_WHILE;
}
return TokenType.IDENTIFIER;
}
}
또한 식별자 내에 포함될 수 있는 특수 문자의 처리도 중요하게 다뤄져야 해요. 많은 언어에서 식별자에 공백이나 특정 기호를 직접 넣는 것을 금지하지만, 이스케이프 시퀀스를 통해 이를 허용하는 경우도 있거든요. 이때 파서는 단순히 문자열을 그대로 받아들이는 것이 아니라, 백슬래시와 같은 이스케이프 문자를 해석하여 실제 식별자 이름을 추출하는 과정을 거쳐야 해요. 이러한 규칙이 정확하게 구현되어야만 코드 내에서 복잡한 이름이나 특수 기호가 포함된 식별자가 등장해도 파서가 중단 없이 안정적으로 동작할 수 있어요.
결과적으로 견고한 파서를 만들기 위해서는 어휘 분석 단계에서부터 식별자의 정의를 명확히 하고, 예약어와의 충돌을 원천 차단하는 구조를 설계하는 것이 중요해요. 초기 설계 단계에서 키워드 매핑 테이블이나 해시셋을 활용해 타입을 분리해두면 이후의 구문 분석 로직이 훨씬 단순해지고 유지보수도 쉬워지기 때문이에요.
'develop > Parser' 카테고리의 다른 글
| 재귀 하강 파서 연산자 우선순위 처리 구조 (0) | 2026.07.28 |
|---|---|
| 문자열 파싱 시 숫자 리터럴 처리 구현 (부동소수점 정밀도 대응) (1) | 2026.07.20 |
| 재귀 하강 파서 공백 및 주석 건너뛰기 구현 (0) | 2026.07.15 |
| Ragel 기초 정리 (0) | 2014.10.27 |