Java 令牌类型取决于以下令牌

Java 令牌类型取决于以下令牌,java,antlr,Java,Antlr,我的语法很简单。谷歌搜索和阅读书籍没有帮助。我最近开始使用ANTLR,所以这可能是一个非常简单的问题 我正在尝试使用ANTLRv3编写一个非常简单的Lexer grammar TestLexer; options { language = Java; } TEST_COMMENT : '/*' WS? TEST WS? '*/' ; ML_COMMENT : '/*' ( options {greedy=false;} : .)* '*/' {$chan

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

我正在尝试使用ANTLRv3编写一个非常简单的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*/”时,lexer按预期生成两个标记。一个带有类型测试注释和一个带有EOF。一切都好

但如果测试字符串最后包含一个额外的空格:“/*test*/”lexer生成三个标记:ML\u COMMENT、WS和EOF

为什么第一个令牌获得ML_注释类型?我认为检测标记的方式只取决于语法中lexer规则的优先级。当然,它不应该依赖于以下令牌

谢谢你的帮助


另外,我可以使用lexer option filter=true-令牌将获得正确的类型,但这种方法需要在令牌定义中进行额外的工作。老实说,我不想使用这种类型的lexer

ANTLR从顶部规则向下标记字符流,并尝试尽可能多地匹配。因此,是的,我还希望为
“/*TEST*/”
“/*TEST*/”
创建
测试注释。您可以查看lexer生成的源代码,了解它为什么选择为第二个输入创建
ML\u注释

无论这是一个bug,还是预期的行为,我都不会使用看起来像a的单独lexer规则。你能解释一下你到底想解决什么问题吗

用户776872写道:

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

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

编辑 在区分多行注释和Javadoc注释的情况下,最好将它们保持在相同的规则中,如果标记以
/**
开头,则更改标记的类型,如下所示:

grammar T;

// options

tokens {
  DOC_COMMENT;
}

// rules

COMMENT
  :  '/*' (~'*' .*)? '*/'
  |  '/**' ~'/' .* '*/' {$type=DOC_COMMENT;}
  ;
注意,在ANTLR中,
*
+
在默认情况下都是非贪婪的(与流行的观点相反)

演示 产生:

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 COMMENT :: /**/ COMMENT :: /*foo*/ DOC_COMMENT :: /**bar*/ 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*/ANTLR标记从顶部规则向下开始的字符流,并尝试尽可能多地匹配。因此,是的,我还希望为
“/*TEST*/”
“/*TEST*/”
创建
测试注释。您可以查看lexer生成的源代码,了解它为什么选择为第二个输入创建
ML\u注释

无论这是一个bug,还是预期的行为,我都不会使用看起来像a的单独lexer规则。你能解释一下你到底想解决什么问题吗

用户776872写道:

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

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

编辑 在区分多行注释和Javadoc注释的情况下,最好将它们保持在相同的规则中,如果标记以
/**
开头,则更改标记的类型,如下所示:

grammar T;

// options

tokens {
  DOC_COMMENT;
}

// rules

COMMENT
  :  '/*' (~'*' .*)? '*/'
  |  '/**' ~'/' .* '*/' {$type=DOC_COMMENT;}
  ;
注意,在ANTLR中,
*
+
在默认情况下都是非贪婪的(与流行的观点相反)

演示 产生:

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 COMMENT :: /**/ COMMENT :: /*foo*/ DOC_COMMENT :: /**bar*/ 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*/+1我花了一秒钟的时间来解析它,但这似乎很“奇怪”。你为什么要把
WS?
另一条规则放进去?如果处于隐藏通道或被跳过,则不会在其他规则中发生。@Kay我指定了标记TEST_注释,该注释可以包含任意数量的空白(WS)。在解析阶段,我不需要WS-token本身。@Kay,
WS
只有当它自己是一个标记时才会放在隐藏通道上。当作为另一条规则的一部分时,它所匹配的空白字符位于该特定令牌的通道上。和@Bart:我的错,谢谢你澄清了这一点。:)+1我花了一秒钟的时间来解析,但这似乎很“奇怪”。你为什么要把
WS?
另一条规则放在这里?如果处于隐藏通道或被跳过,则不会在其他规则中发生。@Kay我指定了标记TEST_注释,该注释可以包含任意数量的空白(WS)。在解析阶段,我不需要WS-token本身。@Kay,
WS
只有当它自己是一个标记时才会放在隐藏通道上。当作为另一条规则的一部分时,它所匹配的空白字符位于该特定令牌的通道上。和@Bart:我的错,谢谢你澄清了这一点。:)谢谢你,巴特!我对生成的Lexer类使用了调试器,老实说,我在DFA.predict方法调用中失败了。我没想到我这么快就发现了ANTLR中的错误(我刚开始使用这个工具),这就是为什么我决定先问这里的原因。我认为我的案例与java文档注释和常规jav非常相似