如何在antlr中找到令牌的长度?

Pre*_*eti 2 antlr

我试图创建一个接受任何字符或数字或几乎任何东西的语法,只要它的长度等于1.

有检查长度的功能吗?

编辑

让我用一个例子更清楚地说明我的问题.我写了以下代码:

grammar first;

tokens {
    SET =   'set';
    VAL =   'val';
    UND =   'und';
    CON =   'con';
    ON  =   'on';
    OFF =   'off';
}

@parser::members {
  private boolean inbounds(Token t, int min, int max) {
    int n = Integer.parseInt(t.getText());
    return n >= min && n <= max;
  }
}

parse   :   SET expr;

expr    :   VAL('u'('e')?)? String |
        UND('e'('r'('l'('i'('n'('e')?)?)?)?)?)? (ON | OFF) |
        CON('n'('e'('c'('t')?)?)?)? oneChar
    ;

CHAR    :   'a'..'z';

DIGIT   :   '0'..'9';

String  :   (CHAR | DIGIT)+;

dot :   .;

oneChar :   dot { $dot.text.length() == 1;} ;

Space  : (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};
Run Code Online (Sandbox Code Playgroud)

我希望我的语法能做以下事情:

  1. 接受如下命令:'set value abc','set underli on','set conn#'.语法应足够智能,以接受不完整的单词,如'underl'而不是'underline.等等
  2. 第三种语法:'set connect oneChar'应该接受任何字符,但只接受一个字符.它可以是数字或字母或任何特殊字符.因此,我在生成的解析器文件中收到编译器错误.
  3. 第一种语法:'set value'应该接受所有可能的字符串,甚至是on和off.但是当我给出类似的东西:'set value offer'时,语法就失败了.我认为这种情况正在发生,因为我已经有一个令牌'OFF'.

在我的语法中,我上面列出的所有三个要求都没有正常工作.不知道为什么.

Bar*_*ers 8

你的语法中有一些错误和/或不良做法:


#1

以下不是验证谓词:

{$dot.text.length() == 1;}
Run Code Online (Sandbox Code Playgroud)

ANTLR中的正确验证谓词在末尾有一个问号,而内码在末尾没有半冒号.所以它应该是:

{$dot.text.length() == 1}?
Run Code Online (Sandbox Code Playgroud)

代替.


#2

您不应该处理这些替代命令:

expr
  :  VAL('u'('e')?)? String 
  |  UND('e'('r'('l'('i'('n'('e')?)?)?)?)?)? (ON | OFF) 
  |  CON('n'('e'('c'('t')?)?)?)? oneChar
  ;
Run Code Online (Sandbox Code Playgroud)

在解析器规则中.你应该让词法分析器来处理它.像这样的东西会这样做:

expr
  :  VAL String
  |  UND (ON | OFF)
  |  CON oneChar
  ;

// ...

VAL : 'val' ('u' ('e')?)?;
UND : 'und' ( 'e' ( 'r' ( 'l' ( 'i' ( 'n' ( 'e' )?)?)?)?)?)?;
CON : 'con' ( 'n' ( 'e' ( 'c' ( 't' )?)?)?)?;
Run Code Online (Sandbox Code Playgroud)

(另见#5!)


#3

你的词法规则:

CHAR    :   'a'..'z';
DIGIT   :   '0'..'9';  
String  :   (CHAR | DIGIT)+;
Run Code Online (Sandbox Code Playgroud)

让事情变得复杂.词法分析器可以生成三种不同类型的令牌:CHAR,DIGITString.理想情况下,您应该只创建String令牌,因为String已经可以是单个CHARDIGIT.您可以通过fragment在这些规则之前添加关键字来实现此目的:

fragment CHAR  : 'a'..'z' | 'A'..'Z';
fragment DIGIT : '0'..'9';
String : (CHAR | DIGIT)+;
Run Code Online (Sandbox Code Playgroud)

您的令牌流中现在只有令牌CHARDIGIT令牌,只有String令牌.简而言之:fragment规则仅用于词法规则内,其他词法规则.它们永远不会是自己的标记(并且因此永远不会出现在任何解析器规则中!).


#4

规则:

dot :   .;
Run Code Online (Sandbox Code Playgroud)

不会做你认为它做的事情.它匹配"任何标记",而不是"任何字符".在词法分析器规则中,.匹配任何字符,但在解析器规则中,它匹配任何标记.意识到解析器规则只能使用词法分析器创建的标记.

首先根据词法规则对输入源进行标记.在完成之后,解析器(尽管它的解析器规则)可以对这些令牌(不是字符!!!)进行操作.确保你明白这一点!(如果没有,请要求澄清或拿一本关于ANTLR的书)

- 一个例子 -

采用以下语法:

p : . ;
A : 'a' | 'A';
B : 'b' | 'B';
Run Code Online (Sandbox Code Playgroud)

解析器规则p现在将匹配词法分析器产生的任何标记:它只是一个A- 或B--token.所以,p只能匹配的人物之一'a','A','b'或者'B',没有别的.

并在以下语法中:

prs : . ;
FOO : 'a';
BAR : . ;
Run Code Online (Sandbox Code Playgroud)

词法分析器规则BAR匹配范围中的任何单个字符\u0000 .. \uFFFF,但它永远不会匹配该字符,'a'因为词法分析器规则FOOBAR规则之前定义并且'a'已经捕获了该规则.解析器规则prs再次匹配任何令牌,即FOO或者BAR.


#5

将单个字符'u'放在解析器规则中会导致词法分析器将其标记u为单独的标记:您不希望这样.此外,通过将它们放在解析器规则中,不清楚哪个令牌优先于其他令牌.您应该将所有这些文字保留在解析器规则之外,并使它们成为明确的词法规则.仅在解析器规则中使用词法分析器规则.

所以,不要这样做:

pRule  : 'u' ':' String
String : ...
Run Code Online (Sandbox Code Playgroud)

但是:

pRule  : U ':' String
U      : 'u';
String : ...
Run Code Online (Sandbox Code Playgroud)

你可以制定':'词法规则,但这不太重要.该'u'然而,也可以是String因此它必须显示为词法规则之前String规则.


好的,那些是我想到的最明显的事情.基于它们,这是一个建议的语法:

grammar first;

parse
  :  (SET expr {System.out.println("expr = " + $expr.text);} )+ EOF
  ;

expr
  :  VAL String    {System.out.print("A :: ");}
  |  UL (ON | OFF) {System.out.print("B :: ");}
  |  CON oneChar   {System.out.print("C :: ");}
  ;

oneChar 
  :  String {$String.text.length() == 1}?
  ;

SET : 'set';
VAL : 'val' ('u' ('e')?)?;
UL  : 'und' ( 'e' ( 'r' ( 'l' ( 'i' ( 'n' ( 'e' )?)?)?)?)?)?;
CON : 'con' ( 'n' ( 'e' ( 'c' ( 't' )?)?)?)?;
ON  : 'on';
OFF : 'off';

String : (CHAR | DIGIT)+;

fragment CHAR  : 'a'..'z' | 'A'..'Z';
fragment DIGIT : '0'..'9';

Space : (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};
Run Code Online (Sandbox Code Playgroud)

可以使用以下类测试:

import org.antlr.runtime.*;

public class Main {
    public static void main(String[] args) throws Exception {
        String source = 
                "set value abc  \n" + 
                "set underli on \n" + 
                "set conn x     \n" + 
                "set conn xy      ";
        ANTLRStringStream in = new ANTLRStringStream(source);
        firstLexer lexer = new firstLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        firstParser parser = new firstParser(tokens);
        System.out.println("parsing:\n======\n" + source + "\n======");
        parser.parse();
    }
}
Run Code Online (Sandbox Code Playgroud)

在生成词法分析器和解析器之后:

java -cp antlr-3.2.jar org.antlr.Tool first.g 
javac -cp antlr-3.2.jar *.java
java -cp .:antlr-3.2.jar Main

打印以下输出:

parsing:
======
set value abc  
set underli on 
set conn x     
set conn xy      
======
A :: expr = value abc
B :: expr = underli on
C :: expr = conn x
line 0:-1 rule oneChar failed predicate: {$String.text.length() == 1}?
C :: expr = conn xy
Run Code Online (Sandbox Code Playgroud)

如您所见,最后一个命令C :: expr = conn xy会产生错误,如预期的那样.

  • @Preeti,添加了一个示例来说明差异。 (2认同)