背景
某项目编译系统关键字, 在lex中被强制识别为某 token,
后来发现, 而此关键字 也会出现在其他语法中, 但是被lex强制识别为了 token, 导致编译失败!
如:
某系统的语法识别, 将文本中的 uint32,int32, uint64, int64, ipv4. ipv6. time, mac 等识别为数据类型,
ipv4 一开始被lex强制定义为某数据的数据解码器,
后期发现在非数据解码器的地方, 需要将 ipv4 作为一种数据类型。
结果就是, ipv4 无法出现在预期的地方, 总是被lex识别为 数据解码器。
类似的 ipv6, time 关键字也是一样的现象。
Bison 冲突
bison 发现了4处 冲突1
2
3
4[root@localhost src]# bison -v --report=all -d sdt_grammar.y
[root@localhost src]# bison -v -d sdt_grammar.y # 或者
sdt_grammar.y: warning: 4 shift/reduce conflicts [-Wconflicts-sr]
[root@localhost src]#
–report=all 可以得到更多信息
xxx.output
打开 sdt_grammar.output
这里看到了完整的 BNF 语法描述1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61Grammar
0 $accept: sdt $end
1 sdt: mode rule_header payload_matcher rule_action
2 | mode rule_header payload_matcher '$' rule_body rule_action
3 | COMMENT_LINE
4 mode: IDENTIFIER
5 payload_matcher: %empty
6 | '#' payload_and_expression
7 payload_and_expression: payload_match_expression
8 | payload_match_expression ',' payload_and_expression
9 payload_match_expression: payload_expression
10 | '!' payload_expression
11 | NUMBER ':' payload_expression
12 | NUMBER ':' '!' payload_expression
13 | IDENTIFIER '=' NUMBER
14 payload_expression: payload_expression_atomic
15 | payload_expression payload_expression_atomic
16 | payload_expression payload_expression_atomic_not
17 payload_expression_atomic_not: '!' IDENTIFIER
18 | '!' hex_payload
19 payload_expression_atomic: IDENTIFIER
20 | hex_payload
21 hex_payload: HEX_TEXT
22 rule_body: expression ';'
23 | logical_and_expression ';'
24 rule_action: %empty
25 | rule_action IDENTIFIER LP RP ';'
26 | rule_action ACTION LP rule_action_args RP ';'
27 | rule_action ACTION_survive LP action_survive_args RP ';'
28 rule_action_args: rule_action_arg
29 | rule_action_arg ',' rule_action_args
30 rule_action_arg: NUMBER
31 | FLOAT
32 | IDENTIFIER
33 | QUOTE_TEXT
34 | dyn_mask_hex
35 dyn_mask_hex: HEX_TEXT
36 action_survive_args: IDENTIFIER RELATIONAL_OPERATOR NUMBER
37 | action_survive_args ',' IDENTIFIER RELATIONAL_OPERATOR NUMBER
38 rule_header: IDENTIFIER '@' host_ip ':' port dir host_ip ':' port
39 | NUMBER '@' host_ip ':' port dir host_ip ':' port
40 | '*' '@' host_ip ':' port dir host_ip ':' port
...
状态解读:
1 |
|
解读 State 0 :
- State 0 表示当前状态没有任何输入的状态,
- . 代表在当前输入的 光! 标! 位! 置!
- 移进指示: 如果遇到一个 COMMENT_LINE 转移到 [状态1]
- 移进指示: 如果遇到一个 IDENTIFIER 转移到 [状态2]
- 归约指示: 如果遇到一个 sdt 转移到 [状态3]
- 归约指示: 如果遇到一个 mode 转移到 [状态4]
解读 State 1 :
- State 1 表示当前已经得到 COMMENT_LINE,
- . 代表在当前输入的 光! 标! 位! 置!
- 归约指示: 默认 转移到 [语法规则3]
解读 State 2 :
- State 2 表示当前已经得到 IDENTIFIER,
- . 代表在当前输入的 光! 标! 位! 置!
- 归约指示: 默认 转移到 [语法规则4]
冲突
在文件的开始处, 可以看到 有4处状态存在问题
1 |
|
定位到 第1处的冲突1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21State 30
9 payload_match_expression: payload_expression . [$end, IDENTIFIER, ACTION_survive, ACTION, '$', ',']
15 payload_expression: payload_expression . payload_expression_atomic
16 | payload_expression . payload_expression_atomic_not
17 payload_expression_atomic_not: . '!' IDENTIFIER
18 | . '!' hex_payload
19 payload_expression_atomic: . IDENTIFIER
20 | . hex_payload
21 hex_payload: . HEX_TEXT
IDENTIFIER shift, and go to state 44
HEX_TEXT shift, and go to state 25
'!' shift, and go to state 47
IDENTIFIER [reduce using rule 9 (payload_match_expression)]
$default reduce using rule 9 (payload_match_expression)
payload_expression_atomic_not go to state 48
payload_expression_atomic go to state 49
hex_payload go to state 32
解读 State 30 :
- State 30 表示当前已经得到 payload_expression,
.代表在当前输入的 光! 标! 位! 置!
移进指示: 如果遇到一个 IDENTIFIER 转移到 [状态44]
- 移进指示: 如果遇到一个 HEX_TEXT 转移到 [状态25]
- 移进指示: 如果遇到一个 ‘!’ 转移到 [状态47]
- 归约指示: 如果遇到一个 IDENTIFIER 转移到 [语法规则9]
- 归约指示: 默认什么也没有 转移到 [语法规则9]
- 归约指示: 如果遇到一个 payload_expression_atomic_not 转移到 [语法规则48]
- 归约指示: 如果遇到一个 payload_expression_atomic 转移到 [语法规则49]
- 归约指示: 如果遇到一个 hex_payload 转移到 [语法规则32]
冲突原因:
在 State 30 , 位于已经得到 payload_expression的状态,
再输入一个 IDENTIFIER 会得到 歧义.
此处的 IDENTIFIER 对应着两个 转移,
一个 是 payload_match_expression 的向下移进模式 payload_expression_atomic 内的 IDENTIFIER1
2
3
4
59 payload_match_expression: payload_expression 向下
14 payload_expression: payload_expression_atomic 向下
19 payload_expression_atomic: IDENTIFIER 此处
注意: 这里是 payload_match_expression 向内展开, 最终得到依然是 payload_match_expression.
一个 是 payload_match_expression 的向上归约模式 payload_expression_atomic 内的 IDENTIFIER1
2
3
4
5
6
7
8
9
10
11
121 sdt: mode rule_header payload_matcher rule_action # 向上 邻接 rule_action
5 payload_matcher: %empty # 向上
6 | '#' payload_and_expression
7 payload_and_expression: payload_match_expression # 向上
9 payload_match_expression: payload_expression # 向上
24 rule_action: %empty # rule_action内部存在 IDENTIFIER 可以匹配 -- 冲突
25 | rule_action IDENTIFIER LP RP ';'
注意: 这里是 payload_match_expression 内上收缩, 最终转换为 payload_matcher.
在 payload_matcher 后面的 rule_action,
rule_action 内展开 会出现一个需要 IDENTIFIER的模式.
冲突本质:
在 State 30 : 位于已经得到 payload_expression 的状态,
payload_expression . 输入光标处于 payload_expression 模式后面,
如果下一个 token 是 IDENTIFIER, 存在二义性, 既可以向下移进, 又可以向上规约。
bison 发出冲突警告!
IDENTIFIER 的 移入/归约冲突
解决:
IDENTIFIER 的 移进/规约 只能二选一,
要么 保留 IDENTIFIER 的 移进模式。
要么 保留 IDENTIFIER 的 规约模式。
参考2
1 |
|
%empty [CR, ‘C’, ‘A’, ‘B’] 代表 后面的可选 token 是 CR, ‘C’, ‘A’, ‘B’
在 State 13 光标位于 ‘.’时,
当出现 ‘C’ 是应该 shift, 还是 reduce ?
参考3
1 |
|
%empty [$end, ‘C’, ‘A’, ‘B’] 代表 后面的可选 token 是 $end, ‘C’, ‘A’, ‘B’
在 State 13 光标位于 ‘.’时,
当出现 ‘C’ 是应该 shift, 还是 reduce ?