2010-02-24 106 views
1

我想在使用C作为输出语言的C++项目中使用ANTLR v3.2生成的分析器。理论上,生成的解析器可以编译为C++,但是在处理解析器操作中的C++类型时遇到问题。下面是一个C++头文件中定义了几个类型的,我想在解析器使用方法:在ANTLR生成的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++后端?

+0

对于那些正在计划回答的人,您可以首先查看http://www.antlr.org/pipermail/antlr-interest/2010-February/037764.html,看它是否已经提交给Chris。 – 2010-02-24 21:42:50

+0

我相信我不知道为什么在另一个论坛上要求得到一个赞誉。我很乐意在此提出/接受任何有用的答案。 – 2010-02-24 23:50:19

+0

我会这样说,如果它是我的。虽然我曾经考虑过低调,但我没有提出你的问题,因为你的问题是非常好的:细节层次高,问题明确等。但是,我*明白downvote:在多个论坛中提问**而不提这个在其中任何一个**是国际海事组织不好的做法。我的意思是,为什么不直接在这里发布一个链接到ANTLR邮件列表上的帖子?这样,其他人就可以看到已经回答了什么,并且不用花时间复制在这里已经在其他地方建议的(精心制作的)答案。 – 2010-02-25 06:51:23

回答

3

下面是我发现这个问题的解决方案。问题的关键是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属性,你需要一个无参数的构造和operator=,可以采取NULL

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

我知道这很哈克,但我暂时与#4一起去。恰巧我的Expr类对这些操作有相当自然的定义。

P.S. On the ANTLR list,C后端的维护者提示说这个问题可能在未来的版本中得到解决。