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>
,您可以重写这些方法,但上面的代码对于您的要求可能是最简单的。
我正在 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>
,您可以重写这些方法,但上面的代码对于您的要求可能是最简单的。