一、实验目的:
根据某一文法编制调试LL(1)分析程序,以便对任意输入的符号串进行分析。本次实验的目的主要是加深对预测分析LL(1)分析法的理解。
二、实验说明
1、LL(1)分析法的功能
LL(1)分析法的功能是利用LL(1)控制程序根据显示栈栈顶内容、向前看符号以及LL(1)分析表,对输入符号串自上而下的分析过程。
2、LL(1)分析法的前提
改造文法:消除二义性、消除左递归、提取左因子,判断是否为LL(1)文法,
3、LL(1)分析法实验设计思想及算法
三、实验要求
(一)准备:
1.阅读课本有关章节,
2.考虑好设计方案;
3.设计出模块结构、测试数据,初步编制好程序。
(二)上课上机:
将源代码拷贝到机上调试,发现错误,再修改完善。第二次上机调试通过。
(三)程序要求:
程序输入/输出示例:
对下列文法,用LL(1)分析法对任意输入的符号串进行分析:
(1)E->TG
(2)G->+TG|-TG
(3)G->ε
(4)T->FS
(5)S->*FS|/FS
(6)S->ε
(7)F->(E)
(8)F->i
输出的格式如下:
(1)LL(1)分析程序,编制人:姓名,学号,班级
(2)输入一以#结束的符号串(包括+—*/()i#):在此位置输入符号串
(3)输出过程如下:
步骤 | 分析栈 | 剩余输入串 | 所用产生式 |
1 | E | i+i*i# | E->TG |
(4)输入符号串为非法符号串(或者为合法符号串)
(1)在“所用产生式”一列中如果对应有推导则写出所用产生式;如果为匹配终结符则写明匹配的终结符;如分析异常出错则写为“分析出错”;若成功结束则写为“分析成功”。
(2) 在此位置输入符号串为用户自行输入的符号串。
(3)上述描述的输出过程只是其中一部分的。
注意:1.表达式中允许使用运算符(+-*/)、分割符(括号)、字符i,结束符#;
2.如果遇到错误的表达式,应输出错误提示信息(该信息越详细越好);
3.对学有余力的同学,测试用的表达式事先放在文本文件中,一行存放一个表达式,同时以分号分割。同时将预期的输出结果写在另一个文本文件中,以便和输出进行对照;
(四)程序思路
模块结构:
(1)定义部分:定义常量、变量、数据结构。
(2)初始化:设立LL(1)分析表、初始化变量空间(包括堆栈、结构体、数组、临时变量等);
(3)控制部分:从键盘输入一个表达式符号串;
(4)利用LL(1)分析算法进行表达式处理:根据LL(1)分析表对表达式符号串进行堆栈(或其他)操作,输出分析结果,如果遇到错误则显示错误信息。
(五)练习该实验的目的和思路:
程序相当复杂,需要利用到大量的编译原理,也用到了大量编程技巧和数据结构,通过这个练习可大大提高软件开发能力。
(六)为了能设计好程序,注意以下事情:
1.模块设计:将程序分成合理的多个模块(函数),每个模块做具体的同一事情。
2.写出(画出)设计方案:模块关系简图、流程图、全局变量、函数接口等。
3.编程时注意编程风格:空行的使用、注释的使用、缩进的使用等。
- 实验环境
Visual Studio 2019, Windows 10
- 实验步骤
- 对此文法写出分析表;
i | + | - | * | / | ( | ) | # | |
E | TG | TG | ||||||
G | +TG | -TG | ε | ε | ||||
T | FS | FS | ||||||
S | ε | ε | *FE | /FE | ε | ε | ||
F | i | (i) |
- 代码实现如下:
char Vn[M] = { 'E', 'G', 'T', 'S', 'F' };
char Vt[N] = { 'i', '+', '-', '*', '/', '(', ')', '#' };
string L[M][N] = {
{ "TG", "ERROR", "ERROR", "ERROR", "ERROR", "TG", "ERROR", "ERROR" },
{ "ERROR", "+TG", "-TG", "ERROR", "ERROR", "ERROR", "NULL", "NULL" },
{ "FS", "ERROR", "ERROR", "ERROR", "ERROR", "FS", "ERROR", "ERROR" },
{ "ERROR", "NULL", "NULL", "*FS", "/FS", "ERROR", "NULL", "NULL" },
{ "i", "ERROR", "ERROR", "ERROR", "ERROR", "(i)", "ERROR", "ERROR" }
};
- 栈输出函数设计
定义了一个暂存栈,先将分析栈的数据逐个弹出并压入暂存栈。全部存完后,将分析栈的数据逐个弹出输出并回分析栈。
代码实现如下:
void outstack()
{
//定义一个栈暂存数据
stack<char> temp;
//存放
int l = analyse.size();
for (int i = 0; i < l; i++)
{
char a = analyse.top();
analyse.pop();
temp.push(a);
}
//放回输出
l = temp.size();
for (int i = 0; i < l; i++)
{
char a = temp.top();
temp.pop();
analyse.push(a);
cout << a;
}
}
- LL(1)分析算法设计如下:
- 先将‘#’与文法开始符E压入分析栈,并使输入字符指向input的第一位;
- 弹出栈顶元素v,判断v是否与输入字符相同,是转至3,否转至4;
- 输入字符移向input下一位,转至3;若已为最后一位转至8;
- 判断v是否为非终结符,是转至5;否转至9;
- 判断v与输入字符是否有产生式,是转至6,否转至9;
- 判断产生式是否为ε;是转至2,否转至7;
- 将产生式逆序压入分析栈中,转至2;
- 输出过程步骤,以及为合法字符串;
- 输出错误位置并结束;
代码实现如下:
void LL_1(string input)
{
//压入#和开始符
analyse.push('#');
analyse.push(Vn[0]);
//
cout << "步骤" << "\t剩余分析栈" << "\t剩余输入串" << "\t所用产生式" << "\t动作" << endl;
cout << '0' << "\t";
outstack();
cout << "\t\t" << input;
cout << "\t\t\t\t" << "初始化" << endl;
int i = 0;
for (int j = 1; i < input.length(); j++)
{
//取出栈顶
char v = analyse.top();
analyse.pop();
//判断是否匹配
if (v == input[i])
{
cout << j << "\t";
outstack();
cout << "\t\t";
outinput(input, i);
cout << "\t\t" << input[i] << "匹配" << "\t\tPOP" << endl;
i++;
continue;
}
else
{
int m, n;
m = -1;
//未匹配时,判断栈弹出的字符是否为非终结符
for (int j = 0; j < M; j++)
{
if (v == Vn[j])
{
m = j;
break;
}
}
if (m == -1)
{
cout << "fzjf";
cout << "第" << i << "个字符" << input[i] << "匹配错误" << endl;
cout << input << "为非法符号串" << endl;
break;
}
//栈弹出的字符为非终结符
for (int j = 0; j < N; j++)
{
if (input[i] == Vt[j])
{
n = j;
break;
}
}
//匹配情况
if (L[m][n] == "ERROR")
{
cout << "第" << i << "个字符" << input[i] << "匹配错误" << endl;
cout << input << "为非法符号串" << endl;
break;
}
if (L[m][n] == "NULL")
{
cout << j << "\t";
outstack();
cout << "\t\t";
outinput(input, i);
cout << "\t\t\t\t" << v << "->" << "ε" << endl;
continue;
}
else
{
//
string t = "";
for (int j = L[m][n].length() - 1; j >= 0; j--)
{
t += L[m][n][j];
analyse.push(L[m][n][j]);
}
cout << j << "\t";
outstack();
cout << "\t\t";
outinput(input, i);
cout << "\t\t" << v << "->" << L[m][n];
cout << "\t\tPOP,PUSH(" << t << ")";
}
}
cout << endl;
}
if(i == input.length())
cout << input << "为合法符号串" << endl;
}
运行结果:
代码总览:
#include<iostream>
#include<string>
#include<stack>
#define M 5 //非终结符数
#define N 8 //终结符数
using namespace std;
stack<char> analyse;
char Vn[M] = { 'E', 'G', 'T', 'S', 'F' };
char Vt[N] = { 'i', '+', '-', '*', '/', '(', ')', '#' };
string L[M][N] = {
{ "TG", "ERROR", "ERROR", "ERROR", "ERROR", "TG", "ERROR", "ERROR" },
{ "ERROR", "+TG", "-TG", "ERROR", "ERROR", "ERROR", "NULL", "NULL" },
{ "FS", "ERROR", "ERROR", "ERROR", "ERROR", "FS", "ERROR", "ERROR" },
{ "ERROR", "NULL", "NULL", "*FS", "/FS", "ERROR", "NULL", "NULL" },
{ "i", "ERROR", "ERROR", "ERROR", "ERROR", "(i)", "ERROR", "ERROR" }
};
void outstack()
{
//定义一个栈暂存数据
stack<char> temp;
//存放
int l = analyse.size();
for (int i = 0; i < l; i++)
{
char a = analyse.top();
analyse.pop();
temp.push(a);
}
//放回输出
l = temp.size();
for (int i = 0; i < l; i++)
{
char a = temp.top();
temp.pop();
analyse.push(a);
cout << a;
}
}
void outinput(string s, int n)
{
for (; n < s.length(); n++)
cout << s[n];
}
void LL_1(string input)
{
//压入#和开始符
analyse.push('#');
analyse.push(Vn[0]);
//
cout << "步骤" << "\t剩余分析栈" << "\t剩余输入串" << "\t所用产生式" << "\t动作" << endl;
cout << '0' << "\t";
outstack();
cout << "\t\t" << input;
cout << "\t\t\t\t" << "初始化" << endl;
int i = 0;
for (int j = 1; i < input.length(); j++)
{
//取出栈顶
char v = analyse.top();
analyse.pop();
//判断是否匹配
if (v == input[i])
{
cout << j << "\t";
outstack();
cout << "\t\t";
outinput(input, i);
cout << "\t\t" << input[i] << "匹配" << "\t\tPOP" << endl;
i++;
continue;
}
else
{
int m, n;
m = -1;
//未匹配时,判断栈弹出的字符是否为非终结符
for (int j = 0; j < M; j++)
{
if (v == Vn[j])
{
m = j;
break;
}
}
if (m == -1)
{
cout << "fzjf";
cout << "第" << i << "个字符" << input[i] << "匹配错误" << endl;
cout << input << "为非法符号串" << endl;
break;
}
//栈弹出的字符为非终结符
for (int j = 0; j < N; j++)
{
if (input[i] == Vt[j])
{
n = j;
break;
}
}
//匹配情况
if (L[m][n] == "ERROR")
{
cout << "第" << i << "个字符" << input[i] << "匹配错误" << endl;
cout << input << "为非法符号串" << endl;
break;
}
if (L[m][n] == "NULL")
{
cout << j << "\t";
outstack();
cout << "\t\t";
outinput(input, i);
cout << "\t\t\t\t" << v << "->" << "ε" << endl;
continue;
}
else
{
//
string t = "";
for (int j = L[m][n].length() - 1; j >= 0; j--)
{
t += L[m][n][j];
analyse.push(L[m][n][j]);
}
cout << j << "\t";
outstack();
cout << "\t\t";
outinput(input, i);
cout << "\t\t" << v << "->" << L[m][n];
cout << "\t\tPOP,PUSH(" << t << ")";
}
}
cout << endl;
}
if(i == input.length())
cout << input << "为合法符号串" << endl;
}
int main()
{
cout << "输入一以#结束的符号串(包括+—*/()i#):";
string in;
cin >> in;
cout << "匹配过程如下" << endl;
LL_1(in);
system("pause");
}