1

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

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

然后在野牛我有:

%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 和基本 /terminal 案例中的 $1 没有声明的类型。正如我所看到的,它们实际上确实有声明类型。它们是 LiteralType,因此,可以是字符串、整数或浮点数,应该通过将最后一个终端产品留空来自动设置(假设我做的第一件事是通过从全局联合中选择适当的来明确它们的类型) .

其次,我不希望 Bison 抱怨没有声明的类型,而是因为我分配给 $$->value 而存在冲突或歧义,但 $2,$1 可能具有三个可能值中的任何一个(取决于哪个联合成员被分配到各自的作品中)。对于这种情况,我将 ListElementType 结构中的值成员设为联合。我在想而不是试图利用结构的第一个成员将位于结构地址本身的“标签”位置以及联合的成员都从联合的 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 案例设置了联合。我认为这没有必要,但如果我错了,请有人纠正我。而且,我也尝试了无类型联合赋值,但仍然出现相同的错误:$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 可以具有三个值中的任何一个,然后尝试提取无类型联合分配?

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

4

3 回答 3

1

我最终选择了这种方法。

  1. 请注意,不是让 Element 产生式(例如 LiteralType 非终结符)简化为联合,而是简化为具有联合和类型成员的结构。类型成员是告诉集合中存储的每个元素的类型的方法。
  2. 另请注意,ListType 结构有一个指向元素的 void* 指针。在这个人为的示例中,ElementType 结构类型的成员就足够了。但是,我将元素设置为通用指针,以使用相同的结构依次存储由元素列表组成的声明列表。

%code 需要 { 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';    
}
;
于 2012-10-16T19:40:20.563 回答
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; }
于 2012-10-16T21:11:39.530 回答
0

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

此外,您编码的联合值的两倍,这似乎没有必要:

%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
;
于 2012-10-16T07:45:06.443 回答