문법 작성 테스트



안녕하세요?


먼저 연재가 너무 늦어서 죄송하다는 말씀부터 드립니다 ㅠ.ㅜ

핑계를 좀 대자면, 그동안 너무 많은 일들이 있고 바빠서 신경쓰지 못했네요. 이직도 하고 결혼도 하고 참 많은 일들이 있었습니다. 네, 맞습니다. 다 핑계입니다. :)


사실 다른 일회성 포스팅은 쓰기 편합니다만, 이렇게 연재 형식으로 된 포스팅은 너무나 힘이 드네요. 진심으로 책 출간하시는 분들에게 존경을 표하는 바입니다. 처음 컴파일러를 제대로 공부하지 못했던 대학시절의 제 모습을 반성하며, 다시 공부하겠노라 마음먹고 시작한 연재인데, 그게 참 어렵네요. :)


모쪼록 이렇게 2년만에 다시 연재를 시작(?)하는데, 그동안 잊어버린 것도 많고 하시거라 생각합니다. 고로 이번시간에는 전에 공부했던 부분들을 실습(?) 해 보는 쪽으로 하려 합니다. 가볍게 시작하자구요 :)




JavaCC 다운로드


전에 배웠던 문법들을 총 동원하여 작성해 보고, JavaCC 로 테스트를 해 보려고 합니다. 그러기 위해서는 아래 사이트에서 JavaCC 를 다운로드 받아 보시기 바랍니다.

https://javacc.java.net/


저는 현재기준 최신버전인 JavaCC 6.0  를 다운로드 하였습니다. 참 여담이긴 한데, 보다보니 요즘 Parser Combinator 라는 새로운 패러다임이 등장한 것 같더군요. 무엇인고 하면 JavaCC 처럼 문법파일을 작성하고, 이것을 JavaCC 를 통해서 빌드하게는 아니라, 문법파일이 실제 존재하는 랭귀지 파일인 것입니다. 말이 어렵네요. 예를 들어서 Java Parser Combinator 라고 하면, 문법을 Java 로 구현하는 것이죠. 임의의 문법파일이 아니라, 진짜 java 코드 말입니다. C# Parser Combinator 라고 한다면, 문법 파일을 C# 으로 작성하는 것이고요. Parser Combinator 라고 검색하면, 많이 나옵니다. 한번 보시는것도 좋을 것 같습니다. ^-^


다운로드 받은 파일의 압축을 풀어보면, bin\lib 안에 javacc.jar 파일이 있습니다. 물론 각종 document 파일들도 있으니 한번 보시길 바랍니다.


자, 질문드립니다. jar 파일은 어떻게 실행시킬까요? 왜 갑자기 이런 질문을 드리냐고요? 하하.. 자바를 마지막으로 만진게 2년정도 됬떠니 갑자기 기억이 나지 않더라.. 이겁니다. 그래서 질문한번 드려 보았습니다 =_=;


C:\>java javacc.jar


이렇게 실행하시면, 안됩니다.(제가 그랬습니다 -_-)


C:\>java -jar javacc.jar


jar 파일은 이렇게 실행해야 합니다. 결과는...??


E:\Download\javacc-6.0\javacc-6.0\bin\lib>java -jar javacc.jar

javacc.jar에 기본 Manifest 속성이 없습니다.


요러게 나왔을거라고 생각합니다 :) 이유는 javacc 에는 기능(클래스)이 javacc, jjdoc, jjtree 이렇게 3개가 있습니다. 그래서 Manifest 파일이 따로 없습니다. 그러므로 우리는 클래스를 직접 지정해서 실행해 주어야 합니다. 각각 아래처럼 실행해 주면 됩니다.


C:\>java -classpath javacc.jar javacc

C:\>java -classpath javacc.jar jjdoc

C:\>java -classpath javacc.jar jjtree




문법파일(*.jj) 작성


이제 문법 파일을 생성해 봅시다. 참고로 JavaCC 의 문법파일의 확장자는 *.jj 입니다. (앞서 말씀드렸던 Parser Combinator 라면, 확장자가 그냥 자바파일인 *.java 였겠죠?)


두 정수의 곱셈을 계산하는 문법을 작성해 봅시다. 좀 특이하게 * 표시 대신 '곱하기' 라는 한글 키워드도 동작 가능하도록 해 보려 합니다. "3 * 4" 와 "3 곱하기 4" 모두 동작하도록 말이죠.


그럼 한줄씩 한줄씩 작성해 보도록 하겠습니다. JavaCC 의 문법 파일은 이전 포스팅에서 설명 드렸지만, 다시한번 짚고 넘어가겠습니다.


javacc_input ::= javacc_options

"PARSER_BEGIN" "(" <IDENTIFIER> ")"

java_compilation_unit

"PARSER_END" "(" <IDENTIFIER> ")"

( production )*

<EOF>


요렇게 생겼습니다. 자 그럼 문법파일을 작성해 보도록 합니다.


options {

    STATIC = false;

    UNICODE_INPUT = true;

}


options 부분에는 파서를 만들때 필요한 각종 옵션들이 들어갑니다. 지원하는 옵션들은 여러가지가 있습니다만, 여기서 일일이 설명하기에는 너무 많아서 자세한 설명은 하지 않도록 하겠습니다. 대략적인 이름만 보아도 감이 잡히실거라고 생각합니다. :)


지원하는 Option 항목 [더보기]


모쪼록 STATIC 옵션은 현재 작성중인 문법파일(*.jj)에 정의된 모든 멤버와 메서드를 static 으로 간주할 것인지에 대한 것입니다. 아시겠지만 멤버가 static 으로 처리되어 버리면, 멀티쓰레드 환경에서는 뒤죽박죽 되어 버릴 것입니다. 그러므로 false 로 처리해 주었습니다.

UNICODE_INPUT 은 '곱하기' 라는 키워드를 위해서 TRUE 로 설정해 주었습니다. 



PARSER_BEGIN(Multi)


import java.io.*;


class Multi {

public static void main(String[] args) {

for(String arg : args) {

try {

System.out.println(eval(arg));

} catch (ParseException e) {

System.err.println(e.getMessage());

}

}

}


public static long eval(String state) throws ParseException {

Reader reader = new StringReader(state);

try {

return new Multi(reader).exp();

} finally {

try { reader.close(); } catch(Exception e) {}

}

}

}


PARSER_END(Multi)



main 함수가 Entry Point 입니다. 실행되면 커맨드라인 파라미터를 가져와서 eval 함수를 실행시킵니다. 여기서 각 파라미터는 "3 * 4", "10 * 5" 같은 수식이 될 것입니다. eval 함수부분을 보시면, 이상한 것이 있습니다. 


return new Multi(reader).exp();


이부분 입니다. 왜 Reader 객체를 만들어서 Multi 생성자 파라미터로 넣었을까요? 분명 생성자는 정의한 적도 없는데 말입니다. 이유는 JavaCC 에서 파서를 만들때, 기본 생성자를 자동으로 만들어서 넣어 줍니다. 총 4개로 아래와 같습니다.


1. Parser(InputStream s)

2. Parser(InputStream s, String encoding)

3. Parser(Reader r)

4. Parser(XXXXTokenManager t)


우리는 여기서 3번 생성자를 호출한 것입니다. 그리고 exp() 는 우리가 문법파일에 정의한 규칙 부분입니다. (곧 나오니 걱정마세요 ㅎ)



SKIP: { <[" ", "\t", "\r", "\n"]> }

TOKEN: {

        <INTEGER: (["-", "+"])?(["0"-"9"])+>

}


앞선 포스팅에서 다 설명한 부분이니 넘어가도록 하겠습니다.


long exp():

{

        Token x, y;

}

{

      x=<INTEGER> ("곱하기" | "*") y=<INTEGER> <EOF>

{

return Long.parseLong(x.image) * Long.parseLong(y.image);

}

}


이제 이 문법을 JavaCC 로 테스트 해 보도록 하겠습니다. 위 문법들을 Multi.jj 라는 이름으로 파일을 저장하도록 하겠습니다.

Multi.jj [더보기]


아래처럼 별 문제없이 javacc 가 실행될 것입니다.


C\javacc-6.0\bin\lib>java -classpath javacc.jar javacc Multi.jj

Java Compiler Compiler Version 6.0_1 (Parser Generator)

(type "javacc" with no arguments for help)

Reading from file Multi.jj . . .

Note: UNICODE_INPUT option is specified. Please make sure you create the parser/lexer using a Reader with the correct character encoding.

File "TokenMgrError.java" does not exist.  Will create one.

File "ParseException.java" does not exist.  Will create one.

File "Token.java" does not exist.  Will create one.

File "SimpleCharStream.java" does not exist.  Will create one.

Parser generated successfully.


폴더에 보시면, 여러개의 java 소스코드 파일들이 생성 된 것을 확인하실 수 있습니다. JavaCC 는 파서 제너레이터니까요 :)

이제 이 파서를 빌드해 봅니다.


C\javacc-6.0\bin\lib>javac Multi.java


빌드가 완료되면, 실행해 보겠습니다. 두둥~~



C\javacc-6.0\bin\lib>java Multi "3 * 3" " 10 * -5" "     20 *    -3" "10 곱하기 8" "5 곱하기 -10" "-7 곱하기 -5"

9

-50

-60

80

-50

35


SKIP 으로 정의했던 것들은 잘 SKIP 되고, 값도 잘 나오고, 모두 잘 동작하네요 :) 이번에 작성한 문법은 너무나도 심플해서 간결하게 나올 수 밖에 없습니다. 조금씩 변형도 해 보면서 테스트 해 보시면 좋을 것 같습니다. Java 의 경우, 문법 파일이 1500 줄 정도 되더군요. ^-^



그럼 오늘 포스팅은 여기서 마치도록 하겠습니다. 다음 포스팅은 ... 많이 노력하겠습니다. ㅠ.ㅜ





신고

'개발관련 > 컴파일러' 카테고리의 다른 글

컴파일러 만들기 6부  (6) 2014.05.31
컴파일러 만들기 5부  (9) 2012.07.13
컴파일러 만들기 4부  (0) 2012.07.12
컴파일러 만들기 3부  (1) 2012.07.12
컴파일러 만들기 2부  (3) 2012.07.11
컴파일러 만들기 1부  (4) 2012.07.10