Antlr生产产生一系列单元素阵列

Antlr生产产生一系列单元素阵列,antlr,antlr4,Antlr,Antlr4,我的语法是有效的,但我在树中有一堆元素是单元素数组,我真的不明白为什么。我试着阅读关于访客的信息,但我很确定“问题”在于语法,也许还有它的冗长。这里有东西跳出来吗?也许我只是看错了东西。在下面的示例中,我不会对visitFnArgs或visitArgs做出反应,只会调用visitFunctionCall。函数参数和语句之类的东西有时似乎被包装在单元素数组中 grammar Txl; root: program; // High level language program: stmt (NE

我的语法是有效的,但我在树中有一堆元素是单元素数组,我真的不明白为什么。我试着阅读关于访客的信息,但我很确定“问题”在于语法,也许还有它的冗长。这里有东西跳出来吗?也许我只是看错了东西。在下面的示例中,我不会对visitFnArgs或visitArgs做出反应,只会调用visitFunctionCall。函数参数和语句之类的东西有时似乎被包装在单元素数组中

grammar Txl;

root: program;

// High level language
program: stmt (NEWLINE stmt)* NEWLINE? EOF # Statement
    ;

stmt: require    # Condition
    | entry      # CreateEntry
    | assignment # Assign
    ;

require: REQUIRE valueExpression;
entry: (CREDIT | DEBIT) journal valueExpression (IF valueExpression)? (LPAREN 'id:' valueExpression RPAREN)?;
assignment: IDENT ASSIGN valueExpression;

journal: IDENT COLON IDENT;

valueExpression: expr # Expression;

expr: expr (MULT | DIV) expr         # MulDiv
    | expr (PLUS | MINUS) expr       # AddSub
    | expr MOD expr                  # Mod
    | expr POW expr                  # Pow
    | MINUS expr                     # Negative
    | expr AND expr                  # And
    | expr OR expr                   # Or
    | NOT expr                       # Not
    | expr EQ expr                   # Equality
    | expr NEQ expr                  # Inequality
    | expr (LTE | GTE) expr          # CmpEqual
    | expr (LT | GT) expr            # Cmp
    | expr QUESTION expr COLON expr  # Ternary
    | LPAREN expr RPAREN             # Parens
    | NUMBER                         # NumberLiteral
    | IDENT LPAREN args RPAREN       # FunctionCall
    | IDENT                          # Identifier
    | STRING_LITERAL                 # StringLiteral
    ;

fnArg: expr | journal;
args: (fnArg (',' fnArg)*)?;

// Reserved words
CREDIT: 'credit';
DEBIT: 'debit';
IF: 'if';
REQUIRE: 'require';

// Operators
MULT: '*';
DIV: '/';
MINUS: '-';
PLUS: '+';
POW: '^';
MOD: '%';
LPAREN: '(';
RPAREN: ')';
LBRACE: '[';
RBRACE: ']';
COMMA: ',';
EQ: '==';
NEQ: '!=';
GTE: '>=';
LTE: '<=';
GT: '>';
LT: '<';
ASSIGN: '=';
QUESTION: '?';
COLON: ':';
AND: 'and';
OR: 'or';
NOT: 'not';
HASH: '#';
NEWLINE : [\r\n];
WS: [ \t] + -> skip;

// Entities
NUMBER: ('0' .. '9') + ('.' ('0' .. '9') +)?;
IDENT: [a-zA-Z]+[0-9a-zA-Z]*;
EXTID: [a-zA-Z0-9-]+;
STRING_LITERAL : '"' (~('"' | '\\' | '\r' | '\n') | '\\' ('"' | '\\'))* '"';
生成以下单元素数组:

SINGLE ELEMENT INSTRUCTION MathOperation (>)
SINGLE ELEMENT INSTRUCTION JournalReference { identifier: 'assets:cash' }
SINGLE ELEMENT INSTRUCTION JournalReference { identifier: 'assets:earnings' }
我不知道我的部分问题是我没有正确地参观东西。这是我的数学访客:

  visitMath(ctx) {
    const visited = this.visitChildren(ctx);
    return new MathOperation(
      visited[0],
      ctx.getChild(1).getText(),
      visited[2],
    );
  }
但我假设问题出在包含数学运算的东西上,我认为这是VisiteRequest:

  visitRequire(ctx) {
    return new Condition(this.visitExpression(ctx.getChild(1)));
  }

或者在visitValueExpression或visitCondition中,它们在我的访问者中没有被覆盖。

非常简短的回答:单元素数组没有什么问题。如果一个事物只有一个实例可以存在多次,那么它必须是一个数组(或列表),并且该列表将只有一个项目,如果有多少个项目的话

Antlr不会“展开”不在阵列中的单个项目。(这只在非类型化语言或允许联合类型的语言中有效,使用起来会很麻烦,因为您总是需要检查您是否有“东西”或“东西”列表)

任何时候,当匹配规则时,“同一类型的事物”可以存在不止一次,ANTLR将使其作为该类型的数组/列表可用

示例:

journal: IDENT COLON IDENT;
有2个
IDENT
标记,因此可以通过上下文将其作为这些类型的列表进行访问

(在Java中,我不确定您使用的是哪种语言)

public List IDENT(){return getTokens(TxlParser.IDENT);}
您的两个示例是“JournalReference”,因此这将解释如何获取列表(如果您使用
ctx.IDENT()
ctx.getChild(n)
方法)

如果我将日记规则更改为:

journal: j1=IDENT COLON j2=IDENT;
我为每个
IDENT
指定了名称,因此我为它们获取了单独的访问器(除了返回列表的IDENT()访问器外:


公共静态类JournalContext扩展了ParserRuleContext{
公共令牌j1;
公共令牌j2;
public TerminalNode COLON(){return getToken(TxlParser.COLON,0);}
public List IDENT(){return getTokens(TxlParser.IDENT);}
有了这些标签,您可以使用
cox.j1
cox.j2
来获取单个令牌(当然,您可以根据您的用例来命名它们)

因为
expr
规则的
FunctionCall
替代方案使用
args
规则

args: (fnArg (',' fnArg)*)?;
该规则可以有多个fnArg,它必须是上下文中
fnArg
s的列表:

public静态类ArgsContext扩展了ParserRuleContext{
公共列表fnArg(){
返回getRuleContext(FnArgContext.class);
}
你真的没有什么可以做的(或者应该做的),如果列表中没有,可以有一个或多个

由于您呈现的代码中没有任何一个显示您在哪里编写输出,因此要比这更具体有点困难

您的
visitMath(cox)
示例也有点复杂,因为
math
不是语法中的规则,因此它不会出现在访问者界面中


我建议您仔细查看为您生成的
*上下文
类。它们提供的实用方法将比
getChild(n)
getChild(n)更易于使用和阅读
是不明确的,因为您必须重新引用规则并努力计算规则成员以确定要获取哪个子级,而且它也非常脆弱,因为
n
将随着语法的任何修改而改变。(维护人员或未来的您将欣赏使用实用工具方法。)

“函数参数和语句之类的东西有时似乎被包装在单元素数组中。”您能澄清一下吗?
FunctionCallContext.args()
应该返回类型为
ArgsContext
的对象,而不是数组。对于
ConditionContext.require()
(返回
RequireContext
)也一样等。只有当一个非终端在同一个产品中多次出现,或者被
*
等量化时,才应该涉及列表。所以我不明白为什么在您提到的地方会遇到列表(或数组)。
ArgsContext.fnArg()
会给您一个列表(参数),但这正是你想要的,对吗?更新了帖子,提供了示例输入和更多关于单元素数组位置的详细信息。lhs/rhs似乎没有改变任何东西,尽管它确实看起来很有用。我已经修改了答案,以更具体地给出修改后的问题。visitMath只是一个“通用”问题“从每一个与数学相关的访问者调用。我应该更详细地说明这一点。您描述的大多数数组似乎都是“正确的”。但我不明白为什么函数规则本身最终会出现在一个数组中,或者出现在数学规则中。这就好像上面还有一些其他规则,我没有覆盖其访问方法,然后是默认的“visitChildren”返回一个数组。如果这是真的,那么我想问题是我应该修复它,还是可以在访问者方法中处理它。您使用的是什么语言?在Java中,访问者被定义为泛型类(公共类TxlBaseVisitor扩展AbstractParseTreeVisitor实现TxlVisitor)其中T是所有访问者方法的返回类型。因此,是的,所有visit*()方法都返回相同的类型,但在实现访问者(派生类)时,您可以确定这一点。所有返回值都必须是类的,但如果需要,您可以定义基类并返回该类的子类
args: (fnArg (',' fnArg)*)?;