วิธีที่ดีที่สุดในการสร้างอาร์เรย์ (หรือคอลเลกชัน) ของประเภทข้อมูลที่แตกต่างกันในการดำเนินการทางความหมายของ 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
;

มีบางสิ่ง / ปัญหาที่นี่ แต่ก่อนอื่น พยายามสร้าง parser เช่น Bison นี้บอกว่า $3 ในการผลิตแบบเรียกซ้ำและ $1 ในกรณีฐาน /terminal ไม่มีประเภทที่ประกาศ เท่าที่ฉันเห็น จริงๆ แล้วพวกเขาได้ประกาศประเภทไว้แล้ว เป็น LiteralType และด้วยเหตุนี้ อาจเป็นสตริงหรือ ints หรือ floats ซึ่งควรตั้งค่าโดยอัตโนมัติโดยปล่อยเทอร์มินัลสุดท้ายให้ว่างไว้ (เนื่องจากสิ่งแรกที่ฉันทำคือทำให้ประเภทชัดเจนโดยเลือกสิ่งที่เหมาะสมจากสหภาพสากล) .

ประการที่สอง ฉันไม่คาดหวังว่า Bison จะบ่นว่าไม่มีประเภทที่ประกาศไว้ แต่มีข้อขัดแย้งหรือความคลุมเครือเนื่องจากฉันกำหนดให้กับ $$->value แต่ $2,$1 สามารถมีค่าใดก็ได้จากสามค่าที่เป็นไปได้ (ขึ้นอยู่กับว่าสหภาพใด สมาชิกได้รับมอบหมายให้ทำหน้าที่ในการผลิตตามลำดับ) สำหรับสถานการณ์นี้ ฉันกำหนดให้สมาชิกค่าใน ListElementType struct เป็นสหภาพ ฉันคิดว่าแทนที่จะพยายามใช้ประโยชน์จากความจริงที่ว่าสมาชิกคนแรกของ struct จะอยู่ในตำแหน่ง "ป้ายกำกับ" ของที่อยู่ struct เองบวกกับที่สมาชิกของสหภาพแรงงานทั้งหมดเริ่มต้นจากที่อยู่ 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 แยกกันโดยที่สิ่งเดียวที่เปลี่ยนแปลงคือ nonterminal ทางด้านขวามือนั้นตรงกับประเภทองค์ประกอบเฉพาะในรายการ เช่น:

//----------------------------------------------------
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 สามารถมีค่าใด ๆ จากสามค่าจากนั้นลองดึงการกำหนดสหภาพแบบไม่มีประเภท

หรือวิธีการทั้งหมดผิดและมีวิธีที่ดีกว่า?


person SaldaVonSchwartz    schedule 16.10.2012    source แหล่งที่มา


คำตอบ (3)


โดยทั่วไปสิ่งที่คุณต้องการทำคือการมีแท็กประเภทในประเภทรายการที่แตกต่างกันของคุณ:

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; }
person Chris Dodd    schedule 16.10.2012
comment
ฉันเห็นด้วย. แต่นอกเหนือจากที่คุณให้ enum แทนประเภทถ่านและฟังก์ชันคอนสตรัคเตอร์ของฉัน นี่ไม่ใช่สิ่งที่ฉันทำในคำตอบของฉันใช่ไหม - person SaldaVonSchwartz; 17.10.2012

ฉันลงเอยด้วยการใช้แนวทางนี้

  1. โปรดทราบว่าแทนที่จะให้การผลิตองค์ประกอบ (เช่น LiteralType nonterminal) ลดลงเป็นสหภาพ แต่จะลดเป็นโครงสร้างที่มีสหภาพและสมาชิกประเภท สมาชิกประเภทคือวิธีการบอกประเภทของแต่ละองค์ประกอบที่เก็บไว้ในคอลเลกชัน
  2. โปรดทราบว่าโครงสร้าง ListType มีตัวชี้ void* ไปยังองค์ประกอบ ในตัวอย่างที่วางแผนไว้นี้ สมาชิกของประเภทโครงสร้าง ElementType น่าจะเพียงพอแล้ว อย่างไรก็ตาม ฉันกำลังสร้างองค์ประกอบให้เป็นตัวชี้ทั่วไปเพื่อใช้โครงสร้างเดียวกันในการจัดเก็บ ในทางกลับกัน รายการประกาศที่ประกอบด้วยรายการองค์ประกอบ

% รหัสต้องการ { องค์ประกอบโครงสร้าง typedef {

%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';    
}
;
person SaldaVonSchwartz    schedule 16.10.2012
comment
อย่าเล่นกับ yylval ในตัวแยกวิเคราะห์ คุณต้องการเพิ่ม $1->charptr_type ฟรี แน่นอนว่า malloc ของคุณสำหรับสตริงในการจัดการ STRING ควรใช้ strdup แต่ก็ไร้ประโยชน์โดยสิ้นเชิง เพียงแค่ส่งตัวชี้: $$->charptrVal = $1 คือทั้งหมดที่คุณต้องการ - person akim; 17.10.2012
comment
ฉันไม่รู้ว่ามันถูกเก็บไว้แบบนั้น มีเหตุผล. ขอบคุณ! - person SaldaVonSchwartz; 17.10.2012

ฉันคิดว่าคุณพลาดความจริงที่ว่า 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
;
person akim    schedule 16.10.2012
comment
ฉันเห็นประเด็นของคุณ แต่ถ้าฉันดูตัวอย่างของคุณ Bison ยังคงให้ typeclashes สำหรับการกำหนดค่าเริ่มต้นของ LiteralType: ‹value› != ‹charptr_type› ‹value› != ‹int_type› ‹value› != ‹float_type› - person SaldaVonSchwartz; 16.10.2012
comment
นอกจากนี้แม้ว่าฉันจะเพิ่มตัวเอง $$.intVal = $1;,etc ในแต่ละการกระทำสำหรับตัวอักษร แต่วัวกระทิงก็ไม่บ่นอีกต่อไป แต่ตอนนี้ gcc บ่นว่า ListelementType เป็นประเภทที่ไม่รู้จัก - person SaldaVonSchwartz; 16.10.2012
comment
ไม่ มันไม่รายงานการปะทะกัน คุณอาจละทิ้งส่วนที่ฉันระบุ %type <value> LiteralType และคุณยังทิ้งการเปลี่ยนแปลงทั้งหมด %token <value> ไว้ด้วย ดังนั้น จริงๆ ด้วยอย่างอื่นนอกเหนือจากที่ผมตอบไป คุณอาจได้ผลลัพธ์ที่แตกต่างออกไป :) - person akim; 16.10.2012
comment
1- เวอร์ชันของฉัน (2.6) ยังคงต้องการให้ฉันทำงานที่ได้รับมอบหมายอย่างชัดเจนในการผลิต LiteralType มันจะไม่มีมันหากไม่มีพวกเขาและรายงานการปะทะกัน 2 - การเพิ่มสิ่งนี้ช่วยแก้ไขปัญหาได้จริง 3 - ประเภทที่ไม่รู้จัก ListElementType เกิดจากวัวกระทิง 2.3 (เมื่อฉันเปลี่ยนเป็น 2.6 ที่หายไป) 4 - ฉันไม่เห็นว่าฉันกำลังเขียนโค้ดสหภาพสองครั้งที่ไหน 5 - เหตุใดคุณจึงประกาศ STRING, REAL และ INTEGER ให้เป็นประเภทเดียวกันกับ LiteralType ซึ่งไม่ใช่ 3 อันนั้นเป็นโทเค็น / เทอร์มินัล ไม่ใช่เทอร์มินัลและมีประเภทเฉพาะ - person SaldaVonSchwartz; 16.10.2012
comment
6 - ปัญหาหนึ่งที่ฉันเห็นในแนวทางของคุณคือ ไม่มีวิธีใดที่จะบอกได้ในภายหลังว่าคุณมีองค์ประกอบประเภทใดในคอลเลกชัน เนื่องจากคอลเลกชันอาจแตกต่างกัน - person SaldaVonSchwartz; 16.10.2012
comment
สวัสดีซัลดา 1 - ไม่จริง มันไม่บ่น ฉันดาวน์โหลดและติดตั้งเวอร์ชัน 2.6 แล้ว และไฟล์ที่ฉันให้มานั้นคอมไพล์ได้หมดจด 4 - คุณกำลังระบุสมาชิกของสหภาพของคุณสองครั้ง: ครั้งแรกในคำจำกัดความของค่าสหภาพ จากนั้นใน %union ฉันแนะนำวิธีการหลีกเลี่ยงความซ้ำซ้อนนี้ 5 - รหัสของคุณเลือกใช้ประเภทสหภาพเดียวในประเภทรายการของคุณ ดังนั้นฉันจึงดำเนินการต่อไป: มีประเภทเดียวกัน: ค่าสหภาพ 6 - ปัญหาไม่ได้อยู่ในแนวทาง แต่ในประเภทรายการของคุณที่ควรใช้ enum บางอย่างเพื่อบันทึกสิ่งที่เก็บไว้ - person akim; 17.10.2012