语法分析器实验报告

合集下载

词法分析的实验报告

词法分析的实验报告

《词法分析》实验报告目录目录 (1)1 实验目的 (2)2 实验容 (2)2.1 TINY计算机语言描述 (2)2.2 实验要求 (2)3 此法分析器的程序实现 (3)3.1 状态转换图 (3)3.2 程序源码 (4)3.3 实验运行效果截图 (9)4 实验体会 (10)1实验目的1、学会针对DFA转换图实现相应的高级语言源程序。

2、深刻领会状态转换图的含义,逐步理解有限自动机。

3、掌握手工生成词法分析器的方法,了解词法分析器的部工作原理。

2实验容2.1TINY计算机语言描述TINY计算机语言的编译程序的词法分析部分实现。

从左到右扫描每行该语言源程序的符号,拼成单词,换成统一的部表示(token)送给语法分析程序。

为了简化程序的编写,有具体的要求如下:1、数仅仅是整数。

2、空白符仅仅是空格、回车符、制表符。

3、代码是自由格式。

4、注释应放在花括号之,并且不允许嵌套TINY语言的单词2.2实验要求要现编译器的以下功能1、按规则拼单词,并转换成二元式形式2、删除注释行3、删除空白符 (空格、回车符、制表符)4、列表打印源程序,按照源程序的行打印,在每行的前面加上行号,并且打印出每行包含的记号的二元形式5、发现并定位错误词法分析进行具体的要求1、记号的二元式形式中种类采用枚举方法定义;其中保留字和特殊字符是每个都一个种类,标示符自己是一类,数字是一类;单词的属性就是表示的字符串值。

2、词法分析的具体功能实现是一个函数GetToken(),每次调用都对剩余的字符串分析得到一个单词或记号识别其种类,收集该记号的符号串属性,当识别一个单词完毕,采用返回值的形式返回符号的种类,同时采用程序变量的形式提供当前识别出记号的属性值。

这样配合语法分析程序的分析需要的记号及其属性,生成一个语法树。

3、标示符和保留字的词法构成相同,为了更好的实现,把语言的保留字建立一个表格存储,这样可以把保留字的识别放在标示符之后,用识别出的标示符对比该表格,如果存在该表格中则是保留字,否则是一般标示符。

实验五用语法制导方式生成中间代码生成器

实验五用语法制导方式生成中间代码生成器

实验5 用语法制导方式生成中间代码生成器魏陈强23020092204168一、实验目的掌握语法制导定义和翻译的原理和技术,在语法分析器的基础上,加上语义分析,构造一个中间代码生成器。

二、实验内容在实验四生成的语法分析器基础上加入语义动作,将源程序翻译为对应的中间代码序列。

三、实验要求1.实验报告中给出采用测试源代码片断,及其对应的三地址码形式(内部表示形式可以自行考虑)。

例如,程序片断对应的中间代码为:四、实验思路在前4次实验的基础上进行中间代码生成的编写,采用linux环境下的lex和yacc工具。

首先编写goto.c函数,该函数实现符号表、回填、创建节点、定义节点属性等功能。

Lex.l文件无需修改,yacc.y文件中,在每个生成式后加上语法制导翻译,主要是依据truelist和falselist 来实现回填功能。

编写完后,在yacc.y中以头文件的方式加入goto.c,编译即可。

五、实验代码1、lex.l%{%}……letter [A-Za-z]digit [0-9]……%option noyywrap%%if { return( IF ); }else { return( ELSE ); }……"<"|"<="|">"|">="|"!="|"==" { op_in(&yylval, yytext); return( REL); }……%%2、Yacc.y%{#include "goto.c"#define YYSTYPE nodeint yyerror();int yyerror(char* msg);extern int yylex();codelist* list;%}%token BASIC NUMBER REAL ID TRUE FALSE%token INT CHAR BOOL FLOAT%token REL%token IF ELSE WHILE DO BREAK SWITCH CASE DEFAULT%token OR AND%left OR%left AND%right '!'%left '+' '-'%left '*' '/'%right UMINUS%right INC DEC%expect 1%%program : block { };block : '{' decls statementlist '}' { };decls : decls decl { }| { };decl : type ID ';' { };type : type '[' NUMBER ']' { }| BASIC { };statementlist : statementlist M statement { backpatch(list, $1.nextlist, $2.instr);$$.nextlist = $3.nextlist; }| statement { $$.nextlist = $1.nextlist; };statement : IF '(' boolean ')' M statement ELSE N M statement{ backpatch(list, $3.truelist, $5.instr);backpatch(list, $3.falselist, $9.instr);$6.nextlist = merge($6.nextlist, $8.nextlist);$$.nextlist = merge($6.nextlist, $10.nextlist); }| IF '(' boolean ')' M statement { backpatch(list, $3.truelist, $5.instr);$$.nextlist = merge($3.falselist, $6.nextlist); }| WHILE M '(' boolean ')' M statement { backpatch(list, $7.nextlist, $2.instr);backpatch(list, $4.truelist, $6.instr);$$.nextlist = $4.falselist;gen_goto(list, $2.instr); }| DO M statement M WHILE '(' boolean ')' M ';' { backpatch(list, $3.nextlist, $4.instr);backpatch(list, $7.truelist, $9.instr);$$.nextlist = $7.falselist;gen_goto(list, $2.instr); }| BREAK ';' { }| '{' statementlist '}' { $$.nextlist = $2.nextlist; }| assignment ';' { $$.nextlist = NULL; };assignment : ID '=' boolean { address(&$1, $1.lexeme); gen_assignment(list, $1, $3); } ;loc : loc '[' boolean ']' { }| ID { address(&$$, $1.lexeme); };boolean : boolean OR M boolean { backpatch(list, $1.falselist, $3.instr);$$.truelist = merge($1.truelist, $4.truelist);$$.falselist = $4.falselist; }| boolean AND M boolean { backpatch(list, $1.truelist, $3.instr);$$.truelist = $4.truelist;$$.falselist = merge($1.falselist, $4.falselist); }| '!' boolean { $$.truelist = $1.falselist;$$.falselist = $1.truelist; }| '(' boolean ')' { $$.truelist = $1.truelist;$$.falselist = $1.falselist; }| expression REL expression { $$.truelist = new_instrlist(nextinstr(list));$$.falselist = new_instrlist(nextinstr(list)+1);gen_if(list, $1, $2.oper, $3);gen_goto_blank(list); }| TRUE { address(&$$, "TRUE");gen_goto_blank(list); }| FALSE { address(&$$, "FALSE");gen_goto_blank(list); }| expression { addr_node(&$$, $1); };M : { $$.instr = nextinstr(list); };N : { $$.nextlist = new_instrlist(nextinstr(list));gen_goto_blank(list); };expression : expression '+' expression { new_temp(&$$, get_temp_index(list)); gen_3addr(list, $$, $1, "+", $3); } | expression '-' expression { new_temp(&$$, get_temp_index(list)); gen_3addr(list, $$, $1, "-", $3); }| expression '*' expression { new_temp(&$$, get_temp_index(list)); gen_3addr(list, $$, $1, "*", $3); }| expression '/' expression { new_temp(&$$, get_temp_index(list)); gen_3addr(list, $$, $1, "/", $3); }| '-' expression %prec UMINUS { new_temp(&$$, get_temp_index(list)); gen_2addr(list, $$, "-", $2); }| loc { addr_node(&$$, $1); }| NUMBER { address(&$$, $1.lexeme); }| REAL { address(&$$, $1.lexeme); };%%#include"lex.yy.c"int yyerror(char* msg){printf("\nERROR with message: %s\n", msg);return 0;}int main(){list = newcodelist();yyparse();print(list);return 0;}2、Goto.c#ifndef GOTO_C#define GOTO_C#include <stdio.h>#include <string.h>#include <malloc.h>typedef struct listele{int instrno;struct listele *next;}listele;listele* new_listele(int no){listele* p = (listele*)malloc(sizeof(listele));p->instrno = no;p->next = NULL;return p;}typedef struct instrlist{listele *first,*last;}instrlist;instrlist* new_instrlist(int instrno){instrlist* p = (instrlist*)malloc(sizeof(instrlist));p->first = p->last = new_listele(instrno);return p;}instrlist* merge(instrlist *list1, instrlist *list2){instrlist *p;if (list1 == NULL) p = list2;else{if (list2!=NULL){if (list1->last == NULL){list1->first = list2->first;list1->last =list2->last;}else list1->last->next = list2->first;list2->first = list2->last = NULL;free(list2);}p = list1;}return p;}typedef struct node{instrlist *truelist, *falselist, *nextlist;char addr[256];char lexeme[256];char oper[3];int instr;}node;int op_in(node *dst, char *src){strcpy(dst->oper, src);return 0;}int lexeme_in(node *dst, char *yytext){strcpy(dst->lexeme, yytext);return 0;}int address(node *dst, char *src){strcpy(dst->addr, src);return 0;}int new_temp(node *dst, int index){sprintf(dst->addr, "t%d", index);return 0;}int addr_node(node *dst, node src){strcpy(dst->addr, src.addr);return 0;}typedef struct codelist{int linecnt, capacity;int temp_index;char **code;}codelist;codelist* newcodelist(){codelist* p = (codelist*)malloc(sizeof(codelist));p->linecnt = 0;p->capacity = 1024;p->temp_index = 0;p->code = (char**)malloc(sizeof(char*)*1024);return p;}int get_temp_index(codelist* dst){return dst->temp_index++;}int nextinstr(codelist *dst) { return dst->linecnt; }int Gen(codelist *dst, char *str){if (dst->linecnt >= dst->capacity){dst->capacity += 1024;dst->code = (char**)realloc(dst->code, sizeof(char*)*dst->capacity);if (dst->code == NULL){printf("Error!\n");return 0;}}dst->code[dst->linecnt] = (char*)malloc(strlen(str)+20);strcpy(dst->code[dst->linecnt], str);dst->linecnt++;return 0;}char tmp[1024];int gen_goto_blank(codelist *dst){sprintf(tmp, "goto");Gen(dst, tmp);return 0;}int gen_goto(codelist *dst, int instrno){sprintf(tmp, "goto L%d", instrno);Gen(dst, tmp);return 0;}int gen_if(codelist *dst, node left, char* op, node right){sprintf(tmp, "if %s %s %s goto", left.addr, op, right.addr);Gen(dst, tmp);return 0;}int gen_1addr(codelist *dst, node left, char* op){sprintf(tmp, "%s %s", left.addr, op);Gen(dst, tmp);return 0;}int gen_2addr(codelist *dst, node left, char* op, node right){sprintf(tmp, "%s = %s %s", left.addr, op, right.addr);Gen(dst, tmp);return 0;}int gen_3addr(codelist *dst, node left, node op1, char* op, node op2){sprintf(tmp, "%s = %s %s %s", left.addr, op1.addr, op, op2.addr);Gen(dst, tmp);return 0;}int gen_assignment(codelist *dst, node left, node right){gen_2addr(dst, left, "", right);return 0;}int backpatch(codelist *dst, instrlist *list, int instrno){if (list!=NULL){listele *p=list->first;char tmp[20];sprintf(tmp, " L%d", instrno);while (p!=NULL){if (p->instrno<dst->linecnt)strcat(dst->code[p->instrno], tmp);p=p->next;}}return 0;}int print(codelist* dst){int i;for (i=0; i < dst->linecnt; i++)printf("L%d: %s\n", i, dst->code[i]);return 0;}#endif六、实验结果1、输入程序2、输出结果七、实验心得本次实验要实现中间代码生成,也是本学期的最后一次实验,要在前4次实验的基础上完成,综合比较前4次实验,感觉本次实验难度挺高,首先,前4次实验的词法分析和语法分析需要有足够高的正确率,否者本次实验中会遇到一些神奇的问题,不知道错在哪里;其次,对语法制导定义和语法制导翻译要掌握好才能把握编写思路。

词法分析器实验报告

词法分析器实验报告

用户子程序:
void print() { count++; if((fpout=fopen("My.txt","a"))==NULL){
printf("cannot write the file /n"); exit(0); } if(value<=5){ //正常情况下处理方式 switch(value){ case 0:entity[count-1].idproperty="BasicKey";break; case 1:entity[count-1].idproperty="identifier";break; case 2:entity[count-1].idproperty="number";break; case 3:entity[count-1].idproperty="arithmetic-op";break; case 4:entity[count-1].idproperty="relation-op";break; case 5:entity[count-1].idproperty="boundary-op";break; } entity[count-1].idname=yytext; fprintf(fpout,"%d <符号: \"%s\" , 类型:%s > \n",count,entity[count1].idname,entity[count-1].idproperty); }else{ //wrongid时处理方式 errnum+=1; switch(value){ case 6:entity[count-1].idproperty="Mixed number and letter:";break; case 7:entity[count-1].idproperty="Unkown operator:";break; } entity[count-1].idname=yytext; fprintf(fpout,"%d [line:%d]: \"%s\" %s \n",count,linenum,entity[count1].idname,entity[count-1].idproperty); } fclose(fpout); } void main() { printf("please input the PL//0 program file: "); scanf("%s",&filename); if((fpin=fopen(filename,"r"))==NULL){ //打开文件 printf("can't open the file: %s",filename);

语法分析器文档

语法分析器文档
这里我们采用递归下降分析方法:直接以程序的方式模拟产生式产生语言的过程。它的基本设计思想是:为每一个非终结符构造一个子程序,每一个子程序的过程体中按该产生式的候选项分情况展开,遇到终结符直接匹配,而遇到非终结符就调用相应非终结符的子程序。该分析从调用文法开始符号的子程序开始,直到所有非终结符都展开为终结符并得到匹配为止。若分析过程中达到这一步则表明分析成功,否则表明输入中有语法错误。递归下降分析对文法的限制是不能有公共左因子和左递归。由于文法是递归定义的,因此子程序也是递归的。
初使化词法分析器
识别出具有独立意义的最小语法单位
辅助性模块
②重要数据结构
·语法树节点类型
struct ExprNode { //语法树节点类型
enum Token_Type OpCode;
union {
struct {
ExprNode *Left, *Right;
} CaseOperator;
struct {
重复此过程,直到所有A产生式的候选项中均不再有公共前缀。
·构造递归下降子程序的方法:
①构造文法的状态转换图并且简化;
②将转换图转化为EBNF表示;
③从EBNmain.cpp)
#include <stdio.h>
#include "parser.h"
·消除左递归算法
输入:无回路文法G
输出:无左递归的等价文法G’
方法:将非终结符合理排序:A1,A2,…,An,然后运用下述过程:
for i in 2..n
loop for j in 1..i-1
loop用AjQ1|Q2|…|Qk的右部替换每个形如AiAj产生式中的Aj,得到新产生式:

词法分析器实验报告

词法分析器实验报告

词法分析器实验报告引言:词法分析器(Lexical Analyzer)是编译器的重要组成部分,其主要任务是将源代码转化为一个个独立的词法单元,为语法分析器提供输入。

在本次实验中,我们设计并实现了一个简单的词法分析器,通过对其功能和性能的测试,评估其在不同场景下的表现。

实验目的:1. 确定词法分析器的输入和输出要求;2. 通过构建适当的正则表达式规则,匹配不同类型的词法单元;3. 实现一个高效的词法分析器,确保在处理大型源代码时性能不受影响;4. 对词法分析器的功能和性能进行测试和评估。

实验过程:1. 设计词法分析器的接口:1.1 确定输入:源代码字符串。

1.2 确定输出:词法单元流,每个词法单元包含类型和对应的字符串值。

2. 构建正则表达式规则:2.1 识别关键字:根据编程语言的关键字列表构建正则表达式规则,将关键字与标识符区分开。

2.2 识别标识符:一般由字母、下划线和数字组成,且以字母或下划线开头。

2.3 识别数字:整数和浮点数可以使用不同的规则来识别。

2.4 识别字符串:使用引号(单引号或双引号)包裹的字符序列。

2.5 识别特殊符号:各类操作符、括号、分号等特殊符号需要单独进行规则设计。

3. 实现词法分析器:3.1 读取源代码字符串:逐个字符读取源代码字符串,并根据正则表达式规则进行匹配。

3.2 保存词法单元:将匹配到的词法单元保存到一个词法单元流中。

3.3 返回词法单元流:将词法单元流返回给调用者。

4. 功能测试:4.1 编写测试用例:针对不同类型的词法单元编写测试用例,包括关键字、标识符、数字、字符串和特殊符号。

4.2 执行测试用例:将测试用例作为输入传递给词法分析器,并检查输出是否和预期一致。

4.3 处理错误情况:测试词法分析器对于错误输入的处理情况,如非法字符等。

5. 性能测试:5.1 构建大型源代码文件:生成包含大量代码行数的源代码文件。

5.2 执行词法分析:使用大型源代码文件作为输入,测试词法分析器的性能。

编译原理实验报告总结

编译原理实验报告总结

学年第学期《编译原理》实验报告学院(系):计算机科学与工程学院班级:11303070A学号:***********姓名:无名氏指导教师:保密式时间:2016 年7 月目录1.实验目的 (1)2.实验内容及要求 (1)3.实验方案设计 (1)3.1 编译系统原理介绍 (1)3.1.1 编译程序介绍 (2)3.1.2 对所写编译程序的源语言的描述 (2)3.2 词法分析程序的设计 (3)3.3 语法分析程序设计 (4)3.4 语义分析和中间代码生成程序的设计 (4)4. 结果及测试分析 (4)4.1软件运行环境及限制 (4)4.2测试数据说明 (5)4.3运行结果及功能说明 (5)5.总结及心得体会 (7)1.实验目的根据Sample语言或者自定义的某种语言,设计该语言的编译前端。

包括词法分析,语法分析、语义分析及中间代码生成部分。

2.实验内容及要求(1)词法分析器输入源程序,输出对应的token表,符号表和词法错误信息。

按规则拼单词,并转换成二元形式;滤掉空白符,跳过注释、换行符及一些无用的符号;进行行列计数,用于指出出错的行列号,并复制出错部分;列表打印源程序;发现并定位词法错误;(2)语法分析器输入token串,通过语法分析,寻找其中的语法错误。

要求能实现Sample 语言或自定义语言中几种最常见的、基本的语法单位的分析:算术表达式、布尔表达式、赋值语句、if语句、for语句、while语句、do while语句等。

(3)语义分析和中间代码生成输入token串,进行语义分析,修改符号表,寻找其中的语义错误,并生成中间代码。

要求能实现Sample语言或自定义语言中几种最常见的、基本的语法单位的分析:算术表达式、布尔表达式、赋值语句、if语句、for语句、while 语句、do while语句等。

实验要求:功能相对完善,有输入、输出描述,有测试数据,并介绍不足。

3.实验方案设计3.1 编译系统原理介绍编译器逐行扫描高级语言程序源程序,编译的过程如下:(1).词法分析识别关键字、字面量、标识符(变量名、数据名)、运算符、注释行(给人看的,一般不处理)、特殊符号(续行、语句结束、数组)等六类符号,分别归类等待处理。

SLR(1)文法分析实验报告

SLR(1)文法分析实验报告

《编译原理》课程设计报告SLR(1)分析的实现院计算机科学与技术业计算机科学与技术指导教师姓名2015年12月26日目录1.设计的目的与内容1.1课程设计的目的1.2设计内容1.3设计要求1.4理论基础 2算法的基本思想2.1主要功能函数2.2算法思想SLR文法构造分析表的主要思想: 解决冲突的方法:SLR语法分析表的构造方法:3主要功能模块流程图3.1主函数功能流程图 4系统测试5结论附录程序源码清单11121.设计的目的与内容1.1课程设计的目的编译原理课程设计是计算机专业重要的教学环节, 本上的理论知识和实际有机的结合起来,独立分析和解决实际问题的机会。

进一步巩固和复习编译原理的基础知识。

培养学生结构化程序、模块化程序设计的方法和能力。

提高学生对于编程语言原理的理解能力。

加深学生对于编程语言实现手段的印象。

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

1.3设计要求SLR(1分析表的生成可以选择编程序生成,也可选择手动生成;1.4理论基础由于大多数适用的程序设计语言的文法不能满足 量说明这样简单的文法也不一定是 LR(0)文法。

因此对于LR(O规范族中有冲突的项目集 (状态) 它为学生提供了一个既动手又动脑, 将课 2) 程序要求要配合适当的错误处理机制;3) 要打印句子的文法分析过程。

1) LR(0)文法的条件,即使是描述一个实数变用向前查看一个符号的办法进行处理,以解决冲突。

这种办法将能满足一些文法的需要,因为只对有冲突的状态才向前查看一个符号,以确定做那种动作,因而称这种分析方法为简单的LR(1 分析法,用SLR(1表示。

2算法的基本思想2. 1 主要功能函数class WFWF (char s1 [], char s2 [], int x ,int yWF (const string & s1 , const stri ng & s2 bool op erator < (con st WF & a ) con stbool op erator ==(con st WF & a ) con st void p ri nt () )int x , int y )};class Closurevoid p rint(string strbool op erator (con st Closure & a ) const};void make item ()void dfs (const stri ng & xvoid make first ()void append (con st stri ng & str1 , const stri ng & str2bool check (con st vector <int >& id , const string str void make_follow ()void make_set ()void make_V ()void make_cmp ( vector <WF>& cmp1void make_go ()void make_table ()void print (string s1 ,string s2 s6 , stri ng s7 )stri ng get_ste ps (int x )stri ng get_stk((vector <T> stk ) stri ng get_shift (WF& temp ),string s3 , stringint i , char ch )s4 , string s5 , stringvoid analyse ( string src )2.2算法思想SLR文法构造分析表的主要思想:许多冲突性的动作都可能通过考察有关非终结符的FOLLOW集而获解决。

词法分析器实验报告

词法分析器实验报告

词法分析器实验报告词法分析器实验报告一、引言词法分析器是编译器中的重要组成部分,它负责将源代码分解成一个个的词法单元,为之后的语法分析提供基础。

本实验旨在设计和实现一个简单的词法分析器,以深入理解其工作原理和实现过程。

二、实验目标本实验的目标是设计和实现一个能够对C语言代码进行词法分析的程序。

该程序能够将源代码分解成关键字、标识符、常量、运算符等各种词法单元,并输出其对应的词法类别。

三、实验方法1. 设计词法规则:根据C语言的词法规则,设计相应的正则表达式来描述各种词法单元的模式。

2. 实现词法分析器:利用编程语言(如Python)实现词法分析器,将源代码作为输入,根据词法规则将其分解成各种词法单元,并输出其类别。

3. 测试和调试:编写测试用例,对词法分析器进行测试和调试,确保其能够正确地识别和输出各种词法单元。

四、实验过程1. 设计词法规则:根据C语言的词法规则,我们需要设计正则表达式来描述各种词法单元的模式。

例如,关键字可以使用'|'操作符将所有关键字列举出来,标识符可以使用[a-zA-Z_][a-zA-Z0-9_]*的模式来匹配,常量可以使用[0-9]+的模式来匹配等等。

2. 实现词法分析器:我们选择使用Python来实现词法分析器。

首先,我们需要读取源代码文件,并将其按行分解。

然后,针对每一行的代码,我们使用正则表达式进行匹配,以识别各种词法单元。

最后,我们将识别出的词法单元输出到一个结果文件中。

3. 测试和调试:我们编写了一系列的测试用例,包括各种不同的C语言代码片段,以测试词法分析器的正确性和鲁棒性。

通过逐个测试用例的运行结果,我们可以发现和解决词法分析器中的问题,并进行相应的调试。

五、实验结果经过多次测试和调试,我们的词法分析器能够正确地将C语言代码分解成各种词法单元,并输出其对应的类别。

例如,对于输入的代码片段:```cint main() {int a = 10;printf("Hello, world!\n");return 0;}```我们的词法分析器将输出以下结果:```关键字:int标识符:main运算符:(运算符:)运算符:{关键字:int标识符:a运算符:=常量:10运算符:;标识符:printf运算符:(常量:"Hello, world!\n"运算符:)运算符:;关键字:return常量:0运算符:;```可以看到,词法分析器能够正确地将代码分解成各种词法单元,并输出其对应的类别。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

语法分析器的设计实验报告 一、实验内容 语法分析程序用LL(1)语法分析方法。首先输入定义好的文法书写文件(所用的文 法可以用LL(1)分析),先求出所输入的文法的每个非终结符是否能推出空, 再分 别计算非终结符号的FIRST集合,每个非终结符号的FOLLOW集合,以及每个 规则的SELECT集合,并判断任意一个非终结符号的任意两个规则的 SELECT 集的交集是不是都为空,如果是,则输入文法符合 LL(1)文法,可以进行分析。 对于文法: G[E]: E->E+T|T T->T*F|F F->i|(E) 分析句子i+i*i是否符合文法。

二、基本思想 1、语法分析器实现 语法分析是编译过程的核心部分,它的主要任务是按照程序的语法规则,从由词 法分析输出的源程序符号串中识别出各类语法成分, 同时进行词法检查,为语义 分析和代码生成作准备。这里采用自顶向下的 LL(1)分析方法。

该程序可分为如下几步: (1) 读入文法 (2) 判断正误 (3) 若无误,判断是否为LL(1)文法 (4) 若是,构造分析表; (5) 由句型判别算法判断输入符号串是为该文法的句型。 三、核心思想 该分析程序有15部分组成: (1)首先定义各种需要用到的常量和变量; (2)判断一个字符是否在指定字符串中; (3)读入一个文法; (4)将单个符号或符号串并入另一符号串; (5)求所有能直接推出 & 的符号; (6)求某一符号能否推出‘ & '; (7)判断读入的文法是否正确; (8)求单个符号的 FIRST; (9)求各产生式右部的 FIRST; (10)求各产生式左部的 FOLLOW ; (11)判断读入文法是否为一个 LL(1) 文法; (12)构造分析表 M ; (13)句型判别算法; (14)一个用户调用函数; (15)主函数; 下面是其中几部分程序段的算法思想: 1、求能推出空的非终结符集 I、实例中求直接推出空的 empty集的算法描述如下:

void emp(char c){ 参数 c 为空符号

char temp[10]; 定义临时数组 int i;

for(i=0;i<=count-1;i++) 从文法的第一个产生式开始查找 { if 产生式右部第一个符号是空符号并且右部长度为 1, then 将该条产生式左部符号保存在临

时数组 temp 中 将临时数组中的元素合并到记录可推出 & 符号的数组 empty 中。 } U、求某一符号能否推出&

int _emp(char c) { //若能推出 & ,返回 1;否则,返回 0

int i,j,k,result=1,mark=0; char temp[20];

temp[0]=c; temp[1]='\0'; 存放到一个临时数组 empt 里,标识此字符已查找其是否可推出空字 如果c在可直接推出空字的 empty[]中,返回1 for(i=0;;i++) { if(i==count)

return(0);

找一个左部为 c 的产生式 j=strlen(right[i]); //j 为 c 所在产生式右部的长度 if右部长度为1且右部第一个字符在 empty[]中.then返回1(A->B,B可推出空) if 右部长度为 1 但第一个字符为终结符 ,then 返回 0(A->a,a 为终结符 ) else{ for(k=0;k<=j-1;k++) { 查找临时数组 empt[]. 并标记 mark-=1(A->AB)

if 找到的字符与当前字符相同 (A->AB)

结束本次循环 else(mark 等于 0) 查找右部符号是否可推出空字 ,把返回值赋给 result 把当前符号加入到临时数组 empt[] 里. } if 当前字符不能推出空字且还没搜索完全部的产生式 then 跳出本次循环继续搜索

下一条产生式 else if //当前字符可推出空字 ,返回 1 }

} }

2、计算每个符号的 first 集: 实例中求单个符号的 FIRST 集的算法描述如下: void first2 (int i) { 参数 i 为符号在所有输入符号中的序号 c 等于指示器 i 所指向的符号 在保存终结符元素的 termin[] 数组查找 c if c 为终结符(c€ VT ), then FIRST(c)={c} 在保存终结符元素的 non_ter[] 数组查找 c if c是非终结符(c € VN ) 在所有产生式中查找 c 所在的产生式 if 产生式右部第一个字符为终结符或空 (即cTa (a€ VT)或c^ &) then 把a或&加进FIRST(c) if 产生式右部第一个字符为非终结符 then if 产生式右部的第一个符号等于当前字符 then 跳到下一条产生式进行查找 求当前非终结符在所有字符集中的位置 if 当前非终结符还没求其 FIRST 集 then 查找它的 FIRST 集并标识此符号已求其 FIRST 集 求得结果并入到 c 的 FIRST 集. if 当前产生式右部符号可推出空字且当前字符不是右部的最后一个字符 then 获取右部符号下一个字符在所有字符集中的位置 if 此字符的 FIRST 集还未查找 then 找其 FIRST 集,并标其查找状态为 1 把求得的FIRST集并入到c的FIRST集. if 当前右部符号串可推出空且是右部符号串的最后一个字符 (即产生式为 ct Y1Y2…Yk,若对一切 1<=i<=k,均有 & € FIRST(Y i),则将& €符号加进 FIRST(c)) the n 把空字加入到当前字符 c的FIRST集.

else 不能推出空字则结束循环 标识当前字符c已查找其FIRST集.}

3. 计算 FOLLOW FOLLOW集的构造可用如下方法来求: 对于文法中的符号 X • VN ,其FOLLOW(A)集合可反复应用下列规则计算,直到 FOLLOW(A)集合不再增大为止。 (1) 对于文法开始符号 S,因为S S,故# FOLLOW(S); (2) 若 Ar B 一:,其中 B VN^.三(VT VN)*、“(VT VN)+,贝V FIRST( -)-{ } FOLLOW(B); (3) 若 AT :• B 或 AT :• B 1 (「丄• 3,贝U FOLLOW(A) FOLLOW(B)。 FOLLOW集的算法描述如下: void FOLLOW(i nt i) X为待求的非终结符

把当前字符放到一临时数组 fo叩中,标识求已求其FOLLOW集.避免循环递归 if X 为开始符号 then # € FOLLOW(X) 对全部的产生式找一个右部含有当前字符 X的产生式 注:比如求 FOLLOW(B)则找AT a X或AT X £

)的产生式

if X在产生式右部的最后(形如产生式AT : X) then 查找非终结符A是否已经求过其 FOLLOW集.避免循环递归 if 非终结符 A已求过其 FOLLOW集 then FOLLOW(A) € FOLLOW(X) 继续查下一条产生式是否含有 X else 求A之FOLLOW 集,并标识为 A已求其FOLLOW 集

else if X不在产生式右部的最后(形如AT.^B :) then if右部X后面的符号串[能推出空字;then 查找P是否已经求过其 FOLLOW集.避免循环递归 if 已求过[的FOLLOW 集then

FOLLOW(A) € FOLLOW(B) 结束本次循环 else if :不能推出空字 then 求 FIRST(-) 把FIRST( 1)中所有非空元素加入到 FOLLOW(B)中 标识当前要求的非终结符 X的FOLLOW集已求过

4. 计算 SELECT! SELECT集的构造算法如下: 对所有的规则产生式 ATx : (1)若 x 不能推出空字 ,贝y SELECT(A Tx) = FIRST(x); ⑵若 x 可推出空字 ,贝y SELECT(A Tx)=FIRST(x) - { } FOLLOW(A)。 算法描述如下: for(i=0;i<=产生式总数-1;i++) 先把当前产生式右部的 FIRST集(一切非空元素,不包括&放入到当前产生式的 SELECT(i); if 产生式右部符号串可推出空字 ;then 把i指向的当前产生式左部的非终结符号的 FOLLOW集并入到SELECT(i)中

5. 判断是否LL(1)文法 要判断是否为LL(1)文法,需要输入的文法 G有如下要求: 具有相同左部的规则的 SELECT集两两不相交,即: SELECT(A T : ) ASELECT(A T 1)=. 如果输入的文法都符合以上的要求,则该文法可以用 LL(1)方法分析。 算法描述如下: 把第一条产生式的 SELECT(0)集放到一个临时数组 temp[]中 for(i=1;i<=产生式总数-1;i++) 求temp的长度length if i指向的当前产生式的左部等于上一条产生式的左部 then 把SELECT(i)并入到temp数组中 If temp的长度小于length加上SELECT (i)的长度 返回0 else 把temp清空 把SELECT (i)存放到temp中 结果返回1; 四、算法 #in clude #in clude #in clude

char first[50][50],follow[50][50]; 〃各产生式右部的 FIRST 和左部的 FOLLOW 集合 char first1[50][50]; char select[50][50]; II所有单个符号的FIRST集合

//各个产生式的 SELECT集合 char firstflag[50],followflag[50]; //记录各符号的 FIRST 和 FOLLOW 是否已求过 char empty[20]; //记录可推出&的符号

int coun t=0; int nu mber; char start; char term in[ 50]; char non _ter[50]; char v[50]; char left[50]; char right[50][50];

//产生式的个数 //所有终结符和非终结符的总数 〃开始符号 //终结符号 //非终结符号 //所有符号 //左部 〃右部

***************************************

相关文档
最新文档