[英]Fix shift/reduce conflict in bison grammar
我為解析文本文件編寫的野牛語法給了我10個移位/減少沖突的機會。 parser.output文件對我的幫助不足。 該文件為我提供了以下信息:
State 38 conflicts: 5 shift/reduce
State 40 conflicts: 4 shift/reduce
State 46 conflicts: 1 shift/reduce
Grammar
0 $accept: session $end
1 session: boot_data section_start
2 boot_data: head_desc statement head_desc head_desc
3 head_desc: OPEN_TOK BOOT_TOK statement CLOSE_TOK
4 | OPEN_TOK statement CLOSE_TOK
5 statement: word
6 | statement word
7 word: IDENTIFIER
8 | TIME
9 | DATE
10 | DATA
11 section_start: section_details
12 | section_start section_details
13 | section_start head_desc section_details
14 $@1: /* empty */
15 section_details: $@1 section_head section_body section_end
16 section_head: START_TOK head_desc START_TOK time_stamp
17 time_stamp: statement DATE TIME
18 section_body: log_entry
19 | section_body log_entry
20 log_entry: entry_prefix body_statements
21 | entry_prefix TIME body_statements
22 body_statements: statement
23 | head_desc
24 entry_prefix: ERROR_TOK
25 | WARN_TOK
26 | /* empty */
27 $@2: /* empty */
28 section_end: END_TOK statement $@2 END_TOK head_desc
state 38
8 word: TIME .
21 log_entry: entry_prefix TIME . body_statements
OPEN_TOK shift, and go to state 1
TIME shift, and go to state 6
DATE shift, and go to state 7
DATA shift, and go to state 8
IDENTIFIER shift, and go to state 9
OPEN_TOK [reduce using rule 8 (word)]
TIME [reduce using rule 8 (word)]
DATE [reduce using rule 8 (word)]
DATA [reduce using rule 8 (word)]
IDENTIFIER [reduce using rule 8 (word)]
$default reduce using rule 8 (word)
head_desc go to state 39
statement go to state 40
word go to state 11
body_statements go to state 45
state 39
23 body_statements: head_desc .
$default reduce using rule 23 (body_statements)
state 40
6 statement: statement . word
22 body_statements: statement .
TIME shift, and go to state 6
DATE shift, and go to state 7
DATA shift, and go to state 8
IDENTIFIER shift, and go to state 9
TIME [reduce using rule 22 (body_statements)]
DATE [reduce using rule 22 (body_statements)]
DATA [reduce using rule 22 (body_statements)]
IDENTIFIER [reduce using rule 22 (body_statements)]
$default reduce using rule 22 (body_statements)
word go to state 19
state 46
9 word: DATE .
17 time_stamp: statement DATE . TIME
TIME shift, and go to state 48
TIME [reduce using rule 9 (word)]
$default reduce using rule 9 (word)
我的語法的等效部分是:
statement : word
{
printf("WORD\n");
$$=$1;
}
|statement word
{
printf("STATEMENTS\n");
$$=$1;
printf("STATEMENT VALUE== %s\n\n",$$);
}
;
word : IDENTIFIER
{
printf("IDENTIFIER\n");
$$=$1;
}
|TIME
{
printf("TIME\n");
$$=$1;
}
|DATE
{
printf("DATE\n");
$$=$1;
}
|DATA
{
}
;
section_start : section_details
{
printf("SINGLE SECTIONS\n");
}
|section_start section_details
{
printf("MULTIPLE SECTIONS\n");
}
|section_start head_desc section_details
;
section_details :
{
fprintf(fp,"\n%d:\n",set_count);
}
section_head section_body section_end
{
printf("SECTION DETAILS\n");
set_count++;
}
;
section_head : START_TOK head_desc START_TOK statement time_stamp
{
printf("SECTION HEAD...\n\n%s===\n\n%s\n",$2,$4);
fprintf(fp,"%s\n",$4);
}
;
time_stamp : DATET TIME
{
}
;
section_body :log_entry
{
}
|section_body log_entry
{
}
;
log_entry : entry_prefix body_statements
{
}
|entry_prefix TIME body_statements
{
}
;
body_statements : statement
{
}
|head_desc
{
}
;
請幫我解決這個問題。
謝謝
yacc / bison解析器中的沖突意味着語法不是LALR(1)-通常意味着某些東西是模棱兩可的或者需要超過1個超前標記。 默認解決方案是選擇始終轉換為減少,或選擇始終減少第一個規則(以減少/減少沖突),這將導致解析器識別語法描述的語言的子集。 這可能行不行,在語法不明確的情況下,“子集”實際上就是整個語言,並且默認分辨率會消除歧義。 但是,對於需要更多前瞻性的情況以及某些模棱兩可的情況,默認分辨率將導致無法解析該語言中的某些內容。
為了找出在任何給定的沖突中出了什么問題, .output
文件通常為您提供所需的一切。 在您的情況下,您有3個有沖突的狀態-通常,單個狀態中的沖突都是單個相關問題。
state 38
8 word: TIME .
21 log_entry: entry_prefix TIME . body_statements
此沖突是log_entry
和body_statements
規則之間的歧義:
log_entry: entry_prefix body_statements
| entry_prefix TIME body_statements
body_statements
可以是一個或多個TIME
/ DATE
/ DATA
/ IDENTIFIER
標記的序列,因此當您輸入帶有(例如) entry_prefix TIME DATA
的輸入時,它可以是第一個以TIME DATA
作為body_statements
log_entry
規則,也可以是第二個log_entry
DATA
作為body_statements
log_entry
規則。
在這種情況下,將有利於第二個規則的默認分辨率(轉移治療TIME
為一部分log_statements
而不是減少它word
是一個部分body_statements
),並會導致一個“子集”這是整個語言-唯一會遺漏的解析是模棱兩可的。 這與某些語言中出現的“懸空”類似,在這種情況下,默認移位可能完全滿足您的要求。
為了消除這種沖突,最簡單的方法就是擺脫log_entry: entry_prefix TIME body_statements
規則。 這與默認分辨率有相反的效果-現在TIME將始終被視為BODY的一部分。 問題在於,如果您想以不同的方式對待體內初始TIME
的情況,現在沒有單獨的規則可以減少。 如果您需要做一些特別的事情,則可以檢查以TIME
開頭的主體的動作。
state 40
6 statement: statement . word
22 body_statements: statement .
這是另一個模棱兩可的問題,這一次是section_body
,它無法分辨一個log_entry
在哪里結束而另一個在哪里開始。 section_body
由一個或多個log_entries
,每個entry_prefix
是一個entry_prefix
后跟一個body_statements
。 如上所述的body_statements
可以是一個或多個word
令牌,而entry_prefix
可以為空。 因此,如果您的section_body
只是word
標記的序列,則可以將其解析為單個log_entry
(不帶entry_prefix
)或一系列log_entry
規則,每個規則都沒有entry_prefix
。 缺省情況下, body_statement
分辨率的轉換將有利於在減少body_statement
之前將盡可能多的標記放入單個statement
,因此將其解析為單個log_entry
,這可能是可以的。
為了消除這種沖突,您需要重構語法。 由於任何的尾部statement
中log_entry
可能是另一個log_entry
沒有entry_prefix
和statement
為body_statements
,你很可能需要消除的情況下(這是默認的沖突解決一樣)。 假設您已通過刪除第二條log_entry
規則解決了先前的沖突,則首先log_entry
對log_entry的log_entry
以使有問題的情況成為其自己的規則:
log_entry: ERROR_TOK body_statements
| WARN_TOK body_statements
| head_desc
initial_log_entry: log_entry
| statements
現在更改section_body
規則,以便它僅對第一個規則使用分割規則:
section_body: initial_log_entry
| section_body log_entry
沖突消失了。
state 46
9 word: DATE .
17 time_stamp: statement DATE . TIME
此沖突是一個前瞻性歧義問題-由於DATE
和TIME
令牌可能出現在statement
,因此在解析time_stamp
它無法判斷該statement
在何處結束以及終端DATE
/ TIME
開始。 默認分辨率將導致將任何DATE TIME
對視為time_stamp
的結尾。 現在,因為time_stamp
只出現在年底section_head
只是之前section_body
和section_body
可以用開始statement
,這可能是罰款為好。
因此,很可能情況是語法中的所有沖突都是可忽略的,甚至這樣做是可取的,因為這比重寫語法來消除它們更簡單。 另一方面,沖突的存在使修改語法變得更加困難,因為在任何時候,您都需要重新檢查所有沖突以確保它們仍然是良性的。
“沖突的默認解決方案”和“狀態中的默認操作”存在一個令人困惑的問題。 這兩個默認值彼此無關-第一個是yacc / bison在構建解析器時做出的決定,第二個是解析器在運行時做出的決定。 因此,當您在輸出文件中有一個狀態時,例如:
state 46
9 word: DATE .
17 time_stamp: statement DATE . TIME
TIME shift, and go to state 48
TIME [reduce using rule 9 (word)]
$default reduce using rule 9 (word)
這是在告訴您,野牛已確定從該狀態開始的可能動作是轉移到狀態48或減少規則9。僅當先行標記為TIME
,才可以執行shift動作,而對於許多先行標記,則可以進行reduce動作。包括時間。 因此,為了最小化表的大小,而不是枚舉reduce動作的所有可能的下一個標記,它只說$default
,這意味着只要沒有先前的動作與lookahead標記匹配,解析器就會執行reduce動作。 $default
操作將始終是該狀態中的最后一個操作。
因此,此狀態下的解析器代碼將沿動作列表運行,直到找到與前瞻標記匹配的動作並執行該動作為止。 僅包括TIME [reduce
...]操作,目的是清楚表明在此狀態下存在沖突,並且當先行TIME
為TIME
時,野牛通過禁止操作reduce
解決了該沖突。 因此,為此狀態構造的實際操作表將只有一個操作(在令牌TIME
),后跟一個默認操作(在任何令牌上減少)。
請注意,盡管並非所有word
在一個word
后都是合法的,但它仍然會對任何令牌進行歸約。 這是因為即使下一個令牌是非法的,由於還原操作不會從輸入中讀取它(它仍然是超前的),所以稍后的狀態(可能是多個默認還原操作之后)將看到(並報告)錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.