문제

나는 Antlr을 시작하고 싶지만 몇 시간을 보낸 후에는 예제를 검토했습니다. antlr.org 사이트, 나는 여전히 문법에 대한 자바 과정에 대한 명확한 이해를 얻을 수 없습니다.

Parser 정의를 통해 ANTLR과 Java 소스 코드까지의 4 개 작동 계산기와 같은 간단한 예가 있습니까?

도움이 되었습니까?

해결책

메모:이 대답은입니다 antlr3! 당신이 찾고 있다면 antlr4 예를 들어, 이것 Q & A 간단한 표현 파서를 만드는 방법을 보여주고 평가자를 사용하는 방법을 보여줍니다. antlr4.


먼저 문법을 만듭니다. 아래는 4 개의 기본 수학 연산자 인 +, -, * 및 /를 사용하여 구축 된 표현식을 평가하는 데 사용할 수있는 작은 문법입니다. 괄호를 사용하여 표현을 그룹화 할 수도 있습니다.

이 문법은 매우 기본적인 것입니다. 단지 연산자 (-1+9의 마이너스 : -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;}
    ;

(파서 규칙은 소문자로 시작하고 Lexer 규칙은 대문자로 시작합니다)

문법을 만든 후에는 파서와 Lexer를 생성하고 싶을 것입니다. 다운로드 Antlr 항아리 문법 파일과 동일한 디렉토리에 저장하십시오.

쉘/명령 프롬프트에서 다음 명령을 실행하십시오.

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 코드를 추가하고 싶습니다. 코드를 추가하면 배치하여 수행 할 수 있습니다 { 그리고 } 평범한 자바 코드가있는 문법 내부.

그러나 먼저 : 문법 파일의 모든 파서 규칙은 원시 이중 값을 반환해야합니다. 추가하여 할 수 있습니다 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 규칙은 이제 두 배를 반환하고 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
    }
}

다시 (Re) 문법 (1)에서 신선한 Lexer와 Parser를 생성하고 모든 클래스 (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 Wiki 그리고 방금 게시 한 내용과 함께 튜토리얼을 읽거나 조금 재생하십시오.

행운을 빕니다!

편집하다:

이 게시물 위의 예를 확장하여 Map<String, Double> 제공된 표현식에서 변수를 보유하는 제공 될 수 있습니다.

이 코드가 현재 버전의 Antlr (2014 년 6 월)으로 작동하도록하려면 몇 가지 변경이 필요했습니다. ANTLRStringStream 필요합니다 ANTLRInputStream, 변경하는 데 필요한 반환 된 값 parser.eval() 에게 parser.eval().value, 그리고 나는 그것을 제거해야했다 WS 다음과 같은 속성 값 때문에 끝에있는 조항 $channel 더 이상 Lexer 행동에 나타나지 않습니다.

다른 팁

Antlr 메가 튜토리얼 Gabriele Tomassetti는 매우 도움이됩니다

문법 예제, 다른 언어 (Java, JavaScript, C# 및 Python)의 방문자의 예 및 기타 많은 것들이 있습니다. 추천.

편집 : Antlr의 Gabriele Tomassetti의 기타 유용한 기사

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

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

그에 따라 ClassPath에서 항아리 이름을 업데이트하십시오.

~에 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

곱하고 작업을 추가 할 수있는 간단한 표현 언어입니다.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. 수입 된 렉스 바이스 문법

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);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top