[java] ANTLR : 간단한 예가 있습니까?

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더 이상 렉서 작업에 표시 할 수 없습니다.


답변

Gabriele Tomassetti의 ANTLR 메가 튜토리얼 은 매우 도움이됩니다

문법 예제, 다른 언어 (Java, JavaScript, C # 및 Python)의 방문자 예 및 기타 여러 가지가 있습니다. 추천.

편집 : ANTLR의 Gabriele Tomassetti의 다른 유용한 기사


답변

Antlr 4의 경우 Java 코드 생성 프로세스는 다음과 같습니다.

java -cp antlr-4.5.3-complete.jar org.antlr.v4.Tool Exp.g

클래스 경로에서 jar 이름을 적절히 업데이트하십시오.


답변

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 파서입니다.

  1. 파서 문법
  2. 렉서 문법
  3. 가져온 LexBasic 문법

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);


답변