ANTLR을 시작하고 싶지만 antlr.org 사이트 에서 예제를 검토하는 데 몇 시간을 보낸 후에도 문법에서 Java 프로세스에 대한 명확한 이해를 얻을 수 없습니다.
ANTLR로 구현 된 4 개의 연산 계산기와 같이 파서 정의를 거쳐 Java 소스 코드까지가는 간단한 예제가 있습니까?
ANTLR을 시작하고 싶지만 antlr.org 사이트 에서 예제를 검토하는 데 몇 시간을 보낸 후에도 문법에서 Java 프로세스에 대한 명확한 이해를 얻을 수 없습니다.
ANTLR로 구현 된 4 개의 연산 계산기와 같이 파서 정의를 거쳐 Java 소스 코드까지가는 간단한 예제가 있습니까?
답변:
참고 :이 답변은 ANTLR3 에 대한 것입니다 ! ANTLR4 예제를 찾고 있다면 이 Q & A 는 간단한 표현식 파서를 작성하고 ANTLR4를 사용하여 평가하는 방법을 보여줍니다 .
먼저 문법을 만듭니다. 다음은 4 가지 기본 수학 연산자 인 +,-, * 및 /를 사용하여 작성된 표현식을 평가하는 데 사용할 수있는 작은 문법입니다. 괄호를 사용하여 표현식을 그룹화 할 수도 있습니다.
이 문법은 매우 기본적인 것입니다. 단항 연산자 (--1 + 9의 마이너스) 나 .99 (앞의 숫자없이)와 같은 소수는 처리하지 않고 두 가지 단점을 지정합니다. 이것은 당신이 스스로 할 수있는 예일뿐입니다.
문법 파일 Exp.g 의 내용은 다음과 같습니다 .
grammar Exp;
/* This will be the entry point of our parser. */
eval
: additionExp
;
/* Addition and subtraction have the lowest precedence. */
additionExp
: multiplyExp
( '+' multiplyExp
| '-' multiplyExp
)*
;
/* Multiplication and division have a higher precedence. */
multiplyExp
: atomExp
( '*' atomExp
| '/' atomExp
)*
;
/* An expression atom is the smallest part of an expression: a number. Or
when we encounter parenthesis, we're making a recursive call back to the
rule 'additionExp'. As you can see, an 'atomExp' has the highest precedence. */
atomExp
: Number
| '(' additionExp ')'
;
/* A number: can be an integer value, or a decimal value */
Number
: ('0'..'9')+ ('.' ('0'..'9')+)?
;
/* We're going to ignore all white space characters */
WS
: (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;}
;
(파서 규칙은 소문자로 시작하고 어휘 규칙은 대문자로 시작)
문법을 만든 후에는 구문 분석기와 어휘 분석기를 생성해야합니다. ANTLR jar을 다운로드 하여 문법 파일과 동일한 디렉토리에 저장하십시오.
쉘 / 명령 프롬프트에서 다음 명령을 실행하십시오.
java -cp antlr-3.2.jar org.antlr.Tool Exp.g
그것은 오류 메시지 및 파일 생성 안 ExpLexer.java , ExpParser.java을 하고 Exp.tokens는 이제 생성해야한다.
모두 제대로 작동하는지 확인하려면 다음 테스트 클래스를 작성하십시오.
import org.antlr.runtime.*;
public class ANTLRDemo {
public static void main(String[] args) throws Exception {
ANTLRStringStream in = new ANTLRStringStream("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
parser.eval();
}
}
그것을 컴파일하십시오 :
// *nix/MacOS
javac -cp .:antlr-3.2.jar ANTLRDemo.java
// Windows
javac -cp .;antlr-3.2.jar ANTLRDemo.java
그런 다음 실행하십시오.
// *nix/MacOS
java -cp .:antlr-3.2.jar ANTLRDemo
// Windows
java -cp .;antlr-3.2.jar ANTLRDemo
모두 제대로 작동하면 콘솔에 아무것도 인쇄되지 않습니다. 이는 파서가 오류를 찾지 못했음을 의미합니다. 변경하는 경우 "12*(5-6)"
에 "12*(5-6"
후 재 컴파일하고 실행, 다음이 인쇄되어야한다 :
line 0:-1 mismatched input '<EOF>' expecting ')'
이제 문법에 약간의 Java 코드를 추가하여 파서가 실제로 유용한 기능을 수행하려고합니다. 추가 코드를 배치하여 수행 할 수 있습니다 {
및 }
그 내부의 일반 자바 코드와 문법 내부.
그러나 먼저 문법 파일의 모든 파서 규칙은 기본 double 값을 반환해야합니다. returns [double value]
각 규칙 뒤에 추가하면됩니다 .
grammar Exp;
eval returns [double value]
: additionExp
;
additionExp returns [double value]
: multiplyExp
( '+' multiplyExp
| '-' multiplyExp
)*
;
// ...
설명이 거의 필요하지 않습니다. 모든 규칙은 두 배의 값을 반환해야합니다. 이제 double value
코드 블록 내부 에서 반환 값 (일반 Java 코드 블록 안에 있지 않음) 과 "상호 작용"하려면 {...}
앞에 달러 기호를 추가해야합니다 value
.
grammar Exp;
/* This will be the entry point of our parser. */
eval returns [double value]
: additionExp { /* plain code block! */ System.out.println("value equals: "+$value); }
;
// ...
문법은 다음과 같지만 이제 Java 코드가 추가되었습니다.
grammar Exp;
eval returns [double value]
: exp=additionExp {$value = $exp.value;}
;
additionExp returns [double value]
: m1=multiplyExp {$value = $m1.value;}
( '+' m2=multiplyExp {$value += $m2.value;}
| '-' m2=multiplyExp {$value -= $m2.value;}
)*
;
multiplyExp returns [double value]
: a1=atomExp {$value = $a1.value;}
( '*' a2=atomExp {$value *= $a2.value;}
| '/' a2=atomExp {$value /= $a2.value;}
)*
;
atomExp returns [double value]
: n=Number {$value = Double.parseDouble($n.text);}
| '(' exp=additionExp ')' {$value = $exp.value;}
;
Number
: ('0'..'9')+ ('.' ('0'..'9')+)?
;
WS
: (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;}
;
그리고 우리의 eval
규칙은 이제 double을 반환하므로 ANTLRDemo.java를 다음과 같이 변경하십시오.
import org.antlr.runtime.*;
public class ANTLRDemo {
public static void main(String[] args) throws Exception {
ANTLRStringStream in = new ANTLRStringStream("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
System.out.println(parser.eval()); // print the value
}
}
다시 문법에서 새로운 어휘 분석기와 파서를 생성하고 (1) 모든 클래스를 컴파일하고 (2) ANTLRDemo (3)를 실행합니다 :
// *nix/MacOS
java -cp antlr-3.2.jar org.antlr.Tool Exp.g // 1
javac -cp .:antlr-3.2.jar ANTLRDemo.java // 2
java -cp .:antlr-3.2.jar ANTLRDemo // 3
// Windows
java -cp antlr-3.2.jar org.antlr.Tool Exp.g // 1
javac -cp .;antlr-3.2.jar ANTLRDemo.java // 2
java -cp .;antlr-3.2.jar ANTLRDemo // 3
이제 12*(5-6)
콘솔에 표현 결과가 인쇄됩니다!
다시 : 이것은 매우 간단한 설명입니다. ANTLR 위키 를 탐색하고 튜토리얼을 읽거나 방금 게시 한 내용으로 약간의 게임을 즐기십시오.
행운을 빕니다!
편집하다:
이 포스트 는 Map<String, Double>
제공된 표현식에서 변수를 보유 하는 a 를 제공 할 수 있도록 위의 예제를 확장하는 방법을 보여줍니다 .
이 코드를 현재 버전의 Antlr (2014 년 6 월)에서 작동하게하려면 몇 가지 사항을 변경해야했습니다. ANTLRStringStream
되기 위해 필요한 ANTLRInputStream
에서 변화에 필요한 반환 된 값 parser.eval()
에 parser.eval().value
, 그리고 나는 제거하는 데 필요한 WS
속성 값과 같은 때문에, 마지막에 절을 $channel
더 이상 렉서 작업에 표시 할 수 없습니다.
eval
를 반환하는 파서 규칙입니다 double
. 따라서 에서 설명한 것처럼 eval()
의 인스턴스를 호출 할 수 있는 방법이 있습니다 . 렉서 / 파서를 생성 한 후 파일을 열면를 반환 하는 메소드 가 있음을 알 수 있습니다 . ExpParser
ANTLRDemo.main(...)
ExpParser.java
eval()
double
Gabriele Tomassetti의 ANTLR 메가 튜토리얼 은 매우 도움이됩니다
문법 예제, 다른 언어 (Java, JavaScript, C # 및 Python)의 방문자 예 및 기타 여러 가지가 있습니다. 추천.
편집 : ANTLR의 Gabriele Tomassetti의 다른 유용한 기사
https://github.com/BITPlan/com.bitplan.antlr 에는 유용한 도우미 클래스와 몇 가지 완전한 예제가 포함 된 ANTLR Java 라이브러리가 있습니다. maven과 함께 사용할 수 있으며 Eclipse 및 maven을 좋아하는 경우 사용할 수 있습니다.
https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/exp/Exp.g4
곱하고 추가 할 수있는 간단한 Expression 언어입니다. https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestExpParser.java 에는 해당 단위 테스트가 있습니다.
https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/iri/IRIParser.g4 는 세 부분으로 나뉘어 진 IRI 파서입니다.
https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIRIParser.java 에 유닛 테스트가 있습니다.
개인적으로 나는 이것이 가장 까다로운 부분이라는 것을 알았습니다. 참조 http://wiki.bitplan.com/index.php/ANTLR_maven_plugin를
https://github.com/BITPlan/com.bitplan.antlr/tree/master/src/main/antlr4/com/bitplan/expr
이전 버전에서 ANTLR4의 성능 문제에 대해 작성된 세 가지 예가 더 있습니다. 그 동안 테스트 케이스 https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIssue994.java가 표시 하는 것처럼이 문제가 해결되었습니다 .
버전 4.7.1은 약간 다릅니다. 가져 오기 :
import org.antlr.v4.runtime.*;
주요 세그먼트-CharStreams를 참고하십시오.
CharStream in = CharStreams.fromString("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);