Antlr 生产产生一堆单元素数组

Antlr production producing a bunch of single element arrays

我的语法没有问题,但我在树中有一堆元素是单元素数组,我不太明白为什么。我尝试阅读有关访问者的信息,但我很确定“问题”出在语法上,也许是它的冗长。这里有什么东西跳出来吗?或者也许我只是错误地访问了事物。在下面的示例中,我不对 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') | '\' ('"' | '\'))* '"';

这次输入:

require balance(assets:cash) + balance(assets:earnings) > AMT

生成以下单元素数组:

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],
    );
  }

但我认为问题出在包含数学运算的部分,我认为是 visitRequire:

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

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

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

Antlr 不会将单个项目“解包”为不在数组中。 (这仅在无类型语言或允许联合类型的语言中有效,并且使用起来会很痛苦,因为您总是必须检查您是否有“事物”或“事物”列表)

任何时候“相同类型的事物”可以 在匹配规则时存在不止一次,ANTLR 将把它作为该类型的 Array/List 提供。

示例:

journal: IDENT COLON IDENT;

有 2 个 IDENT 标记,因此可以通过上下文访问这些类型的列表

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

public List<TerminalNode> IDENT() { return getTokens(TxlParser.IDENT); }

你的两个例子是“JournalReference”,所以这将解释如何获得一个列表(如果你使用 ctx.IDENT()ctx.getChild(n) 方法)。

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

journal: j1=IDENT COLON j2=IDENT;

我已经为每个 IDENT 命名,所以我为它们获得了单独的访问器(除了 IDENT() 访问器 returns 一个列表:


    public static class JournalContext extends ParserRuleContext {
        public Token j1;
        public Token j2;
        public TerminalNode COLON() { return getToken(TxlParser.COLON, 0); }
        public List<TerminalNode> IDENT() { return getTokens(TxlParser.IDENT); }

通过标签,您可以使用 cox.j1cox.j2 来获取单个令牌。 (当然,您会根据您的用例为它们命名)。

因为 FunctionCall 替代 expr 规则使用 args 规则

args: (fnArg (',' fnArg)*)?;

并且该规则可以有多个 fnArg,在上下文中它必然是 fnArgs 的列表:

    public static class ArgsContext extends ParserRuleContext {
        public List<FnArgContext> fnArg() {
            return getRuleContexts(FnArgContext.class);
        }

你真的没有多少可以做的(或者应该想做的是不在列表中,可以有一个或多个。

由于您提供的代码没有显示您正在编写输出的位置,因此很难比这更具体。

您的 visitMath(cox) 示例也有点令人困惑,因为 math 不是您语法中的规则,因此它不会存在于访问者界面中。

我建议您仔细查看为您生成的 *Context 类。他们将提供比 getChild(n) 更易于使用和阅读的实用方法。 getChild(n) 是晦涩的,因为你必须返回参考规则并努力计算规则成员以确定要获得哪个 child,而且它也非常脆弱,因为 n将随着对语法的任何修改而改变。 (维护者或未来的您会喜欢使用实用方法。)