如何在忽略其他空格的同时在特定规则中获取强制空格?

How can I get mandatory whitespace in a specific rule while having other whitespace ignored?

我语法的相关部分结构如下:

someRule: subrule1 | WS sign=('+' | '-') subrule2 ; // whitespace required here
// ... etc
WS: [ \t\r\n]+ -> channel(HIDDEN) ; // whitespace is usually ignored

我想忽略空格,但在特定规则中需要它。我很确定在以前的 ANTLR 版本中有一种方法可以做到这一点(虽然我记不太清了,但我认为有一种语法允许在特定规则下不隐藏它们)。我不知道如何在 ANTLR4 中做到这一点,是否可以在不使用特定于语言的操作的情况下完成。

我考虑过以某种方式使 WS 成为解析器规则,但我认为这不是正确的方法...
(显然我不想把 WS? 放在语法的任何地方)

是否有一种(最好是独立于语言的)方法来 (a) 确保特定点有空格,或 (b) 确保特定点的两端在该通道上不“接触”,或 ( c) 根据它以某种方式出现的规则有选择地选择 WS 通道(默认或隐藏)?
我猜 (c) 是不可能的,并且 (a|b) 需要依赖于语言的操作,除非我遗漏了什么?

我认为没有任何方法可以让解析器规则在 HIDDEN 通道(或 0 以外的任何通道)上评估标记。也许我遗漏了一些东西,但我找不到它。

我无法从您的摘录中回答的问题是,如果您的符号前没有 WS,是否还有另一个解析器规则应该匹配。这有很大的不同。

我倾向于将成功的语法视为将生成代表解释输入流的正确方法的解析树的语法。恕我直言,太多人试图将“所有规则”编码到语法中,从而使语法复杂化。如果您有一个解释输入的唯一方法的准确树(无论它是否“无错误”),那么您可以编写一个 Listener(可能是一个访问者)来访问该树并执行其他规则的编辑(例如“ 'sign' 前面有很多空格)。

这完成了几件事:

  • 使语法更简单
  • 允许您在错误消息中非常具体。

ANTLR 非常擅长处理错误消息,因为它有什么信息,但是“期望 WS,但看到 '+'”,只是不如“符号必须跟在空格后面”那样好的错误消息。

考虑到这一点,您可以进入侦听器中的隐藏频道。

首先,您需要使令牌流在您的监听器中可用:

class TestListener extends TestBaseListener {
    BufferedTokenStream tokens;

    public TestListener(BufferedTokenStream tokens) {
        this.tokens = tokens;
    }
    // ...
}

并将其传递给监听器的构造函数:

  CommonTokenStream tokens = new CommonTokenStream(lexer);
  TestListener listener = new TestListener(tokens);

然后,在 enter* 方法中,无论您需要将其添加到什么规则,您都可以执行如下操作:

    const int HIDDEN = 1;

    @Override
    public void enterAddSub(TxlParser.AddSubContext ctx) {
        Token op = ctx.op;
        int opIndex = op.getTokenIndex();
        List<Token> hiddenChannel = tokens.getHiddenTokensToLeft(opIndex, HIDDEN);
        if (hiddenChannel != null) {
            Token ws = hiddenChannel.get(0);
            if (ws != null) {
                System.out.println("Found Ws (" + ws.getText() + ")");
            }
        } else {
            System.out.println("There was no WS to the left of the operator");
            // Your code here to add an error 
        }
    }

供参考,这是我在 AddSub

中使用的规则
expr:
    expr (MULT | DIV) expr                          # MulDiv
    | lExpr = expr op = (PLUS | MINUS) rExpr = expr # AddSub
    // ...
    ;

如果我 运行 输入 a=x+y 我得到:

There was no WS to the left of the operator

但是输入 a=x +y 给我:

Found Ws ( )