antlr visitor:有效查找保留字

antlr visitor: lookup of reserved words efficiently

我正在学习 Antlr。在这一点上,作为我学习过程的一部分,我正在编写一种基于堆栈的小语言——想想 PostScript 或 Forth。一种 RPN 语言。例如:

10 20 多

这会将 10 和 20 压入堆栈,然后执行乘法运算,弹出两个值,将它们相乘,然后压入 200。我正在使用访问者模式。我发现自己写了一些有点疯狂的代码。必须有更好的方法。

这是我的 WaveParser.g4 文件的一部分:

any_operator:
    value_operator |
    stack_operator |
    logic_operator |
    math_operator |
    flow_control_operator;

value_operator:
  BIND | DEF
  ;

stack_operator:
  DUP |
  EXCH |
  POP |
  COPY |
  ROLL |
  INDEX |
  CLEAR |
  COUNT
  ;

BIND 只是 bind 关键字等。所以我的访问者有这个方法:

antlrcpp::Any WaveVisitor::visitAny_operator(Parser::Any_operatorContext *ctx);

现在这里是我正在编写的非常难看的代码的地方,这导致了问题。

Value::Operator op = Value::Operator::NO_OP;

WaveParser::Value_operatorContext * valueOp = ctx->value_operator();
WaveParser::Stack_operatorContext * stackOp = ctx->stack_operator();
WaveParser::Logic_operatorContext * logicOp = ctx->logic_operator();
WaveParser::Math_operatorContext * mathOp = ctx->math_operator();
WaveParser::Flow_control_operatorContext * flowOp = ctx->flow_control_operator();

if (valueOp) {
    if (valueOp->BIND()) {
        op = Value::Operator::BIND;
    }
    else if (valueOp->DEF()) {
        op = Value::Operator::DEF;
    }
}
else if (stackOp) {
    if (stackOp->DUP()) {
        op = Value::Operator::DUP;
    }
    ...
}
...

我支持大约 50 个运算符,我要用这一系列的 if 语句来确定这是哪个运算符,这太疯狂了。必须有更好的方法来做到这一点。我在上下文中找不到映射到我可以在哈希图中使用的内容的字段 table.

我不知道是否应该让我的每个操作员都有一个单独的规则,并在我的访问者中使用相应的方法,或者我还缺少什么。

有没有更好的方法?

使用 ANTLR,标记规则的组件以及高级替代项通常非常有帮助。

如果解析器规则的一部分只能是单一类型的事物,通常默认访问器就可以了。但是,如果您有多个备选方案,这些备选方案本质上是“同一事物”的备选方案,或者您可能多次在解析器规则中引用相同的子规则并希望区分它们,那么给它们命名会非常方便。 (一旦您开始这样做并看到对 Context classes 的影响,它们提供价值的地方就会变得非常明显。)

此外,当规则有多个顶级备选方案时,给每个备选方案贴上标签会非常方便。这将导致 ANTLR 为每个备选方案生成一个单独的上下文 class,而不是将每个备选方案中的所有内容转储到单个 class.

(为了获得有效的编译而编造一些东西)

grammar WaveParser
    ;

any_operator
    : value_operator        # val_op
    | stack_operator        # stack_op
    | logic_operator        # logic_op
    | math_operator         # math_op
    | flow_control_operator # flow_op
    ;

value_operator: op = ( BIND | DEF);

stack_operator
    : op = (
        DUP
        | EXCH
        | POP
        | COPY
        | ROLL
        | INDEX
        | CLEAR
        | COUNT
    )
    ;

logic_operator:        op = (AND | OR);
math_operator:         op = (ADD | SUB);
flow_control_operator: op = (FLOW1 | FLOW2);

AND: 'and';
OR:  'or';
ADD: '+';
SUB: '-';

FLOW1: '>>';
FLOW2: '<<';

BIND:  'bind';
DEF:   'def';
DUP:   'dup';
EXCH:  'exch';
POP:   'pop';
COPY:  'copy';
ROLL:  'roll';
INDEX: 'index';
CLEAR: 'clear';
COUNT: 'count';