编译原理 LL(1)文法源代码

编译原理 LL(1)文法源代码
编译原理 LL(1)文法源代码

LL(1)文法(源代码)

#include "stdio.h"

#include "stdlib.h"

#define MaxRuleNum 8

#define MaxVnNum 5

#define MaxVtNum 5

#define MaxStackDepth 20

#define MaxPLength 20

#define MaxStLength 50

struct pRNode /*产生式右部结构*/

{

int rCursor;

struct pRNode *next;

};

struct pNode

{

int lCursor;

int rLength; /*右部长度*/

struct pRNode *rHead; /*右部结点头指针*/

};

char Vn[MaxVnNum + 1]; /*非终结符集*/

int vnNum;

char Vt[MaxVtNum + 1]; /*终结符集*/

int vtNum;

struct pNode P[MaxRuleNum];

int PNum;

char buffer[MaxPLength + 1];

char ch;

char st[MaxStLength]; /*要分析的符号串*/

struct collectNode

{

int nVt;

struct collectNode *next;

};

struct collectNode* first[MaxVnNum + 1]; /*first集*/ struct collectNode* follow[MaxVnNum + 1]; /*follow集*/

int analyseTable[MaxVnNum + 1][MaxVtNum + 1 + 1];

int analyseStack[MaxStackDepth + 1]; /*分析栈*/

int topAnalyse; /*分析栈顶*/

void Init();/*初始化*/

int IndexCh(char ch);

void InputVt(); /*输入终结符*/

void InputVn();/*输入非终结符*/

void ShowChArray(char* collect, int num);/*输出Vn或Vt的内容*/ void InputP();/*产生式输入*/

bool CheckP(char * st);/*判断产生式正确性*/

void First(int U);

void AddFirst(int U, int nCh); /*加入first集*/

bool HaveEmpty(int nVn);

void Follow(int V);/*计算follow集*/

void AddFollow(int V, int nCh, int kind);

void ShowCollect(struct collectNode **collect);/*输出first或follow集*/ void FirstFollow();/*计算first和follow*/

void CreateAT();/*构造预测分析表*/

void ShowAT();/*输出分析表*/

void Identify(char *st);

void InitStack();

void ShowStack();

void Pop();

void Push(int r);

void main(void)

{

char todo,ch;

Init();

InputVn();

InputVt();

InputP();

getchar();

FirstFollow();

printf("所得first集为:");

ShowCollect(first);

printf("所得follow集为:");

ShowCollect(follow);

CreateA T();

ShowA T();

todo = 'y';

while('y' == todo)

{

printf("\n是否继续进行句型分析?(y / n):"); todo = getchar();

while('y' != todo && 'n' != todo)

{

printf("\n(y / n)? ");

todo = getchar();

}

if('y' == todo)

{

int i;

InitStack();

printf("请输入符号串(以#结束) : ");

ch = getchar();

i = 0;

while('#' != ch && i < MaxStLength)

{

if(' ' != ch && '\n' != ch)

{

st[i++] = ch;

}

ch = getchar();

}

if('#' == ch && i < MaxStLength)

{

st[i] = ch;

Identify(st);

}

else

printf("输入出错!\n");

}

}

getchar();

}

void Init()

{

int i,j;

vnNum = 0;

vtNum = 0;

PNum = 0;

for(i = 0; i <= MaxVnNum; i++)

Vn[i] = '\0';

for(i = 0; i <= MaxVtNum; i++)

Vt[i] = '\0';

for(i = 0; i < MaxRuleNum; i++) {

P[i].lCursor = NULL;

P[i].rHead = NULL;

P[i].rLength = 0;

}

PNum = 0;

for(i = 0; i <= MaxPLength; i++) buffer[i] = '\0';

for(i = 0; i < MaxVnNum; i++)

{

first[i] = NULL;

follow[i] = NULL;

}

for(i = 0; i <= MaxVnNum; i++) {

for(j = 0; j <= MaxVnNum + 1; j++) analyseTable[i][j] = -1;

}

}

int IndexCh(char ch)

{

int n;

n = 0; /*is Vn?*/

while(ch != Vn[n] && '\0' != Vn[n]) n++;

if('\0' != Vn[n])

return 100 + n;

n = 0; /*is Vt?*/

while(ch != Vt[n] && '\0' != Vt[n]) n++;

if('\0' != Vt[n])

return n;

return -1;

}

/*输出Vn或Vt的内容*/

void ShowChArray(char* collect)

{

int k = 0;

while('\0' != collect[k])

{

printf(" %c ", collect[k++]);

}

printf("\n");

}

/*输入非终结符*/

void InputVn()

{

int inErr = 1;

int n,k;

char ch;

while(inErr)

{

printf("\n请输入所有的非终结符,注意:");

printf("请将开始符放在第一位,并以#号结束:\n");

ch = ' ';

n = 0;

/*初始化数组*/

while(n < MaxVnNum)

{

Vn[n++] = '\0';

}

n = 0;

while(('#' != ch) && (n < MaxVnNum))

{

if(' ' != ch && '\n' != ch && -1 == IndexCh(ch))

{

Vn[n++] = ch;

vnNum++;

}

ch = getchar();

}

Vn[n] = '#'; /*以“#”标志结束用于判断长度是否合法*/ k = n;

if('#' != ch)

{

if( '#' != (ch = getchar()))

{

while('#' != (ch = getchar()))

;

printf("\n符号数目超过限制!\n");

inErr = 1;

continue;

}

}

/*正确性确认,正确则,执行下下面,否则重新输入*/ Vn[k] = '\0';

ShowChArray(Vn);

ch = ' ';

while('y' != ch && 'n' != ch)

{

if('\n' != ch)

{

printf("输入正确确认?(y/n):");

}

scanf("%c", &ch);

}

if('n' == ch)

{

printf("录入错误重新输入!\n");

inErr = 1;

}

else

{

inErr = 0;

}

}

}

/*输入终结符*/

void InputVt()

{

int inErr = 1;

int n,k;

char ch;

while(inErr)

{

printf("\n请输入所有的终结符,注意:");

printf("以#号结束:\n");

ch = ' ';

n = 0;

/*初始化数组*/

while(n < MaxVtNum)

{

Vt[n++] = '\0';

}

n = 0;

while(('#' != ch) && (n < MaxVtNum))

{

if(' ' != ch && '\n' != ch && -1 == IndexCh(ch)) {

Vt[n++] = ch;

vtNum++;

}

ch = getchar();

}

Vt[n] = '#';

k = n;

if('#' != ch)

{

if( '#' != (ch = getchar()))

{

while('#' != (ch = getchar()))

;

printf("\n符号数目超过限制!\n");

inErr = 1;

continue;

}

}

Vt[k] = '\0';

ShowChArray(Vt);

ch = ' ';

while('y' != ch && 'n' != ch)

{

if('\n' != ch)

{

printf("输入正确确认?(y/n):");

}

scanf("%c", &ch);

}

if('n' == ch)

{

printf("录入错误重新输入!\n");

inErr = 1;

}

else

{

inErr = 0;

}

}

}

/*产生式输入*/

void InputP()

{

char ch;

int i = 0, n,num;

printf("请输入文法产生式的个数:");

scanf("%d", &num);

PNum = num;

getchar(); /*消除回车符*/

printf("\n请输入文法的%d个产生式,并以回车分隔每个产生式:", num); printf("\n");

while(i < num)

{

printf("第%d个:", i);

/*初始化*/

for(n =0; n < MaxPLength; n++)

buffer[n] = '\0';

/*输入产生式串*/

ch = ' ';

n = 0;

while('\n' != (ch = getchar()) && n < MaxPLength)

{

if(' ' != ch)

buffer[n++] = ch;

}

buffer[n] = '\0';

if(CheckP(buffer))

{

pRNode *pt, *qt;

P[i].lCursor = IndexCh(buffer[0]);

pt = (pRNode*)malloc(sizeof(pRNode));

pt->rCursor = IndexCh(buffer[3]);

pt->next = NULL;

P[i].rHead = pt;

n = 4;

while('\0' != buffer[n])

{

qt = (pRNode*)malloc(sizeof(pRNode));

qt->rCursor = IndexCh(buffer[n]);

qt->next = NULL;

pt->next = qt;

pt = qt;

n++;

}

P[i].rLength = n - 3;

i++;

}

else

printf("输入符号含非法在成分,请重新输入!\n"); }

}

/*判断产生式正确性*/

bool CheckP(char * st)

{

int n;

if(100 > IndexCh(st[0]))

return false;

if('-' != st[1])

return false;

if('>' != st[2])

return false;

for(n = 3; '\0' != st[n]; n ++)

{

if(-1 == IndexCh(st[n]))

return false;

}

return true;

}

void First(int U)

{

int i,j;

for(i = 0; i < PNum; i++)

{

if(P[i].lCursor == U)

{

struct pRNode* pt;

pt = P[i].rHead;

j = 0;

while(j < P[i].rLength)

{

if(100 > pt->rCursor)

{

AddFirst(U, pt->rCursor);

break;

}

else

{

if(NULL == first[pt->rCursor - 100])

{

First(pt->rCursor);

}

AddFirst(U, pt->rCursor);

if(!HaveEmpty(pt->rCursor))

{

break;

}

else

{

pt = pt->next;

}

}

j++;

}

if(j >= P[i].rLength) /*当产生式右部都能推出空时*/ AddFirst(U, -1);

}

}

}

/*加入first集*/

void AddFirst(int U, int nCh)

{

struct collectNode *pt, *qt;

int ch; /*用于处理Vn*/

pt = NULL;

qt = NULL;

if(nCh < 100)

{

pt = first[U - 100];

while(NULL != pt)

{

if(pt->nVt == nCh)

break;

else

{

qt = pt;

pt = pt->next;

}

}

if(NULL == pt)

{

pt = (struct collectNode *)malloc(sizeof(struct collectNode));

pt->nVt = nCh;

pt->next = NULL;

if(NULL == first[U - 100])

{

first[U - 100] = pt;

}

else

{

qt->next = pt; /*qt指向first集的最后一个元素*/

}

pt = pt->next;

}

}

else

{

pt = first[nCh - 100];

while(NULL != pt)

{

ch = pt->nVt;

if(-1 != ch)

{

AddFirst(U, ch);

}

pt = pt->next;

}

}

}

bool HaveEmpty(int nVn)

{

if(nVn < 100)

return false;

struct collectNode *pt;

pt = first[nVn - 100];

while(NULL != pt)

{

if(-1 == pt->nVt)

return true;

pt = pt->next;

}

return false;

}

void Follow(int V)

{

int i;

struct pRNode *pt ;

if(100 == V) /*当为初始符时*/

AddFollow(V, -1, 0 );

for(i = 0; i < PNum; i++)

{

pt = P[i].rHead;

while(NULL != pt && pt->rCursor != V)

pt = pt->next;

if(NULL != pt)

{

pt = pt->next;

if(NULL == pt)

{

if(NULL == follow[P[i].lCursor - 100] && P[i].lCursor != V) {

Follow(P[i].lCursor);

}

AddFollow(V, P[i].lCursor, 0);

}

else

{

while(NULL != pt && HaveEmpty(pt->rCursor))

{

AddFollow(V, pt->rCursor, 1);

pt = pt->next;

}

if(NULL == pt)

{

if(NULL == follow[P[i].lCursor - 100] && P[i].lCursor != V) {

Follow(P[i].lCursor);

}

AddFollow(V, P[i].lCursor, 0);

}

else

{

AddFollow(V, pt->rCursor, 1);

}

}

}

}

}

void AddFollow(int V, int nCh, int kind)

{

struct collectNode *pt, *qt;

int ch;

pt = NULL;

qt = NULL;

if(nCh < 100) /*为终结符时*/

{

pt = follow[V - 100];

while(NULL != pt)

{

if(pt->nVt == nCh)

break;

else

{

qt = pt;

pt = pt->next;

}

}

if(NULL == pt)

{

pt = (struct collectNode *)malloc(sizeof(struct collectNode));

pt->nVt = nCh;

pt->next = NULL;

if(NULL == follow[V - 100])

{

follow[V - 100] = pt;

}

else

{

qt->next = pt; /*qt指向follow集的最后一个元素*/

}

pt = pt->next;

}

}

else

{

if(0 == kind)

{

pt = follow[nCh - 100];

while(NULL != pt)

{

ch = pt->nVt;

AddFollow(V, ch, 0);

pt = pt->next;

}

}

else

{

pt = first[nCh - 100];

while(NULL != pt)

{

ch = pt->nVt;

if(-1 != ch)

{

AddFollow(V, ch, 1);

}

pt = pt->next;

}

}

}

}

/*输出first或follow集*/

void ShowCollect(struct collectNode **collect) {

int i;

struct collectNode *pt;

i = 0;

while(NULL != collect[i])

{

pt = collect[i];

printf("\n%c:\t", Vn[i]);

while(NULL != pt)

{

if(-1 != pt->nVt)

{

printf(" %c", Vt[pt->nVt]);

}

else

printf(" #");

pt = pt->next;

}

i++;

}

printf("\n");

}

/*计算first和follow*/

void FirstFollow()

{

int i;

i = 0;

while('\0' != Vn[i])

{

if(NULL == first[i])

First(100 + i);

i++;

}

i = 0;

while('\0' != Vn[i])

{

if(NULL == follow[i])

Follow(100 + i);

i++;

}

}

/*构造预测分析表*/

void CreateAT()

{

int i;

struct pRNode *pt;

struct collectNode *ct;

for(i = 0; i < PNum; i++)

{

pt = P[i].rHead;

while(NULL != pt && HaveEmpty(pt->rCursor)) {

ct = first[pt->rCursor - 100];

while(NULL != ct)

{

if(-1 != ct->nVt)

analyseTable[P[i].lCursor - 100][ct->nVt] = i; ct = ct->next;

}

pt = pt->next;

}

if(NULL == pt)

{

ct = follow[P[i].lCursor - 100];

while(NULL != ct)

{

if(-1 != ct->nVt)

analyseTable[P[i].lCursor - 100][ct->nVt] = i; else

analyseTable[P[i].lCursor - 100][vtNum] = i;

ct = ct->next;

}

}

else

{

if(100 <= pt->rCursor) /*不含空的非终结符*/ {

ct = first[pt->rCursor - 100];

while(NULL != ct)

{

analyseTable[P[i].lCursor - 100][ct->nVt] = i;

ct = ct->next;

}

}

else /*终结符或者空*/

{

if(-1 == pt->rCursor)

{

ct = follow[P[i].lCursor - 100];

while(NULL != ct)

{

if(-1 != ct->nVt)

analyseTable[P[i].lCursor - 100][ct->nVt] = i;

else /*当含有#号时*/

analyseTable[P[i].lCursor - 100][vtNum] = i;

ct = ct->next;

}

}

else /*为终结符*/

{

analyseTable[P[i].lCursor - 100][pt->rCursor] = i;

}

}

}

}

}

/*输出分析表*/

void ShowAT()

{

int i,j;

printf("构造预测分析表如下:\n");

printf("\t|\t");

for(i = 0; i < vtNum; i++)

{

printf("%c\t", Vt[i]);

}

printf("#\t\n");

printf("- - -\t|- - -\t");

for(i = 0; i <= vtNum; i++)

printf("- - -\t");

printf("\n");

for(i = 0; i < vnNum; i++)

{

printf("%c\t|\t", Vn[i]);

for(j = 0; j <= vtNum; j++)

{

if(-1 != analyseTable[i][j])

printf("R(%d)\t", analyseTable[i][j]);

else

printf("error\t");

}

printf("\n");

}

}

void Identify(char *st)

{

int current,step,r; /*r表使用的产生式的序号*/ printf("\n%s的分析过程:\n", st);

printf("步骤\t分析符号栈\t当前指示字符\t使用产生式序号\n");

step = 0;

current = 0;

printf("%d\t",step);

ShowStack();

printf("\t\t%c\t\t- -\n", st[current]);

while('#' != st[current])

{

if(100 > analyseStack[topAnalyse])

{

if(analyseStack[topAnalyse] == IndexCh(st[current]))

{

Pop();

current++;

step++;

printf("%d\t", step);

ShowStack();

printf("\t\t%c\t\t出栈、后移\n", st[current]);

}

else

{

printf("%c-%c不匹配!", analyseStack[topAnalyse], st[current]);

printf("此串不是此文法的句子!\n");

return;

}

}

else /*当为非终结符时*/

{

r = analyseTable[analyseStack[topAnalyse] - 100][IndexCh(st[current])]; if(-1 != r)

{

Push(r);

step++;

printf("%d\t", step);

ShowStack();

printf("\t\t%c\t\t%d\n", st[current], r);

}

else

{

printf("此串不是此文法的句子!\n");

return;

}

}

if('#' == st[current])

{

if(0 == topAnalyse && '#' == st[current])

{

step++;

printf("%d\t", step);

ShowStack();

printf("\t\t%c\t\t分析成功!\n", st[current]);

printf("%s是给定文法的句子!\n", st);

}

else

{

while(topAnalyse > 0)

{

if(100 > analyseStack[topAnalyse])

{

printf("此串不是此文法的句子!\n");

return;

}

else

{

r = analyseTable[analyseStack[topAnalyse] - 100][vtNum];

if(-1 != r)

{

Push(r); /*产生式右部代替左部,指示器不移动*/

step++;

printf("%d\t", step);

ShowStack();

if(0 == topAnalyse && '#' == st[current])

{

printf("\t\t%c\t\t分析成功!\n", st[current]);

printf("%s是给定文法的句子!\n", st);

}

else

printf("\t\t%c\t\t%d\n", st[current], r);

}

else

{

printf("此串不是此文法的句子!\n");

return;

}

}

}

}

}

/*初始化栈及符号串*/

void InitStack()

{

int i;

/*分析栈的初始化*/

for(i = 0; i < MaxStLength; i++)

st[i] = '\0';

analyseStack[0] = -1; /*#(-1)入栈*/ analyseStack[1] = 100; /*初始符入栈*/ topAnalyse = 1;

}

/*显示符号栈中内容*/

void ShowStack()

{

int i;

for(i = 0; i <= topAnalyse; i++)

{

if(100 <= analyseStack[i])

printf("%c", Vn[analyseStack[i] - 100]); else

{

if(-1 != analyseStack[i])

printf("%c", Vt[analyseStack[i]]);

else

printf("#");

}

}

}

/*栈顶出栈*/

void Pop()

{

topAnalyse--;

}

void Push(int r)

{

int i;

struct pRNode *pt;

Pop();

编译原理

致谢: 2005级周朝丽、丛志环、张云华、周娇、陈亮、陶锌、张世强等同学不仅对讲义的进一步完善提出了宝贵的意见和建议,而且提出的许多富有探讨性的问题,不仅令我进一步思考,同时也令讲义的许多内容进一步丰富,在此,本人、现在已经看到、未来将会看到该讲义的人对各位的“答疑解惑”表示由衷的谢意! 参考书目: 1.编译原理,Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman著,李建中,姜守旭译。机械工 业出版社,2003 Compilers Principles, Techniques, and Tools(英文版名字) 2.编译原理及实践,(美)Kenneth C. Louden著,冯博琴等译。机械工业出版社,2000 Compiler Construction: Principles and Practice (英文版名字) 3.编译原理习题与解析(第2版)/伍春香编著-.--北京:清华大学出版社,2006 4.编译原理=Compiling Principle/周经野,张继福主编-.--武汉:武汉理工大学出版社,2003 5.程序设计语言编译方法. 肖军模编著. 大连理工大学出版社,2000。 6.程序设计语言编译原理/陈火旺等编.--北京:国防工业出版社,1984 7.编译方法/金成植编.--北京:高等教育出版社,1984 8.编译原理/蒋立源主编.--西安:西北工业大学出版社,1993.8 9.编译原理和技术/陈意云, 马万里编译.--安徽:中国科学技术大学出版社,1989.12 10.编译原理及其习题解答/何炎祥...[等]编著-.--武汉:武汉大学出版社,2004。 11.形式语言与自动机理论 12.FORTRAN语言程序设计,谭浩强、田淑清编著,高等教育出版社,1987年5月。 13.PASCAL程序设计,郗曼丽编著,陕西科学技术出版社。 14.讲义的一些部分来源于互联网上的多种资源,其链接难以一一提供,在此,谨向大家 致以真诚地敬意和诚挚的谢意,感谢大家通过互联网提供的极为有益的帮助和指导。 1

编译原理复习题(经典)

编译原理复习题 一、是非题 1.计算机高级语言翻译成低级语言只有解释一种方式。(×) 3.每个文法都能改写为 LL(1) 文法。 (×) 4.算符优先关系表不一定存在对应的优先函数。 (√) 5.LR分析方法是自顶向下语法分析方法。 (×) 6.“用高级语言书写的源程序都必须通过编译,产生目标代码后才能投入运行”这种说法。(× ) 7.一个句型的句柄一定是文法某产生式的右部。 (√) 8.仅考虑一个基本块,不能确定一个赋值是否真是无用的。 (√ ) 9.在中间代码优化中循环上的优化主要有不变表达式外提和削减运算强度。 (× ) 10.对于数据空间的存贮分配,FORTRAN采用动态贮存分配策略。(×) 11.甲机上的某编译程序在乙机上能直接使用的必要条件是甲机和乙机的操作系统功能完全相同。(× ) 12.递归下降分析法是自顶向下分析方法。(√ ) 13.产生式是用于定义词法成分的一种书写规则。 (×) 14.在 SLR(1)分析法的名称中,S的含义是简单的。(√) 15.综合属性是用于“自上而下”传递信息。(× ) 16.符号表中的信息栏中登记了每个名字的属性和特征等有关信息,如类型、种属、所占单元大小、地址等等。(×) 17.程序语言的语言处理程序是一种应用软件。 (×) 18.解释程序适用于 COBOL 和 FORTRAN 语言。 (×) 19.一个 LL(l)文法一定是无二义的。 (√) 20.正规文法产生的语言都可以用上下文无关文法来描述。 (√) 21.一张转换图只包含有限个状态,其中有一个被认为是初态,最多只有一个终态。 (×) 22.目标代码生成时,应考虑如何充分利用计算机的寄存器的问题。 (√) 22.逆波兰法表示的表达式亦称后缀式。 (√ ) 23.如果一个文法存在某个句子对应两棵不同的语法树,则称这个文法是二义的。 (√ ) 24.数组元素的地址计算与数组的存储方式有关。(√) 25.算符优先关系表不一定存在对应的优先函数。 (×) 26.编译程序是对高级语言程序的解释执行。(× ) 27.一个有限状态自动机中,有且仅有一个唯一的终态。(×) 28.一个算符优先文法可能不存在算符优先函数与之对应。 (√ ) 29.语法分析时必须先消除文法中的左递归。 (×) 30.LR分析法在自左至右扫描输入串时就能发现错误,但不能准确地指出出错地点。 (√) 31.逆波兰表示法表示表达式时无须使用括号。 (√ ) 32.静态数组的存储空间可以在编译时确定。 (√) 33.进行代码优化时应着重考虑循环的代码优化,这对提高目标代码的效率将起更大作用。 (√) 34.两个正规集相等的必要条件是他们对应的正规式等价。 (√) 35.一个语义子程序描述了一个文法所对应的翻译工作。 (×) 36.设r和s分别是正规式,则有L(r|s)=L(r)L(s)。(×) 37.确定的自动机以及不确定的自动机都能正确地识别正规集。(√) 38.词法分析作为单独的一遍来处理较好。 (× ) 39.构造LR分析器的任务就是产生LR分析表。 (√) 40.规范归约和规范推导是互逆的两个过程。 (√) 41.同心集的合并有可能产生新的“移进”/“归约”冲突。 (× ) 42.LR分析技术无法适用二义文法。 (× )

编译原理知识点汇总

编译原理的复习提纲 1.编译原理=形式语言+编译技术 2.汇编程序: 把汇编语言程序翻译成等价的机器语言程序 3.编译程序: 把高级语言程序翻译成等价的低级语言程序 4.解释执行方式: 解释程序,逐个语句地模拟执行 翻译执行方式: 翻译程序,把程序设计语言程序翻译成等价的目标程序 5.计算机程序的编译过程类似,一般分为五个阶段: 词法分析、语法分析、语义分析及中间代码生成、代码优化、目标代码生成 词法分析的任务: 扫描源程序的字符串,识别出的最小的语法单位(标识符或无正负号数等) 语法分析是: 在词法分析的基础上的,语法分析不考虑语义。语法分析读入词法分析程序识别出的符号,根据给定的语法规则,识别出各个语法结构。 语义分析的任务是检查程序语义的正确性,解释程序结构的含义,语义分析包括检查变量是否有定义,变量在使用前是否具有值,数值是否溢出等。

语法分析完成之后,编译程序通常就依据语言的语义规则,利用语法制导技术把源程序翻译成某种中间代码。所谓中间代码是一种定义明确、便于处理、独立于计算机硬件的记号系统,可以认为是一种抽象机的程序 代码优化的主要任务是对前一阶段产生的中间代码进行等价变换,以便产生速度快、空间小的目标代码 编译的最后一个阶段是目标代码生成,其主要任务是把中间代码翻译成特定的机器指令或汇编程序 编译程序结构包括五个基本功能模块和两个辅助模块 6.编译划分成前端和后端。 编译前端的工作包括词法分析、语法分析、语义分析。编译前端只依赖于源程序,独立于目标计算机。前端进行分析 编译后端的工作主要是目标代码的生成和优化后端进行综合。独立于源程序,完全依赖于目标机器和中间代码。 把编译程序分为前端和后端的优点是: 可以优化配置不同的编译程序组合,实现编译重用,保持语言与机器的独立性。 7.汇编器把汇编语言代码翻译成一个特定的机器指令序列 第二章 1.符号,字母表,符号串,符号串的长度计算P18,子符号串的含义,符号串的简单运算XY,Xn, 2.符号串集合的概念,符号串集合的乘积运算,方幂运算,闭包与正闭包的概念P19,P20A0 ={ε} 3.重写规则,简称规则。非xx(V

编译原理中间代码优化Word版

实验三中间的代码优化 某些编译程序在中间代码或目标代码生产之后要对其进行优化,所谓优化就是对代码进行等价的变换。而变换后的代码运行结果与变换前的代码运行结果相同。而运行速度加快或占用内存空间减少。中间的代码优化就是对中间代码进行等价的变换。 基本块的有向图DAG(Directed Acyclic Graph) 有向图中任何一条通路都不是环路,则称该有向图为无环路有向图,简称为DAG。 一、实验题目: 中间代码的局部优化 二、实验目的: 掌握局部优化方法、提高机器的运行速度 三、实验内容: 1 、构造基本块内的优化DAG 假设:(1)ni 为已知结点号,n为新结点号; (2)访问各结点信息时,按结点号逆序排序 2、完成对下例三类表达式的优化 (1)常值表达式的优化 (2)公共表达式的优化 (3)无用赋值的优化 3、输出根据优化的DAG重组四元式 四、设计概要: 首先要实现表达式中间代码生成,采用递归下降子程序法实现。 E→T{ω0 “push(SYN,w)”T“QUAT” } T→F{ω1”push(SYN,w)”F“QUAT”} F→i“push(SEM,entry(w))”|(E) 其中:·push(SYN,w)---当前单词w入符号栈SYN; ·push(SEM,entry(w))--- 当前i在符号表中的入口值压入语义栈SEM;·QUAT---生成四元式函数 ①T:=newtemp; ②QT[j]=(SYN[k],SEM[s-1],SEM[s],T);j++; ③ pop(SYN,_);pop(SEM,_);pop(SEM,_); push(SEM,T);

在对中间代码进行局部优化

编译原理习题及答案(整理后)

第一章 1、将编译程序分成若干个“遍”是为了。 b.使程序的结构更加清晰 2、构造编译程序应掌握。 a.源程序b.目标语言 c.编译方法 3、变量应当。 c.既持有左值又持有右值 4、编译程序绝大多数时间花在上。 d.管理表格 5、不可能是目标代码。 d.中间代码 6、使用可以定义一个程序的意义。 a.语义规则 7、词法分析器的输入是。 b.源程序 8、中间代码生成时所遵循的是- 。 c.语义规则 9、编译程序是对。 d.高级语言的翻译 10、语法分析应遵循。 c.构词规则 二、多项选择题 1、编译程序各阶段的工作都涉及到。 b.表格管理c.出错处理 2、编译程序工作时,通常有阶段。 a.词法分析b.语法分析c.中间代码生成e.目标代码生成 三、填空题 1、解释程序和编译程序的区别在于是否生成目标程序。 2、编译过程通常可分为5个阶段,分别是词法分析、语法分析中间代码生成、代码优化和目标代码生成。 3、编译程序工作过程中,第一段输入是源程序,最后阶段的输出为标代码生成程序。 4、编译程序是指将源程序程序翻译成目标语言程序的程序。

一、单项选择题 1、文法G:S→xSx|y所识别的语言是。 a. xyx b. (xyx)* c. x n yx n(n≥0) d. x*yx* 2、文法G描述的语言L(G)是指。 a. L(G)={α|S+?α , α∈V T*} b. L(G)={α|S*?α, α∈V T*} c. L(G)={α|S*?α,α∈(V T∪V N*)} d. L(G)={α|S+?α, α∈(V T∪V N*)} 3、有限状态自动机能识别。 a. 上下文无关文法 b. 上下文有关文法 c.正规文法 d. 短语文法 4、设G为算符优先文法,G的任意终结符对a、b有以下关系成立。 a. 若f(a)>g(b),则a>b b.若f(a)

(完整版)编译原理课后习题答案

第一章 1.典型的编译程序在逻辑功能上由哪几部分组成? 答:编译程序主要由以下几个部分组成:词法分析、语法分析、语义分析、中间代码生成、中间代码优化、目标代码生成、错误处理、表格管理。 2. 实现编译程序的主要方法有哪些? 答:主要有:转换法、移植法、自展法、自动生成法。 3. 将用户使用高级语言编写的程序翻译为可直接执行的机器语言程序有哪几种主要的方式? 答:编译法、解释法。 4. 编译方式和解释方式的根本区别是什么? 答:编译方式:是将源程序经编译得到可执行文件后,就可脱离源程序和编译程序单独执行,所以编译方式的效率高,执行速度快; 解释方式:在执行时,必须源程序和解释程序同时参与才能运行,其不产生可执行程序文件,效率低,执行速度慢。

第二章 1.乔姆斯基文法体系中将文法分为哪几类?文法的分类同程序设计语言的设计与实现关 系如何? 答:1)0型文法、1型文法、2型文法、3型文法。 2) 2. 写一个文法,使其语言是偶整数的集合,每个偶整数不以0为前导。 答: Z→SME | B S→1|2|3|4|5|6|7|8|9 M→ε | D | MD D→0|S B→2|4|6|8 E→0|B 3. 设文法G为: N→ D|ND D→ 0|1|2|3|4|5|6|7|8|9 请给出句子123、301和75431的最右推导和最左推导。 答:N?ND?N3?ND3?N23?D23?123 N?ND?NDD?DDD?1DD?12D?123 N?ND?N1?ND1?N01?D01?301 N?ND?NDD?DDD?3DD?30D?301 N?ND?N1?ND1?N31?ND31?N431?ND431?N5431?D5431?75431 N?ND?NDD?NDDD?NDDDD?DDDDD?7DDDD?75DDD?754DD?7543D?75431 4. 证明文法S→iSeS|iS| i是二义性文法。 答:对于句型iiSeS存在两个不同的最左推导: S?iSeS?iiSes S?iS?iiSeS 所以该文法是二义性文法。 5. 给出描述下面语言的上下文无关文法。 (1)L1={a n b n c i |n>=1,i>=0 } (2)L2={a i b j|j>=i>=1} (3)L3={a n b m c m d n |m,n>=0} 答: (1)S→AB A→aAb | ab B→cB | ε (2)S→ASb |ab

编译原理(PL0编译程序源代码)

/*PL/0编译程序(C语言版) *编译和运行环境: *Visual C++6.0 *WinXP/7 *使用方法: *运行后输入PL/0源程序文件名 *回答是否将虚拟机代码写入文件 *回答是否将符号表写入文件 *执行成功会产生四个文件(词法分析结果.txt符号表.txt虚拟代码.txt源程序和地址.txt) */ #include #include"pl0.h" #include"string" #define stacksize 500//解释执行时使用的栈 int main(){ bool nxtlev[symnum]; printf("请输入源程序文件名:"); scanf("%s",fname); fin=fopen(fname,"r");//以只读方式打开pl0源程序文件 cifa=fopen("词法分析结果.txt","w"); fa1=fopen("源程序和地址.txt","w");//输出源文件及各行对应的首地址 fprintf(fa1,"输入pl0源程序文件名:"); fprintf(fa1,"%s\n",fname); if(fin){ printf("是否将虚拟机代码写入文件?(Y/N)");//是否输出虚拟机代码 scanf("%s",fname); listswitch=(fname[0]=='y'||fname[0]=='Y'); printf("是否将符号表写入文件?(Y/N)");//是否输出符号表scanf("%s",fname); tableswitch=(fname[0]=='y'||fname[0]=='Y'); init();//初始化 err=0; cc=cx=ll=0; ch=' '; if(-1!=getsym()){ fa=fopen("虚拟代码.txt","w"); fas=fopen("符号表.txt","w"); addset(nxtlev,declbegsys,statbegsys,symnum); nxtlev[period]=true; if(-1==block(0,0,nxtlev)){//调用编译程序 fclose(fa); fclose(fa1); fclose(fas); fclose(fin); return 0; } if(sym!=period){ error(9);//结尾丢失了句号 }

编译原理第三版附带的实验源码

Scanner: #include #include #include #define _KEY_WORD_END "waiting for your expanding" typedef struct { int typenum; char * word; } WORD; char input[255]; char token[255]=""; int p_input; int p_token; char ch; char* KEY_WORDS[]={"main","int","char","if","else","for","while",_KEY_WORD_END}; WORD* scaner(); void main() { int over=1; WORD* oneword=new WORD; printf("Enter Your words(end with $):"); scanf("%[^$]s",input); p_input=0; printf("Your words:\n%s\n",input); while(over<1000&&over!=-1){ oneword=scaner(); if(oneword->typenum<1000) printf("(%d,%s)",oneword->typenum,oneword->word); over=oneword->typenum; } printf("\npress # to exit:"); scanf("%[^#]s",input); } char m_getch(){ ch=input[p_input]; p_input=p_input+1; return (ch); } void getbc(){

编译原理论文

《编译原理》课程论文 编译程序是现代计算机系统的基本组成部分之一,而且多数计算机系统都配有不止一个高级语言的编译程序,对有些高级语言甚至配置了几个不同性能的编译程序。从功能上讲,一个编译程序就是一个语言翻译程序。语言翻译程序把一种源语言书写的程序翻译成另一种目标语言的等价程序,所以总的说编译程序是一种翻译程序,其源程序是高级语言,目标语言程序是低级语言。 编译程序完成从源程序到目标程序的翻译工作,是一个复杂的整体的过程。从概念上来讲,一个编译程序的整个工作过程是划分成几个阶段进行的,每个阶段将源程序的一种表示形式转换成另一种表示形式,各个阶段进行的操作在逻辑上是紧密连接在一起的。一般一个编译过程是词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成。 编写编译器的原理和技术具有十分普遍的意义,以至于在每个计算机工作者的职业生涯中,本书中的原理和技术都会反复用到。在这本书中,向我们介绍了文法的概念,在讲词法分析的章节中讲述了构造一个有穷自动机的方法,以及如何将一个不确定的有穷自动机转化成确定的有穷自动机和有穷自动机的最小化等方法。 词法分析相对来说比较简单。可能是词法分析程序本身实现起来很简单吧,很多没有学过编译原理的人也同样可以写出各种各样的词法分析程序。不过编译原理在讲解词法分析的时候,重点把正则表达式和自动机原理加了进来,然后以一种十分标准的方式来讲解词法分析程序的产生。这样的做法道理很明显,就是要让词法分析从程序上升到理论的地步。 词法分析中的重点是有穷自动机DFA的生成以及DFA和正规式与正规文法的关系。还要熟练掌握NFA转换为DFA的方法及DFA的化简。 词法分析的核心应该是构建DFA,最后维护一个状态转移表。通过转态转移的结果来识别词性。DFA的思想和字典树很像。NFA通过求每个状态的闭包后构造出的自动机与DFA等价。正则表达式闭包,连接,或三种操作都有相应的NFA与其等价。所以正则表达式==NFA==DFA。DFA状态最小化算法化简DFA。LL(1)文法主要就是根据FIRST集判断向哪条路径走,来避免回溯;LR(0)文法构造项

编译原理习题答案

《编译原理》习题答案: 第一次: P14 2、何谓源程序、目标程序、翻译程序、汇编程序、编译程序和解释程序?它们之间可能有何种关系? 答:被翻译的程序称为源程序; 翻译出来的程序称为目标程序或目标代码; 将汇编语言和高级语言编写的程序翻译成等价的机器语言,实现此功能的程序称为翻译程序; 把汇编语言写的源程序翻译成机器语言的目标程序称为汇编程序; 解释程序不是直接将高级语言的源程序翻译成目标程序后再执行,而是一个个语句读入源程序,即边解释边执行; 编译程序是将高级语言写的源程序翻译成目标语言的程序。 关系:汇编程序、解释程序和编译程序都是翻译程序,具体见P4 图 1.3。 P14 3、编译程序是由哪些部分组成?试述各部分的功能? 答:编译程序主要由8个部分组成:(1)词法分析程序;(2)语法分析程序;(3)语义分析程序;(4)中间代码生成;(5)代码优化程序;(6)目标代码生成程序;(7)错误检查和处理程序;(8)信息表管理程序。具体功能见P7-9。 P14 4、语法分析和语义分析有什么不同?试举例说明。 答:语法分析是将单词流分析如何组成句子而句子又如何组成程序,看句子乃至程序是否符合语法规则,例如:对变量 x:= y 符合语法规则就通过。语义分析是对语句意义进行检查,如赋值语句中x与y类型要一致,否则语法分析正确,语义分析则错误。 P15 5、编译程序分遍由哪些因素决定? 答:计算机存储容量大小;编译程序功能强弱;源语言繁简;目标程序优化程度;设计和实现编译程序时使用工具的先进程度以及参加人员多少和素质等等。 补充: 1、为什么要对单词进行内部编码?其原则是什么?对标识符是如何进行内部编码的? 答:内部编码从“源字符串”中识别单词并确定单词的类型和值;原则:长度统一,即刻画了单词本身,也刻画了它所具有的属性,以供其它部分分析使用。对于标识符编码,先判断出该单词是标识符,然后在类别编码中写入相关信息,以表示为标识符,再根据具体标识符的含义编码该单词的值。 补充: 2、赋值语句: A:= 5 * C的语法和语义指的是什么? 答:语法分析将检查该语句是否符合赋值语句规则,语义是指将 5 * C 的结果赋值为 A 。

(编译原理)逆波兰式算法的源代码

一.实验目的 1.深入理解算符优先分析法 2.掌握FirstVt和LastVt集合的求法有算符优先关系表的求法 3.掌握利用算符优先分析法完成中缀表达式到逆波兰式的转化 二.实验内容及要求 将非后缀式用来表示的算术表达式转换为用逆波兰式来表示的算术表达式,并计算用逆波兰式来表示的算术表达式的值。 程序输入/输出示例: 输出的格式如下: (1) (2)输入一以#结束的中缀表达式(包括+—*/()数字#) (3) (4)逆波兰式 备注:(1)在生成的逆波兰式中如果两个数相连则用&分隔,如28和68,中间用&分隔; 注意:1.表达式中允许使用运算符(+-*/)、分割符(括号)、数字,结束符#; 2.如果遇到错误的表达式,应输出错误提示信息(该信息越详细越好); 3.对学有余力的同学,测试用的表达式事先放在文本文件中,一行存放一个表达式,同时以分号分割。同时将预期的输出结果写在另一个文本文件中,以便和输出进行对照; 三.实验过程 1、逆波兰式定义 将运算对象写在前面,而把运算符号写在后面。用这种表示法表示的表达式也称做后缀式。逆波兰式的特点在于运算对象顺序不变,运算符号位置反映运算顺序。采用逆波兰式可以很好的表示简单算术表达式,其优点在于易于计算机处理表达式。 2、产生逆波兰式的前提 中缀算术表达式 3、逆波兰式生成的实验设计思想及算法

(1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。 (2)读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。 (3)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。 (4)如果不是数字,该字符则是运算符,此时需比较优先关系。 做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。倘若不是的话,则将此运算符栈顶的运算

编译原理词法分析和语法分析报告+代码(C语言版)

词法分析 一、实验目的 设计、编制并调试一个词法分析程序,加深对词法分析原理的理解。 二、实验要求 2.1 待分析的简单的词法 (1)关键字: begin if then while do end 所有的关键字都是小写。 (2)运算符和界符 : = + - * / < <= <> > >= = ; ( ) # (3)其他单词是标识符(ID)和整型常数(SUM),通过以下正规式定义: ID = letter (letter | digit)* NUM = digit digit* (4)空格有空白、制表符和换行符组成。空格一般用来分隔ID、SUM、运算符、界符和关键字,词法分析阶段通常被忽略。 2.2 各种单词符号对应的种别码: 输入:所给文法的源程序字符串。 输出:二元组(syn,token或sum)构成的序列。 其中:syn为单词种别码; token为存放的单词自身字符串; sum为整型常数。 例如:对源程序begin x:=9: if x>9 then x:=2*x+1/3; end #的源文件,经过词法分析后输出如下序列: (1,begin)(10,x)(18,:=)(11,9)(26,;)(2,if)…… 三、词法分析程序的算法思想: 算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到单词符号的第一个字符的种类,拼出相应的单词符号。

3.1 主程序示意图: 主程序示意图如图3-1所示。其中初始包括以下两个方面: ⑴关键字表的初值。 关键字作为特殊标识符处理,把它们预先安排在一张表格中(称为关键字表),当扫描程序识别出标识符时,查关键字表。如能查到匹配的单词,则该单词为关键字,否则为一般标识符。关键字表为一个字符串数组,其描述如下: Char *rwtab[6] = {“begin”, “if”, “then”, “while”, “do”, “end”,}; 图3-1 (2)程序中需要用到的主要变量为syn,token和sum 3.2 扫描子程序的算法思想: 首先设置3个变量:①token用来存放构成单词符号的字符串;②sum用来整型单词;③syn用来存放单词符号的种别码。扫描子程序主要部分流程如图3-2所示。

编译原理课后习题答案+清华大学出版社第二版

第 1 章引论 第1 题 解释下列术语: (1)编译程序 (2)源程序 (3)目标程序 (4)编译程序的前端 (5)后端 (6)遍 答案: (1)编译程序:如果源语言为高级语言,目标语言为某台计算机上的汇编语言或机器语言,则此翻译程序称为编译程序。 (2)源程序:源语言编写的程序称为源程序。 (3)目标程序:目标语言书写的程序称为目标程序。 (4)编译程序的前端:它由这样一些阶段组成:这些阶段的工作主要依赖于源语言而与目标机无关。通常前端包括词法分析、语法分析、语义分析和中间代码生成这些阶 段,某些优化工作也可在前端做,也包括与前端每个阶段相关的出错处理工作和符 号表管理等工作。 (5)后端:指那些依赖于目标机而一般不依赖源语言,只与中间代码有关的那些阶段,即目标代码生成,以及相关出错处理和符号表操作。 (6)遍:是对源程序或其等价的中间语言程序从头到尾扫视并完成规定任务的过程。 第2 题 一个典型的编译程序通常由哪些部分组成?各部分的主要功能是什么?并画出编译程序的总体结构图。 答案: 一个典型的编译程序通常包含8个组成部分,它们是词法分析程序、语法分析程序、语义分析程序、中间代码生成程序、中间代码优化程序、目标代码生成程序、表格管理程序和错误处理程序。其各部分的主要功能简述如下。 词法分析程序:输人源程序,拼单词、检查单词和分析单词,输出单词的机内表达形式。 语法分析程序:检查源程序中存在的形式语法错误,输出错误处理信息。 语义分析程序:进行语义检查和分析语义信息,并把分析的结果保存到各类语义信息表中。 中间代码生成程序:按照语义规则,将语法分析程序分析出的语法单位转换成一定形式的中间语言代码,如三元式或四元式。 中间代码优化程序:为了产生高质量的目标代码,对中间代码进行等价变换处理。目标代码生成程序:将优化后的中间代码程序转换成目标代码程序。

编译原理 LL(1)文法源代码

LL(1)文法(源代码) #include "stdio.h" #include "stdlib.h" #define MaxRuleNum 8 #define MaxVnNum 5 #define MaxVtNum 5 #define MaxStackDepth 20 #define MaxPLength 20 #define MaxStLength 50 struct pRNode /*产生式右部结构*/ { int rCursor; struct pRNode *next; }; struct pNode { int lCursor; int rLength; /*右部长度*/ struct pRNode *rHead; /*右部结点头指针*/ }; char Vn[MaxVnNum + 1]; /*非终结符集*/ int vnNum; char Vt[MaxVtNum + 1]; /*终结符集*/ int vtNum; struct pNode P[MaxRuleNum]; int PNum; char buffer[MaxPLength + 1]; char ch; char st[MaxStLength]; /*要分析的符号串*/ struct collectNode { int nVt; struct collectNode *next; }; struct collectNode* first[MaxVnNum + 1]; /*first集*/ struct collectNode* follow[MaxVnNum + 1]; /*follow集*/

编译原理实验 中间代码生成

实验四中间代码生成 一.实验目的: 掌握中间代码的四种形式(逆波兰式、语法树、三元式、四元式)。 二.实验内容: 1、逆波兰式定义:将运算对象写在前面,而把运算符号写在后面。用这种表示法表示的表 达式也称做后缀式。 2、抽象(语法)树:运算对象作为叶子结点,运算符作为内部结点。 3、三元式:形式序号:(op,arg1,arg2) 4、四元式:形式(op,arg1,arg2,result) 三、以逆波兰式为例的实验设计思想及算法 (1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。 (2)读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。 (3)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。 (4)如果不是数字,该字符则是运算符,此时需比较优先关系。 做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。倘若不是的话,则将此运算符栈顶的运算符从栈中弹出,将该字符入栈。 (5)重复上述操作(1)-(2)直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。 四、程序代码: //这是一个由中缀式生成后缀式的程序 #include<> #include<> #include<> #include<> #define maxbuffer 64 void main() { char display_out(char out_ch[maxbuffer], char ch[32]); //int caculate_array(char out_ch[32]); static int i=0; static int j=0; char ch[maxbuffer],s[maxbuffer],out[maxbuffer]; cout<<"请输入中缀表达式: ";

编译原理课程设计-词法分析器(附含源代码)

编译原理-词法分析器的设计 一.设计说明及设计要求 一般来说,编译程序的整个过程可以划分为五个阶段:词法分析、语法分析、中间代码生成、优化和目标代码生成。本课程设计即为词法分析阶段。词法分析阶段是编译过程的第一个阶段。这个阶段的任务是从左到右一个字符一个字符地读入源程序,对构成源程序的字符流进行扫描和分解,从而识别出一个个单词(也称单词符号或符号)。如保留字(关键字或基本字)、标志符、常数、算符和界符等等。 二.设计中相关关键字说明 1.基本字:也称关键字,如C语言中的 if , else , while , do ,for,case,break, return 等。 2.标志符:用来表示各种名字,如常量名、变量名和过程名等。 3.常数:各种类型的常数,如12,6.88,和“ABC” 等。 4.运算符:如 + ,- , * , / ,%, < , > ,<= , >= 等。5.界符,如逗点,冒号,分号,括号,# ,〈〈,〉〉等。 三、程序分析 词法分析是编译的第一个阶段,它的主要任务是从左到右逐个字符地对源 程序进行 扫描,产生一个个单词序列,用以语法分析。词法分析工作可以是独立的一遍,把字符流的源程序变为单词序列,输出在一个中间文件上,这个文件做为语法分析程序的输入而继续编译过程。然而,更一般的情况,常将

词法分析程序设计成一个子程序,每当语法分析程序需要一个单词时,则 调用该子程序。词法分析程序每得到一次调用,便从源程序文件中读入一 些字符,直到识别出一个单词,或说直到下一个单词的第一个字符为止。 四、模块设计 下面是程序的流程图 五、程序介绍 在程序当前目录里建立一个文本文档,取名为infile.txt,所有需要分析的程序都写在此文本文档里,程序的结尾必须以“@”标志符结束。程序结果输出在同一个目录下,文件名为outfile.txt,此文件为自动生成。本程序所输出的单词符号采用以下二元式表示:(单词种别,单词自身的值)如程序输出结果(57,"#")(33,"include")(52,"<")(33,"iostream") 等。 程序的功能:(1)能识别C语言中所有关键字(共32个)(单词种别分别为1 — 32 ,详情见程序代码相关部分,下同) (2)能识别C语言中自定义的标示符(单词种别为 33) (3)能识别C语言中的常数(单词种别为0) (4)能识别C语言中几乎所有运算符(单词种别分别为41 — 54) (5)能识别C语言中绝大多数界符(单词种别分别为 55 — 66)六、运行结果 输入文件infile.txt 运行结果(输出文件 outfile.txt)

编译原理第一章练习和答案

例1设有文法G[S]: S →a|(T )| T →T,S|S (1) 试给出句子(a,a,a)的最左推导。 (2) 试给出句子(a,a,a)的分析树 (3) 试给出句子(a,a,a)的最右推导和最右推导的逆过程(即最左规约)的每一步的句柄。 【解】(1) (a,a,a)的最左推导 S=>(T) =>(T,S) =>( T,S,S) =>( S,S,S) =>(a,S,S) =>(a,a,S) =>(a,a,a) (2)(a,a,a)的分析树 S ( T ) T , S S T , S a a (3) (a,a,a)最右推导 最左规约每一步的句柄 S=>(T) 句柄为:(T) =>(T,S) 句柄为:T,S =>(T,a) 句柄为:a =>(T,S,a) 句柄为:T,S =>(T,a,a) 句柄为:第一个a =>(S,a,a) 句柄为:S =>(a,a,a) 句柄为:第一个a 例2已知文法G[Z]: Z →0U|1V U →1Z|1 V →0Z|0 (1) 请写出此文法描述的只含有4个符号的全部句子。 (2) G [Z]产生的语言是什么? (3) 该文法在Chomsky 文法分类中属于几型文法? 【解】(1)0101,0110,1010, 1001 (2)分析G[Z]所推导出的句子的特点:由Z 开始的推导不外乎图1所示的四种情形。 图 1文法G[Z]可能的几种推导 Z 1 U Z U Z 1 Z 1 Z 1 V 由Z 推导出10或01后就终止或进入递归,而Z 的每次递归将推导出相同的符号串:10或

01。所以G[Z]产生的语言L(G[Z])={x|x∈(10|01)+ } (3)该文法属于3型文法。 例3 已知文法G=({A,B,C},{a,b,c},P,A), P由以下产生式组成: A→abc A→aBbc Bb→bB Bc→Cbcc bC→Cb aC→aaB aC→aa 此文法所表示的语言是什么? 【解】 分析文法的规则: 每使用一次Bc→Cbcc,b、c的个数各增加一个; 每使用一次aC→aaB或aC→aa, a的个数就增加一个; 产生式Bb→bB、 bC→Cb起连接转换作用。 由于A是开始符号,由产生式A→abc推导得到终结符号串abc;由产生式A→aBbc推导得到B后,每当使用产生式Bb→bB、Bc→Cbcc、bC→Cb、aC→aaB就会递归调用B一次,所产生的a、b、c的个数分别增加一个,因此推导所得的终结符号串为abc、aabbcc、aaabbbccc、…所以文法描述的语言为{ a n b n c n|n>0}. 例4 构造描述语言L(G[S])={(n)n|n≥0} 的文法。 【解】(1)找出语言的一些典型句子: n=0 ε n=1 ( ) n=2 (()) … 所以, L(G[S])={ ε、( ) (())、((()))、…} (2)分析句子的特点: 只含有(和),(和)的个数相同且对称, 句子中所含的符号数可无限, 句子的个数可无限。 (3)凑规则:由 S→ε|() 得到ε|(),由 A→ (S) 得到 (()),(()) 是在()的两边再加上一对()得到,((()))是在(())的两边再加上一对()得到,…所以将上述产生式合并为S→(S) |ε。 (4)得到文法 G[S]: S→(S) |ε (5)检验:语言所有的句子均可由文法G[S]推导出来, 文法G[S]推导出来的所有终结符号串均为语言的句子. 例5 构造描述语言L(G[S])={a m b n |n>m>0} 的文法。 【解】找出语言的一些典型句子:abb、abbb、…、aabbb、aabbbb、…,语言的句子的特点是仅含有a、b, a在b的左边,b的个数大于a的个数,a的个数至少是1。 单独生成c k, k>1 可用产生式 C→c |Cc 句子中要求b的个数大于a的个数,所以得到文法:

编译原理复习(有答案)

第一章引论 1.编译过程的阶段 由词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成六个阶段 2.编译程序的概念 3.编译程序的结构 例:(B)不是编译程序的组成部分。 A. 词法分析器; B. 设备管理程序 C. 语法分析程序; D. 代码生成程序 4.遍的概念 对源程序(或其中间形式)从头至尾扫描一次并进行有关加工处理,生成新的中间形式或最终目标程序,称为一遍。 5.编译程序与解释程序的区别 例:解释程序和编译程序是两类程序语言处理程序,它们的主要区别在于(D)。 A. 单用户与多用户的差别 B. 对用户程序的差错能力 C. 机器执行效率 D. 是否生成目标代码 第三章文法和语言 文法的概念 字母表、符号串和集合的概念及运算 例:(ab|b)*c 与下面的那些串匹配?(ACD) A. ababbc; B. abab; C. c; D. babc; E. aaabc 例:ab*c*(a|b)c 与后面的那些串匹配?(BC) A.acbbc B.abbcac C.abc D.acc 例:(a|b)a+(ba)*与后面的那些串匹配? (ADE)A.ba B.bba C.ababa D.aa E.baa 文法的定义(四元组表示) 文法G定义为四元组(V N,V T,P,S) V N:非终结符集 V T:终结符集 P:产生式(规则)集合 S:开始符号(或识别符号) 例:给定文法,A::= bA | cc,下面哪些符号串可由其推导出(①② ⑤)。 ①cc ②b*cc ③b*cbcc ④bccbcc ⑤bbbcc 什么是推导 例:已知文法G: E->E+T|E-T|T T->T*F|T/F|F F->(E)|i 试给出下述表达式的推导:i*i+i 推导过程:E->E+T ->T+T ->T*F+T ->F*F+T ->i*F+T ->i*i+T ->i*i+F ->i*i+i ●句型、句子的概念 例:假设G一个文法,S是文法的开始符 号,如果S=>*x,则称x是句型。 例:对于文法G,仅含终结符号的句型称 为句子。 ●语言的形式定义 例:设r=(a|b|c)(x|y|z),则L(r)中元素为 9个。 例:文法G产生式为S→AB,A→aAb|ε, B→cBd|cd,则B∈L(G)。 A. ababcd; B. ccdd; C. ab; D. aabb ●等价文法 例:如果两个文法描述了同一个语言,则这两个文法是等价文法。 ●文法的类型 0型:左边至少有一个非终结符 1型:右边长度>=左边长度 2型:左边有且仅有一个非终结符 3型:形如:A->aB,A->a 各类型文法都是逐级包含关系, 例:文法S→abC|c,bC→d是几型文法?0 型 例:文法S→abC,bC→ad是几型文法?1 型 例:文法G[A]:A→ε,A→aB,B→Ab,B→a 是几型文法?2型 例:文法S→a|bC,C→d是几型文法? 3

相关文档
最新文档