JavaCC [tm]: README for SimpleExamples

Posted 2009.03.02 19:38 by shinji

 

JavaCC 4.2 안의 SimpleExamples 예제의 README 파일의 번역.

 

JavaCC [tm]: SimpleExamples 에 대한 README

이 디렉토리에는 JavaCC를 사용하여 여러분이 시작할 수 있는 다섯개의 예제들이 포함된다. 각 예제는 하나의 문법파일이 아래 나열된 바대로 포함되어 있다.

Simple1.jj
Simple2.jj
Simple3.jj
Simple4.jj
NL_Xlator.jj
IdList.jj

이러한 예제들을 한번 살펴보고 이해하게 되면, 여러분은 예제 디렉토리의 다른 하위 디렉토리에 위치한 보다 복답한 예제들을 봐야 한다. 하지만 이 예제들만으로도 적당히 복잡한 문법들을 시작할 수 있을 것이다.

명령들의 요약

만약 여러분들이 어떤 파서나 문법적 분석기에 대한 전문가이고 이 예제들을 그낭 한번보는 것만으로도 이해가 된다면, 아래 명령들은 JavaCC로 어떻게 예제를 시작하는지를 나타낸다. 아래 명령들은 Simple1.jj 를 대상으로 하나 동일한 명령집합을 사용해서 어떤 파서라도 빌드할 수 있다.

  1. 파서와 문법 분석기(또는 토큰 매니저)를 구현하는 자바소스 파일의 한 다발을 생성하기 위해 문법파일을 인수로 javacc 를 실행해라

    javacc Simple1.jj
    
  2. 이제 생성된 자바파일을 컴파일 하라.

    javac *.java
    
  3. 파서가 이제 사용할 준비가 되었다. 파서를 실행해라.

    java Simple1
    

이 디렉토리의 Simple1 파서와 그 외 것들은 표준입력으로 부터 입력을 받도록 작성되었다. Simple1은 하나도 없거나 줄바꿈(line terminators)문자 또는 파일끝(end of file)문자가 뒤따라오는 좌우쌍이 맞는 중괄호들을 인식한다.

이 문법에서 유효한 문자들의 예는 아래와 같다.

"{}", "{{{{{}}}}}", 등등

유효하지 않은 문자들의 예는 아래와 같다.

"{{{{", "{}{}", "{}}", "{{}{}}", "{ }", "{x}", 등등

Simple1 에 다양한 다른 입력들을 시도해보자. <컨트롤-D>는 대개 파일끝(EOF)을 가리키는데 사용되는 것을 상기해라.(이것은 유닉스일 경우다). 여기에 몇몇 샘플 실행의 예가 있다.

% java Simple1
{{}}<return>
<control-d>
%

% java Simple1
{x<return>
Lexical error at line 1, column 2. Encountered: "x"
TokenMgrError: Lexical error at line 1, column 2. Encountered: "x" (120), after : ""
at Simple1TokenManager.getNextToken(Simple1TokenManager.java:146)
at Simple1.getToken(Simple1.java:140)
at Simple1.MatchedBraces(Simple1.java:51)
at Simple1.Input(Simple1.java:10)
at Simple1.main(Simple1.java:6)
%

% java Simple1
{}}<return>
ParseException: Encountered "}" at line 1, column 3.
Was expecting one of:
<EOF>
"\n" ...
"\r" ...

at Simple1.generateParseException(Simple1.java:184)
at Simple1.jj_consume_token(Simple1.java:126)
at Simple1.Input(Simple1.java:32)
at Simple1.main(Simple1.java:6)
%

Simple1.jj 의 자세한 설명

이것은 좌중괄호의 집합뒤에 같은 수의 우중괄호가 따라오고 그 다음에 없거나 그 보다 많은 줄바꿈(line terminators)이 오고 최종적으로 파일끝(EOF)가 오는 것을 인식하는 간단한 JavaCC 문법이다. 이 문법에서 유효한 문자열의 예는 다음과 같다.

"{}", "{{{{{}}}}}", etc.

유효하지 않은 문자열의 예는 다음과 같다.

"{{{{", "{}{}", "{}}", "{{}{}}", etc.

이 문법은 JavaCC에서 제공하는 모든 옵션들을 세팅하는 것에서 시작한다. 이 경우에서는 옵션이 그것의 기본값으로 세팅된다. 그러므로 이러한 옵션 세팅은 사실상 불필요하다. 모든 이러한 옵션세팅을 완전히 생략하거나 또는 그 보다 맣은 개별적인 옵션을 생략할 수 있을 것이다. 개별적인 옵션의 상세한 설명은 웹페이지에 있는 JavaCC 문서에서 설명되어진다.

"PARSER_BEGIN(name)" 와 "PARSER_END(name)" 둘러싸여 뒤따라오는 것은 자바 컴파일 구성단위이다. 이 컴파일 구성단위는 임의의 복잡한 것이 될 수 있다. 이 컴파일 구성단위의 유일한 제약사항은 -PARSER_BEGIN 과 PARSER_END에서 같은 인수로 사용되는- "name" 이라 불리는 클래스를 정의해야 하는 것이다. 그 이름은 파서 생성기에 의해서 생성되어지는 자바 소스 파일들의 이름 앞에 붙여지는 접두어으로 쓰인다. 그 생성될 파서의 코드는 "name" 을 불리는 클래스의 중괄호가 닫히기 전 바로 앞에 삽입될 것이다.

위의 예제에서는 생성되어질 파서인 그 클래스는 메인 메쏘드를 포함한다. 그 메인 메쏘드는 java.io.InputStream(이 경우 "System.in") 클래스를 하나의 인수로 가지는 생성자를 호출함으로써 파서 객체를 생성한다(Simple1 클래스의 객체).

메인 메쏘드는 그 후 파싱할 문법 안에 있는 논터미널 - 이 경우 "Input" - 을 호출한다. 모든 논터미널은 JavaCC 가 생성한 파서에서 같은 지위를 같고 그러므로 그것은 어떤 문법에 관해서라도 논터미널으로 파싱할 것이다.(The main program then makes a call to the non-terminal in the grammar that it would like to parse - "Input" in this case. All non-terminals have equal status in a JavaCC generated parser, and hence one may parse with respect to any grammar non-terminal.)

그 뒤에 나오는 것은 프로덕션의 목록이다. 이 예제에서는 "Input" 과 "MatchedBraces" 로 각각 정의되는 논터미널을 정의하는 두개의 프로덕션이 있다. JavaCC 문법에서는 논터미널은 (JavaCC에 의해) 자바 메쏘드로 쓰여지고 구현된다. 논터미널이 프로덕션의 왼편에 사용되면, 선언되어지는 것으로 생각되고 그 선언 문법은 자바의 것을 따른다. 오른편에서 그것의 사용은 자바에서 메쏘드를 호출하는 것과 비슷하다.

각 프로덕션 선언은 그것의 왼편에 논터미널을 정의하고 콜론이 뒤따라온다. 그 다음 중괄호안에 한다발의 선언문과 명령문들을 뒤따라오게한다. (위 예제에서 두 경우는 아무 선언이 없고 따라서 "{}" 이렇게 보인다.) 그 선언과 명령문은 생성되어질 메쏘드의 선언문과 명령문으로 생성되게 된다. 그 뒤에는 역시 중괄호로 둘러싸여지는 확장셋들이 나타난다.

JavaCC 입력 문법에서 문법적 토큰들 (정규 표현식들)은 간단한 문자들이거나(위 예제에서는 "{","}","\n", "\r" 임) 보다 복잡한 정규 표현식이다. 우리의 위 예제에서는 파일끝과 일치하는 하나의 그런 정규 표현식 "<EOF>" 가 있다. 모든 복잡한 정규 표현식들은 꺾여진괄호로 둘러싸여진다.

위의 첫번째 프로덕션은 논터미널 "Input"을 논터미널 "MatchedBraces" 와 그 뒤에 하나도 없거나 또는 그 이상 개의 줄바꿈("\n 또는 "\r")문자와 그 뒤의 파일끝문자로 확장한다.

위의 두번째 프로덕션에서는 논터미널 "MatchedBraces" 를 토큰 "{" 뒤에 부가적인(없어도 되는) 자신에게 다시 포함되게 되는 "MatchedBraces" 와 토큰 "}" 으로 확장한다. JavaCC 입력 파일에서 사각괄호 [...] 는 ... 것이 부가적인(없어도 되는) 것임을 의미한다.

[...] 는 (...)? 로 쓰여질 수 도 있다. 이 두 형태는 동일하다. 확장들에서 나타나게 될 다른 구조들은 아래와 같다.

e1 | e2 | e3 | ... : e1, e2, e3, 등등 중의 하나.
( e )+ : 하나 또는 그 이상의 갯수의 e
( e )* : 하나도 없거나 또는 그 이상의 갯수의 e

이러한 것들은 다른것들과 중첩될 수 있다는 것에 주의해라 따라서 우리는 이러한 것들을 보게될 수도 있다.

(( e1 | e2 )* [ e3 ] ) | e4

이 파서를 생성하려면 간단히 JavaCC 이 파일과 같이 실행하고 생성된 자바파일들을 컴파일 한다.

javacc Simple1.jj
javac *.java

이제 생성된 파서를 실행시킬 수 있다. 현재디렉토리가 CLASSPATH에 포함되는지 확인하고 아래 명령을 쳐라.

java Simple1

짝이 맞는 중괄호들을 연속으로 입력하고 리턴문자나 파일끝(유닉스 기계에서 컨토를-D)문자를 입력해 보자. 만약 이것이 여러분의 기계에서 문제가 생긴다면 여러분은 파일을 만들고 아래와 같이 파이프로 생성된 파서에 넘길 수도 있다.(파이프는 물론 모든 기게들에서 동작하지는 않는다. - 만약 이것도 문제라면 그냥 문법파일에서 "System.in" 을 'new FileInputStream("testfile")' 으로 바꾸고 입력할 것들을 그 파일 안에 넣어두자)

java Simple1 < myfile

짝이 맞지 않는 중괄호나 공백문자 또는 중괄호들 사이에 리턴문자나 그외 다른 문자들 같은 유효하지 않는 입력도 시도해보자. 그 결과로 파서에 의해 에러메세지가 출력될 것이다.

Simple2.jj 의 상세 설명

Simple2.jj 는 중괄호 사이에 공백문자를 허용하게 하는 작은 수정을 Simple1.jj 로 부터 했다. 이제 아래와 같은 입력은

"{{  }\n}\n\n"

유효하다.

Simple2.jj을 열어서 살펴보자. 첫번째로 우리는 옵션을 생략했다. 이것은 Simple1.jj 에서 모두 기본값으로 설정했으므로 아무런 변화도 주지 않는다.

Simple1.jj 와 다른 차이는 이 파일에는 "SKIP" 으로 시작하는 영역에 어휘적인 정의(lexical specification)가 추가된다. 이 영역에는 4개의 정규 표현식이 있다. - 공백, 탭, 새줄, 리턴. 이것은 이러한 정규 표현식과 일치하는 모든 것은 무시되어진다.(파싱에서 고려되지 않는다.) 그러므로 몇개라도 이러한 4문자중 하나가 나타나던지간에 그냥 지나친다. SKIP 에 추가로 JavaCC는 3개에 다른 어휘적인 정의 영역을 가진다. 그것은 아래와 같다.

. TOKEN:         이것은 어휘적인 토큰을 정의하는 사용한다.(다음번의 예제를 살펴봐라)
. SPECIAL_TOKEN: 이것은 파싱하는 동안에 무시될 어휘적인 토큰들을 정의하는데 사용된다.
이 경우 SPECIAL_TOKEN은 SKIP과 같겠지만, 이러한 토큰들은
적절히 처리하기 위해 파서의 액션들 안에서 복구되어질 수 있다.
. MORE: 이것은 토큰의 부분을 정의한다. 완전한 토큰은 MORE 와 뒤따르는
TOKEN 또는 SPECIAL_TOKEN으로 구성되어 진다.

이 어휘적인 정의 영역에 대한 사용법의 예는 자바 문법같은 보다 복잡한 문법을 보기 바란다.

여러분은 Simple2를 빌드하고 생성된 파서를 키보드 같은 표준입력으로 실행할 수 있을 것이다.

여러분은 또한 다양한 디버그 옵션을 켜서 파서를 생성하여 어떻게 출력되는지 볼수 있다. 그렇게 하려면 아래와 같이 해라.

javacc -debug_parser Simple2.jj
javac Simple2*.java
java Simple2

그리고 아래와 같이 쳐라.

javacc -debug_token_manager Simple2.jj
javac Simple2*.java
java Simple2

토큰 매너저는 아주 많은 디버깅 진단 정보를 생성하는 것에 주의하고 그것은 주로 하나의 토큰을 차례로 디버그하기 위해 사용된다.

Simple3.jj 의 상세 설명

Simple3.jj 는 세번째 그리고 이 중괄호 짝검사기의 마지막 버전이다. 이 예제는 어휘적인 토큰들을 정의하는 TOKEN 영역의 사용을 보여준다. 이 경우 "{" 와 "}" 는 각각 LBRACE 와 RBRACE로 이름지어진 토큰으로 정의된다. 이러한 이름표는 (예제에 있는 바와 같이)꺾여진 괄호안에서 그 토큰을 참조하기 위해 사용되어질 수 있다. 주로 식별자나 문자열같은 복잡한 토튼들에 그런 토큰 정의가 사용된다. (이전 예제에서) 간단한 문자의 토큰들은 그냥 둔다.

이 예제는 문법 프로덕션에서 또한 액션들의 사용을 보여준다. 이 예제에 포함된 액션들은 매치되는 중괄호의 수를 센다. "count" 과 "nested_count" 변수들의 선언하기 위해 선언영역의 사용을 주시해라. 논터미널 "MatchedBraces"이 어떻게 그자신의 값을 함수의 반환값과 같이 반환하는지 또한 주시해라.

NL_Xlator.jj 의 상세 설명

이 예제는 JavaCC 문법 파일에서 정규 표현식을 보다 깊이 사용한다. 그것은 또한 문법으로 설명된 표현식들을 영어로 해석하게하는 약간 복잡한 액션들의 집합으로 표현한다.

위 예제의 새 개념은 보다 복잡한 정규 표현식을 사용하는 것이다. 그 정규 표현식은 아래와 같다.

< ID: ["a"-"z","A"-"Z","_"] ( ["a"-"z","A"-"Z","_","0"-"9"] )* >

ID라는 이름의 새로운 정규 표현식을 생성한다. 이것은 단순히 <ID>로 문법의 어느곳에서라도 참조될 수 있다. 그 뒤의 사각괄호로 된 것은 허용되는 문자들의 집합이다. - 이 경우 그것은 어떠한 소문자나 대문자와 밑줄문자가 된다. 그 뒤에는 하나도 없거나 그 보다 많은 수의 소문자, 대문자, 숫자, 밑줄문자가 올 수 있다.

정규 표현식에서 나타날 수 있는 다른 구성은 아래와 같다.

( ... )+ : 하나 또는 보다 많은 수의 ...
( ... )? : 부수적인(없어도 되는) ... (이 경우에는, (...)? 와 [...] 는 동일하지 않음에 주의해라.)
( r1 | r2 | ... ) : r1, r2, ... 중에 아무거나

[...]형태의 구성은 ... 에 정의된 문자들과 매치되는 패턴이다. 이러한 문자들은 개별적인 문자들이거나 범위를 가지는 문자이다. 문자구성 앞에 "~"것이 붙으면 그것은 "..." 로 정의된 것에 포함되지 않는 모든 문자와 일치되는 패턴이다. 그리하여

["a"-"z"] 모든 소문자와 일치되고
~[] 는 모든 문자와 일치되고
~["\n","\r"] 는 새줄문자를 제외한 모든 문자와 일치된다.

정규 표현식이 확장영역에 사용되면 그것은 "Token" 형태의 값을 가진다. 이것은 생성되는 파서 디렉토리에 "Token.java" 파일을 생성한다. 위 예제에서는 우리는 "Token" 형태의 변수를 정의했고 그것을 정규 표현식의 값으로 할당했다.

IdList.jj 의 상세 설명

이 예제는 SKIP 정의의 중요한 속성을 설명한다. 주요한 이해의 포인트는 SKIP 정의에서 정규 표현식은 *토큰들 사이에서* 무시될 뿐 *토큰들 안에서*는 무시되지 않는다. 이 문법은 그 사이에 공백들이 있는 식별자들의 연속입력은 받아들인다.

이 문법에 대해 유효한 입력은 아래와 같다.

"abc xyz123 A B C \t\n aaa"

왜냐하면 어떠한 수의 SKIP 정규 표현식이든지 연속적인 <Id>의 사이에서는 그것이 허용되기 때문이다. 하지만 아래는 유효한 입력이 아니다.

"xyz 123"

"xyz"뒤의 공백 문자는 SKIP 영역이고 그리하여 하나의 토큰이 끝나고 또 다른 토큰이 시작되도록 하기 때문이다. 이것은 "123"이 분리된 새로운 토큰으로 만들기 때문에 문법에 맞지 않는다.

만약 <Id> 에서 공백들이 올바르게 하도록 하려면, 해야할 한가지는 Id의 정의를 아래와 같이 바꾸는 것이다.

TOKEN :
{
< Id: ["a"-"z","A"-"Z"] ( (" ")* ["a"-"z","A"-"Z","0"-"9"] )* >
}

TOKEN 정의에 있는 공백문자는 SKIP 정에에서 공백이 쓰이지 않는것을 의미하지 않음에 주의해라. 이 모든것이 의미하는 것은 어떠한 공백이든지 <Id>와 일치하기 위해 참여하는 식별자의 안에서는 나타날 수 있고, 반면에 모든 다른 공백들은 무시될 것이라는 것을 의미한다. 보다 상세한 일치 알고리즘은 JavaCC 문서에 설명되어 있다.

결론적으로 제공되면 안되는 공백문자같은 문자를 그 자신이 가지는 토큰을 정의 해도 된다. 위 예제에서 만약 <Id> 가 어휘적인 토큰이 아닌 문법 프로덕션으로 아래와 같이 정의되었다면, 그때는 "xyz 123" 는 문법으로 (잘못된) <Id>로 인식되어졌을 것이다.

void Id() :
{}
{
<["a"-"z","A"-"Z"]> ( <["a"-"z","A"-"Z","0"-"9"]> )*
}

논터미널 Id 에 대한 위의 정의는 하나의 문자토큰들의 연속적인 구성일 뿐임에 주의하고( <...>들의 위치를 주시해라), 그러므로 공백문자는 이러한 문자들 사이에 놓여질 수 있다.

This article was written in springnote.

« PREV : 1 : ··· : 5 : 6 : 7 : 8 : 9 : 10 : 11 : 12 : 13 : ··· : 158 : NEXT »