![](/img/trans.png)
[英]What are the requirements/limitations of the error recovery productions in YACC/Bison?
[英]Dropping a token in Yacc/Bison when an error is matched in the rhs production
我正在編寫一個簡單的計算器,其中表達式可以簡化為語句或語句列表。 如果錯誤的表達式觸發了語法錯誤,我會嘗試使用生產規則來捕獲它,然后通過不給規則任何操作來忽略它。 但是,我相信這仍然會簡化為stmt_list
,盡管它不是一個有效的聲明。
有沒有辦法讓它簡單地忽略與錯誤匹配的令牌並防止它們被減少和以后使用?
下面的代碼匹配error ';'
並將其簡化為stmt_list
。 然后它將嘗試使用有效表達式減少該stmt_list
,但由於從未調用過第一個產生式,這將觸發 memory 異常。 我的目標是在匹配錯誤時讓 Bison 字面上什么都不做,這樣以后的有效表達式可以成為stmt_list
的第一個縮減。
stmt_list:
expr ';' {
// Allocate memory for statement array
stmts = (float*)malloc(24 * sizeof(float));
// Assign pointer for array
$$ = stmts;
// At pointer, assign statement value
*$$ = $1;
// Increment pointer (for next job)
$$++;
}
| stmt_list expr ';' {
$$ = $1;
*$$ = $2;
$$++;
}
| error ';' { } // Do nothing (ignore bad stmt)
| stmt_list error ';' { } // Do nothing (ignore bad stmt)
;
如果您沒有為規則提供任何操作,bison/yacc 會提供默認操作$$ = $1
。
事實上,你並沒有提供任何行動。 您正在提供一個不執行任何操作的顯式操作。 碰巧的是,如果您使用 C 模板,解析器仍將執行默認操作。 在其他模板中,未將值分配給$$
的操作可能會在解析器生成期間引發警告。 但它肯定不會修改您的數據結構以使操作無效。 它不知道那是什么意思。 如果你知道,你應該把它寫成動作:-)。
我不是 100% 清楚為什么要將評估結果保存在固定大小的動態分配數組中。 您沒有嘗試檢測數組何時填滿,因此您完全有可能最終溢出分配並覆蓋隨機 memory。 此外,使用這樣的全局變量通常不是一個好主意,因為它會阻止您同時構建多個列表。 (例如,如果你想實現 function 調用,因為函數的 arguments 也是表達式列表。)
總的來說,最好將擴展表達式列表的實現放在一個簡單的 API 中,它在其他地方實現。 在這里,我假設你已經做到了; 為了具體起見,我將假設以下 API (盡管這只是一個示例):
/* The list header structure, which contain all the information
* necessary to use the list. The forward declaration makes it
* possible to use pointers to ExprList objects without having to
* expose its implementation details.
*/
typedef struct ExprList ExprList;
/* Creates a new empty expression-list and returns a pointer to its header. */
ExprList* expr_list_create(void);
/* Resizes the expression list to the supplied size. If the list
* currently has fewer elements, new elements with default values are
* added at the end. If it currently has more elements, the excess
* ones are discarded. Calling with size 0 empties the list (but
* doesn't delete it).
*/
int expr_list_resize(ExprList* list, int new_length);
/* Frees all storage associated with the expression list. The
* argument must have been created with expr_list_create, and its
* value must not be used again after this function returns.
*/
void expr_list_free(ExprList* list);
/* Adds one element to the end of the expression-list.
* I kept the float datatype for expression values, although I
* strongly believe that its not ideal. But an advantage of using an
* API like this is that it is easier to change.
*/
void expr_list_push(ExprList* list, float value);
/* Returns the number of elements in the expression-list. */
int expr_list_len(ExprList* list);
/* Returns the address of the element in the expression list
* with the given index. If the index is out of range, behaviour
* is undefined; a debugging implementation will report an error.
*/
float* expr_list_at(ExprList* list, int index);
使用 API,我們可以重寫有效表達式的產生式:
stmt_list: expr ';' { $$ = expr_list_create();
expr_list_push($$, $1);
}
| stmt_list expr ';' { $$ = $1;
expr_list_push($$, $1);
}
現在針對錯誤情況。 你有兩個錯誤規則; 一個在錯誤位於列表開頭時觸發,另一個在處理一個或多個(可能是錯誤的)表達式后遇到錯誤時觸發。 這兩個都是stmt_list
的產品,因此它們必須具有與stmt_list
相同的值類型( ExprList*
)。 因此,當產生語法錯誤時,他們必須做任何你認為合適的事情。
第一個,當錯誤在列表的開頭時,只需要創建一個空列表。 很難看出它還能做什么。
stmt_list: error ';' { $$ = expr_list_create(); }
在我看來,當列表具有至少一個成功計算的值之后檢測到錯誤時,其他錯誤操作至少有兩種選擇。 一種可能性是丟棄錯誤的項目,使列表中的 rest 完好無損。 這僅需要默認操作:
stmt_list: stmt_list error ';'
(當然,如果您願意,您可以顯式添加操作{ $$ = $1; }
。)
另一種可能性是清空整個列表,以便從下一個元素開始:
stmt_list: stmt_list error ';' { $$ = $1;
expr_list_resize($$, 0);
}
毫無疑問,還有其他可能性。 正如我所說,野牛無法弄清楚您的意圖是什么(我也無法弄清楚,真的)。 你必須實現你想要的任何行為。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.