【编译原理】【C语言】实验五:利用LL(1)分析表实现语法分析


C语言
实验环境:Visual Studio 2019
author:zoxiii


1、实验内容

  利用前面构造得到的LL(1)预测分析表,分析一个输入语句,输出具体的分析过程。

2、前期准备

2.1 LL(1)分析法分析过程

在这里插入图片描述

图1-LL(1)分析器

  对图1所示的LL(1)分析器说明如下:
(1)输入串是待分析的符号串,它以界符“#”作为结束标志(注:#∈ V T V_T VT但不是文法符号,是由分析程序自动添加的)。
(2)分析栈中存放分析过程中的文法符号。分析开始时栈底先放一个“#”,然后再压入文法的开始符号;当分析栈中仅剩“#”,输入串指针也指向串尾的“#”时,分析成功。
(3)分析表用一个二维数组 M M M表示,它概括了相应文法的全部信息。数组的每一行与文法的一个非终结符相关联,而每一列与文法的一个终结符或界符“#”相关联。对 M [ A , a ] M[A,a] M[A,a]来说, A A A为非终结符,而 a a a为终结符或“#”。分析表元素 M [ A , a ] M[A,a] M[A,a]中的内容为一条关于 A A A的产生式,表明当 A A A面临输入符号 a a a时当前推到所应采用的候选式;当元素内容为空白(空白表示“出错标志”)时,则表明 A A A不应该面临这个输入符号 a a a,即输入串含有语法错误。
(4)控制程序根据分析栈顶符号 x x x和当前输入符号 a a a来决定分析器的动作:

  • 1)若 x = a = x=a= x=a=“#”,则分析成功,分析器停止工作。
  • 2)若 x = a ≠ x=a≠ x=a=“#”,即栈顶符号 x x x与当前扫描的输入符号 a a a、匹配;则将 x x x从栈顶弹出,输入指针指向下一个输入符号,继续对下一个字符进行分析。
  • 3)若 x x x为一非终结字符 A A A,则查 M [ A , a ] M[A,a] M[A,a]
    ①若 M [ A , a ] M[A,a] M[A,a]中为一个 A A A的产生式,则将 A A A自栈顶弹出,并将 M [ A , a ] M[A,a] M[A,a]中的产生式右部符号串按逆序逐一压入栈中;如果 M [ A , a ] M[A,a] M[A,a]中的产生式为 A − > ε A->ε A>ε,则只将A自栈顶弹出。
    ②若 M [ A , a ] M[A,a] M[A,a]中为空,则发现语法错误,调用出错处理程序进行处理。

2.2 增设分析过程的相关变量和函数

  对于分析输入串的过程参照实验三的递归下降分析法,使用string类型的变量来模拟栈,然后对其中的分析过程代码进行编写。
  变量和函数如下:

string str, stackStr;//str:输入串、stackStr : 模拟栈
int i;//输入串的索引(指针)
string ch;//当前分析字符
vector<string> v;//字符串类型的向量(文法+分析栈)
vector<string> vnote;//说明调用的是哪一个产生式
int check();//验证是否已经到栈底
void push(string pre, string value);//将字符串存入输出栈
void analyze();       //分析文法的语句
void Print_analyze(); //打印分析过程

3、分析过程

3.1 输入串分析过程

  根据书上的例子,我们选择实验三中递归下降分析法所选择的文法G[E]如下:

G[E]:	E->TG
G->+TG|ε
T->FS
S->*FS|ε
F->(E)|i

  按照实验四的方法分析可以得到对应的LL(1)分析表如下:

#()*+i
EE->TGE->TG
FF->(E)F->i
GG->$G->$G->+TG
SS->$S->$S->*FSS->$
TT->FST->FS

  接下来根据LL(1)分析器的输入串分析方法,对输入串i+i*i进行分析过程如下表所示:

符号栈当前输入符号输入串说明
E#i+i*i#弹出栈顶E,M[E,i]中产生式逆序压栈
TG#i+i*i#弹出栈顶T,M[T,i]中产生式逆序压栈
FSG#i+i*i#弹出栈顶F,M[F,i]中产生式逆序压栈
iSG#i+i*i#匹配,弹出i,读入+
SG#+i*i#弹出栈顶S,M[S,+]中产生式逆序压栈
G#+i*i#弹出栈顶G,M[G,+]中产生式逆序压栈
+TG#+i*i#匹配,弹出+,读入i
TG#i*i#弹出栈顶T,M[T,i]中产生式逆序压栈
FSG#i*i#弹出栈顶F,M[F,i]中产生式逆序压栈
iSG#i*i#匹配,弹出i,读入*
SG#*i#弹出栈顶S,M[S,*]中产生式逆序压栈
*FSG#*i#匹配,弹出*,读入i
FSG#i#弹出栈顶F,M[F,i]中产生式逆序压栈
iSG#i#匹配,弹出i,读入#
SG##弹出栈顶S,M[S,3]中产生式逆序压栈
G##弹出栈顶G,M[G,#]中产生式逆序压栈
##匹配,分析成功

3.2 主程序分析

  主程序在实验四的基础上增加了输入要分析的语句和分析的子函数过程。
  其中要对输入串的指针、分析栈的内容进行初始化赋值,为后面的分析过程做准备。
  因此得到主程序的流程图如下:
在这里插入图片描述

图2-主程序流程图

3.3 子程序分析

  子程序中最重要的就是求解FIRST、FOLLOE集合的过程,其中求解FIRST集、FOLLOW集的流程图如下图所示。
  另外对于LL(1)分析器对输入串分析的过程参照递归下降分析法的步骤,添加了压栈和检查输入串是否结束的两个辅助函数来控制分析栈的存储。
  这两个函数的过程比较简单,主要是运用了下面这些C++自带的函数:

1substr(a,b):a:起始位置、b:字符串长度;
(2)	str.find_first_of(ch):从str开始查找ch的位置;
(3erase(idx, n):删除从位置idx开始的n个字符;
(4)	v.push_back():将字符串压入向量v中;

在这里插入图片描述

图3-求FIRST集流程图

在这里插入图片描述

图4-求FOLLOW集流程图

在这里插入图片描述

图5-analyze()流程图

3.4 源代码

✅⬇️代码传送地址

3.5 运行结果

  测试时首先使用前面分析过的文法和输入串进行测试的输出结果如下图所示,其中可以观察到相对于前面的分析过程分析栈的输出结果中都略去了匹配成功的那一步,直接进行了下一步的分析。
  “说明”中的内容为每一步所选择的文法产生式在分析表中的位置。

在这里插入图片描述

图6-结果截图1

  再对输入串i+i#进行分析,得到的结果如下图所示,其中分析步骤共有8步,得到了每一步分析栈中的内容。
在这里插入图片描述

图7-结果截图2

  而如果是不符合文法的语句,就没有任何输出结果,程序直接退出。
  比如下图对输入串i-i进行分析,但该输入串并不符合该文法。

在这里插入图片描述

图8-结果截图3

4、遇到问题

  (1)对于更新后栈顶是终结符是,无法在预测分析表中查找到对应的文法表达式而出错,所以在压栈时对于栈顶进行处理,保证栈顶为非终结符,这样就不会出错了。
  (2)使用string类型的二维数组来存储预测分析表,只能设置固定的大小,浪费了很多内存空间,使代码运行速度变慢。

  • 3
    点赞
  • 140
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
下面是一个简单的基于C语言实现LL(1)语法分析器示例代码,仅供参考: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_BUF_SIZE 1024 #define MAX_STACK_SIZE 1024 // 预测分析 char parsing_table[10][10] = { /* E T F + * ( ) i $ */ {'1', ' ', ' ', ' ', ' ', '2', ' ', '3', ' ', ' '}, // E {' ', '4', ' ', '5', ' ', ' ', ' ', ' ', '6', ' '}, // T {'7', ' ', '8', ' ', ' ', '9', ' ', ' ', '10', ' '}, // F }; // 符号栈 char stack[MAX_STACK_SIZE]; int top = -1; // 输入缓冲区 char buffer[MAX_BUF_SIZE]; int buf_idx = 0; // 读入输入 char read_input() { if (buf_idx == strlen(buffer)) { return '$'; } return buffer[buf_idx++]; } // 获取栈顶元素 char get_top() { if (top >= 0) { return stack[top]; } return ' '; } // 弹出栈顶元素 void pop() { if (top >= 0) { top--; } } // 压入元素到栈顶 void push(char c) { if (top < MAX_STACK_SIZE-1) { stack[++top] = c; } } // LL(1)语法分析 void parse() { push('$'); push('E'); char lookahead = read_input(); while (top >= 0) { char top_char = get_top(); if (top_char == lookahead) { printf("Match %c\n", lookahead); pop(); lookahead = read_input(); } else if (top_char >= 'A' && top_char <= 'Z') { int row = top_char - 'A'; int col = lookahead - '0'; char *production = parsing_table[row][col]; if (production == ' ') { printf("Error: Parsing table entry is empty!\n"); exit(1); } else { printf("Apply production %c -> %s\n", top_char, production); pop(); int i; for (i = strlen(production)-1; i >= 0; i--) { push(production[i]); } } } else { printf("Error: Invalid character %c on stack!\n", top_char); exit(1); } } } int main() { printf("Enter input string: "); fgets(buffer, MAX_BUF_SIZE, stdin); buffer[strlen(buffer)-1] = '\0'; // 去掉换行符 parse(); return 0; } ``` 该代码实现了一个简单的LL(1)语法分析器,其基本思路是:使用一个符号栈来模拟语法分析过程,根据预测分析进行分析,将分析结果输出为语法树。用户需要输入一个待分析的字符串,程序将输出分析过程中的每个步骤,包括匹配、应用产生式等。需要注意的是,该代码只能处理特定的文法,如果需要处理其他文法,需要修改预测分析和产生式。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zoxiii

越打赏越生长

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值