ANTLR中的 语义谓词 是什么?
答案 0 :(得分:159)
对于ANTLR 4中的谓词,请检查这些堆栈溢出 Q& A's:
语义谓词 是一种在语法上强制执行额外(语义)规则的方法 使用普通代码的行动。
有三种语义谓词:
假设您有一个由仅由数字分隔的数字组成的文本块
逗号,忽略任何空格。您想要解析此输入
确保数字最多为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
;
会变得很麻烦。语义谓词可以帮助简化这种类型的规则。
验证语义谓词什么都不是 不仅仅是一段代码后跟一个问号:
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位数时。
使用以下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");
门控语义谓词类似于验证语义谓词,
只有 gated 版本会产生语法错误,而不是FailedPredicateException
。
门控语义谓词的语法是:
{ /* a boolean expression in here */ }?=> RULE
要使用 gated 谓词来匹配上述问题,以匹配长达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");
你将会看到最后一个会抛出错误。
最终类型的谓词是消除歧义的语义谓词,它看起来有点像验证谓词({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
答案 1 :(得分:11)
我总是在wincent.com上使用对ANTLR predicates的简洁参考作为我的向导。