在bison / yacc语义动作中构建异构数据类型的数组(或集合)的最佳方法

时间:2012-10-16 06:32:58

标签: c bison yacc

将其视为更多的元素集合,这些元素不一定都是相同的类型。我有以下代码:

// The struct I'll use inside Bison to dynamically create collections:
typedef struct ListElementType {
    union value {
        int intVal;
        float floatVal;
        char* charptrVal;
    } value;

    struct ListElementType* next;
} ListElementType;

然后在Bison我有:

%union
{
    int int_type;
    char char_type;
    float float_type;
    char* charptr_type;
    ListElementType* listElementType;
}
//----------------------------------------------------
%token <charptr_type> STRING
%token <int_type> INTEGER
%token <float_type> REAL
%type<listElementType> ElementList
//----------------------------------------------------
//----------------------------------------------------
ElementList
: ElementList ',' LiteralType
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = $1;
    $$->value = $3;
}

| LiteralType
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = 0;
    $$->value = $1;
}
;
//----------------------------------------------------
LiteralType
: STRING
| INTEGER
| REAL
;

这里有一些事情/问题。但首先,尝试生成像Bison这样的解析器,在递归生成中为3美元,在基本/终端情况下为1美元没有声明类型。在我看来,它们确实已经声明了类型。它们是LiteralType,因此可以是字符串或整数或浮点数,应该通过将最后一个终端产品留空来自动设置(假设我做的第一件事是通过从全局联合中选择适当的方式使其类型显式)

其次,我不希望Bison抱怨没有声明的类型,而是因为我分配给$$ - &gt;值但是$ 2,$ 1可能有三种可能值中的任何一种,因此存在冲突或模糊性(取决于在各自的制作中分配了哪个工会成员)。对于这种情况,我使ListElementType结构中的value成员成为一个联合。我正在考虑而不是试图利用这样一个事实:struct的第一个成员将位于struct address本身的“label”位置,而且union的成员也都在union的mem地址上开始尝试直接分配而不管类型。类似(void )$$ = $ 2的东西,无论是2美元。

所以,我改为代码:

//----------------------------------------------------
ElementList
: ElementList ',' LiteralType
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = $1;
    *$$ = (void*)$3;
}

| LiteralType
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = 0;
    $$->value = $1;
}
;
//----------------------------------------------------
LiteralType
: STRING
{
    $<charptr_type>$ = $1;
}

| INTEGER
{
    $<int_type>$ = $1;
}

| REAL
{
    $<float_type>$ = $1;
}

;

现在我已经为INT,REAL,STRING情况明确设置了联合。我认为没有必要,但如果我错了,有人会纠正我。 AND,我也尝试了无类型联合赋值但仍然是相同的错误:$ 3和$ 1没有声明的类型。

所以我的想法,问题:

我必须创建单独的StringList,IntList和RealList产品,其中唯一改变的是右侧非终结符合列表中的特定类型的元素,如下所示:

//----------------------------------------------------
ElementList
: IntElementList
| RealElementList
;

IntElementList
: IntElementList ',' INTEGER
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = $1;
    $$->intVal = $3;
}

| INTEGER
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = 0;
    $$->intVal = $1;
}

RealElementList
: RealElementList ',' REAL
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = $1;
    $$->floatVal = $3;
}

| REAL
{ 
    $$ = malloc(sizeof(listElementType));
    $$->next = 0;
    $$->floatVal = $1;
}

;

或者有没有办法说明LiteralType可以有三个值中的任何一个,然后尝试拉出无类型联合赋值?

或者整个方法是错误的还有更好的方法吗?

3 个答案:

答案 0 :(得分:1)

我最终选择了这种方法。

  1. 请注意,不是将元素产生(从LiteralType非终结符号)缩减为并集,而是将其缩减为具有union和类型成员的结构。类型成员是告诉集合中存储的每个元素的类型的方法。
  2. 另请注意,ListType结构有一个指向element的void *指针。在这个人为的例子中,ElementType结构类型的成员就足够了。但是,我正在使元素成为一个通用指针,使用相同的结构来存储由元素列表组成的声明列表。
  3. %代码需要     {             typedef struct Element {

    %code requires {
        typedef struct Element {
            union {
                int intVal;
                float floatVal;
                char* charptrVal;            
            };
    
        char type;
    
        } ElementType;
    
        typedef struct ListType {
            void* element;
            struct ListType* next;
    
        } ListType;
    }
    
    %union
    {
        int int_type;
        char char_type;
        float float_type;
        char* charptr_type;
        ListType* ListType;
        ElementType* ElementType;
    }
    
    
    
    %token <charptr_type> KEYWORD
    %token <charptr_type> ID
    %token <charptr_type> STRING
    %token <int_type> INTEGER
    %token <float_type> REAL
    %token END 0
    
    
    %type<ElementType> Element
    %type<ListType> ElementList
    
    //----------------------------------------------------
    ElementList
    : Element ',' ElementList
    {
        $$ = malloc(sizeof(ListType));
        $$->element = (void*)$1;
        $$->next = $3;
    }
    
    | Element
    {
        $$ = malloc(sizeof(ListType));
        $$->element = (void*)$1;
        $$->next = NULL;
    }
    ;
    //----------------------------------------------------
    Element
    : STRING
    {
        char* aString = malloc(sizeof(char)*strlen($1)+1);
        strcpy(aString, $1);
        free(yylval.charptr_type);
    
        $$ = malloc(sizeof(ElementType));
        $$->charptrVal = aString;
        $$->type = 's';
    }
    | INTEGER
    {
        $$ = malloc(sizeof(ElementType));
        $$->intVal = $1;
        $$->type = 'i';
    }
    
    | REAL
    {
        $$ = malloc(sizeof(ElementType));
        $$->floatVal = $1;
        $$->type = 'f';    
    }
    ;
    

答案 1 :(得分:1)

通常,您要做的是在异构列表类型中使用类型标记:

typedef enum ListElementType { INTEGER, REAL, STRING } ListElementType
typedef struct ListElement {
    ListElementType  type;
    union {
        int intVal;
        float floatVal;
        char* charptrVal;
    } value;
    struct ListElement* next;
} ListElement;

然后,无论何时创建ListElement,都要适当地设置type字段。稍后,您可以查看type字段以查看它是什么。

然后您的野牛代码变为:

%union
{
    int int_type;
    char char_type;
    float float_type;
    char* charptr_type;
    ListElement* listElement;
    struct { ListElement *head, *tail } list;
}
//----------------------------------------------------
%token <charptr_type> STRING
%token <int_type> INTEGER
%token <float_type> REAL
%type<list> ElementList
%type<listElement> LiteralType
//----------------------------------------------------
%%
//----------------------------------------------------
ElementList
: ElementList ',' LiteralType
    { $$.head = $1.head;
      $$.tail = $1.tail->next = $3; }
| LiteralType
    { $$.head = $$.tail = $1; }
;
//----------------------------------------------------
LiteralType
: STRING  { ($$ = NewListElement(STRING))->value.charptrVal = $1; }
| INTEGER { ($$ = NewListElement(INTEGER))->value.intVal = $1; }
| REAL    { ($$ = NewListElement(REAL))->value.floatVal = $1; }
;
%%
ListElement *NewListElement(ListElementType type) {
    ListElement *rv = malloc(sizeof(ListElement));
    rv->type = type;
    rv->next = 0;
    return rv; }

答案 2 :(得分:0)

我认为你错过了Bison不试图实现完整C类型检查的事实。由于您为STRING和LiteralType指定了不同的类型名称,因此报告其默认操作($$ = $ 1)从(bison-)类型检查的角度做了一些奇怪的事情。如果您确实想使用默认分配,只需给它们相同的类型(在您的情况下为值)。

此外,您正在编码两次union值,这似乎没有必要:

%code requires
{
  typedef struct ListElementType {
    union value {
      int intVal;
      float floatVal;
      char* charptrVal;
    } value;

    struct ListElementType* next;
  } ListElementType;
}

%union
 {
   union value value;
   ListElementType* list;
 };

%token <value> STRING INTEGER REAL
%type <value> LiteralType 
%type <list> ElementList
%%
ElementList
: ElementList ',' LiteralType
{ 
  $$ = malloc(sizeof($$));
  $$->next = $1;
  $$->value = $3;
}
| LiteralType
{ 
  $$ = malloc(sizeof($$));
  $$->next = 0;
  $$->value = $1;
}
;
//----------------------------------------------------
LiteralType
: STRING
| INTEGER
| REAL
;