ANTLR中的“语义谓词”是什么?


103

ANTLR中的语义谓词是什么?


3
请注意,由于我找不到合适的在线资源来发布给想知道语义谓词是什么的人,因此我决定将问题自己发布在这里(我也将很快回答自己)。
巴特·基尔斯

1
感谢您这样做;当人们回答自己的问题时,尤其是如果他们问这样的问题,我总是喜欢它。
Daniel H

1
读了这本书。权威性ANTLR 4参考的第11章涉及语义谓词。没有书吗?得到它!值得每一美元。
james.garriss

Answers:


169

ANTLR 4

对于ANTLR 4中的谓词,请检查以下堆栈溢出的问答:


ANTLR 3

语义谓词是执行在使用时的明码语法操作的额外(语义)规则的方法。

语义谓词有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");

并再次进行测试:您将在控制台上在string后面紧接出现错误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规则时,第一个语句将初始化。实际的谓词是:{ N <= 10 }?FailedPredicateException 只要数字长于10位数字,解析器就会引发a 。

使用以下命令进行测试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.门控语义谓词

门控语义谓词类似于验证语义谓词,只有门控版本产生的一个语法错误代替FailedPredicateException

门控语义谓词的语法为:

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

要改为使用门控谓词来解决最长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
Yuri Ghensev 2012年

5
@Bart Kiers:请写一本关于ANTLR的书
santosh singh,

2
对于ANTLR v4,现在input.LT(1)getCurrentToken():-)
肖佳

太棒了...这是应在文档中进行的详尽解释和示例!
以西结·维克多

+1。这个答案远比《权威ANTLR 4参考》书要好。这个答案很好地举例说明了这个概念。
2014年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.