代码之家  ›  专栏  ›  技术社区  ›  Joseph Larson

antlr访问者:高效查找保留字

  •  0
  • Joseph Larson  · 技术社区  · 3 年前

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

    10 20 mul

    这将在堆栈上推送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语句来计算这是哪个运算符,这太疯狂了。必须有更好的方法来做到这一点。我在上下文中找不到映射到可以在hashmap表中使用的内容的字段。

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

    有更好的方法吗?

    0 回复  |  直到 3 年前
        1
  •  2
  •   Mike Cargal    3 年前

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

    如果解析器规则的一部分只能是一个具有单个类型的东西,那么通常默认的访问器就可以了。但是,如果你有几个基本上是“同一件事”的替代方案,或者你在解析器规则中不止一次有相同的子规则引用,并且想区分它们,那么给它们命名是非常方便的。(一旦你开始这样做,并看到对Context类的影响,它们提供价值的地方就会变得非常明显。)

    此外,当规则有多个顶级替代方案时,给每个规则一个标签是非常方便的。这将导致ANTLR为每个备选方案生成一个单独的Context类,而不是将每个备选方案中的所有内容都转储到一个类中。

    (编一些东西只是为了得到一个有效的编译)

    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';