首页 > 其他分享 >【编译原理】LL(1)预测分析法

【编译原理】LL(1)预测分析法

时间:2024-05-25 20:33:35浏览次数:20  
标签:分析 文法 LL top 分析法 编译 exp print stack

一、实验目的

LL(1)的含义:第一个L表明自顶向下分析是从左向右扫描输入串,第2个L表明分析过程中将使用最左推导,1表明只需向右看一个符号便可决定如何推导,即选择哪个产生式进行推导。

LL(1) 预测分析方法是确定的自顶向下的语法分析技术。本次实验的主要目的就是要加深对LL(1)预测分析法的理论和预测分析程序工作过程的理解。

二、实验要求

实现LL(1)预测分析法需要:

(1)判别文法是否为LL(1)文法。为此需要依次计算FIRST集、FOLLOW集和SELLECT集,根据SELLECT集可判断文法否为LL(1)文法。

(2)构造出分析表。根据SELLECT集和文法产生式构造出LL(1)分析表。

(3)进行句子分析。依据分析表判断出某句子是否为给定文法的句子。

为了降低实现的难度,本实验只要求实现步骤(3)的部分,即手动实现步骤(1)和(2),然后依据步骤(2)建立的分析表编写一个总控程序,实现的句子的分析。

程序应满足下列要求:

  1. 输入一个分析表,则输出预测分析的步骤。要求从输入文件(input.txt)和键盘中输入预测分析表,把结果输出到结果文件(result.txt)和显示器。

输出格式,如:

步骤         符号栈        输入串       所用产生式

0        #E          i1*i2+i3#               

1      #ET          i1*i2+i3#         T-->FT

…       ………        …………        …………

2、程序应能判断出某句子是否为该文法的句子。(结论)

3、准备多组测试数据存放于input.txt文件中,测试数据中应覆盖是文法的句子和不是文法的句子两种情况,测试结果要求以原数据与结果对照的形式输出并保存在result.txt中,同时要把结果输出到屏幕。

4、对于上面步骤(1)和(2)虽不需要通过程序来实现,但要求和测试数据一起在实验报告中写明。

5、提前准备

① 实验前,先编制好程序,上机时输入并调试程序。

  • 准备好多组测试数据(存放于文件input.txt中)。

三、实验过程:

  • 算法分析:
  • 初始化文法和预测分析表:
  • 首先,定义了文法的终结符集合 v1 和非终结符集合 v2。
  • 接着,创建了几个产生式对象,并将它们赋值给预测分析表 C 的不同位置。
  • 读取文件内容:
  • 从指定的文件中读取文本内容,并存储在变量 exps 中,每行表示一个要分析的文法。
  • 预测分析:
  • 对于每个读取的文法,先检查是否只包含终结符,然后开始进行预测分析。
  • 预测分析过程通过遍历输入字符并根据预测分析表中的产生式进行推导和匹配。
  • 复杂度分析:
  • 初始化阶段的时间复杂度取决于产生式数量和预测分析表的大小,通常为 O(n)。
  • 读取文件内容和预测分析的时间复杂度主要取决于文法的长度和输入长度,通常为 O(m), 其中 m 为文法或输入的长度。
  • 空间复杂度主要取决于预测分析表的大小和其他数据结构的空间占用,通常为 O(n^2)。
  • 优点:
  • 预测分析器具有较好的可读性和易于实现的特点。
  • 可以快速识别输入串是否符合给定文法规则。
  • 局限性:
  • 预测分析器需要提前构建预测分析表,对于大型文法或复杂语言可能会变得复杂。
  • 对于某些文法,可能需要使用其他类型的解析器来处理。
  • 程序流程图:

  • 程序代码:

class Type:

    def __init__(self):

        self.origin = 'N'  # 产生式左侧字符 大写字符

        self.array = ""  # 产生式右边字符

        self.length = 0  # 字符个数



    def init(self, a, b):

        self.origin = a

        self.array = b

        self.length = len(self.array)





# 预测分析表

C = [[Type() for _ in range(10)] for _ in range(10)]





def is_terminator(c):

    # 判断是否是终结符

    v1 = "i+*()#"

    return c in v1





def read_file(file_name):

    # 读文件

    res = []

    try:

        with open(file_name, 'r') as file:

            for line in file:

                res.append(line.strip())

    except Exception as e:

        print(e)

    return res





def init(exp):

    global ridx, len_exp, rest_stack, top

    top = ridx = 0

    len_exp = len(exp)  # 分析串长度

    rest_stack = list(exp)





def print_stack():

    # 输出分析栈和剩余栈

    global top, ridx, len_exp, analye_stack, rest_stack

    print(''.join(analye_stack[:top + 1]), end=' '*(20-top))



    for i in range(ridx):

        print(' ', end='')

    for i in range(ridx, len_exp):

        print(rest_stack[i], end='')

    print('\t\t\t', end='')





def analyze(exp):

    # 分析一个文法

    global ridx, len_exp, analye_stack, rest_stack, C, top

    init(exp)

    k = 0

    analye_stack[top] = '#'

    top += 1

    analye_stack[top] = 'E'  # '#','E'进栈

    print("步骤\t\t分析栈 \t\t\t\t\t剩余字符 \t\t\t\t所用产生式 ")

    while True:

        ch = rest_stack[ridx]

        x = analye_stack[top]

        top -= 1  # x为当前栈顶字符

        print(str(k + 1).ljust(8), end='')

        if x == '#':

            print("分析成功!AC!\n")  # 接受

            return

        if is_terminator(x):

            if x == ch:  # 匹配上了

                print_stack()

                print(ch, "匹配")

                ridx += 1  # 下一个输入字符

            else:  # 出错处理

                print_stack()

                print("分析出错,错误终结符为", ch)  # 输出出错终结符

                return

        else:  # 非终结符处理

            m = v2.find(x) if x in v2 else -1  # 非终结符下标

            n = v1.find(ch) if ch in v1 else -1  # 终结符下标

            if m == -1 or n == -1:  # 出错处理

                print_stack()

                print("分析出错,错误非终结符为", x)

                return

            elif C[m][n].origin == 'N':  # 无产生式

                print_stack()

                print("分析出错,无法找到对应的产生式")  # 输出无产生式错误

                return

            else:  # 有产生式

                length = C[m][n].length

                temp = C[m][n].array

                print_stack()

                print(x, "->", temp)  # 输出所用产生式

                for i in range(length - 1, -1, -1):

                    if temp[i] != '^':

                        top += 1

                        analye_stack[top] = temp[i]  # 将右端字符逆序进栈

        k += 1





ExpFileName = "./input.txt"

v1 = "i+*()#"  # 终结符

v2 = "EGTSF"  # 非终结符



e = Type()

t = Type()

g = Type()

g1 = Type()

s = Type()

s1 = Type()

f = Type()

f1 = Type()



e.init('E', "TG")

t.init('T', "FS")

g.init('G', "+TG")

g1.init('G', "^")

s.init('S', "*FS")

s1.init('S', "^")

f.init('F', "(E)")

f1.init('F', "i")



C[0][0] = C[0][3] = e

C[1][1] = g

C[1][4] = C[1][5] = g1

C[2][0] = C[2][3] = t

C[3][2] = s

C[3][4] = C[3][5] = C[3][1] = s1

C[4][0] = f1

C[4][3] = f

exps = read_file(ExpFileName)



analye_stack = [' ' for _ in range(20)]



for exp in exps:

    flag = True

    for ch in exp:

        if not is_terminator(ch):

            flag = False

            break



    if flag:

        analyze(exp)

  • 程序运行结果截图:

四、思考题:

请描述确定的自顶向下分析思想。

确定的自顶向下分析思想是指自顶向下地对输入串进行分析,在每一步都根据当前的符号栈顶符号和输入串的当前符号,在分析表中查找所用产生式,然后将产生式右部的符号依次推入符号栈中,直到符号栈中只剩下底符号和输入串中所有符号都被读入为止。如果在分析过程中遇到了分析表中没有对应的产生式,则分析失败。

五、实验小结: 

通过本次实验,我初步了解了LL(1)预测分析法的基本原理和实现方法,掌握了自顶向下分析思想。在实验过程中,我手动计算了文法的FIRST集、FOLLOW集和SELECT集,构造出了LL(1)分析表,并编写了一个总控程序,实现了句子的分析。在测试过程中,我准备了多组测试数据,覆盖了是文法的句子和不是文法的句子两种情况,测试结果以原数据与结果对照的形式输出并保存在result.txt中,同时输出到屏幕。通过本次实验,我深刻理解了LL(1)预测分析法的工作原理,并掌握了其实现方法。

标签:分析,文法,LL,top,分析法,编译,exp,print,stack
From: https://blog.csdn.net/qq_42531954/article/details/139193747

相关文章

  • 《拯救大学生课设不挂科第二期之Windows11下安装VC6.0(VC++6.0)与跑通Hello World C语言
    背景与目标人群:大学第一次学C语言的时候,大部分老师会选择VC6这个编辑器。但由于很多人是新手,第一次上大学学C语言。老师要求VC6.0(VC++6.0)写C语言跑程序可能很多人还是第一次接触电脑。需要安装VC6这个编辑器并且编译C语言程序,但是不怎么会装。博主结合自己当时学习与现在......
  • shell脚本的相关知识点
    shell脚本的相关知识本质:编程语言,shell命令的有序集合编译型语言    示例:C/C++   支持:编译器支持   效率:高解释型语言    示例:shell脚本,python,js   支持:命令解释器支持   效率:低   开始shell脚本编程    1.创建shell脚本文件,......
  • 使用RAG-GPT和Ollama搭建智能客服
    引言前面介绍了使用RAG-GPT和OpenAI快速搭建LangChain官网智能客服。有些场景,用户可能无法通过往外网访问OpenAI等云端LLM服务,或者由于数据隐私等安全问题,需要本地部署大模型。本文将介绍通过RAG-GPT和Ollama搭建智能客服。RAG技术原理介绍在介绍RAG-GPT项目之前,我们首先要......
  • UE4之宏与预编译指令定义
    在UBT中添加宏定义UnrealEngine\Engine\Source\Programs\UnrealBuildTool\Configuration\ModuleRules.cs UnrealEngine\Engine\Source\Programs\UnrealBuildTool\Configuration\UEBuildPlatform.cs UnrealEngine\Engine\Source\Programs\UnrealBuildTool\Con......
  • aspnetcore插件开发dll热加载 二
    这一篇文章应该是个总结。投简历的时候是不是有人问我有没有abp的开发经历,汗颜!在各位大神的尝试及自己的总结下,还是实现了业务和主机服务分离,通过dll动态的加载卸载,控制器动态的删除添加。项目如下: 演示效果: 下面就是代码部分:重点1.IActionDescriptorChangeProvider......
  • Windows pyinstaller wxPython pyecharts无法正常显示问题
    WindowspyinstallerwxPythonpyecharts无法正常显示问题最近遇到一个pyinstaller打包wxPythonpyecharts无法显示的问题,pyecharts生成的html页面显示空白。未使用pyinstaller打包时显示正常。问题原因WebViewBackendDefault=b''WebViewBackendEdge=b'wxWebViewEdge'Web......
  • Android编译命令
    AOSP原生单编命令(所有安卓衍生项目都支持)Settings模块单编:cdLINUX/androidsourcebuild/envsetup.sh//orlunch然后自己选择编译项lunch#mmm比mm编译速度快mmmpackages/app/Settingsormmpackages/app/SettingsormakeSettings代码编译编译指令解释m......
  • pyinstaller 打包无窗口python http.server无法启动
    最近在写一个简单的文件服务器用来访问静态文件,遇到在pyinstaller无窗口模式下无法启动的问题,记录一下解决方案。原因:http.server需要将记录输出到窗口,而pyinstaller打包无窗口模式没有地方输出classBaseHTTPRequestHandler(socketserver.StreamRequestHandler):.........
  • Python中动态调用C#的dll动态链接库中方法
    在Python中调用C#的dll库_哔哩哔哩_bilibili 环境准备: 安装pythonnetpipinstallpythonnet 在Python中调用C#动态链接库(DLL),可以使用pythonnet库,它允许直接使用.NET的程序集。以下是一个示例,展示如何使用pythonnet调用C#动态链接库中的方法。【pythonnet详解】—......
  • reactk中useCallback的使用场景
    useCallback 是React中的一个Hook,用于优化性能并避免不必要的函数重新创建。在React中,当一个组件重新渲染时,其内部的函数也会被重新创建。这可能会导致向子组件传递的回调函数发生变化,从而导致子组件不必要地重新渲染。为了避免这种情况,可以使用 useCallback 来创建记忆......