我需要帮助从这个语法中删除间接左递归:
A -> B (sB)*
| dAd
| z
B -> <id>
| sB
| A
所以你可以从A-> B-> A ...移动而不消耗任何字符。
我试图通过几种不同的方式修复它,但由于这一点(sB)*
而继续遇到问题我不确定我做错了什么,或者语法是否错误。
答案 0 :(得分:0)
有趣。我看不到这样做的机械方法。这是指定语言的方式,还是通过其他一些简化来最终得到它?无论如何,针对特定问题的解决方案是在左递归部分中“内联”B:
A - &gt; (&lt; id&gt; | sB | dAd | z)(sB)*
B - &gt; &LT; ID&GT; | sB |甲
基本思想是用递归部分中的非递归项替换并将递归项移到最后。
答案 1 :(得分:0)
从
开始A -> B (sB)* | dAd | z
B -> <id> | sB | A
替代
A -> (<id> | sB | A) (sB)* | dAd | z
定义
C -> (sB)*
替代
A -> (<id> | sB | A) C | dAd | z
因子
A -> <id> C | sBC | AC | dAd | z
定义
D -> <id> C | sBC | dAd | z
所以
A -> D | AC
删除左递归
A -> D (C)*
代替C和D
A -> (<id> (sB)* | sB(sB)* | dAd | z) (sB)**
自x** = x*
A -> (<id> (sB)* | sB(sB)* | dAd | z) (sB)*
自x*x* = x*
A -> (<id> | sB | dAd | z) (sB)*
B -> <id> | sB | A
与斯里尼瓦萨的结果相同。
在看到@ Rymoid的回答后添加了编辑。
此时左移递已被删除,所以我们完成了。但正如@Rymoid指出的那样,语法仍然含糊不清,因此不是LL(1)。下面我将尝试处理歧义,但不是找到LL(1)语法。
一个问题是,自A =>* sB
以来,选择sB | A
不明确且不需要。让我们从删除该选择开始。我们有
A -> (<id> | sB | dAd | z) (sB)*
B -> <id> | A
同样A =>* <id>
,因此选择<id> | A
不明确且不需要。我们有
A -> (<id> | sB | dAd | z) (sB)*
B -> A
然后我们不再需要B
。
A -> (<id> | sA | dAd | z) (sA)*
剩下的问题是,由于s
位于A
的后续集合中,因此无法通过一个前瞻标记来判断是否留在(sA)*
循环或退出。
原始问题没有要求LL(1)语法,但由于帖子被标记为[JavaCC],我们可能会认为所需的是与JavaCC一起使用的。这与LL(1)并不完全相同,尽管LL(1)意味着语法可以很好地与JavaCC一起使用。
我假设在A
定义之外A
的所有用途绝对不会被s
所遵循。具体来说,我假设(仅)还有一个生产S -> A <EOF>
,而S
是非终结的开始。但真正重要的是,除了A
当前定义中的循环之外,您永远不会有s
后跟A
。
我们有
S -> A <EOF>
A -> (<id> | sA | dAd | z) (sA)*
当你有一个含糊不清的语法但想要消除歧义时,问自己的问题是:在模糊的情况下我想要哪个解析?两个答案是:&#34;尽可能长时间地保持循环。&#34;和&#34;尽快跳出循环。&#34; (其他答案是可能的,但不太可能。)
&#34;尽可能长时间地保持循环&#34;
这是JavaCC的默认值,因此无需更改语法。它可能会生成警告。可以在循环开始时用LOOKAHEAD( <s> )
来抑制该警告。
&#34;尽快退出循环&#34;
制作A
的两个版本。 A0
永远不会跟s
。 A1
后面跟着s
。 (事实上它后面跟着第一个s
,所以不需要(sA)*
部分。这个选择对应于尽快退出循环。)
S -> A0 <EOF>
A0 -> (<id> | sA0 | dA0d | z) [ s (A1s)* A0 ]
A1 -> <id> | sA1 | dA0d | z
我非常确定这是明确的,A0
定义的语言与A
相同。它不是LL(1),JavaCC会给出一个应该注意的警告。
为了使它适合JavaCC,我们可以在循环开始时添加LOOKAHEAD( A1 <s> )
的语法预测。
答案 2 :(得分:0)
在开始之前,让我们为您的作品编号,以便我们可以参考:
1: A -> B (s B)*
2: A -> d A d
3: A -> z
4: B -> <id>
5: B -> s B
6: B -> A
由于您试图消除左递归,我只能假设您尝试应用LL解析。但是,这个语法含糊不清,因此它不能成为LL(1)语法。例如,短语zszsz
可以(最左边)以多种方式从A
派生:
A ->+ B s B (1)
->+ A s B (6)
->+ z s B (3)
->+ z s B s B (1)
->+ z s z s z (6, 3, 6, 3)
A ->+ B s B (1)
->+ A s B (6)
->+ B s B s B (1)
->+ A s B s B (6)
->+ z s B s B (3)
->+ z s z s z (6, 3, 6, 3)
第一步是简化这种语法,这样每个产品只会在&#34;扩展&#34;侧。规则#1有一个Kleene星,所以让我们通过用非终端C
取代它来摆脱它:
1: A -> B C
2: A -> d A d
3: A -> z
4: B -> <id>
5: B -> s B
6: B -> A
7: C -> <empty>
8: C -> s B C
现在,所有制作都很简单。
接下来,我们识别间接左递归(如果有的话),并将其转换为直接左递归。通过查看以非终端开头的所有产品,我们发现A
和B
涉及间接左递归(通过规则#1和#6)。我们可以通过用规则#1中的B
代替它可以产生的东西来打破这个循环;我们用
9: A -> <id> C
10: A -> s B C
11: A -> A C
或者,我们可以通过替换#6中的作品#1,#2和#3来打破循环。但是我们这样做,结果语法没有间接的左递归。
然后我们在语法中消除直接左递归(如果有的话)。由于我们的替换,这发生在非终端A
中:
2: A -> d A d
3: A -> z
...
9: A -> <id> C
10: A -> s B C
11: A -> A C
我们引入另一个非终端D
,并用
12: A -> d A d D
13: A -> z D
14: A -> <id> C D
15: A -> s B C D
17: D -> <empty>
18: D -> A D
结果语法没有左递归:
4: B -> <id>
5: B -> s B
6: B -> A
7: C -> <empty>
8: C -> s B C
12: A -> d A d D
13: A -> z D
14: A -> <id> C D
15: A -> s B C D
17: D -> <empty>
18: D -> A D
如开头所述,您无法根据此语法构造LL(1)解析表,因为来自zszsz
的{{1}}的最左侧推导仍然不明确。