什么是';语义谓词&x27;在ANTLR?

什么是';语义谓词&x27;在ANTLR?,antlr,antlr3,antlr4,Antlr,Antlr3,Antlr4,什么是ANTLR中的语义谓词?ANTLR 4 对于ANTLR 4中的谓词,请检查以下stackoverflow Q&A: ANTLR 3 语义谓词是对语法强制执行额外(语义)规则的一种方法 使用普通代码的操作 有3种类型的语义谓词: 验证语义谓词 选通语义谓词 消除语义谓词的歧义 范例语法 假设您有一个文本块,该文本块仅由以空格分隔的数字组成 逗号,忽略任何空格。您想解析此输入 确保数字最多为3位“长”(最多999)。以下 语法(Numbers.g)会做这样的事情: grammar

什么是ANTLR中的语义谓词?

ANTLR 4 对于ANTLR 4中的谓词,请检查以下stackoverflow Q&A:


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();
    }
}
通过生成lexer和解析器,编译所有
.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 Main 进入:

然后再次执行测试:您将看到控制台上的字符串
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 }?
  ;
不会产生异常,而以下情况不会产生异常:

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

2.门控语义谓词 选通语义谓词类似于验证语义谓词, 只有选通版本才会产生语法错误,而不是
FailedPredicateException

选通语义谓词的语法为:

相反,要解决上述问题,请使用选通谓词匹配长度不超过10位的数字,您可以编写:

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

您将看到最后一个on将抛出一个错误


3.语义谓词消歧 最后一种类型的谓词是消歧语义谓词,它看起来有点像验证谓词(
{boolean expression}?
),但更像一个选通语义谓词(当布尔表达式的计算结果为
false
时,不会引发异常)。您可以在规则开始时使用它来检查规则的某些属性,并让解析器与所述规则匹配或不匹配

假设示例语法创建的
Number
标记(lexer规则而不是解析器规则)将匹配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();}
  ;

我一直使用wincent.com上的简短参考作为我的指南。

注意,由于我找不到合适的在线资源为想知道语义谓词是什么的人发帖,所以我决定自己在这里发帖(我很快也会回答自己)。谢谢你这么做;我总是喜欢人们回答他们自己的问题,特别是如果他们问这个问题是为了用这种方式来回答的话。读这本书。ANTLR 4参考文献的第11章是关于语义谓词的。你没有这本书吗?了解了!值得每一美元。是的,一个优秀的链接!但是,正如您所提到的,对于ANTLR的新手(相对而言),这可能有点困难。我只希望我的回答对蚂蚁草蜢(稍微)友好一点。:)你真的应该考虑写一个初学者指南:P@Bart基尔斯:请为ANTLR v4写一本关于ANTLR的书,
input.LT(1)
is
getCurrentToken()
now:-)太棒了。。。这是一种详尽的解释和例子,应该在文档中+1.这个答案远远好于权威的ANTLR 4参考书。这个答案非常符合概念,并有很好的例子。
// '12345678901' is more than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,12345678901");
{ /* a boolean expression in here */ }?=> RULE
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");
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();}
  ;
low  = 123
high = 999
low  = 456
high = 700
low  = 89
low  = 0