微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决Antlr 生产产生一堆单元素数组

我的语法有效,但树中有一堆元素是单元素数组,我真的不明白为什么。我尝试阅读有关访问者的信息,但我很确定“问题”出在语法及其冗长上。这里有什么跳出来的吗?或者也许我只是错误地访问了事物。在下面的示例中,我不会对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 会将其作为该类型的数组/列表提供。

示例:

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() 访问器:


    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,它必然是上下文中的 fnArg 列表:

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

你能做的事情真的不多(或者应该做的就是不要把它放在一个列表中,可以有一个或多个。

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

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。