ANTLR에서 '의미 적 술어'는 무엇입니까?


103

ANTLR에서 의미 론적 술어 는 무엇입니까 ?


3
의미 론적 술어 가 무엇인지 알고 싶어하는 사람을 위해 게시 할 적절한 온라인 리소스를 찾을 수 없었기 때문에 여기에 질문을 직접 게시하기로 결정했습니다 (곧 나도 대답하겠습니다).
Bart Kiers

1
이렇게 해주셔서 감사합니다. 나는 사람들이 자신의 질문에 대답 할 때 항상 그것을 좋아합니다. 특히 그들이 이런 식으로 대답하기 위해 특별히 질문하는 경우에는 더욱 그렇습니다.
Daniel H

1
책을 읽으십시오. The Definitive ANTLR 4 Reference의 Ch 11은 의미 론적 술어에 있습니다. 책이 없습니까? 잡아! 모든 달러 가치.
james.garriss

답변:


169

ANTLR 4

ANTLR 4의 조건 자의 경우 다음 스택 오버플로 Q & A를 확인하십시오.


ANTLR 3

의미 술어 일반 코드를 사용하여 문법 조치에 따라 별도의 (의미) 규칙을 적용하는 방법입니다.

의미 론적 술어에는 세 가지 유형이 있습니다.

  • 의미 론적 술어 검증 ;
  • 게이트 된 의미 론적 술어;
  • 의미 론적 술어를 명확하게합니다 .

문법 예

공백을 무시하고 쉼표로 구분 된 숫자로만 구성된 텍스트 블록이 있다고 가정 해 보겠습니다. 이 입력을 구문 분석하여 숫자가 최대 3 자리 "긴"(최대 999)인지 확인하려고합니다. 다음 문법 ( Numbers.g)은 이러한 작업을 수행합니다.

grammar Numbers;

// entry point of this parser: it parses an input string consisting of at least 
// one number, optionally followed by zero or more comma's and numbers
parse
  :  number (',' number)* EOF
  ;

// matches a number that is between 1 and 3 digits long
number
  :  Digit Digit Digit
  |  Digit Digit
  |  Digit
  ;

// matches a single digit
Digit
  :  '0'..'9'
  ;

// ignore spaces
WhiteSpace
  :  (' ' | '\t' | '\r' | '\n') {skip();}
  ;

테스팅

문법은 다음 클래스로 테스트 할 수 있습니다.

import org.antlr.runtime.*;

public class Main {
    public static void main(String[] args) throws Exception {
        ANTLRStringStream in = new ANTLRStringStream("123, 456, 7   , 89");
        NumbersLexer lexer = new NumbersLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        NumbersParser parser = new NumbersParser(tokens);
        parser.parse();
    }
}

렉서와 파서를 생성하고 모든 .java파일을 컴파일 하고Main 클래스를 .

java -cp antlr-3.2.jar org.antlr.Tool Numbers.g
javac -cp antlr-3.2.jar * .java
java -cp. : antlr-3.2.jar 기본

이렇게하면 콘솔에 아무 것도 인쇄되지 않아 아무 문제도 발생하지 않았 음을 나타냅니다. 변경 시도 :

ANTLRStringStream in = new ANTLRStringStream("123, 456, 7   , 89");

으로:

ANTLRStringStream in = new ANTLRStringStream("123, 456, 7777   , 89");

테스트를 다시 수행하십시오. 콘솔에서 문자열 바로 뒤에 오류가 표시됩니다 777.


시맨틱 술어

이것은 의미 론적 술어로 우리를 가져옵니다. 1 자리에서 10 자리 사이의 숫자를 구문 분석하려고한다고 가정 해 보겠습니다. 다음과 같은 규칙 :

number
  :  Digit Digit Digit Digit Digit Digit Digit Digit Digit Digit
  |  Digit Digit Digit Digit Digit Digit Digit Digit Digit
     /* ... */
  |  Digit Digit Digit
  |  Digit Digit
  |  Digit
  ;

번거로울 것입니다. 시맨틱 술어는 이러한 유형의 규칙을 단순화하는 데 도움이 될 수 있습니다.


1. 의미 론적 술어 검증

확인하는 의미 술어는 뒤에 물음표가 코드 블록에 지나지 않는다 :

RULE { /* a boolean expression in here */ }?

유효성 검증 시맨틱 술어를 사용하여 위의 문제를 해결하려면 number문법 의 규칙을 다음과 같이 변경하십시오 .

number
@init { int N = 0; }
  :  (Digit { N++; } )+ { N <= 10 }?
  ;

파서가 규칙을 "입력"할 때 첫 번째가 초기화되는 부분 { int N = 0; }{ N++; }일반 Java 문입니다 number. 실제 술어는 숫자가 10 자리 이상일 때마다 { N <= 10 }?구문 분석기가 a를 던지도록 FailedPredicateException합니다.

다음을 사용하여 테스트하십시오 ANTLRStringStream.

// all equal or less than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,1234567890"); 

예외가 발생하지 않는 반면 다음은 예외를 발생시킵니다.

// '12345678901' is more than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,12345678901");

2. 게이트 시맨틱 술어

게이트 의미 술어는 A와 비슷합니다 확인하는 의미 술어문이 버전은 대신 구문 오류가 발생합니다 FailedPredicateException.

게이트 시맨틱 술어 의 구문 은 다음과 같습니다.

{ /* a boolean expression in here */ }?=> RULE

대신 gated 술어를 사용하여 최대 10 자리 숫자와 일치 하도록 위의 문제를 해결하려면 다음과 같이 작성하십시오.

number
@init { int N = 1; }
  :  ( { N <= 10 }?=> Digit { N++; } )+
  ;

두 가지 모두로 다시 테스트하십시오.

// all equal or less than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,1234567890"); 

과:

// '12345678901' is more than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,12345678901");

마지막에 오류가 발생하는 것을 볼 수 있습니다.


3. 의미 론적 술어 명확화

최종 유형의 술어는 명확화 의미 술어 이며, 이는 유효성 검사 술어 ( {boolean-expression}?) 와 비슷해 보이지만 게이트 된 의미 술어처럼 작동합니다 (부울 표현식이로 평가 될 때 예외가 발생하지 않음 false). 규칙 시작시이를 사용하여 규칙의 일부 속성을 확인하고 파서가 해당 규칙과 일치하는지 여부를 확인할 수 있습니다.

예제 문법 Number이 0..999 범위의 숫자와 일치하는 토큰 (파서 규칙 대신 렉서 규칙)을 생성한다고 가정 해 보겠습니다 . 이제 파서에서 낮은 숫자와 높은 숫자를 구분하려고합니다 (낮음 : 0..500, 높음 : 501..999). 이는 토큰이 낮거나 높은지 확인하기 위해 스트림 ( )의 다음 토큰을 검사 하는 명확성 의미 론적 술어를 사용하여 수행 할 수 있습니다 input.LT(1).

데모 :

grammar Numbers;

parse
  :  atom (',' atom)* EOF
  ;

atom
  :  low  {System.out.println("low  = " + $low.text);}
  |  high {System.out.println("high = " + $high.text);}
  ;

low
  :  {Integer.valueOf(input.LT(1).getText()) <= 500}? Number
  ;

high
  :  Number
  ;

Number
  :  Digit Digit Digit
  |  Digit Digit
  |  Digit
  ;

fragment Digit
  :  '0'..'9'
  ;

WhiteSpace
  :  (' ' | '\t' | '\r' | '\n') {skip();}
  ;

이제 문자열을 파싱 "123, 999, 456, 700, 89, 0"하면 다음 출력이 표시됩니다.

low  = 123
high = 999
low  = 456
high = 700
low  = 89
low  = 0

12
ANTLR에 초보자 가이드를 작성하는 것이 좋습니다 당신을 정말 남자 : P
유리 Ghensev

5
@Bart Kiers은하십시오 ANTLR에 대한 책을 쓰기 바랍니다
싱 산토

2
ANTLR의 V4를 들어, input.LT(1)이다 getCurrentToken()지금 :-)
샤오 지아

환상적 ... 이것은 문서에 있어야하는 철저한 설명과 예제입니다!
Ezekiel Victor

+1. 이 답변은 The Definitive ANTLR 4 Reference 책보다 훨씬 낫습니다. 이 대답은 좋은 예와 함께 개념에 있습니다.
asyncwait

11

나는 항상 wincent.com의 ANTLR 술어 에 대한 간결한 참조를 가이드로 사용했습니다.


6
예, 훌륭한 링크입니다! 그러나 언급했듯이 ANTLR을 처음 접하는 사람에게는 (상대적으로) 약간 어려울 수 있습니다. 내 대답이 ANTLR 잔디 호퍼에 대해 (조금) 더 친근하기를 바랍니다. :)
Bart Kiers 2010-06-16
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.