什么是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谓词的简要引用作为我的指南。