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

时间:2011-02-18 06:47:39

标签: 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;};

我希望我的语法能够做到以下几点:

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

1 个答案:

答案 0 :(得分:7)

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


#1

以下不是验证谓词:

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

ANTLR中的正确验证谓词最后有一个问号,内部代码末尾没有半冒号。所以它应该是:

{$dot.text.length() == 1}?

代替。


#2

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

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

在解析器规则中。你应该让lexer处理这个。这样的事情会做到:

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' )?)?)?)?;

(另见#5!)


#3

你的词霸规则:

CHAR    :   'a'..'z';
DIGIT   :   '0'..'9';  
String  :   (CHAR | DIGIT)+;

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

fragment CHAR  : 'a'..'z' | 'A'..'Z';
fragment DIGIT : '0'..'9';
String : (CHAR | DIGIT)+;

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


#4

规则:

dot :   .;

不符合您的想法。它匹配“任何标记”,而不是“任何字符”。在词法分析器规则中,.匹配任何字符,但在解析器规则中,它匹配任何标记。意识到解析器规则只能使用词法分析器创建的标记。

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

- 一个例子 -

采用以下语法:

p : . ;
A : 'a' | 'A';
B : 'b' | 'B';

解析器规则p现在将匹配词法分析器生成的任何标记:这只是A - 或B - 标记。因此,p只能与其中一个字符'a''A''b''B'匹配。

以下语法:

prs : . ;
FOO : 'a';
BAR : . ;

词法分析器BAR匹配\u0000 .. \uFFFF范围内的任何单个字符,但它永远不会匹配字符'a',因为词法分析器FOOBAR之前定义{1}}已经规则并捕获此'a'。解析器规则prs再次匹配任何令牌,即FOOBAR


#5

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

所以,不要这样做:

pRule  : 'u' ':' String
String : ...

但是:

pRule  : U ':' String
U      : 'u';
String : ...

您可以将':'作为词法分析器规则,但这不太重要。 'u'也可以是String,因此它必须在<{strong> 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;};

可以使用以下类进行测试:

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

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

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

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