First(A) 为A的开始符或者首符号集
Follow(A) 指的是在某些句型中紧跟在A右边的终结符号的集合
求下列右递归语法的First集和Follow集
第一行:Expr → Term Expr '
第二行:Expr ' → + Term Expr ' | - Term Expr ' | ∈
第三行:Term → Factor Term '
第四行:Term ' → * Factor Term ' | / Factor Term ' | ∈
第五行:Factor → ident | num | ( Expr )
First(Factor) = {
ident num ( } //Factor → ident | num | ( Expr ) 以什么开始显而易见
First(Term ') = {
* / ∈} //同理看上面Term ‘所在语法行就好了
First(Term) = {
ident num ( } //Term是Factor为首 那么Factor又以ident num ( 为首
First(Expr ') = {
+ - ∈ }
First(Expr) = {
ident num ( } //Expr以Term为首 Term以Factor为首 Factor又以ident num ( 为首
//Expr作为起始位置永远包含eof 同时语法最后一行Expr可以跟 )
Follow(Expr) = {
eof ) }
//第一行Expr '可以作为Expr结尾 那么Expr ’包含Expr所有follow集
//第二行右边的Expr '作为Expr '结尾 Expr ‘ 包含 Expr ’自身 所以无用
Follow(Expr ') = {
eof ) }
//第一行Term出现在Expr ’前面 所以Follow(Term)包含First(Expr ')
//同时因为Expr ‘可能为∈ 那么说明Term可以作为Expr的最后出现 所以要包含Follow(Expr)
Follow(Term) = {
+ - eof ) }
//第三行可以发现Term ‘可以作为Term的最后 所以要包含Follow(Term)
//第四行右边的Term '作为Term '结尾 Term ‘ 包含 Term ’自身 所以无用
Follow(Term ') = {
+ - eof ) }
//第三行Factor后面紧跟Term ‘ 所以包含First(Term ')
//同时第三行Term ‘可以为∈ 那么说明Factor可以作为Term的最后出现 所以要包含Follow(Term)
Follow(Factor) = {
* / + - eof ) }
⚠️:这些集合中装的都是terminal 即语法中不再需要置换的 eof代表表达式结束符号
first集合非常简单 但是要注意follow集合
主要是先看语法箭头右边的内容找到需要判断的符号所在位置 如果在符号串尾 那么就包含箭头左边符号的所有follow集合
S → A |B //需要找follow(A) 这时follow(A)就包含follow(B)
其次如果不在符号串尾 就包含后面紧跟着的符号的first集 但是要注意的是如果紧跟着的符号在first集可以为∈ 那么说明这个紧跟着的符号可以消失 需要继续向后包含下下个符号的first集 直到找到一个符号的first集不包含∈ 如果遍历完后续所有符号的first集都有空 那么就要包含箭头左边的follow集
S →ACbS | ∈ //需要找follow(A) A后面紧跟C 那么就包含first(C) 但是如果C的first的集中有∈
//那么说明C可以消失 取而代之就是后面的b了 b是一个ternimal 肯定不是∈ 所
//以包含b 所以流程就是一直向后遍历找到那么一个first集没有空的就停止
//如果遍历完了也没找到呢?那就说明A后面的东西全可以消失 A的follow就可以
//是箭头左边S的follow
A grammar is LL(1) if
for every production
Define:
例2:
S → A | B
A → b
B → BACb | a //这一行是左递归 需要转右递归
C → AD | ∈
D → BC | bC
转换后:
第一行:S → A | B
第二行:A → b
第三行:B → aB '
第四行:B ' → ACbB ' | ∈
第五行:C → AD | ∈
第六行:D → BC | bC
First(A) = {b}
First(B) = {a}
First(B ') = {b ∈}
First(C) = {b ∈}
First(D) = {a b}
First(S) = {b a}
//找Follow从起始位置开始 eof是起始位置固定的 发现右边没有S 那么就结束了
Follow(S) = {eof}
//我们发现第一行中A作为符号尾 那么就要包含Follow(S)
//同时第四行A后面紧跟着C 所以要包含First(C) 当然要剔除掉∈ 因为C的first中有∈ 我们要继续向 //后遍历 看到了b b显然不是∈ 所以停止
//第五行A后面跟着D 所以要包含first(D) 而且first(D)中没有∈ 所以结束
Follow(A) = {eof b a}
//第一行中符号B在符号串尾部 所以包含Follow(S)
//第六行中B紧跟着C 所以包含first(C) 但是first(C)有∈ 所以继续 但是符号串后面没东西了 这时候
//说明在第六行中B后面的东西可以全部消失 因此D的follow就是B的follow 所以包含follow(D)
Follow(B) = {eof b}
//第四行C后面有个terminal b
//第六行C作为符号串的结尾 所以因此D的follow就是C的follow 所以包含follow(D)
Follow(C) = {b }
//第五行看出D包含follow of C
Follow(D) = {b}
//第三行看出B ‘包含follow of B
Follow(B ') = {eof b}
这里我们发现一个有趣的循环依赖关系 B ’是依赖B里的东西 B是依赖D的东西 D是依赖C的东西 C又依赖D的东西
A grammar is LL(1) if
for every production
Define:
我们来检查一下这个适不适合LL1
第一行:S → A | B开始:
(S → A) = First(A) = {b}
(S → B) = First(B) = {a}
(S → A) (S → B) = 通过
第二行三行不需要检查 因为他们只有一个选择
第四行B ' → ACbB ' | ∈
(B ' → ACbB ') = {b}
(B ' → ∈) = Follow(B ') = {eof b}
(B ' → ACbB ') (B ' → ∈) = {b}
所以出错了 证明LL1不适用
之前我们通过了左递归转成右递归让一个语法适用了LL1 我们可以检查一个特定的语法是不是LL1 就和我们上面所做的过程一样 也就是说一个语法本来不适用LL1 经过一些转换就能适用LL1
是不是说任何语法都可以通过一些转换适用LL1呢?显然不是的 有些语法不管你怎么转换都是不适用LL1 parsing技巧的
而且 并没有一个算法可以直接判断出某个特定的语法是否能成功转换成LL1 你只能不停的尝试 或许某一次会成功 或许不会成功 只有转换完才能去验证 听上去是一个很大的问题 但是实际中对于大部分context-free语言 通过经验都是可以转换成LL1适用的
所以说 逻辑并不需要完美 实际中可以通过经验最大化减小带来的问题
比LL(1)更通用的一个语法是LR(1)
其中最中央的是regular language ,regular language甚至不需要一个语法去约束 只用有限自动机就可以去表达了
这一圈一圈往外走的语言的语法会越来越复杂 也越来越难以去约束表达 可以想象 最难去约束表达的一定是人类日常的自然语言 比如中文 英语 因为很多时候人说的自然语言都不会按照语法来 甚至加入了感情和联想在其中 导致了用一套死板的语法或者说代码去约束几乎是不可能的 这也是当代人工智能本质上无法逾越的鸿沟
比如下面的两句话:
缘起 我在人群中看见你
缘落 我看见你 在人群中
我相信cpu烧冒烟了也无法体会其中的意境