Flex의 사용방법

개요

학교 컴파일러 과제를 진행하다가 Flex를 사용해 볼 기회가 있었다. 이론 수업을 듣다 보면 어느 정도 이해가 가기는 하지만, 본래 프로그래밍이란게 백문이 불여일타 이기 때문에 직접 작성해 보고 언제 쓸 수 있을지는 모르겠지만 기록으로 남겨두어야겠다는 생각이 들어서 블로그 아티클을 작성해본다.

lex란?

Flex에 대하여 말하기 이전에, Lex에 대해서 이해해야 한다. LexLexical Analyzer Generator이다. 이 말인즉슨, 어떠한 규칙에 따라서 Input Stream으로 들어온 Source Code를 미리 정의된 문법 규칙에 따라서 Tokenize해 주는 것 Lexical을 말한다. 다시 말하면 Lexical Analyzer의 주 역할은 다음과 같다. Lexical Analyzer의 역할: Source Code -> Token으로 Tokenize해 준다. flex

flex란?

그렇다면 flex란 무엇일까? FlexFast Lexical Analyzer Generator 란 것으로 Lex의 Open-Source 버전이라고 생각하면 된다.

flex 설치하기

필자의 환경은 Mac이므로 Mac 기준으로 설명하겠다.

$ brew install flex

flex 사용하기

기본적인 Lex 파일의 구조는 다음과 같다.

/* FileName: lex.l */

%{
/* C Header Include, Define */
%}

/* regexp definitions */

%%
/* Translation Rules */
%%

/* C Code */
/***
Already Defined Variables
yyval = Lexeme Begin
yytexr = Pointer To Lexeme begin
yyleng = Length of Lexeme
***/

그러면 예제 코드를 작성해 보도록 하자. 아래 프로그램은 In-fix -> Post-Fix 변환하는 코드이다. 예를들어 1+2+3을 입력하면 12+3+으로 변환해 준다.

/* FileName: lex.l */

%{
/* C Header Include, Define */
#include <stdio.h>
#include <string.h>

#define NUMBER 256 // 숫자
#define IDENTIFIER 257 // 변수
#define OPERATOR 258 // 덧셈, 뺄셈 연산자
#define BUFSIZE 100 // 버퍼 사이즈, 배열 크기
#define EOL 100 // 엔드 오브 라인

%}

/* regexp definitions */
num ([0-9]*[.])?([0-9]+)([E][-]?[0-9]+)?
EOL \n

%%
/* Translation Rules */
{num} { return NUMBER; }
[a-zA-Z0-9]+ { return IDENTIFIER; }
[+,-] { return OPERATOR; }
{EOL} {return EOL;}
%%

/* C Code */
int
main () {
	char operatorStack[BUFSIZE]; // 연산자 스택
	int operatStackTopIdx = -1; // 스택의 Top Index
	int token;

  	while ((token = yylex()) != EOL) { // 토큰이 끝이라면 종료
		switch (token) {
        	case NUMBER:
				printf("%s ", yytext); // 숫자는 바로바로 출력
				break;
			case OPERATOR:
				if(operatStackTopIdx == -1) { // 연산자는 스택이 비어있으면 넣고,
					operatorStack[++operatStackTopIdx] = yytext[0];
				} else { // 스택이 비어있지 않으면 안에 있는것을 빼서 출력하고, 방금 받은 연산자는 새로 넣는다.
					printf("%c ", operatorStack[operatStackTopIdx--]);
				}
				break;
			case IDENTIFIER:
				// printf("IDENTIFIER: %s, LENGTH:%d\n", yytext, yyleng);
				break;
        	default: 
				printf("Error: %s not recognized\n", yytext);
     	}
  	}

	while(operatStackTopIdx > -1) { // 스택이 비어있을 때 까지, 남은 연산자 출력
		printf("%c ", operatorStack[operatStackTopIdx--]);
	}
	printf("\n");
}

위의 파일을 lex.l 이라고 저장한 후에 다음과 같이 실행할 수 있다.

$ flex hello.l # lex를 이용해 .yy.c 파일 생성
$ gcc lex.yy.c -ll # gcc를 이용해 -ll 옵션을 주고 컴파일
$ ./a.out # 실행파일 실행
# 실행 파일의 내용은 in-fix인 1+2+3을 입력하면
# post-fix인 12+3+으로 변환해 준다.

flex_command

정리

Lex, Flex에 대해서 알아보고 실제 Flex를 이용할 때 어떤 명령어를 사용해보고 정리해 보았다. 사실 Lexical AnalyzerParser 또는 Compiler를 직접 만들어서 내가 쓸 수 있을지는 모르겠지만, 정리해 둔다면 언젠가 후배가 물어본다거나 할 때 쓸 수 있지 않을까.


Philographer
Written by@Philographer
Fail Fast, Learn Faster

GitHublinkedInFacebook