编译原理(1)----LL(1)文法(首符号集,后跟符号集,选择符号集)

目录

1.首符号集(First())

2.后跟符号集(Follow()):只针对非终结符

3. 选择符号集:Select(A->)

4.构造LL(1)分析表

5.输入串的分析


1.首符号集(First(\alpha))

技巧:找最左边可能出现的终结符

例:

1.First(E)

E->TE^{'},最左边为T,又因为T->FT^{'},最左边为F,F->(E)|i,则最左边为{(,i }

2.First(TE^{'}):只需要看符号串最左边的符号,即=First(T)

T->FT^{'},最左边为F,F->(E)|i,则最左边为{(,i }

3.First((E)):也只需要看最左边的

   First((E))={ ( }

4.First(i):终结符的first集就是它本身

First(i)={i} 

其他以此类推:

2.后跟符号集(Follow(\alpha)):只针对非终结符

技巧:看”->“的右边,找出非终结符后面所能跟随的所有终结符

1. # \epsilon Follow (S),S为识别符号,即 ”#“要放在开始符号"S"的 Follow集中

2.若存在规则U->xWy,First(y)-{\varepsilon}(空串) \epsilonFollow(W)

3.若存在规则U->xW或U->xWy,其中y能广义推导出\varepsilon(空串),则Follow(U)\epsilonFollow(W)

Follow(E)

首先E是开始符号,Follow(E)={#}

在"->"右边找E,看E后面跟的所有终结符号,这里F->(E)| i,E的右边为")",终结符的First集就是它本身(对应第二条规则)

所以Follow(E)={#,)}

 Follow(T)

首先找”->“的右边,看T后面跟的所有终结符号

E->TE^{'}        E^{'}->+TE^{'}|\varepsilon

T后面跟的是E^{'}E^{'}的First集是{+,\varepsilon},将\varepsilon去掉,最后得到{+},又因为E^{'}可以推导出\varepsilon空串(对应第三条规则),就要把

E->TE^{'}        E^{'}->+TE^{'}|\varepsilon    ”->“左边的E和E^{'}的follow集写上

Follow(T)={+,#,)}

 Follow(E')

首先找”->“的右边,看E’后面跟的所有终结符号

E->TE^{'}        E^{'}->+TE^{'}|\varepsilon

E^{'}后面没有符号,E^{'}的follow集就是”->“左边E和E^{'}的follow集

Follow(E^{'})={#,)}

以此类推:

总结

 在"->"右边寻找需要求的非终结符,如果非终结符后面是终结符号,直接放到follow集中,如果非终结符后面是非终结符,就看非终结符的first集内容是什么,如果有\varepsilon(空串)

1.那么将空串去掉写入follow集中

2.并且将”->“左边的非终结集的follow集也写入该follow集中

例题:

3. 选择符号集:Select(A->\partial)

约束:有两条或两条以上产生式才算可选集

E->TE'        不用算可选集

E‘->+TE'|\varepsilon        算可选集

规则: 

Select (E'->+TE')

根据规则,不能广义推导出\varepsilon,那么运用第一条规则

Select (E'->+TE')={+}

Select(E'->\varepsilon

运用第二条规则,”->“右边的首符号集-空串(这里-\varepsilon后为空集),再并上E’的follow集

Select(E'->\varepsilon) ={#,)}

Select(T'->+FT')

”+"号的首符号集就是”+“,所以

 Select(T'->+FT')={+}

以此类推:

4.构造LL(1)分析表

以例题的形式展开:

接下来构造LL(1)分析表,行表示终结符,列表示非终结符

由于FIRST(A)={a},所以:

FIRST(A')={a,\varepsilon},因为A'能推出\varepsilon,所以要到follow中看,folllow(A’)={#,d},所以将A'->\varepsilon,写到其中:

以此类推,得到最终的分析表:

如何判断是否为LL(1)文法:

A-->\alpha |\beta

\beta \neq \varepsilonFirst(\alpha )\sqcap First(\beta )=\phi

\beta =\varepsilonFirst(\alpha )\sqcap Follow(A)=\phi

其实这中间包含了求select集的过程:

对于A-->\alpha \beta而言:

若 \alpha是终结符,则SELECT(A)=\alpha

若 \alpha是非终结符,则SELECT(A)=FIRST(\alpha)

对于A-->\varepsilon

 SELECT(A)=FOLLOW(A)

这里判断A-->\alpha |\beta是否为LL(1)文法,原理和上面是一模一样的

SELECT(\alpha )\sqcap SELECT(\beta )=\phi

所以流程: ①消除左递归,消除回溯-->②计算FIRST集和Follow集--->③判断是否为LL(1)文法

5.输入串的分析

给出输入串 aad1#的分析过程

由于A->aA',反向写入符号栈中

此时符号栈的最顶层“a”,与当前输入符号“a”相同,所以可以消去

A‘->AB1|\varepsilon,不能写空串,这样符号栈就为\varepsilon

以此类推:

到这上面这一步,因为A’不能推出d,又因为A‘->AB1|\varepsilon,所以这里用A’-->\varepsilon,将A‘消除

现通过完整的题目练习一下:

表达式文法为:

E-->E+T|T

T-->T*F|F

F-->i|(E)

(1)消除左递归

E-->TE'

E'-->+TE'|\varepsilon

T-->FT'

T'-->*FT'|\varepsilon

F-->i|(E)

预测分析表:

字符串得分析过程: 

  • 10
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LL(1)文法是一种上下文无关文法,它可以被用来描述自然语言,编程语言等等。在LL(1)文法中,每个非终结符都有一组产生式,而每个产生式的右部第一个符号相同。因此,LL(1)文法可以被用来构建递归下降语法分析器。 以下是一个简单的LL(1)文法的Java实现: 1. 定义一个非终结符类: public class NonTerminal { private String name; private List<List<Symbol>> productions; public NonTerminal(String name) { this.name = name; this.productions = new ArrayList<>(); } public String getName() { return name; } public List<List<Symbol>> getProductions() { return productions; } } 2. 定义一个符号类: public abstract class Symbol { public abstract boolean isTerminal(); } 3. 定义一个终结符类: public class Terminal extends Symbol { private String name; public Terminal(String name) { this.name = name; } public String getName() { return name; } @Override public boolean isTerminal() { return true; } } 4. 定义一个文法类: public class Grammar { private List<NonTerminal> nonTerminals; private List<Terminal> terminals; private Map<NonTerminal, Set<Terminal>> firstSets; private Map<NonTerminal, Set<Terminal>> followSets; private Map<NonTerminal, Map<Terminal, List<Symbol>>> parsingTable; public Grammar(List<NonTerminal> nonTerminals, List<Terminal> terminals) { this.nonTerminals = nonTerminals; this.terminals = terminals; this.firstSets = new HashMap<>(); this.followSets = new HashMap<>(); this.parsingTable = new HashMap<>(); } public List<NonTerminal> getNonTerminals() { return nonTerminals; } public List<Terminal> getTerminals() { return terminals; } public Map<NonTerminal, Set<Terminal>> getFirstSets() { return firstSets; } public Map<NonTerminal, Set<Terminal>> getFollowSets() { return followSets; } public Map<NonTerminal, Map<Terminal, List<Symbol>>> getParsingTable() { return parsingTable; } public void calculateFirstSets() { // TODO: 实现计算First集的算法 } public void calculateFollowSets() { // TODO: 实现计算Follow集的算法 } public void constructParsingTable() { // TODO: 实现构造分析表的算法 } } 5. 定义一个递归下降语法分析器类: public class RecursiveDescentParser { private Grammar grammar; private Stack<Symbol> stack; private TokenStream tokenStream; private Map<Terminal, Map<Terminal, Integer>> precedenceTable; public RecursiveDescentParser(Grammar grammar, TokenStream tokenStream, Map<Terminal, Map<Terminal, Integer>> precedenceTable) { this.grammar = grammar; this.stack = new Stack<>(); this.tokenStream = tokenStream; this.precedenceTable = precedenceTable; } public void parse() { stack.push(grammar.getNonTerminals().get(0)); Token currentToken = tokenStream.getCurrentToken(); while (!stack.isEmpty()) { Symbol top = stack.pop(); if (top.isTerminal()) { if (top.equals(currentToken.getSymbol())) { currentToken = tokenStream.getNextToken(); } else { // 异常处理 } } else { Map<Terminal, List<Symbol>> row = grammar.getParsingTable().get(top); if (row == null) { // 异常处理 } List<Symbol> production = row.get(currentToken.getSymbol()); if (production == null) { // 异常处理 } for (int i = production.size() - 1; i >= 0; i--) { stack.push(production.get(i)); } } } } } 这些代码仅仅是LL(1)文法在Java中的一个简单实现,实际上还有很多细节需要考虑。但是,这应该足够给你一个概述LL(1)文法在Java中的实现方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值