COMPILERPL0语言的示例语法描述.docx
- 文档编号:27523241
- 上传时间:2023-07-02
- 格式:DOCX
- 页数:54
- 大小:383.66KB
COMPILERPL0语言的示例语法描述.docx
《COMPILERPL0语言的示例语法描述.docx》由会员分享,可在线阅读,更多相关《COMPILERPL0语言的示例语法描述.docx(54页珍藏版)》请在冰豆网上搜索。
COMPILERPL0语言的示例语法描述
补充:
PL/0语言及其编译程序的实现
1概述
✧PL/0语言的编译程序把PL/0语言程序翻译成为一种称为类pcode的假想的栈式计算机汇编语言程序。
这种汇编语言与机器无关,若要在某一机器上实现PL/0语言程序,只需用机器上配置的任何语言对类pcode语言程序进行解释执行。
✧世界著名计算机科学家N.Wirth编写了"PL/0语言的编译程序"。
✧后面附了PL/0编译程序源代码。
2.PL/0语言描述
⏹何为PL/0语言-PL/0语言是PASCAL语言的子集.具备了一般高级语言的必备部分.
(如:
read,write,if-then,do,while,call,begin-end,赋值语句)
☐PL/0语言中的数据类型只有整型,没有浮点数,所以圆周率只能近似为3。
数字最多为14位。
标识符的有效长度是10
☐PL/0语言允许过程嵌套定义和递归调用的。
过程最多可嵌套三层。
☐过程可以引用自己定义的局部标识符,也可以引用包围在它的外过程(包括主程序)定义的标识符。
以下将用扩充的巴科斯-瑙尔范式(BACKUS-NAURFORM)和语法图(EBNF)两种形式给出PL/0语言的语法描述。
-------------------------------------------------------------------------------------------------------------------------------------------------------
(a)PL/0语言文法的EBNF表示
EBNF表示的符号说明
1)〈〉:
用左右尖括号括起来的中文字表示语法构造成分,或称语法单位,为非终结符。
2)∷=:
该符号的左部由右部定义,可读作'定义为'。
3)|:
表示'或',为左部可由多个右部定义。
<标识符|常量>是不允许的.因为非终结符作为一个独立的单位,不可分割
4){}:
花括号表示其内的语法成分可以重复。
在不加上下界时可重复0到任意次数,有上下界时为可重复次数的限制。
如:
{*}表示*重复任意次,{*}38表示*重复3-8次。
例如<标识符>:
:
=l{l|d}09表示标识符的长度小于等于10.
5)[]:
方括号表示其内的成分为任选项。
6)():
表示圆括号内的成分优先。
PL/0语言文法的EBNF表示为:
〈程序〉∷=〈分程序〉.
〈分程序〉∷=[〈常量说明部分〉][〈变量说明部分〉][〈过程说明部分〉]〈语句〉
〈常量说明部分〉∷=CONST〈常量定义〉{,〈常量定义〉};
〈常量定义〉∷=〈标识符〉=〈无符号整数〉
〈无符号整数〉∷=〈数字〉{〈数字〉}
〈变量说明部分〉∷=VAR〈标识符〉{,〈标识符〉};
〈标识符〉∷=〈字母〉{〈字母〉|〈数字〉}
〈过程说明部分〉∷=〈过程首部〉〈分程序〉{;〈过程说明部分〉};
〈过程首部〉∷=PROCEDURE〈标识符〉;
〈语句〉∷=〈赋值语句〉|〈条件语句〉|〈当型循环语句〉|
〈过程调用语句〉|〈读语句〉|〈写语句〉|〈复合语句〉|〈空〉
〈赋值语句〉∷=〈标识符〉∶=〈表达式〉
〈复合语句〉∷=BEGIN〈语句〉{;〈语句〉}END
〈条件〉∷=〈表达式〉〈关系运算符〉〈表达式〉|ODD〈表达式〉
〈表达式〉∷=[+|-]〈项〉{〈加法运算符〉〈项〉}
〈项〉∷=〈因子〉{〈乘法运算符〉〈因子〉}
〈因子〉∷=〈标识符〉|〈无符号整数〉|'('〈表达式〉')'
〈加法运算符〉∷=+|-
〈乘法运算符〉∷=*|/
〈关系运算符〉∷=#|=|<|<=|>|>=
〈条件语句〉∷=IF〈条件〉THEN〈语句〉
〈过程调用语句〉∷=CALL〈标识符〉
〈当型循环语句〉∷=WHILE〈条件〉DO〈语句〉
〈读语句〉∷=READ'('〈标识符〉{,〈标识符〉}')'
〈写语句〉∷=WRITE'('〈表达式〉{,〈表达式〉}')'
〈字母〉∷=a|b|…|X|Y|Z
〈数字〉∷=0|1|2|…|8|9
(b)PL/0语言的语法描述图
☐用椭圆和圆圈中的英文字表示终结符,用长方形内的中文字表示非终结符。
◆画语法图时要注意箭头方向和弧度,语法图中应该无直角,如图2.1给出的PL/0语法描述图。
沿语法图分析时不能走尖狐线。
图2.1(a)程序语法描述图p9
图2.1(b)分程序语法描述图p10
按照上图,不能将常量定义写在变量定义前面.
图2.1(c)语句语法描述图
可以改动上图,增加else语句
可以改动上图,使do语句可以处理多条语句.
图2.1(d)条件语法描述图
图2.1(e)表达式语法描述图
图2.1(f)项语法描述图
图2.1(g)因子语法描述图
附录:
源代码
pl0c.h
/*关键字个数*/
#definenorw13
/*名字表容量*/
#definetxmax100
/*所有的add1用于定义数组*/
#definetxmaxadd1101
/*number的最大位数*/
#definenmax14
/*符号的最大长度*/
#defineal10
/*地址上界*/
#defineamax2047
/*最大允许过程嵌套声明层数*/
#definelevmax3
/*最多的虚拟机代码数*/
#definecxmax200
#definecxmaxadd1201
/*当函数中会发生fatalerror时,返回-1告知调用它的函数,最终退出程序*/
#definegetsymdoif(-1==getsym())return-1
#definegetchdoif(-1==getch())return-1
#definetestdo(a,b,c)if(-1==test(a,b,c))return-1
#definegendo(a,b,c)if(-1==gen(a,b,c))return-1
#defineexpressiondo(a,b,c)if(-1==expression(a,b,c))return-1
#definefactordo(a,b,c)if(-1==factor(a,b,c))return-1
#definetermdo(a,b,c)if(-1==term(a,b,c))return-1
#defineconditiondo(a,b,c)if(-1==condition(a,b,c))return-1
#definestatementdo(a,b,c)if(-1==statement(a,b,c))return-1
#defineconstdeclarationdo(a,b,c)if(-1==constdeclaration(a,b,c))return-1
#definevardeclarationdo(a,b,c)if(-1==vardeclaration(a,b,c))return-1
typedefenum{false,true}bool;
/*符号*/
enumsymbol{nul,ident,number,plus,minus,times,slash,oddsym,eql,neq,lss,leq,gtr,geq,lparen,rparen,comma,semicolon,period,becomes,beginsym,endsym,ifsym,thensym,whilesym,writesym,readsym,dosym,callsym,constsym,varsym,procsym};
#definesymnum32
/*名字表中的类型*/
enumobject{constant,variable,procedur};
/*虚拟机代码*/
enumfct{lit,opr,lod,sto,cal,inte,jmp,jpc};
#definefctnum8
/*虚拟机代码结构*/
structinstruction
{
enumfctf;/*虚拟机代码指令*/
intl;/*引用层与声明层的层次差*/
inta;/*根据f的不同而不同*/
};
FILE*fas;/*输出名字表*/
FILE*fa;/*输出虚拟机代码*/
FILE*fa1;/*输出源文件及其各行对应的首地址*/
FILE*fa2;/*输出结果*/
boollistswitch;/*显示虚拟机代码与否*/
booltableswitch;/*显示名字表与否*/
charch;/*获取字符的缓冲区,getch使用*/
enumsymbolsym;/*当前的符号*/
charid[al];/*当前ident*/
intnum;/*当前number*/
intcc,ll,kk;/*getch使用的计数器,cc表示当前字符(ch)的位置*/
intcx;/*虚拟机代码指针*/
charline[81];/*读取行缓冲区*/
chara[al];/*临时符号*/
structinstructioncode[cxmaxadd1];/*存放虚拟机代码的数组*/
charword[norw][al];/*保留字*/
enumsymbolwsym[norw];/*保留字对应的符号值*/
enumsymbolssym[256];/*单字符的符号值*/
charmnemonic[fctnum][5];/*虚拟机代码指令名称*/
booldeclbegsys[symnum];/*表示声明开始的符号集合*/
boolstatbegsys[symnum];/*表示语句开始的符号集合*/
boolfacbegsys[symnum];/*表示因子开始的符号集合*/
/*名字表结构*/
structtablestruct
{
charname[al];/*名字*/
enumobjectkind;/*类型:
const,varorprocedure*/
intval;/*数值,仅const使用*/
intlevel;/*所处层,仅const不使用*/
intadr;/*地址,仅const不使用*/
intsize;/*需要分配的数据区空间,仅procedure使用*/
};
structtablestructtable[txmaxadd1];/*名字表*/
FILE*fin;
FILE*fout;
charfname[al];
interr;/*错误计数器*/
voiderror(intn);
intgetsym();
intgetch();
voidinit();
intgen(enumfctx,inty,intz);
inttest(bool*s1,bool*s2,intn);
intinset(inte,bool*s);
intaddset(bool*sr,bool*s1,bool*s2,intn);
intsubset(bool*sr,bool*s1,bool*s2,intn);
intmulset(bool*sr,bool*s1,bool*s2,intn);
intblock(intlev,inttx,bool*fsys);
voidinterpret();
intfactor(bool*fsys,int*ptx,intlev);
intterm(bool*fsys,int*ptx,intlev);
intcondition(bool*fsys,int*ptx,intlev);
intexpression(bool*fsys,int*ptx,intlev);
intstatement(bool*fsys,int*ptx,intlev);
voidlistcode(intcx0);
intvardeclaration(int*ptx,intlev,int*pdx);
intconstdeclaration(int*ptx,intlev,int*pdx);
intpostion(char*idt,inttx);
voidenter(enumobjectk,int*ptx,intlev,int*pdx);
intbase(intl,int*s,intb);
pl0c.c
/*
Windows下c语言PL/0编译程序
在VisualC++6.0和VisualC.NET上运行通过
使用方法:
运行后输入PL/0源程序文件名
回答是否输出虚拟机代码
回答是否输出名字表
fa.tmp输出虚拟机代码
fa1.tmp输出源文件及其各行对应的首地址
fa2.tmp输出结果
fas.tmp输出名字表
*/
#include
#include"pl0c.h"
#include"string.h"
/*解释执行时使用的栈*/
#definestacksize500
intmain()
{
boolnxtlev[symnum];
init();/*初始化*/
fas=fopen("fas.tmp","w");
fa1=fopen("fa1.tmp","w");
printf("Inputfile?
");
fprintf(fa1,"Inputfile?
");
scanf("%s",fname);/*输入文件名*/
fin=fopen(fname,"r");
if(fin)
{
fprintf(fa1,"%s\n",fname);
printf("Listobjectcode?
(Y/N)");/*是否输出虚拟机代码*/
scanf("%s",fname);
listswitch=(fname[0]=='y'||fname[0]=='Y');
printf("Listsymboltable?
(Y/N)");/*是否输出名字表*/
scanf("%s",fname);
tableswitch=(fname[0]=='y'||fname[0]=='Y');
err=0;
cc=cx=ll=0;
ch='';
kk=al-1;
if(-1!
=getsym())
{
fa=fopen("fa.tmp","w");
fa2=fopen("fa2.tmp","w");
addset(nxtlev,declbegsys,statbegsys,symnum);
nxtlev[period]=true;
if(-1==block(0,0,nxtlev))/*调用编译程序*/
{
fclose(fa);
fclose(fa1);
fclose(fin);
printf("\n");
return0;
}
fclose(fa);
fclose(fa1);
if(sym!
=period)error(9);
if(err==0)interpret();/*调用解释执行程序*/
else
{
printf("Errorsinpl/0program");
}
}
fclose(fin);
}
else
{
printf("Can'topenfile!
\n");
fprintf(fa1,"Can'topenfile!
\n");
fclose(fa1);
}
fclose(fas);
printf("\n");
return0;
}
/*在适当的位置显示错误*/
voiderror(intn)
{
charspace[81];
memset(space,32,81);
space[cc-1]=0;/*出错时当前符号已经读完,所以cc-1*/
printf("****%s!
%d\n",space,n);
fprintf(fa1,"****%s!
%d\n",space,n);
err++;
}
/*词法分析,获取一个符号*/
intgetsym()
{
inti,j,k;
while(ch==''||ch==10||ch==9)/*忽略空格、换行和TAB*/
{
getchdo;
}
if(ch>='a'&&ch<='z')
{/*名字或保留字以a..z开头*/
k=0;
do
{
if(k { a[k]=ch; k++; } getchdo; } while(ch>='a'&&ch<='z'||ch>='0'&&ch<='9'); a[k]=0; strcpy(id,a); i=0; j=norw-1; do/*搜索当前符号是否为保留字*/ { k=(i+j)/2; if(strcmp(id,word[k])<=0)j=k-1; if(strcmp(id,word[k])>=0)i=k+1; } while(i<=j); if(i-1>j)sym=wsym[k];elsesym=ident;/*搜索失败则,是名字或数字*/ } else { if(ch>='0'&&ch<='9') {/*检测是否为数字: 以0..9开头*/ k=0; num=0; sym=number; do { num=10*num+ch-'0'; k++; getchdo; } while(ch>='0'&&ch<='9');/*获取数字的值*/ k--; if(k>nmax)error(30); } else { if(ch==': ')/*检测赋值符号*/ { getchdo; if(ch=='=') { sym=becomes; getchdo; } else { sym=nul;/*不能识别的符号*/ } } else { if(ch=='<')/*检测小于或小于等于符号*/ { getchdo; if(ch=='=') { sym=leq; getchdo; } else { sym=lss; } } else { if(ch=='>')/*检测大于或大于等于符号*/ { getchdo; if(ch=='=') { sym=geq; getchdo; } else { sym=gtr; } } else { sym=ssym[ch];/*当符号不满足上述条件时,全部按照单字符符号处理*/ getchdo; } } } } } return0; } /*生成虚拟机代码*/ intgen(enumfctx,/*f*/ inty,/*l*/ intz/*a*/ ) { if(cx>cxmax) { printf("Programtoolong");/*程序过长*/ return-1; } code[cx].f=x; code[cx].l=y; code[cx].a=z; cx++; return0; } /*在某一部分(如一条语句,一个表达式)将要结束时时我们希望下一个符号属于某集合(该部分的后跟 符号),test负责这项监测,并且负责当监测不通过时的补救措施,程序在需要检测时指定当前需要的符 号集合和补救用的集合(如之前未完成部分的后跟符号),以及检测不通过时的错误号*/ inttest(bool*s1,/*我们需要的符号*/ bool*s2,/*如果不是我们需要的,则需要一个补救用的集合*/ intn)/*错误号*/ { if(! inset(sym,s1)) { error(n); /*当检测不通过时,不停获取符号,直到它属于需要的集合或补救的集合*/ while((! inset(sym,s1))&&(! inset(sym,s2))) { getsymdo; } } return0; } /*编译程序主体*/ intblock(intlev,/*当前分程序所在层*/ inttx,/*名字表当前尾指针*/ bool*fsys/*当前模块后跟符号集合*/ ) { inti; intdx;/*名字分配到的相对地址*/ inttx0;/*保留初始tx*/ intcx0;/*保留初始cx*/ boolnxtlev[symnum];/*在下级函数的参数中,符号集合均为值参,但由于使用数租实现, 传递进来的是指针,为防止下级函数改变上级函数的集合,开辟新的空间 传递给下级函数,之后所有的nxtlev都是这样*/ dx=3; tx0=tx;/*记录本层名字的初始位置*/ table[tx].adr=cx; gendo(jmp,0,0); if(lev>levmax)error(32);
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- COMPILERPL0 语言 示例 语法 描述