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