【问题标题】:Token type depends on following token令牌类型取决于以下令牌
【发布时间】:2011-05-30 23:28:31
【问题描述】:

我被一个非常简单的语法困住了。谷歌搜索和书籍阅读没有帮助。我最近开始使用 ANTLR,所以这可能是一个非常简单的问题。

我正在尝试使用 ANTLR v3 编写一个非常简单的 Lexer。

grammar TestLexer;

options {
  language = Java;
}

TEST_COMMENT
    :   '/*' WS? TEST WS? '*/'
    ;

ML_COMMENT
    :   '/*' ( options {greedy=false;} : .)* '*/' {$channel=HIDDEN;}
    ;

TEST    :   'TEST'
    ;

WS  :   (' ' | '\t' | '\n' | '\r' | '\f')+ {$channel=HIDDEN;}
    ;

测试类:

public class TestParserInvoker {
    private static void extractCommandsTokens(final String script) throws RecognitionException {

        final ANTLRStringStream input = new ANTLRStringStream(script);
        final Lexer lexer = new TestLexer(input);

        final TokenStream tokenStream = new CommonTokenStream(lexer);
        Token t;
        do {
            t = lexer.nextToken();
            if (t != null) {
                System.out.println(t);
            }
        } while (t == null || t.getType() != Token.EOF);
    }


    public static void main(final String[] args) throws RecognitionException {
        final String script = "/* TEST */";
        extractCommandsTokens(script);
    }
}

因此,当测试字符串为“/* TEST */”时,词法分析器会按预期生成两个标记。一种是 TEST_COMMENT 类型,另一种是 EOF。一切正常。

但如果测试字符串最后包含一个额外的空格:“/* TEST */” 词法分析器会生成三个标记:ML_COMMENT、WS 和 EOF。

为什么第一个令牌得到 ML_COMMENT 类型?我认为如何检测令牌的方式仅取决于语法中词法分析器规则的优先级。当然,它不应该依赖于以下标记。

感谢您的帮助!

附:我可以使用词法分析器选项 filter=true - 令牌将获得正确的类型,但这种方法需要在令牌定义中进行额外的工作。老实说,我不想使用这种类型的词法分析器。

【问题讨论】:

  • +1 我花了一秒钟来解析,但这似乎“奇怪”。
  • 你把WS? 放在另一个规则中是为了什么?在隐藏频道或被跳过时,它永远不会出现在另一个规则中。
  • @Kay 我指定了可以包含任意数量的空格 (WS) 的令牌 TEST_COMMENT。在解析阶段,我自己不需要 WS 令牌。
  • @Kay, WS 只有在它自己的令牌时才会被放入隐藏通道。当另一个规则的一部分时,它匹配的空白字符位于 那个 特定令牌的通道上。
  • 和@Bart:我的错,感谢您解决这个问题。 :)

标签: java antlr


【解决方案1】:

ANTLR 对从顶部规则开始向下的字符流进行标记并且尝试尽可能多地匹配。所以,是的,我还期望为"/* TEST */""/* TEST */ " 创建一个TEST_COMMENT。您可以随时查看生成的词法分析器源代码,了解它为什么选择为第二个输入创建 ML_COMMENT

无论这是错误还是预期的行为,我都不会使用看起来非常相似的单独词法分析器规则。你能解释一下你真正想要解决的问题吗?

user776872 写道:

我可以使用词法分析器选项 filter=true - 令牌将获得正确的类型,但这种方法需要额外的令牌定义工作。老实说,我不想使用这种类型的词法分析器。

我不太明白这句话。您是否只对输入源的一部分感兴趣?在这种情况下,filter=true 肯定是一个不错的选择。如果你想标记所有输入源,那么你不应该使用filter=true

编辑

如果要区分多行 cmets 和 Javadoc cmets,最好将它们保持在同一规则中,如果它以 /** 开头,则更改令牌的类型,如下所示:

grammar T;

// options

tokens {
  DOC_COMMENT;
}

// rules

COMMENT
  :  '/*' (~'*' .*)? '*/'
  |  '/**' ~'/' .* '*/' {$type=DOC_COMMENT;}
  ;

请注意,.*.+ 在 ANTLR 中默认是非贪婪的(与流行的看法相反)。

演示

grammar T;

tokens {
  DOC_COMMENT;
}

@parser::members {
  public static void main(String[] args) throws Exception {
    TLexer lexer = new TLexer(new ANTLRStringStream("/**/ /*foo*/ /**bar*/"));
    TParser parser = new TParser(new CommonTokenStream(lexer));
    parser.parse();
  }
}

parse
  :  (t=. {System.out.println(tokenNames[$t.type] + " :: " + $t.text);})* EOF
  ;

COMMENT
  :  '/*' (~'*' .*)? '*/'
  |  '/**' ~'/' .* '*/' {$type=DOC_COMMENT;}
  ;

SPACE
  :  ' ' {$channel=HIDDEN;}
  ;

产生:

bart@hades:~/Programming/ANTLR/Demos/T$ java -cp antlr-3.3.jar org.antlr.Tool T.g
bart@hades:~/Programming/ANTLR/Demos/T$ javac -cp antlr-3.3.jar *.java
bart@hades:~/Programming/ANTLR/Demos/T$ java -cp .:antlr-3.3.jar TParser
评论 :: /**/
评论 :: /*foo*/
DOC_COMMENT :: /**bar*/

【讨论】:

  • 谢谢你,巴特!我使用调试器生成 Lexer 类,老实说,我迷失在 DFA.predict 方法调用中。没想到我这么快就碰到了ANTLR中的bug(我刚开始用这个工具),所以决定先在这里问。
  • 我认为我的情况与 java doc 注释和常规 java cmets 非常相似。但我从未见过有人提到过这种类型的错误。
  • 我正在开发非常简单的 SQL 解析器,它可以从脚本中提取 SQL 命令。在这种情况下,我只需要在解析器中指定每个命令的边界。你是对的 - 我只需要代码的一部分。首先,我在词法分析器中使用了 filter=true 。后来我把它去掉了,因为它从其他非关键词词中提取关键词。例如令牌 CREATE : 'CREATE' 可以从输入“CREATE”和输入“CREATExxx”中获得
  • @Anton,我没有说它肯定是一个错误,但它可能是。 “因为它从其他非关键字词中提取关键字” 那么您必须定义一些Identifier 规则并将这些非关键字放在隐藏通道上。在您的情况下,filter=true 是一个不错的选择。
猜你喜欢
  • 2016-03-23
  • 1970-01-01
  • 2015-07-27
  • 1970-01-01
  • 2015-09-19
  • 1970-01-01
  • 2019-06-18
  • 1970-01-01
  • 2020-11-26
相关资源
最近更新 更多