14

我有一个 antlr4 词法分析器语法。它有许多单词规则,但我也希望它为其他规则无法匹配的任何单词创建一个未知标记。我有这样的事情:

Whitespace : [ \t\n\r]+ -> skip;
Punctuation : [.,:;?!];
// Other rules here
Unknown : .+? ; 

现在生成的匹配器将 '~' 捕获为未知,但会为输入 '~~~' 创建 3 个 '~' 未知标记,而不是单个 '~~~' 标记。我应该怎么做才能告诉词法分析器为未知的连续字符生成单词标记。我也试过“未知:.;” 和“未知:.+ ;” 没有结果。

编辑:在当前的 antlr 版本中 .+? 现在捕获剩余的单词,所以这个问题似乎得到了解决。

4

2 回答 2

14

.+?在词法分析器规则的末尾将始终匹配单个字符。但是.+会尽可能多地消耗,这在 ANTLR v3(可能也是 v4)的规则末尾是非法的。

您可以做的只是匹配一个字符,然后在解析器中将它们“粘合”在一起:

unknowns : Unknown+ ; 

...

Unknown  : . ; 

编辑

...但我只有一个词法分析器,没有解析器...

啊,我明白了。然后你可以覆盖该nextToken()方法:

lexer grammar Lex;

@members {

  public static void main(String[] args) {
    Lex lex = new Lex(new ANTLRInputStream("foo, bar...\n"));
    for(Token t : lex.getAllTokens()) {
      System.out.printf("%-15s '%s'\n", tokenNames[t.getType()], t.getText());
    }
  }

  private java.util.Queue<Token> queue = new java.util.LinkedList<Token>();

  @Override
  public Token nextToken() {    

    if(!queue.isEmpty()) {
      return queue.poll();
    }

    Token next = super.nextToken();

    if(next.getType() != Unknown) {
      return next;
    }

    StringBuilder builder = new StringBuilder();

    while(next.getType() == Unknown) {
      builder.append(next.getText());
      next = super.nextToken();
    }

    // The `next` will _not_ be an Unknown-token, store it in 
    // the queue to return the next time!
    queue.offer(next);

    return new CommonToken(Unknown, builder.toString());
  }
}

Whitespace  : [ \t\n\r]+ -> skip ;
Punctuation : [.,:;?!] ;
Unknown     : . ; 

运行它:

java -cp antlr-4.0-complete.jar org.antlr.v4.Tool Lex.g4
javac -cp antlr-4.0-complete.jar *.java
java -cp .:antlr-4.0-complete.jar Lex

将打印:

未知的“富”
标点符号','
未知的“酒吧”
标点符号“。”
标点符号“。”
标点符号“。”
于 2013-02-05T19:23:14.810 回答
3

接受的答案有效,但仅适用于 Java。

我将提供的 Java 代码转换为与 C# ANTLR 运行时一起使用。如果其他人需要它...给你!

@members {
private IToken _NextToken = null;
public override IToken NextToken()
{
    if(_NextToken != null)
    {
        var token = _NextToken;
        _NextToken = null;
        return token;
    }

    var next = base.NextToken();
    if(next.Type != UNKNOWN)
    {
        return next;
    }

    var originalToken = next;
    var lastToken = next;
    var builder = new StringBuilder();
    while(next.Type == UNKNOWN)
    {
        lastToken = next;
        builder.Append(next.Text);
        next = base.NextToken();
    }
    _NextToken = next;
    return new CommonToken(
        originalToken
    )
    {
        Text = builder.ToString(),
        StopIndex = lastToken.Column
    };
}
}
于 2020-01-18T00:16:53.980 回答