ANTLR中的“semantic predicate”是什么?

发布于 2021-01-30 16:37:48

ANTLR中的“semantic predicate”是什么?

关注者
0
被浏览
325
1 个回答
  • 面试哥
    面试哥 2021-01-30
    为面试而生,有面试问题,就找面试哥。

    ANTLR 4

    对于ANTLR 4中的谓词,请检查以下堆栈溢出的问答:

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

    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();
        }
    }
    

    通过生成词法分析器和解析器,编译所有.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");
    

    并再次进行测试:您将在控制台上在string后面紧接出现错误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; }{ N++; }是纯Java语句,当解析器“输入”number规则时,第一个语句将初始化。实际的谓词是:{ N <= 10 }?,FailedPredicateException 只要数字长于10位数字,解析器就会抛出a 。

    使用以下命令进行测试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
    


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看