实验2-1 无左递归文法的LL(1)分析表的构建--

本文详细介绍了如何设计和实现一个基于预测分析法的语法分析程序,包括消除文法左递归、提取左公共因子,以及构造预测分析表的过程。具体涉及计算非终结符号的FIRST集和FOLLOW集,以及生成LL(1)分析表,最终用于对简单的程序段进行语法翻译。
摘要由CSDN通过智能技术生成

一、实验目的

1学习消除文法左递归算法和提取左公共因子。

2.掌握预测分析法实现语法分析。

3.自动求得“FIRST”、“FOLLOW”集构造预测分析表。

3.能够使用自己编写的分析程序对简单的程序段进行语法翻译。

二、实验内容

用预测分析法编制语法分析程序,实现可以采用任何一种编程工具。

可用如下文法进行测试,见表一。

表1  语法分析单元的测试文法

编号

文法

A001

E->aA

E->bB

A->cA

A->d

B->cB

B->d

三、实验要求

1*. 定义语言的语法规则。

2. 预测分析表的构造。

3*.对于遇到的语法错误,能够做出简单的错误处理,给出简单的错误提示,保证顺利完成语法分析过程。

四、设计思路和实现方法

算法实现主要包含以下步骤:

求出文法的First集:对于任意一个非终结符号A,求出其所能推导出来的终结符号的集合FIRST(A)。需要逐个考虑A产生的每个推导式子,如果式子的第一个符号是终结符,则将该符号加入FIRST(A);如果是非终结符,则将该非终结符对应的FIRST集与A的FIRST集取并集。需要递归地进行这个操作,直到所有的终结符和非终结符的FIRST集都已求出来。

求出文法的Follow集:对于任意一个非终结符号A,求出其可以出现在哪些符号后面,将这些符号加入到A的FOLLOW集中。对于文法的开始符号S,将$(文法结束符)加入到S的FOLLOW集中。需要逐个考虑每个产生式,将右部每个非终结符的FIRST集加入到其后继符号的FOLLOW集中,如果该非终结符后面还有其他符号,且后继符号的FIRST集中包含ε,则将该非终结符的FOLLOW集加入到后继符号的FOLLOW集中。需要递归地进行这个操作,直到所有的终结符和非终结符的FOLLOW集都已求出来。

构造LL(1)分析表:对于任意一个非终结符号A和终结符号a,如果存在A → α且a∈FIRST(α),则将A → α加入分析表M[A, a]中;如果存在A → α且ε∈FIRST(α),则将A的FOLLOW集中的所有符号加入到分析表M[A, a]中。需要逐一考察所有的非终结符和终结符,生成LL(1)分析表。

五、代码实现和算法分析

5.1定义文法和终结符、非终结符集合

grammar = defaultdict(list)  # 字典,key是产生式左端,value是产生式右端

non_terminals = set()  # 非终结符

terminals = set()  # 终结符



# 读取无左递归文法

with open("C:\\Users\\M\\Desktop\\PycharmProjects\\BY2\\test.txt") as f:

    for line in f:

        left, right = line.strip().split("->")

        non_terminals.add(left)  # 将产生式左侧加入非终结符集

        productions = right.split("|")

        for p in productions:

            grammar[left].append(p)#产生式右侧加入字典

            for q in p:

             if q!='@':

              terminals.add(q)#将产生式右侧里的所以有字符除了@加入终结符

    terminals -= non_terminals#将非终结符取出得到终结符集



first = {}

follow = {}

5.2获取每个非终结符的First集合

for non_term in non_terminals:

    first[non_term] = set()



def get_first(symbol):

        for prod in grammar[symbol]:

            if prod[0] in terminals:

                first[symbol].add(prod[0])#若X->a…,则将终结符a加入FIRST(X)中;

            elif prod[0]=='@':

                first[symbol].add('@')#若X->ε ,则将ε加入FIRST(X)中;

            #若 X->BCD…E,则将First(B)所有元素(除了空集)加入 First(X),然后检测First(B),若First(B)中不存在空集,

            # 即ε,则停止,若存在则向B的后面查看,将First(C)中所有元素(除了空集)加入First(X),然后再检测First(C)中是否有ε…直到最后,

            # 若E之前的所有非终结符的First集中都含有ε,则检测到E时,将First(E)也加入First(X),若First(E)中含有ε,则将ε加入First(X)。

            elif prod[0] in non_terminals:

              for p in prod:

                  for q in get_first(p):

                      if q != '@':

                        first[symbol].add(q)

                  if '@' not in get_first(p):

                      break

                  if p==prod[len(prod)-1]:

                      if '@' in p:

                        first[symbol].add('@')



        return first[symbol]



for non_term in non_terminals:

    get_first(non_term)

5.3获取每个非终结符的Follow集合

for non_term in non_terminals:

    follow[non_term] = set()

follow['E'].add("#")#向开始符的follow集加入#



def get_follow(symbol):

    for left, prods in grammar.items():

        for prod in prods:

            if symbol in prod:

                idx = prod.index(symbol)#求非终止符symbol在产生式中的位置



                if idx != len(prod) - 1: # symbol后还有其他符号情况,一个个遍历

                    for i in range(idx + 1, len(prod)):

                        if prod[i] in terminals:#若为终结符直接加入并跳出循环

                            follow[symbol].add(prod[i])

                            break

                        else:#若为非终结符

                            for p in first[prod[i]]:

                               if p!='@':

                                  follow[symbol].add(p)#将去除@的first集加入

                            if '@'  not in  first[prod[i]]:

                               break#@不在first集中则跳出循环

                            if i==len(prod)-1:

                                follow[symbol]|=get_follow(left)#若@存在于first集中且已经遍历到最后则将左侧的follow集赋给右侧follow集

                else: # symbol在产生式右端末尾

                    if left!=symbol:

                        follow[symbol]|=get_follow(left)#将左侧的follow集赋给右侧follow集

    return follow[symbol]



for non_term in non_terminals:

    get_follow(non_term)

5.4构造LL(1)分析表

table = defaultdict(dict)

for non_term in non_terminals:

    for term in terminals.union(set("#")):

        table[non_term][term] = ""



for left, prods in grammar.items():

    for prod in prods:

        first_set = set()

        if prod != '@':

          for i, symbol  in enumerate(prod) :

            if symbol in terminals:

                first_set.add(symbol)

                break#若为终结符直接加入并跳出

            elif '@' in first[symbol]:

                first_set |= first[symbol] - set("@")#将除了@的first集合加入

            elif i==len(symbol):

                first_set |= first[symbol]#当遍历到最后则可以直接将first集加入

            else:

                first_set |= first[symbol]

                break#如果first集没有@则直接加入并跳出循环

        else:

          first_set |= set("@")



        for term in first_set:#能推导出表中的终结符则直接将产生式填充

            if term in table[left]:

                table[left][term] = prod



        if "@" in first_set:  # 产生式能够推导出空串,则用Follow集合填充

            follow_set = follow[left]

            for term in follow_set:

                if term in table[left]:

                    table[left][term] = prod



# 输出LL(1)分析表

terminals.add('#')

print('             '+f"{'':^1}{'|':^4}".join(terminals))

print('    -------------------------------------')

for non_term in non_terminals:

    row = f"{non_term:^10}|"

    for term in terminals:

        if term in table[non_term]:

            prod_str = " ".join(table[non_term][term])

            row += f"{prod_str:^5}|"

        else:

            row += " " * 7 + "|"

print(row)

六、案例运行结果

运行文法

 

构造结果

 

运行文法

 

构造结果

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值