📄 编译器.cpp
字号:
#include <iostream.h>
#include <fstream>
#include <stdlib.h>
#include <string>
using namespace std;
#define norow 13 //保留字的个数
#define nmax 14 //数字允许的最长位数
#define al 10 //标识符最长长度
#define amax 2047//
#define levmax 3 //最大层差
#define cxmax 200 //类PCODE目标代码数组长度(可容纳代码行数)
#define txmax 100 //标识符表的长度(容量)
fstream infile; //源文件
char ch; //存放最近一次从文件中读出的字符
string sym; //存放最近一次识别出来的保留字的类型
string id; //存放最近一次识别出来的标识符的名字
int num; //存放最近一次识别出来的数字的值
int cc; //行缓冲区指针
int ll; //行缓冲区长度
int kk;
int cx; //代码分配指针,代码生成模块总在cx所指位置生成新的代码
int tx; //登陆表指针
int dx; //数据段内存分配指针,指向下一个被分配空间在数据段中的偏移位置
int cx0; //记录本层开始时代码段分配位置
int tx0; //记录本层开始时符号表位置
int lev; //当前层号
char line[81]; //行缓冲区,用于从文件读出一行
char a[al]; //词法分析器中用于临时存放正在分析的词
string word[norow+1]; //保留字表
string wsym[norow+1]; //保留字表中每一个保留字对应的symbol类型
string ssym['^']; //一些符号对应的symbol类型表
char fname[20]; //PL/0语言源程序
int err; //出错次数
struct Code {
string f;
int l;
int a;
}code[cxmax]; //生成的类PCODE代码表,存放编译得到的类PCODE代码
struct table {
string kind; //符号的类型
string name; //符号的名字
int val; //常量的值
int level; //层差
int adr; //偏移地址
int size; //大小
}table[txmax];
struct symset {
string data[100]; //字符集合
int count; //字符个数
}fsys,facbegsys;
void addsym(symset f,string a) { //把字符a填加进f集合中
f.data[(f.count)++]=a;
}
void addsym2(symset *f,symset a) { //把集合a填加进集合f中
int i=0;
while (i!=a.count) {
f->data[(f->count)++]=a.data[i++];
}
}
bool insym(string a,symset b) { //判断字符串是否在集合b中
int i=0;
while (b.data[i]!=a&&i!=b.count) {
i++;
}
if (i==b.count)
return false;
else
return true;
}
void error(int n) { //出错处理过程
cout<<n<<"号错误!"<<endl;
err++;
}
void getch() { //读取一个字符
if (cc==ll) { //如果行缓冲区指针指向行缓冲区最后一个字符就从文件读一行到行缓冲区
if (infile.eof()) { //文件结束
exit(0);
}
ll=0;
cc=0;
infile.getline(line,sizeof(line)); //读取一行
int i=0;
while (line[i]!=NULL) {
ll++;
i++;
}
line[ll]='\n';
cc--;
}
cc++;
ch=line[cc];
}
void getsym() { //词法分析
int k;
while (ch==' '||ch=='\n') {
getch();
}
if (ch>='a'&&ch<='z') { //如果读出的字符是一个字母,说明是保留字或标识符
k=0; //标识符缓冲区指针置0
for (int j=0;j<al;j++) {//初始化a数组,使里面的内容全部为空
a[j]=NULL;
}
while ((ch>='a'&&ch<='z')||(ch>='0'&&ch<='9')) {
if (k<al) { //如果标识符长度没有超过最大标识符长度(如果超过,就取前面一部分,把多余的抛弃
a[k]=ch;
k++;
}
getch();
}
id=a;
for (int i=0;i<norow;i++) {
if (word[i]==id) {
sym=wsym[i];
break;
}
}
if (i==norow) {
sym="ident";
}
}
else if (ch>='0'&&ch<='9') { //如果读出字符是数字
k=0; //数字位数
num=0; //数字置为0
sym="number"; //置sym为number,表示这一次读到的是数字
while (ch>='0'&&ch<='9') {
num=10*num+ch-'0';
k++; //数字位数加1
getch();
}
if (k>nmax) { //如果组成的数字位数大于最大允许的数字位数
error(30);
}
}
else if (ch==':') { //如果读出的不字母也不是数字而是冒号
getch();
if (ch=='=') { //赋值号
sym="becomes";
getch();
}
else //错误
sym="NULL";
}
else if (ch=='<') { //如果读到小于号
getch();
if (ch=='=') { //<=号
sym="leq";
getch();
}
else
sym="lss"; //<号
}
else if (ch=='>') { //如果读到大于号,处理过程类似于处理小于号
getch();
if (ch=='=') { //>=号
sym="geq";
getch();
}
else
sym="gtr"; //>号
}
else { //说明是一个普通的符号,从符号表中找,并给sym值
sym=ssym[ch];
getch();
}
}
void gen(string x,int y,int z) { //本过程用于把生成的目标代码写入目标代码数组,供后面的解释器解释执行
if (cx>cxmax) {
cout<<"program too long"<<endl;
exit(0);
}
code[cx].f=x;
code[cx].l=y;
code[cx].a=z;
cx++;
}
void enter(string k) { //登陆符号表过程enter,k为constant, variable, procedur
tx++; //符号表指针指向一个新的空位
//开始登陆
table[tx].name=id;
table[tx].kind=k;
if (k=="constant") { //如果是常量名
if (num>amax) { //如果数值超过最大值
error(31);
num=0;
}
table[tx].val=num; //如是合法的数值,就登陆到符号表
}
if (k=="variable") { // 如果是变量名
table[tx].level=lev; //记下它所属的层次号
table[tx].adr=dx; //记下它在当前层中的偏移量
dx++; //偏移量自增一,为下一次做好准备
}
if (k=="procedur") { //如果要登陆的是过程名
table[tx].level=lev;
}
}
void test(symset s1,symset s2,int n) {
if (!insym(sym,s1)) { //如果当前符号不在s1中
error(n);
addsym2(&s1,s2); // 把s2集合补充进s1集合
}
while (!insym(sym,s1)) {
getsym();
}
}
//在符号表中查找指定符号所在位置的函数position
//参数:id:要找的符号
//返回值:要找的符号在符号表中的位置,如果找不到就返回0
int position(string id) {
int i;
table[0].name=id; //先把id放入符号表0号位置
i=tx; //从符号表最后一个位置开始找
while (table[i].name!=id) {
i--;
}
return i; //返回找到的位置号,如果没找到则一定正好为0
}
void listcode() { //列出当前一层类PCODE目标代码过程listcode
int i;
i=cx0;
while (i!=cx) {
cout<<code[i].f.data()<<" "<<code[i].l<<" "<<code[i].a<<endl;
i++;
}
}
/////////////////////////////////////////////////////////////////////////////
void term(symset fsys);
void expression(symset fsys){ //表达式处理
string addop;
if(sym=="plus"||sym=="minus"){ //一个表达式可能会由加号或减号开始,表示正负号
addop=sym; //把当前的正号或负号保存起来,以便下面生成相应代码
getsym();
//fsys+=addop;
addsym(fsys,addop);
term(fsys); //正负号后面应该是一个项,调term子程序分析
if(addop=="minus") //如果保存下来的符号是负号,如果不是负号就是正号,不需生成相应的指令
{
gen("opr",0,1); //生成一条1号操作指令:取反运算
}
}//if
else{
//term(fsys+[plus,minus]); //如果不是由正负号开头,就应是一个项开头,调用term子程序分析项
addsym(fsys,"plus");
addsym(fsys,"minus");
term(fsys);
while(sym=="plus"||sym=="minus"){ //项后应是加运算或减运算
addop=sym;
getsym(); //获取下一个token,加减运算符后应跟的是一个项
//fsys+=addop;
addsym(fsys,addop);
term(fsys); //调term子程序分析项
if(addop=="plus")
{
gen("opr",0,2); //如果项与项之间的运算符是加号,生成2号操作指令:加法
}
else
gen("opr",0,3); //生成3号操作指令:减法
}//while
}//else
}//expression
void factor(symset fsys) { // 因子处理
int i;
int j=0; //标志位
test(facbegsys,fsys,24); //开始因子处理前,先检查当前token是否在facbegsys集合中,如果不是合法的token,抛24号错误,并通过fsys集恢复使语法处理可以继续进行
while(insym(sym,facbegsys)){
if(sym=="ident"){ //如果遇到的是标识符
i=position(id);
if(i==0){
error(11);
}
else
{
if (table[i].kind=="constant")
j=1;
else if (table[i].kind=="variable")
j=2;
else if (table[i].kind=="procedur")
j=3;
switch(j){ //如果在符号表中找到了当前标识符的位置,开始生成相应代码
case 1 :
{
gen("lit",0,table[i].val); //如果这个标识符对应的是常量,值为val,生成lit指令,把val放到栈顶
break;
}
case 2 :
{
gen("lod",lev-table[i].level,table[i].adr); //如果标识符是变量名,生成lod指令,
break;
}
case 3 :
{
error(21); //如果在因子处理中遇到的标识符是过程名,出错了,抛21号错
break;
}
}//switch
}//else
}//if
if(sym=="number"){ //如果因子处理时遇到数字
if(num>amax) //如果数字的大小超过允许最大值amax
{
error(31);
num=0; //把数字按0值处理
}
gen("lit",0,num); //生成lit指令,把这个数值字面常量放到栈顶
getsym();
}//if
if(sym=="lparen"){
getsym();
//fsys="rparen"+fsys;
addsym(fsys,"rparen");
expression(fsys); //递归调用expression子程序分析一个子表达式
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -