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

什么是ANTLR中的语义谓词

ANTLR 4

对于ANTLR 4中的谓词,请查看这些堆栈溢出问答:

  • Antlr4中语义谓词的语法
  • ANTLR4中的语义谓词?

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 Main

这样做时,控制台上没有任何内容,这表明没有任何问题。 尝试改变:

 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; } { int N = 0; }{ N++; } { N++; }是普通的Java语句,当语法分析器“输入” number规则时,首先进行初始化。 实际的谓词是: { N <= 10 }? ,只要数字长度超过10位,就会导致解析器抛出一个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.门控语义谓词

门控语义谓词类似于验证语义谓词 ,只有门控版本产生语法错误而不是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 

我一直使用对wincent.com上的ANTLR谓词的简要引用作为我的指南。