编译原理-实验

运行结果都是截图,上传截图的时候网有点差,所以就没有代码运行结果了。

【实验一】词法分析

〖1.实验目的〗

加深对词法分析器的工作过程的理解;加强对词法分析方法的掌握;能够采用一种编程语言实现简单的词法分析程序;能够使用自己编写的分析程序对简单的程序段进行词法分析。

〖2.实验内容〗 类C语言的手工词法分析器设计

〖3.实验要求〗

(1)C语言子集(最好能对实际的C源代码进行词法分析,从而省去样例设计)

(2)一符一种

(3)单字符单词(算符、界符)的种码等于其ASCII码,不必设计;
     关键字和双字符运算符,种码从130开始编码。

(4)如果以真实的C源程序为测试样例,请酌情考虑一些问题:

转义字符

编译预处理行(以'#'开始的行,#include、#define、#if、#else、……,等等)

带续行的宏定义。

注释(单行注释// ... 和块注释 /* ... */)

……

〖4.实验步骤〗

1.定义目标语言的可用符号表和构词规则;

2.依次读入源程序符号,对源程序进行单词切分和识别,直到源程序结束;

3.对正确的单词,按照它的种别以<种别码,值>的形式保存在符号表中;

4.对不正确的单词,做出错误处理。

〖5.技术路线〗

这里以开始定义的C语言子集的源程序作为词法分析程序的输入数据。在词法分析中,自文件头开始扫描源程序字符,一旦发现符合“单词”定义的源程序字符串时,将它翻译成固定长度的单词内部表示,并查、填适当的信息表。经过词法分析后,源程序字符串(源程序的外部表示)被翻译成具有等长信息的单词串(源程序的内部表示),并产生两个表格:常数表和标识符表,它们分别包含了源程序中的所有常数和所有标识符。

1.关键字: if、int、for、while、do、return、break、continue。

2.标识符;单词。

3.常数为无符号整形数。

4.运算符包括: +、一、*、/、=、、<、=、<=、!= 。

5.分隔符包括:,、 ;、{、}、(、)。

     空格、注释:在词法分析中要去掉。

单词符号

种别码

单词符号

种别码

begin

1

\0

10

if

2

(

26

then

3

)

27

while

4

[

28

do

5

]

29

end

6

{

30

int

7

}

31

main

8

,

32

return

12

:

33

cout

13

;

34

l(l|d)*

10

>

35

:=

18

<

36

dd*

20

>=

37

==

21

<=

38

+

22

!=

40

-

23

41

*

24

#

0

/

25

-1

〖6.实现过程〗

1.如果是多位数,跳过后面的数字直接读数字后的运算符

2.若数字后面不是四个基本运算符,则出错

3. 是/,但 除数是0,则出错

〖6.实现过程〗

1.定义部分:定义常量、变量、数据结构。

2.初始化:从文件将源程序全部输入到字符缓冲区中。3.取单词前:去掉多余空白。

4.取单词:利用实验一的成果读出单词的每一个字符,组成单词,分析类型。

5.显示结果。

代码:

#include<iostream>
#include<fstream>
#include<string>

using namespace std;

#define MAX_KEYWORD 100
#define MAX_OPERATE 50
#define MAX_SEPARAROR 20	//分隔fu

string keyword[MAX_KEYWORD] = {"include","main","void","using",
"namespace","int","long","short","double","float","char",
"wchar_t","true","false","bool","define","signed",
"unsigned","struct","enum","union","new","delete",
"sizeof","const","static","const_cast","if","else","while","do",
"continue","for","goto","break","switch","case","return","class",
"this","public","private","protected","extern","default","friend","try",
"throw","typedef","virtual"
} ;
char operate[MAX_OPERATE] = {
'+','-','*','*','%','=','<','>','|','&','!','^'
};
char separator[MAX_SEPARAROR]={
' ',',',';','{','}','(',')','\"',
'\'','[',']','#','\n','\t'
};
bool isSeparator(char ch){
	for(int i=0;i<MAX_SEPARAROR;i++)
		if(ch==separator[i])
			return true;
	return false;
}
bool isOperate(char ch){
	for(int i = 0;i<MAX_OPERATE;i++)
		if(ch==operate[i])
			return true;
	return false;
}
bool isKeyword(string str){
	for(int i=0;i<MAX_KEYWORD;i++)
		if(!str.compare(keyword[i]))
			return true;
	return false;
}
bool isConstant(string str){
	for(int i=0;i<str.size();i++)
		if(str[i]<'0'||str[i]>'9')
			return false;
	return true;
}
int main()
{
	string buffer = "";
	string token = "";
	char ch = 's';
	FILE *fp = NULL;
	fp = fopen("input.txt","r");
	if(fp == NULL){
		cout<<"open file failed!"<<endl;
		exit(1);
	}
	while((ch = fgetc(fp))!= EOF){
		buffer += ch;
	}
	//cout<<endl<<buffer<<endl;
	//词法分析过程
	for(int i = 0;i<buffer.size();i++){
		//如果是分隔符,判断token并输出,并判断这个分隔符是否为空格,换行符,或制表符,是则跳过,不是则输出
		if(isSeparator(buffer[i])){
			if(isKeyword(token)){
				if(token.size()!=0){
					cout<<"(1,\""<<token<<"\")"<<endl;
					token = "";
				}
			}
			else if(isConstant(token)){
				if(token.size()!=0){
					cout<<"(3,\""<<token<<"\")"<<endl;
					token = "";
				}
			}
			else{
				if(token.size()!=0){
					cout<<"(2,\""<<token<<"\")"<<endl;
					token = "";
				}
			} 
			if(buffer[i]==' '||buffer[i]=='\n'||buffer[i]=='\t'){
				;
			}
			else{
				cout<<"(5,\""<<buffer[i]<<"\")"<<endl;
			}
		}
		else if(isOperate(buffer[i])){
			if(isKeyword(token)){
				if(token.size()!=0){
					cout<<"(1,\""<<token<<"\")"<<endl;
					token = "";
				}
			}
			else if(isConstant(token)){
				if(token.size()!=0){
					cout<<"(3,\""<<token<<"\")"<<endl;
					token = "";
				}
			}
			else{
				if(token.size()!=0){
					cout<<"(2,\""<<token<<"\")"<<endl;
					token = "";
				}
			} 
			if(buffer[i]=='/'&&buffer[i+1]=='/'){
				while(buffer[i]!='\n'){
					i++;
				}
				i+=2;
			}
			else if(buffer[i]=='/'&&buffer[i+1]=='/'){
				while(!(buffer[i]=='*'&&buffer[i+1]=='/')){
					i++;
				}
				i+=2;
			}
			else{
				cout<<"(4,\""<<buffer[i]<<"\")"<<endl;
				if(isOperate(buffer[i+1])){
					cout<<"(4,\""<<buffer[i]<<"\")"<<endl;
					i++;
				}
			}
		}
		else{
			token += buffer[i];
		}
	} 
	return 0;
}

〖7.输入样例〗

〖8.样例输出(实验结果)〗

〖9.实验评价〗

通过实验,我加深对词法分析器的工作过程的理解并;加强对词法分析方法的掌握;能够采用一种编程语言实现简单的词法分析程序;能够使用自己编写的分析程序对简单的程序段进行词法分析。

【实验二】 递归下降分析

〖1.实验目的〗

加深对语法分析器工作过程的理解;加强对预测分析法实现语法分析程序的掌握; 能够采用一种编程语言实现简单的语法分析程序;能够使用自己编写的分析程序对简单的程序段进行语法翻译。

〖2.实验内容〗 算术表达式的分析和计算(算术四则运算;整常数、浮点常数;调用自己写的词法分析)。

〖3.实验要求〗

1. 对语法规则有明确的定义;

2.编写的分析程序能够对实验--的结果进行正确的语法分析;

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

〖4.实验步骤〗

这个题目属于比较典型的递归下降语法分析。需要先将原算术表达式方法改写这个题目属于比较典型的递归下降语法分析。需要先将原算术表达式方法改写

为LL(1)文法为:

E-->TE'

E'-->+TE"ε

T-->FT'

T'->*FT|ε

F-->(E)|i

然后再为每个非终结符设计一个对应的函数,通过各函数之间的递归调用从而

实现递归下降语法分析的功能。具体方法为:

(1)当遇到终结符a时,则编写语句If(当前读到的输入符号==a)读入下一个输入符号

(2)当遇到非终结符A时,则编写语句调用A()。

(3)当遇到A-->ε规则时,则编写语句If(当前读到的输入符号不属于Follow(A))error()

(4)当某个非终结符的规则有多个候选式时,按LL(1)文法的条件能唯一地选择一个候选式进行推导.

〖5.技术路线〗

E-->TE'

E'-->+TE"ε

T-->FT'

T'->*FT|ε

F-->(E)|i

〖6.实现过程〗

1.如果是多位数,跳过后面的数字直接读数字后的运算符

2.若数字后面不是四个基本运算符,则出错

3. 是/,但 除数是0,则出错

代码:

#include<iostream>
#include<string>
using namespace std;
/*
E->TE'
E'->+TE'
T->FT'
T'->*FT'
F->i|(E)
*/
void advance();
void error();
void e();
void e1();
void t(); 
void t1();
void f();
string str;
char sym;
int p = 0;
bool result = true;
int main()
{
	cin>>str;
	sym = str[p];
	e();
	if(result){
		cout<<"Accept!"<<endl;
	}
	else{
		cout<<"Error!"<<endl;
	}
	return 0;
}
void advance(){
	cout<<"advance()"<<endl;
	if(isdigit(str[p])){
		while(isdigit(str[p]))
			p++;
		if(p<str.length()&&str[p]!='+'&&str[p]!='*'&&str[p]!=')'){
			cout<<"数字后出错!"<<endl;
			error();
		}
	}
	else
		p++;
	sym = str[p];
}
void error(){
	result = false;
}

void e(){
	cout<<"e()"<<endl;
	t1();
	e1();
}
void e1(){
	cout<<"e1()"<<endl;
	if(sym == '+'){
		advance();
		t();
		e1();
	}
}
void t(){
	cout<<"t()"<<endl;
	f();
	t1();
}
void t1(){
	cout<<"t1()"<<endl;
	if(sym =='*'){
		advance();
		f();
		t1();
	}
}
void f(){
	cout<<"f()"<<endl;
	if(isdigit(sym)){
		advance();
	}
	else{
		if(sym=='('){
			advance();
			e();
			if(sym==')'){
				advance();
			}
			else{
				error();
			}
		}
		else{
			error();
		}
	}
}

〖7.输入样例〗

1.整数加法:1+1

2.浮点数减法:1.1-1.1

3.浮点数整数混合乘法:1.1*1

4.浮点数除法:1.1/2.1

5.错误样例:1/0 2.1/0 2..2+1 2.2++2 2.1+1=3.2

〖8.样例输出(实验结果)〗

1.整数加法:1+1

2.浮点数减法:1.1-1.1

3.浮点数整数混合乘法:1.1*1

4.浮点数除法:1.1/2.1

5.错误样例:1/0 2.1/0 2..2+1 2.2++2 2.1+1=3.2

1/0

2.1/0

2..2+1

2.2++2

2.1+1=3.2

〖9.实验评价〗

首先尝试进行编写简单的加法与乘法,再进一步完善代码,加入减法与除法,同时从只支持整数判断增加到支持浮点数判断,不断的完善分析器分析过程。

第13周:实验三。

【实验三】 基于预测表的语法分析

〖1.实验目的〗加深对语法分析器工作过程的理解;加强对预测分析法实现语法分析程序的掌握;能够采用一种编程语言实现简单的语法分析程序;能够使用自己编写的分析程序对简单的程序段进行语法翻译。

〖2.实验内容〗(1)LL(1)预测分析;(2)LR(0)分析

〖3.实验要求〗1)对语法规则有明确的定义;(2)对于遇到的语法错误,能够做出简单的错误处理,给出简单的错误提示,保证顺利完成语法分析过程;(3)实验报告要求用文法的形式对语法定义做出详细说明,说明语法分析程序的工作过程,说明错误处理的实现。

〖4.实验步骤〗(1)定义目标语言的语法规则;(2)求解预测分析方法需要的符号集和分析表;(3)依次读入实验一的分析结果,根据预测分析的方法进行语法分析,直到源程序结束;(4)对遇到的语法错误做出错误处理。

(1)LL(1)预测分析

1、判断文法G[S]是否LL(1)文法,若不是,将其转变为LL(1)文法;

2、对转变后的LL(1)文法建立预测分析表;

〖5.技术路线〗

首先给出初始的语法规则,然后依照给出的语法规则建立符号集与分析表,最后读入需要进行分析的语法,得出结果。

〖6.实现过程〗

给定初始文法

G[E]={ E->TA

A->+TA

T->FB

B->*FB

F->i

F->(E)}非终结符:E A T B F

终结符:+ * i ( )

(1)LL(1)分析的过程:
假设当前等待匹配的符号为 a,进行匹配的非终结符为A,A 的所有产生式为 A → α1 | α2 | … | αn

若 a ∈ FIRST(αi),则将 A 推导为 αi,a得到匹配,斯巴拉西,继续分析下一个符号 ①

若 a ∉ FIRST(αi)

若 ε ∈ FIRST(αi),且 a ∈ FOLLOW(A),则将 A 推导为 ε,a 交给 A 后面的串匹配 ②

否则,匹配失败,a 此时在输入串中是语法错误 ③

如果这个文法是LL(1)文法,则每一步都能且只能在①②③中确定,每一步都只有一种情况。这就是LL(1)分析。

因此需要构造FIRST集合和FOLLOW集合,FIRST集合

按照FIRST集合的定义,FIRST(α) = { a | α * a…,a ∈ VT }
将 α 分情况讨论

α = X,X ∈ VT∪VN

α = X1X2…Xn,Xi ∈ VT∪VN

通过反复扫描产生式,运用以下规则构造FIRST集合,直到每个FIRST(α)不再变化

构造每个文法符号的FIRST集合
对于每一个X∈VT∪VN

若X∈VT,则FIRST(X) = { X };

若X∈VN,

若产生式形如 X → a…,则把 a 加入到FIRST(X)中;
若 X → ε 也是产生式,则把 ε 也加入到FIRST(X)中;

若 X → Y… 是一个产生式,且 Y∈VN,则把FIRST(Y)中的所有非 ε 元素加入到FIRST(X)中;

若 X → Y1Y2…Yi-1Yi…Yk,Y1 ~ Yi-1 都是非终结符,
若FIRST(Y1) ~ FIRST(Yi-1)均含有 ε,即 Y1…Yi-1 ⇒* ε,则将FIRST(Yi)加入FIRST(X);
若所有 FIRST(Y) 均含有 ε,即 Y1Y2…Yi-1Yi…Yk ⇒* ε,则将 ε 加入FIRST(X);

构造任何符号串 α 的FIRST集合
对于文法 G 的任何符号串 α = X1X2Xn 构造集合FIRST(α),

将FIRST(X1) - {ε} 加入FIRST(α);

若FISRT(X1) ~ FIRST(Xi-1)均包含 ε,则将FIRST(Xi) - {ε} 加入FIRST(α);

若FISRT(X1) ~ FIRST(Xn)均包含 ε,则将 ε 加入FIRST(α);

构造FOLLOW集合

首先是FOLLOW集合的定义 FOLLOW(A) = { a | S * …Aa…,a ∈ VT }

反复使用以下规则,对文法 G 的每个非终结符A构造FOLLOW(A),直至每个FOLLOW不再变化为止

将输入串结束标志 # 加入文法的开始符号S对应的FOLLOW(S)中

若 A → αBβ 是一个产生式,则把 FIRST(β) - {ε} 加入到FOLLOW(B)中

若 A → αB 是一个产生式,或 A → αBβ 是一个产生式而 β ⇒*ε(即 ε ∈ FIRST(β)),则把 FOLLOW(A)加入到FOLLOW(B)中

代码:

#include <iostream>
#include <string.h>
#include <iomanip>
#include "Stack.h"
using namespace std;
 
#define ROW_SIZE 10
#define COLUMN_SIZE 10
#define RULE_SIZE 10
 
char row[ROW_SIZE];
char column[COLUMN_SIZE];
char Rule[RULE_SIZE];
 
///定义一个结构体,存储规则右部的值
typedef struct
{
    char rules[COLUMN_SIZE][RULE_SIZE];
}analyse;
 
///函数声明部分
int element_Search(char a[],char e,int length);
int rule_Search(analyse anal[],char e,char element,int m,int n);
int analyse_Process(Stack s,char str[],analyse anal[],int m,int n);
 
/**
样例:
5 6
m ( ) + * #
S ->AT ->AT 0 0 0 0
A ->BU ->BU 0 0 0 0
T 0 0 ->$ ->+AT 0 ->$
U 0 0 0 ->$ ->*BU ->$
B ->m ->(S) 0 0 0 0
m+m*m#
2 3
p q #
S ->pR ->qq 0
R ->p ->q 0
pqqppq#
2 3
a b #
S ->aM ->bb 0
M ->a ->b 0
abb#
**/
 
int main()
{
    ///将预测分析表输入进行存储
    analyse anal[ROW_SIZE];
    int m,n;
    int i,j;
    cout<<"请将预测分析表按下列要求进行输入"<<endl;
    cout<<"请输入两个整数"<<endl;
    cout<<"终结符个数:";
    cin>>m;
    cout<<"非终结符个数(包括‘#’):";
    cin>>n;
    cout<<"请输入终结符列:"<<endl;
    for(i=0;i<n;i++)
        cin>>column[i];
    cout<<"请按非终结符行输入预测分析表中的内容(对于没有内容的单元用0表示):"<<endl;
    for(i=0;i<m;i++)
    {
        cin>>row[i];
        for(j=0;j<n;j++)
        {
            cin>>anal[i].rules[j];
        }
    }
    while(1)
    {
        ///输入待分析符号串
        cout<<endl;
        char str[30];
        cout<<"请输入待分析符号串(以#结尾):";
        cin>>str;
        cout<<endl;
        Stack s;
        InitStack(s);
        int result=analyse_Process(s,str,anal,m,n);
        if(result==0)
        {
            cout<<"输入的串不是该文法的句子"<<endl;
            cout<<endl;
        }
        else
        {
            cout<<"输入的分析串是该文法的句子"<<endl;
            cout<<endl;
        }
        int cmd=0;
        while(1){
            cout<<"是否结束分析(Y/N):";
            char want;
            cin>>want;
            if(want=='Y' ||want=='y')
            {
                cmd=1;
                break;
            }
            else if (want=='n' || want=='N')
            {
                break;
            }
            else
            {
                cout<<"输入指令有误,请重新输入"<<endl;
            }
 
        }
 
        if(cmd==1)
        {
            break;
        }
    }
 
    return 0;
}
 
///分析过程的输出
int analyse_Process(Stack s,char str[],analyse anal[],int m,int n)
{
    Push(s,'#');///对栈进行初始化,将‘#’号和开始符入栈
    Push(s,row[0]);
    int i=0,j,step=1,k;
    char e=row[0],a[30];
    cout<<"用预测分析法分析符号串"<<str<<"的过程"<<endl;
    cout<<"step    Stack    String    Rule"<<endl;
    while(e!='#')
    {
       ///输出步骤数以及栈中的内容
       cout<<std::left<<setw(8)<<step;
       reverseTraverse(s);
 
       ///输出剩余匹配串
       k=0;
       for(j=i;str[j]!='\0';j++)
       {
           a[k]=str[j];
           k++;
       }
       a[k]='\0';
       printf("%-10s",a);
 
       Pop(s,e);
        if(e==str[i])
        {
            i++;
        }
        else
        {
            int result=rule_Search(anal,e,str[i],m,n);
            if(result==-1 || result==0)
            {
                cout<<"报错"<<endl;
                return 0;
            }
            else
            {
                int e2;
                for(j=strlen(Rule)-1;j>1;j--)
                {
                    e2=Rule[j];
                    if(e2!='$')
                    Push(s,e2);
                }
            }
        }
        ///输出Rule
       if(e=='#' && str[i-1]=='#')
          cout<<std::left<<setw(10)<<"接受"<<endl;
       else if (e==str[i-1])
          cout<<e<<std::left<<setw(10)<<"匹配"<<endl;
       else
          cout<<e<<std::left<<setw(10)<<Rule<<endl;
       step++;
    }
    return 1;
}
 
///从预测分析表中查找栈顶元素行,剩余串列查找规则
///如果没有该行或者该列,返回值为-1,如果该行没有规则(即为“0”时),返回值为0
///如果有规则,则将规则右部存储在Rule数组内,返回值1;
int rule_Search(analyse anal[],char e,char element,int m,int n)
{
    int row_index=element_Search(row,e,m);
    int col_index=element_Search(column,element,n);
    if(row_index==-1 || col_index==-1)
    {
        return -1;
    }
    else
    {
        if(strcmp(anal[row_index].rules[col_index],"0")==0)
        {
            return 0;
        }
        else
        {
            strcpy(Rule,anal[row_index].rules[col_index]);
            return 1;
        }
    }
}
 
///查找元素,返回其对应的下标,如果不存在返回-1;
int element_Search(char a[],char e,int length)
{
    int i,result=-1;
    for(i=0;i<length;i++)
    {
        if(a[i]==e)
        {
           result=i;
           break;
        }
    }
    return result;
}


stack.h
#ifndef STACK_H_INCLUDED
#define STACK_H_INCLUDED
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#define STACK_INT_SIZE 100 //存储空间初始分配量
#define STACKINCREMENT 10  //存储空间分配增量
typedef struct
{
    char *base;
    char *top;
    int stacksize;
}Stack;
 
///创造一个空栈
int InitStack(Stack &S)
{
   S.base=(char *)malloc(STACK_INT_SIZE*sizeof(char));
   if(!S.base)
    exit(-2);
   S.top=S.base;
   S.stacksize=STACK_INT_SIZE;
   return 1;
};
 
///销毁栈S,栈S不再存在
int DestroyStack(Stack &S)
{
    while(S.top!=S.base)
   {
       free(--S.top);
       S.top--;
   }
   free(S.base);
   return 1;
}
 
///把S置为空栈
int ClearStack(Stack &S)
{
   while(S.top!=S.base)
   {
       free(--S.top);
       S.top--;
   }
   return 1;
}
 
///返回S的元素个数,即栈的长度
int StackLength(Stack S)
{
    return S.top-S.base;
}
 
///返回S的元素个数,即栈的长度
int StackEmpty(Stack S)
{
    if(S.top==S.base)
        return 1;
    else
        return 0;
}
 
///若栈不为空,则用e返回栈顶元素,并返回OK,否则返回ERROR
int GetTop(Stack S,char &e)
{
   if(S.base==S.top)
    return 0;
   e=*(S.top-1);
   return 1;
}
 
///插入元素e为新的栈顶元素
int Push(Stack &S,char e)
{
   if(S.top-S.base>=S.stacksize)///栈满追加存储空间
   {
       S.base=(char *)realloc(S.base,(S.stacksize+STACKINCREMENT)*sizeof(char));
       if(!S.base)///存储分配失败
        exit(-2);
       S.top=S.base+S.stacksize;
       S.stacksize+=STACKINCREMENT;
   }
   *S.top=e;
   S.top++;
   return 1;
}
 
///若栈不为空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR;
int Pop(Stack &S,char &e)
{
    if(S.top==S.base)
        return 0;
    e=*--S.top;
    return 1;
}
 
///遍历栈中的元素
int StackTraverse(Stack S)
{
    if(S.base==NULL)
        return -1;
    if(S.base==S.top)
        printf("栈中没有元素\n");
    char *p;
    p=S.top;
    while(p-S.base>0)
    {
        p--;
        printf("%c ",*p);
    }
    printf("\n");
    return 1;
}
 
int reverseTraverse(Stack S)
{
    if(S.base==NULL)
        return -1;
    if(S.base==S.top)
        printf("栈中没有元素\n");
    char *p,a[30];
    int i=0;
    p=S.base;
    while(p<S.top)
    {
        a[i]=*p;
        i++;
        p++;
    }
    a[i]='\0';
    printf("%-9s",a);
    return 1;
}
#endif // STACK_H_INCLUDED

〖7.输入样例〗

5 6

m ( ) + * #

S ->AT ->AT 0 0 0 0

A ->BU ->BU 0 0 0 0

T 0 0 ->$ ->+AT 0 ->$

U 0 0 0 ->$ ->*BU ->$

B ->m ->(S) 0 0 0 0

m+m*m#

〖8.样例输出(实验结果)〗

〖9.实验评价〗通过实验进一步理解了预测表分析法。

第15周:实验四。

【实验五】 SLR(1)分析

〖1.实验目的〗

构造SLR(1)分析程序,利用它进行语法分析,判断给出的符号串是否为该文法识别的句子,了解LR(K)分析方法是严格的从左向右扫描,和自底向上的语法分析方法。

〖2.实验内容〗 SLR(1)分析〖含FIRST、FOLLOW、分析表生成、基于分析表的自底向上分析、等等〗

〖3.实验要求〗

1. 对语法规则有明确的定义;

2. 编写的分析程序能够对实验一的结果进行正确的语法分析;

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

〖4.实验步骤〗

->读取文件(获取输入的实验一运行结果、得到表达式)
->扫描文法(构建终结符号、非终结符号集、First/Follow集)
->造表(构造项目集族、构造ACTION/GOTO、设置四元式)
->SLR分析(判断是否可以被分析程序接受、生成四元式)

〖5.技术路线〗

读取文件->扫描文法->造表->SLR分析

〖6.实现过程〗

读取文件:readfile()将文件中的实验一的运行结果读入,整合为字符数组,准备好以待匹配。

扫描文法:scan()将文法数组G[]中的string字符串进行扫描,构造终结符号集和非终结符号集;之后按照SLR(1)文法中FIRST集和FOLLOW集的构造方法进行这两个集合的构造。在构造FIRST集时,用到了find函数,用来不断地递归查找产生式首符号为非终结符号的产生式,以构造FIRST集;在构造FOLLOW集时,使用到了findVn函数,用来返回指定的非终结符对应的标号,以进行FOLLOW集的计算。同时,通过不断的循环、加入,当所有非终结符的FOLLOW集总大小不变时,跳出循环,构造完成。至此,造表前的准备工作全部完成,接下来进入造表阶段。

造表:table()完成整个项目集族的生成以及ACTION/GOTO的构造,以及在造表过程中对于四元式的标注,也是个人认为整个程序中最难的部分。

首先,进行初始设定:添加文法的起始符号对应的产生式,用以从初态进行迭代;之后将该产生式(点还处在最开始的部位tag=3, 尚未进入规约状态finish=0)加入第一个状态I0中,接下来进行一波循环操作:首先对于当前所有的项目集族进行闭包操作,该功能被封装在闭包函数closure()中(函数中对于每一个点操作的非终结符均进行闭包操作,加入该状态中,最终返回经过闭包操作的该状态);之后对于当前状态的每一条待移进的产生式分别进行移进操作,并添加为新的状态,等待循环到该状态时的闭包操作。在移进过程中,对于不是规约的(即尚可移进的)产生式,若点操作为非终结符,置GOTO(当前状态, 非终结符) = 新状态号;若为终结符,则置ACTION(当前状态,终结符号) = s+新状态号。若存在移进时的相同符号,则加入同一状态中,不再另设新状态。之后即为对于新状态的加入判断:若新状态的闭包操作closure()已经在已形成的项目集族中出现了,则不再添加这个新状态,并且修改对应的ACTION或者GOTO(一个pop()操作+一个push()操作);否则添加。对于需要规约的产生式,首先对其进行判断是否可以产生四元式:若可以,则进行记录,以便之后判断;否则不进行记录。至此,造表结束,接下来正式进入分析阶段。

SLR分析:SLR()完成最终整个SLR(1)文法的分析任务,也包括分析过程中完成的四元式构建。在分析过程起始时,首先将状态I0压入栈中,分析开始:若ACTION(栈顶,当前符号)不是acc(在程序中为r0),则开始循环:如果是err(在程序中使用0代替),报错;否则如果是sj,状态栈、符号栈、属性栈均压栈,读取下一个符号;如果是ri,则通过需要规约第i条产生式,对于三个栈,执行第i条产生式右部的符号数次pop()操作,同时更新属性栈,用以后续的四元式生成工作;在生成四元式时,首先判断正在规约的产生式是否可以产生四元式,同时确定四元式的op,其次判断两个参数(arg1,arg2)是否为变量or变量组成的表达式。如果是表达式,则查找之前的符号表,进行代替操作。若中间不报错而跳出循环,则成功匹配。至此,按照事先约定好的输出格式进行输出,分析任务完成。

代码:

#include<iostream>
#include<string>
#include<stdlib.h>
#define _ '0'
using namespace std;

int getRow(char ch);
int getColumn(char ch);
bool isS(char ch);
bool isR(char ch);
void reduce(char ch);

string str;
char stck1[50];
char stck2[50];
int p,q1,q2;
char s[12]={'0','1','2','3','4','5','6','7','8','9','A','B'};
char r[7]={'a','b','c','d','e','f','g'};
char table[12][9]={'5',_,_,'4',_,_,'1','2','3',//0
					_,'6',_,_,_,'K',_,_,_,//1
					_,'c','7',_,'c','c',_,_,_,//2
					_,'e','e',_,'e','e',_,_,_,//3
					'5',_,_,'4',_,_,'8','2','3',//4
					_,'g','g',_,'g','g',_,_,_,//5
					'5',_,_,'4',_,_,_,'9','3',//6
					'5',_,_,'4',_,_,_,_,'A',//7
					_,'6',_,_,'B',_,_,_,_,//8
					_,'b','7',_,'b','b',_,_,_,//9
					_,'d','d',_,'d','d',_,_,_,//10
					_,'f','f',_,'f','f',_,_,_//11
};
/*主函数*/
int main()
{
	cin>>str;
	str+="#";
	stck1[0]='0';
	stck2[0]='#';
	p=0;
	q1=0;
	q2=0;
	cout<<"状态栈:"<<"\t符号栈"<<"\t输入串:"<<"\t动作\n";
	while(1)
	{
		int i;
		for(i=0;i<=q1;i++)
		{
			cout<<stck1[i];
		}
		cout<<"\t\t";
		for(i=0;i<=q2;i++)
		{
			cout<<stck2[i];
		}
		cout<<"\t\t";
		for(i=p;i<str.length();i++)
		{
			cout<<str[i];
		}
		cout<<"\t\t";
		char ch;
		if(isdigit(str[p]))
		{
			ch=table[getRow(stck1[q1])][0];
			while(isdigit(str[p]))
			{
				p++;
			}
			p--;
		}
		else
		{
			ch=table[getRow(stck1[q1])][getColumn(str[p])];
		}
		if(isS(ch))
		{
			cout<<"移进!"<<endl;
			q1++;
			stck1[q1]=ch;
			q2++;
			stck2[q2]=str[p];
			p++;
		}
		else if(isR(ch))
		{
			cout<<"规约!"<<endl;
			reduce(ch);
		}
		else if(ch=='K')
		{
			cout<<"Accept!"<<endl;
			break;
		}
		else
		{
			cout<<"Error!"<<endl;
			break;
		}
	}
	return 0;
}
int getRow(char ch)
{
	switch(ch)
	{
		case'0':case'1':case'2':case'3':
		case'4':case'5':case'6':case'7':
		case'8':
		case'9':return ch-'0';
		case'A':return 10;
		case'B':return 11;
		default:return 12;
	}
}
int getColumn(char ch)
{
	switch(ch)
	{
		case'i':return 0;
		case'+':return 1;
		case'*':return 2;
		case'(':return 3;
		case')':return 4;
		case'#':return 5;
		case'E':return 6;
		case'T':return 7;
		case'F':return 8;
		default:return 10;
	}
}
bool isS(char ch)
{
	bool res =false;
	for(int i=0;i<12;i++)
	{
		if(ch==s[i])
		{
			res=true;
		}
	}
	return res;
}
bool isR(char ch)
{
	bool res =false;
	for(int i=0;i<7;i++)
	{
		if(ch==r[i])
		{
			res=true;
		}
	}
	return res;
}
void reduce(char ch)
{
	char tmp;
	switch(ch)
	{
		case'a':
			q1-=1;
			q2-=1;
			tmp=table[getRow(stck1[q1])][getColumn('S')];
			q1++;
			q2++;
			stck1[q1]=tmp;
			stck2[q2]='S';
			break;
		case'b':
			q1-=3;
			q2-=3;
			tmp=table[getRow(stck1[q1])][getColumn('E')];
			q1++;
			q2++;
			stck1[q1]=tmp;
			stck2[q2]='E';
			break;
		case'c':
			q1-=1;
			q2-=1;
			tmp=table[getRow(stck1[q1])][getColumn('E')];
			q1++;
			q2++;
			stck1[q1]=tmp;
			stck2[q2]='E';
			break;
		case'd':
			q1-=3;
			q2-=3;
			tmp=table[getRow(stck1[q1])][getColumn('T')];
			q1++;
			q2++;
			stck1[q1]=tmp;
			stck2[q2]='T';
			break;
		case'e':
			q1-=1;
			q2-=1;
			tmp=table[getRow(stck1[q1])][getColumn('T')];
			q1++;
			q2++;
			stck1[q1]=tmp;
			stck2[q2]='T';
			break;
		case'f':
			q1-=3;
			q2-=3;
			tmp=table[getRow(stck1[q1])][getColumn('F')];
			q1++;
			q2++;
			stck1[q1]=tmp;
			stck2[q2]='F';
			break;
		case'g':
			q1-=1;
			q2-=1;
			tmp=table[getRow(stck1[q1])][getColumn('F')];
			q1++;
			q2++;
			stck1[q1]=tmp;
			stck2[q2]='F';
			break;
	}
}

〖7.输入样例〗

1*2+3*4

254-(15+13)/2

〖8.样例输出(实验结果)〗

〖9.实验评价〗通过本次实验了解了SLR(1)语法和LR(0)和LR(1)语法的区别。

第16周:实验五。

【实验六】中间代码生成

〖1.实验目的〗通过设计、编制、调试一个具体的算术表达式求值的程序,加深对编译器计算表达式方法的理解,并掌握从中缀式到后缀式的转换方法。

〖2.实验内容〗中间代码生成〖含符号表管理、虚拟机、等等〗

〖3.实验要求〗

(1)输入一个算术表达式 ,求出对应的后缀式(逆波兰式);
(2)选择数据结构——栈结构,计算表示的值;
(3)输出中缀式和求得的后缀式以及计算出的值;
(4)输出后缀式的四元式

〖4.实验步骤〗

(1)终结符只有综合属性,它由词法分析器提供。

(2)非终结符既可以有综合属性也可以有继承属性,文法开始符号的所有继承属性作为属性计算前的初始值。

(3)产生式右边符号的继承属性和产生式左边符号的综合属性都必须提供一个计算规则。

(4)产生式左边符号的继承属性和产生式右边符号的综合属性由其它产生式的属性规则计算。

〖5.技术路线〗

算法流程图如下所示,首先输入表达式,然后进行词法分析,把词法分析的结果存在结构体中,之后调用递归下降分析器中的表达式子程序进行分析,最后得到四元组并存在相应的结构体中,下一步进行判断,如果是算术表达式,就计算该算术表达式的值并输出,如果不是算术表达式,就不做处理,直接输出四元组,最后判断程序的输入是否结束,如果没有结束,就再次输入表达式,重复上述步骤,如果结束,则程序退出。

〖6.实现过程〗

代码:

#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#define max 100
typedef struct {
	char operate_sign;
	double operator_1;
	double operator_2;
	double result;
} S;
S siyuan[max];
int siyuan_flag=0;
char ex[max];
void trans(){
	char str[max];
	char stack[max];
	char ch;
	int sum,i,j,t,top=0;
	printf("算数表达式:");
	i=0;
	gets(str);
	sum=strlen(str);
	strcat(str,"#");
	t=1;
	i=0;
	ch=str[i];
	while(ch!='#'){
		switch(ch){
		case '(':
				top++;
				stack[top]=ch;
			    break;
        case ')':
				while(stack[top]!='('){ 
					ex[t]=stack[top];
					top--;
					t++;
				}
			top--;
			break;
        case '+':
		case '-':       
			while(top!=0&&stack[top]!='('){
				ex[t]=stack[top];
				top--;
				t++;
			}
			top++;
			stack[top]=ch;
			break;
		case '*':  
        case '/':
			while(stack[top]=='*'||stack[top]=='/'){
				ex[t]=stack[top];
				top--;
				t++;
			}
			top++;
			stack[top]=ch;
			break;
		case ' ':break;
		default:
			if(ch>='0'&&ch<='9'||ch=='.'){
				while(ch>='0'&&ch<='9'){    
					ex[t]=ch;
					t++;
					i++;
					ch=str[i];
				}
				if(ch=='.'){
				    ex[t]=ch;
					t++;
					i++;
					ch=str[i];
				      	while(ch >='0'&&ch<='9')
						  {
					        ex[t]=ch;
							t++;
							i++;
							ch=str[i];
						  }
					  }
					i--;
					ex[t]='#';
					t++;
			}
			else if(ch!='('||ch!=')'||ch!='+'||ch!='-'||ch!='*'||ch!='/') {
				printf("输入非法字符!!\n");
				exit(0); 
			}
		}
		i++;
		ch=str[i];
	}
	while(top!=0){
		ex[t]=stack[top];
		t++;
		top--;
	}
	ex[t]='#';
	printf("\n\t原来表达式:");
	for(j=0;j<sum;j++)//7
		printf("%c",str[j]);
    printf("\n\t后缀表达式:",ex);
	for(j=1;j<t;j++)
		printf("%c",ex[j]);
}
void compvalue(){
	int i;
	double stack[max],d;
	char ch;
	int t=1,top=0; 
	double _num;
	int len;
	double num; 
	ch=ex[t];
	t++;
    while(ch!='#'){
		switch(ch){
			  case '+':
				  siyuan[siyuan_flag].operate_sign ='+';
				  siyuan[siyuan_flag].operator_1=stack[top];
				  siyuan[siyuan_flag].operator_2=stack[top-1];
				  stack[top-1]=stack[top-1]+stack[top];
				  siyuan[siyuan_flag].result=stack[top-1];
				  siyuan_flag++;
				  top--;
				  break;
		      case '-':
				  siyuan[siyuan_flag].operate_sign ='-';
				  siyuan[siyuan_flag].operator_1=stack[top];
				  siyuan[siyuan_flag].operator_2=stack[top-1];
				  stack[top-1]=stack[top-1]-stack[top];
				  siyuan[siyuan_flag].result=stack[top-1];
				  siyuan_flag++;
				  top--;
				  break;
		      case '*':
				  siyuan[siyuan_flag].operate_sign ='*';
				  siyuan[siyuan_flag].operator_1=stack[top];
				  siyuan[siyuan_flag].operator_2=stack[top-1];
				  stack[top-1]=stack[top-1]*stack[top];
				  siyuan[siyuan_flag].result=stack[top-1];
				  siyuan_flag++;
				  top--;
				  break;
		      case '/':
				  if(stack[top]!=0){
					siyuan[siyuan_flag].operate_sign ='/';
				    siyuan[siyuan_flag].operator_1=stack[top];
				    siyuan[siyuan_flag].operator_2=stack[top-1];
				    stack[top-1]=stack[top-1]/stack[top];
					siyuan[siyuan_flag].result=stack[top-1];
				    siyuan_flag++;
				  }
		          else{  printf("\n\t除零错误!\n");
			          exit(0);
				  }
				  top--;
				  break;
			  default:
				 d=0;
				 _num=0;
				 len=0; 
				 num=0;
				 while(ch>='0'&&ch<='9'){
						  d=10*d+ch-'0';   
						  ch=ex[t];
						  t++;
				 }
				 if(ch=='.'){
				  	ch=ex[t];
					t++;
				  	while(ch >='0'&&ch<='9')
					  {
				       _num=_num*10+ch-'0'; 
				       len++;
				       ch=ex[t];
					   t++;
					  }
				  }
				  num=d+_num/(pow(10,len));
				  top++;
				  stack[top]=num;
		}
			ch=ex[t];
			t++;
	}
		printf("\n\t计算结果:%g\n",siyuan[siyuan_flag-1].result);
		printf("\n\t四元式:\n");
		for(i=0;i<siyuan_flag;i++){
			printf("\t(%c,%g,%g,%g)\n",siyuan[i].operate_sign,siyuan[i].operator_2
				    ,siyuan[i].operator_1,siyuan[i].result);
		}
}
int main(){	
	trans();
	compvalue();
	return 0;
}

〖7.输入样例〗

算数表达式:4*5-6/(1+2)

〖8.样例输出(实验结果)〗

〖9.实验评价〗

  • 5
    点赞
  • 86
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
编译原理实验中,使用Scala编程语言进行分析是一种常见的做法。Scala是一门功能强大的多范式编程语言,它融合了面向对象编程和函数式编程的特性。 在编译原理实验中,我们通常需要实现语言的词法分析和语法分析两个重要的步骤。词法分析器主要负责将源代码分解为各种词法单元,例如关键字、标识符、运算符和常量等。而语法分析器则根据词法单元构建语法树,以验证源代码是否符合给定的文法规则。 Scala提供了丰富的语言特性和工具,便于我们实现词法分析器和语法分析器。例如,我们可以使用Scala的正则表达式库来定义词法规则,并使用模式匹配语法来进行单词的匹配和提取。此外,Scala还支持高阶函数和匿名函数的特性,使得我们可以更方便地实现语法分析器。 除了词法分析和语法分析,编译原理实验中还需要实现其他功能,如语义分析、中间代码生成和目标代码生成等。Scala作为一门面向对象和函数式的编程语言,具有良好的可扩展性和可维护性,能够有效地支持这些功能的实现。 在实验过程中,我们可以利用Scala的强类型系统和模式匹配等特性来提高代码的可靠性和可读性。此外,Scala还提供了丰富的工具库和框架,如ANTLR和ScalaTest等,可以进一步简化编译过程中的实现和测试工作。 总之,使用Scala编程语言进行编译原理实验分析是一种有效和方便的选择。Scala的多范式特性、丰富的语言特性和工具支持,可以帮助我们实现词法分析器、语法分析器和其他编译器的各个部分,并提高代码的可读性和可维护性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值