[cpp] C 컴파일러의 포인터 해석 방법

@codemaru · August 16, 2007 · 3 min read

아래 deSigne님의 질문에 대한 답변글 입니다.
길어질 것 같아서 새로운 글로 남깁니다.

보통 컴파일러를 만들때에는 lex와 yacc라는 툴을 사용합니다. lex는 스캐너를 자동으로 생성해 주는 툴로 정규표현식을 사용한 토큰 단위로 입력을 잘라내는 역할을 합니다. yacc은 이렇게 lex가 분리한 토큰 단위의 배열을 문법 기준으로 분석해 주는 파서를 자동으로 생생해 주는 툴이죠. 이 두 가지를 익히면 컴파일러나 스크립트의 구문 해석기를 쉽게 만들어 낼 수 있습니다. lex와 yacc에 대응하는 GNU 공개 프로그램으로 flexbison이 있습니다.

ANSI C 언어의 일반적인 문법 구조는 아래 페이지에 나와있습니다.
http://www.lysator.liu.se/c/ANSI-C-grammar-y.html#parameter-type-list

간단한 포인터 표현식이 이 yacc 문법에 의해서 파싱되는 과정을 살펴보면 다음과 같습니다. 간단한 표현식이지만 해석 과정은 간단하지 않습니다. 원래 yacc는 bottom-up 형태로 파싱하지만 이해를 돕기 위해서 top down 형태로 적었습니다.

int (*a)[3];
declaration = declaration_specifiers init_declarator_list ';'
declaration_specifiers = type_specifier
type_specifier = int

init_declarator_list = init_declarator
init_declarator = declarator
declarator = direct_declarator
direct_declarator = direct_declarator [ constant_expression ]
direct_declarator = ( declarator )
declarator = pointer direct_declarator
pointer = *
direct_declarator = IDENTIFIER(= a)

constant_expression =  conditional_expression
conditional_expression = logical_or_expression
logical_or_expression = logical_and_expression
logical_and_expression = inclusive_or_expression
이런식으로 쭈욱 따라가보면 constant_expression은 결국 CONSTANT로 귀결됩니다.
컴파일러는 위의 각 단계별로 트리를 만듭니다. 그걸 파싱 트리라고 하죠. 모든 트리가 완성되면 그 다음에는 트리를 순회하면서 구문을 분석합니다. 그리곤 해당하는 코드를 생성해서 기록 하죠.

결론적으론 글에서 설명한 것처럼 "오른쪽으로 이동하면서 해석하고 다시 왼쪽으로 가고..." 이런 식으로 컴파일러가 해석하지는 않습니다.  그것이 사람이 이해하기엔 직관적이지만 컴퓨터를 그렇게 이해시키기는 더 어렵기 때문이죠.

끝으로 간단히 간단한 잡설을 덧붙인다면 컴퓨터 관련 전공을 하는 전공자라면 컴파일러란 과목을 꼭 들어보길을 권해드리고 싶습니다. 학교마다 편차가 많고 진도 나가는 형태도 틀리지만 그 과목의 어떤 부분을 배우더라도 훗날 엄청난 도움이 될겁니다. 장담합니다. 꼭 들으세요. 컴파일러. ㅋ

@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중