antlr4中如何获取多个命令?

How to obtain multiple commands in antlr4?

我正在 Java 中开发一个 shell 实现,我正在使用 antlr4 来解析语法。我想一个一个解析输入的命令,并将它们存储在一个arraylist中,稍后我将在其中执行命令。

例如,“echo hello; echo world”的输入应该 return 两个 Call 对象的数组列表。如果有帮助,Call 对象代表一个简单的命令。

但是,visitChildren 方法的 return 值正在被最新解析的命令覆盖。如何解析一个命令,将其添加到我的数组列表,然后继续解析下一个命令等等?

CommandConverter.java

package parse;

import java.util.ArrayList;

import app.ApplicationFactory;
import shell.ShellGrammarBaseVisitor;
import shell.ShellGrammarParser;

public class CommandConverter extends ShellGrammarBaseVisitor<Command> {
    
    ApplicationFactory appFactory = new ApplicationFactory();

    @Override
    public Command visitCommands(ShellGrammarParser.CommandsContext ctx) {
        //ArrayList<Command> commands = new ArrayList<>();
        return visitChildren(ctx);
    }
    @Override
    public Command visitAtomicCommand(ShellGrammarParser.AtomicCommandContext ctx) {
        int childCount = ctx.getChildCount();
        String appName = ctx.getChild(0).getText();
        ArrayList<String> appArgs = new ArrayList<>();
        if(childCount > 1) {
            for (int i = 1; i < childCount; i++) {
                appArgs.add(ctx.getChild(i).getText());
            }
        }
        return new Call(appFactory.getApplication(appName), appArgs);
    }
}

ShellGrammar.g4(部分)

grammar ShellGrammar;

/*
 * Parser Rules
 */

commands : atomicCommand (';' atomicCommand )*

atomicCommand : NONSPECIAL (value)*;

value : (NONSPECIAL | DOUBLEQUOTED | SINGLEQUOTED);

/*
 * Lexer Rules
 */
NONSPECIAL : ~['";\r\n\t ]+;
DOUBLEQUOTED : '"' (~'"')* '"';
SINGLEQUOTED : '\'' (~'\'')* '\'';

WHITESPACE : [\r\n\t ]+ -> skip ;

如果你大声读出来,这个定义,没有多大意义:

public Command visitCommands(ShellGrammarParser.CommandsContext ctx) {

您正在访问 commands,但预计 return 会出现 Command??

在这种简单的情况下,最好记住访问者不需要处理访问 EVERY 上下文类型(这种情况很少见,适用于完整的语法,因为找到适用于所有节点的通用 return 类型被证明是困难的)。您可以只覆盖您实际打算访问的上下文类型的方法。

在您的情况下,commands 是您的“top-level”规则,因此您从解析(使用 parser.commands(); 调用)返回的解析树节点将是 CommandsContext

你可以做一些类似的事情:(其中 pt 是你从解析中得到的节点。)

  for (AtomicCommandContext child : pt.children) {
   // handle the child command
   // could be adding it to a list
   // could be just processing the command 
  }

命令的好处是您负责为每个 child 调用访问者。 visitChildren 只是一种方便的方法,它遍历所有 children 为您调用访问者。

Visitor class 中有一些方法,如果您想让访问者 return 通过调用 visitChildren() 成为 List<Command>,您可以重写这些方法,但上面的代码对于您的要求可能是最简单的。