COMPILERPL0语言的示例语法描述.docx

上传人:b****3 文档编号:27523241 上传时间:2023-07-02 格式:DOCX 页数:54 大小:383.66KB
下载 相关 举报
COMPILERPL0语言的示例语法描述.docx_第1页
第1页 / 共54页
COMPILERPL0语言的示例语法描述.docx_第2页
第2页 / 共54页
COMPILERPL0语言的示例语法描述.docx_第3页
第3页 / 共54页
COMPILERPL0语言的示例语法描述.docx_第4页
第4页 / 共54页
COMPILERPL0语言的示例语法描述.docx_第5页
第5页 / 共54页
点击查看更多>>
下载资源
资源描述

COMPILERPL0语言的示例语法描述.docx

《COMPILERPL0语言的示例语法描述.docx》由会员分享,可在线阅读,更多相关《COMPILERPL0语言的示例语法描述.docx(54页珍藏版)》请在冰豆网上搜索。

COMPILERPL0语言的示例语法描述.docx

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);

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学 > 天文地理

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1