【问题标题】:Using C++ types in an ANTLR-generated C parser在 ANTLR 生成的 C 解析器中使用 C++ 类型
【发布时间】:2010-02-24 04:19:37
【问题描述】:

我正在尝试在使用 C 作为输出语言的 C++ 项目中使用由 ANTLR v3.2 生成的解析器。理论上,生成的解析器可以编译为 C++,但我在处理解析器操作中的 C++ 类型时遇到了麻烦。这是一个 C++ 头文件,它定义了我想在解析器中使用的几种类型:

/* expr.h */
enum Kind {
  PLUS,
  MINUS
};

class Expr { // stub
};

class ExprFactory {
public:
  Expr mkExpr(Kind kind, Expr op1, Expr op2);
  Expr mkInt(std::string n);  
};

这是一个简单的解析器定义:

/* Expr.g */
grammar Expr;

options {
  language = 'C'; 
}

@parser::includes {
  #include "expr.h"
}

@members {
  ExprFactory *exprFactory;
}

start returns [Expr expr]
  : e = expression EOF { $expr = e; }
  ;

expression returns [Expr e]
  : TOK_LPAREN k=builtinOp op1=expression op2=expression TOK_RPAREN
    { e = exprFactory->mkExpr(k,op1,op2); }
  | INTEGER { e = exprFactory->mkInt((char*)$INTEGER.text->chars); }
  ;

builtinOp returns [Kind kind]
  : TOK_PLUS { kind = PLUS; }
  | TOK_MINUS { kind = MINUS; }
  ;

TOK_PLUS : '+';
TOK_MINUS : '-';
TOK_LPAREN : '(';
TOK_RPAREN : ')';
INTEGER : ('0'..'9')+;

语法通过 ANTLR 运行得很好。当我尝试编译 ExprParser.c 时,出现类似

的错误
  1. conversion from ‘long int’ to non-scalar type ‘Expr’ requested
  2. no match for ‘operator=’ in ‘e = 0l’
  3. invalid conversion from ‘long int’ to ‘Kind’

在每种情况下,语句都是将ExprKind 值初始化为NULL

我可以通过将所有内容更改为Expr* 来解决Expr 的问题。这是可行的,尽管并不理想。但是为像Kind 这样的简单枚举传递指针似乎很荒谬。我发现的一个丑陋的解决方法是创建第二个返回值,它将Kind 值推送到一个结构中并将初始化抑制为NULL。即,builtinOp 变为

builtinOp returns [Kind kind, bool dummy]
  : TOK_PLUS { $kind = PLUS; }
  | TOK_MINUS { $kind = MINUS; }
  ;

第一个 expression 替代变为

TOK_LPAREN k=builtinOp op1=expression op2=expression TOK_RPAREN
    { e = exprFactory->mkExpr(k.kind,*op1,*op2); }

必须有更好的方法来做事吗?我是否缺少 C 语言后端的配置选项?有没有另一种方法来安排我的语法以避免这种尴尬?有没有我可以使用的纯 C++ 后端?

【问题讨论】:

  • 准备回答的朋友可以先查看antlr.org/pipermail/antlr-interest/2010-February/037764.html,看看是不是还没向克里斯提议。
  • 我确定我不知道为什么在另一个论坛上提问会引起反对票。我很乐意在这里投票/接受任何有用的答案。
  • 如果是我的话,我会这么说。虽然我考虑过投反对票,但我没有考虑,因为您的问题是一个非常好的问题:详细程度高、问题明确等。但我确实理解投反对票:在多个论坛中提问在其中任何一个中都没有提及这一点是 IMO 的错误做法。我的意思是,为什么不在这里发布一个链接到您在 ANTLR 邮件列表上的帖子?这样,其他人就可以看到已经回答的内容,而不必花时间在这里复制已经在其他地方提出的(详细的)答案。
  • 如果我在任一论坛上收到任何实质性答复,我想我会更清楚地看到您的观点。事实上,我要以第二种方式打破礼仪并自我回答。

标签: c++ c parsing antlr antlr3


【解决方案1】:

以下是我找到的解决此问题的方法。问题的症结在于 ANTLR 想要初始化所有返回值和属性。对于非原始类型,ANTLR 只是假设它可以使用NULL 进行初始化。因此,例如,上面的 expression 规则将被翻译成类似

static Expr
expression(pExprParser ctx)
{   
    Expr e = NULL; // Declare and init return value
    Kind k; // declare attributes
    Expr op1, op2;
    k = NULL; // init attributes
    op1 = NULL;
    op2 = NULL;
    ...
}

在我看来,选择如下:

  1. 给出可以合法初始化为NULL的原始类型值。例如,使用Expr*Kind* 而不是ExprKind

  2. 如上所述,使用“虚拟”技巧将值推送到不会被初始化的结构中。

  3. 使用引用参数而不是返回值。例如,

    builtinOp[Kind& kind]
      : TOK_PLUS { kind = PLUS; }
      | TOK_MINUS { kind = MINUS; }
      ;
    
  4. 使用使上述声明和初始化合法的操作来增加用作值类型的类。即,对于 Expr 返回值,您需要一个可以采用 NULL 的构造函数:

    Expr(long int n);
    

    对于Expr 属性,您需要一个无参数构造函数和一个可以采用NULLoperator=

    Expr();
    Expr operator=(long int n);
    

我知道这很 hacky,但我暂时选择 #4。碰巧我的Expr 类对这些操作有一个相当自然的定义。

附: On the ANTLR list,C 后端的维护者暗示这个问题可能会在未来的版本中得到解决。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-13
    • 1970-01-01
    • 1970-01-01
    • 2010-10-15
    • 2012-08-05
    • 2022-01-11
    • 2023-03-19
    • 1970-01-01
    相关资源
    最近更新 更多