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


230

ANTLR을 시작하고 싶지만 antlr.org 사이트 에서 예제를 검토하는 데 몇 시간을 보낸 후에도 문법에서 Java 프로세스에 대한 명확한 이해를 얻을 수 없습니다.

ANTLR로 구현 된 4 개의 연산 계산기와 같이 파서 정의를 거쳐 Java 소스 코드까지가는 간단한 예제가 있습니까?


2
그 정확한 예제는 마지막으로 확인한 Antlr 사이트의 튜토리얼로 사용됩니다.
Cory Petosky

1
@Cory Petosky : 당신의 링크를 공급할 수 있습니까?
Eli

방금 ANTLR에 비디오 자습서의 첫 부분을 게시했습니다. javadude.com/articles/antlr3xtut를 참조하십시오. 도움이 되길 바랍니다.
Scott Stanchfield

2
나도 당신의 검색을 공유합니다.
Paul Draper

1
ANTLR 4의 가장 좋은 답변은 Parr의 저서 "The Definitive ANTLR 4 Reference"를 구입하는 것입니다.
james.garriss

답변:


447

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


1
구현은 어디에서 parser.eval()발생합니까? 여기 또는 ANTLR3 Wiki에서는 명확하지 않습니다!

1
@Jarrod, 실수, 죄송합니다, 당신을 정말로 이해하지 못합니다. eval를 반환하는 파서 규칙입니다 double. 따라서 에서 설명한 것처럼 eval()의 인스턴스를 호출 할 수 있는 방법이 있습니다 . 렉서 / 파서를 생성 한 후 파일을 열면를 반환 하는 메소드 가 있음을 알 수 있습니다 . ExpParserANTLRDemo.main(...)ExpParser.javaeval()double
Bart Kiers

@Bart 나는 이것을 일주일 동안 연구 해 왔으며, 이것은 실제로 처음으로 작동하기에 충분하고 완벽하고 내가 이해한다고 생각하는 첫 번째 예입니다. 나는 거의 포기했다. 감사!
Vineel Shah

13

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

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

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


훌륭한 튜토리얼!
Manish Patel

Antlr은 이제 cpp도 대상 언어로 사용합니다. cpp에 대한 예제가있는 자습서가 있습니까?
vineeshvs

같은 사람은 C ++에서 ANTLR에 대한 자습서를 만든 tomassetti.me/getting-started-antlr-cpp 내가있는 거 당신이 여기에서 나 메가 튜토리얼에서 원하는 내용을 찾을 것이다 당신에게 추측하고있어
솔로

7

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

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

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


2

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가 표시 하는 것처럼이 문제가 해결되었습니다 .


2

버전 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);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.