【问题标题】:How to obtain multiple commands in antlr4?antlr4中如何获取多个命令?
【发布时间】:2022-01-17 05:32:16
【问题描述】:

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

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

但是,visitChildren 方法的返回值被最新解析的命令覆盖。如何解析一个命令,将其添加到我的arraylist,然后继续解析下一个命令等等?

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 ;

【问题讨论】:

    标签: java parsing antlr4 visitor-pattern


    【解决方案1】:

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

    public Command visitCommands(ShellGrammarParser.CommandsContext ctx) {
    

    您正在访问commands,但期待Command 作为回报??

    在这种简单的情况下,最好记住访问者不需要处理访问 EVERY 上下文类型(这对于完整的语法来说是相当罕见的,因为它可以找到一个共同的返回适用于所有节点的类型被证明是困难的)。您可以只覆盖您实际打算访问的 Context 类型的方法。

    在您的情况下,commands 是您的“顶级”规则,因此您从解析中返回的解析树节点(使用 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 
      }
    

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

    如果您想让访问者通过调用 visitChildren() 返回 List&lt;Command&gt;,则可以覆盖 Visitor 类中的一些方法,但上面的代码对于您的要求可能是最简单的。

    【讨论】:

    • 哦,我应该说Command是一个接口,我有一些实现它的类,例如Call、Pipe和Seq。 Seq 将保存一个 ArrayList 的命令。
    • 我刚刚尝试遍历节点的所有子节点,这正是我所需要的。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-15
    • 2014-07-09
    • 2014-08-29
    • 2013-12-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多