资源描述
1.实验目的及要求
1、目的通过设计、编制、调试一个具体的词法分析程序,加深对词法分析原理的 理解,并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单 词的词法分析方法。
2、 要求根据具体情况,由同学们自己选取C语言的一个适当大小的子集(可 取一类典型单词,也可以尽可能使各种类型的单词都兼顾到);在实 习前一定要制出相应的表。
• 实验时间:4-8学时
• 检查内容及时间:A)完整的实验报告。
B)在机器上调试成功的源程序。
3、软件、硬件环境• Visual studio 2010 , Windows 7 操作系统
・ 计算机一台2.实验步骤
•分析
对于单词符号我们将其分成四类:保存字K、标识符I、常数C和界符P,每类单词符 号均可使用一张表格表示.在词法分析过程中,保存字K和界符P这两个表格的内容是 固定不变的(由语言确定),源程序字符串只能从其中选取,而标识符I、常数C这两 表是在分析过程中不断形成的.
对于一个具体源程序而言,在扫描字符串时识别出一个单词,假设这个单词的类型是 K、I、C或P中之一,那么就以单词的二元式形式输出.每次调用词法分析程序,它 均能自动继续扫描下去,形成下一个单词,直到整个源程序全部扫描完毕,从而形成相 应的单词串.
各类单词的二元式表述均具有相同的结构与长度,形式如下: (单词种别t ,单词自身的值i )
t是单词种别,而单词种别共分为K、I、C、P四类且每类对应一张表格.因此,t 实际上就是一个指向这四类中某一类对应表格的指针.i那么为指向该类表格中一个特定 工程的指针.
所以整个的词法分析过程就是从源程序中获得一个个的单词符号,将这些符号分别 填入四张类表中,并且有一个二元式序列构成一个索引,这个索引为以后的语法分析提 供处理上的方便.
为了减少实习量,可以适量地选取K, P中的一个子集来进行.如下表:
表1保存字K表
内部地址
~r
2
3
4
5
6
7
8
9
10
保存字
for
while
DO
ELSE
IF
STATIC
TNT
SIZEOF
BREAK
CONTINUE
case ' f': case ' g* : case ' h, : case i : case ' j : case ' k': case '1': case ' m : case 'n : case ' o* : case ' p': case ' q': case ' r : case ' s': case ' t': case ' u : case ' v': case ' w* : case ' x : case ' y': case 'l : case ' A* : case ' B': case ' C': case ' D': case ' E* : case ' F': case ' G': case ' H* : case ' I* : case ' J': case ' K': case ' L* : case ' M': case ' N': case ' O': case ' P* : case ' Q': case ' R': case ' S': case ' T* : case ' U': case ' V': case ' M : case ' X* : case ' Y': case ' Z':
while(letter() | |digit()) {concat (); getNcxtChai'(fp);
)retract(fp);
num=reserve(k); 〃保存字 if(num!=0)
returntof ile(3, num, id, con); else(
val=symbol(id);
returntofile(l, val, id, con); ) bi,eak;
case 5 0' :
case ' 1' :
case ’ 21 :
case ' 3’ :
case ' 4’ :
case ' 5’ :
case ' 6' :
case ' T :
case ' 8’ :
case ' 9' :
while(digit 0)(
concatO ;getNextChar(fp);
)retract(fp);
val=constant(con);returntofile(2, val, id,con);
break;
case ':
getNextChar(fp);if(character二二'二')
returntofile(9, 0, id,con); else {
retract(fp);
returntofile(8, 0, id, con);)
break;
case ' >> :
getNextChar(fp);if(charactcr==,=')
returntofile(l1, 0, id,con); else {
retract(fp);
returntofile(10,0, id,con); ) break;
case getNextChar(fp);if(character=:'二')
returntofile(13, 0, id, con); else {
retract(fp);
returntofile(14, 0, id, con);)
break;
case ':
getNextChar(fp);if(character==* =')
returntofile(12, 0, id, con); elseerror ();
break;
case ' +':
returntofile(4, 0, id, con);break;
case ':
returntofile(5, 0, id, con);break;
case ' *':
returntofile(6, 0, id,con);break;
case ' /':
returntofilc(7, 0, id, con);break;
case '(':
returntofile(15, 0, id, con);break;
case ')':
returntofile(16, 0, id, con);break;
case ',':
returntofi le(17, 0, id, con);break;
case ':
returntofilc(18, 0, id, con);break;
case ':
returntofile(19, 0, id, con);break;
case ' {':
returntofile(20, 0, id, con);break;
case '}':
returntofile(21,0, id, con);break;
default:
error ();ma i n (int argc, char *argv[])
(
〃初始化标识符和常数结构体 identifier* id=(identifier*)malloc(sizeof(identifier)); constnumber * con=(constnumber*)malloc(sizeof(conslnumber)); con->len=0;
id->len=0;
argc=3;
argv[1 ] ="E:\\filel. txt*;//待分析的文件
argv[2]=,,E:\\fi le2. txt〃;〃保存分析结果的文件
〃从翻开目标文件流
if((fp=fopen(argv[l]»)==NULL)
(printf(〃cat: can't open %s\n", *argv);
return 1;
)
〃翻开要写二元式的文件流
i f ((fw=fopen (argv [2], "w")) =NULL) (printf ("cat:can't open %s\n/,, argv[2]);
return 1;
)
while(!feof(fp))( LexAnalyze(k, CODE, id, con, fp, fw); 〃执行词法分析
)
〃关闭流
fclose(fp);
fclose(fw);
return 0;}
4.实验结果要分析的C语言程序(1) int i=0, sum=0;
while(i<10)
(sum=sum+i;
i=i+l;结果为:
⑶-)#int#(1. i)
(14, -) #=# (2,0)(17, -) #,#
(1, sum)(14, -) #=# (2,0)
(19,-) #;#⑶-)#wh i I e# (15,-) #(# (1, i) (8,-) #<# (2,10) (16, -) #)# (20, -) #{#
(1, sum)(14, -) #=#
(1, sum)(4, -) #+#
(1, i)(19,-) #;#
(1. i)(14, -) #=#
(1, i)(4, -) #+#
(2.1)(19,-) #;#
(21.-) #}#要分析的源程序(2) char s[3];
int i=num/10;
while(i>0)char c=i+'()';
strcat(s,&c);
) return s;结果为: (1 ,char) (l,s) (ERROR,[) (2,3) (ERROR,]) (19,-) #;# (3,-) #int# (l,i) (14,-) #=# (l,num) (7,-) #/# (2,10) (19,-) #;# (3,-) #while# (15,-) #(# d,i) (10,-) #># (2,0) (16,-) #)# (20,-) #{# (1 ,char) (l,c) (14,-) #=# (l,i) (4,-) #+# (ERROR;) (2,0) (ERROR;) (19,-) #;# (1,strcat) (15,-) #(# (l,s) (17,-) #,# (ERROR,&) (he) (16,-) #)# (19,-) #;# (21,-) #}# (1,return)
(l,s)(19,-) #;#
5.实睑总结分析
上机前应做好准备.即根据实习目的、要求和分析,选择相应的数据结构,使用c语言 参照算法中的流程编写词法分析的程序.将编好的程序上机进行调试.注意调试的例子应有 词法正确的,也应有词法错误的或是超出所选数据结构范围的.
现在我谈一下关于做这个词法分析程序的心得。这个词法分析程序是全部用c语言的语 法写的,幸亏我以前学过C语言。我认为C语言是很好的语言。这次写这个程序算是对我所 学的C语言的一次的检验。在写程序的过程中,也遇到了一些问题,最主要的是文件的输入 与输出,最头疼的是指针。几乎所在的错误都与指针有关。在遇到问题以后,我们不要良惧, 我们可以通过程序调试,找出问题的所在,只要找到问题的源,就总在解决它的方法。
在这个分析程序中,我加了注释,是很容易看懂的,而且程序的功能模块也分得很清 楚。在写大型的程序时,这两点我认为是非常重要的。
附录资料:不需要的可以自行删除Abstract: Based on the comprehensive analysis on the plastic part's structure service requirement, mounding quality and mould menu factoring cost. A corresponding injection mould of internal side core pulling was designed. By adopting the multi-direction and multi-combination core-pulling. A corresponding injection mould of internal side core pulling was designed, the working process of the mould was introduced
C语言详解一枚举类型注:以下全部代码的执行环境为VC++6.0
在程序中,可能需要为某些整数定义一个别名,我们可以利用预处理指令#define来完成这 项工作,您的代码可能是:
#define MON Idcfine TUE 2
# define WED 3define THU 4
# dcfine FRI 5define SAT 6
# define SUN 7在此,我们定义一种新的数据类型,希望它能完成同样的工作。这种新的数据类型叫枚举型.
1 .定义一种新的数据类型-枚举型
以下代码定义了这种新的数据类型-枚举型enum DAY
(
MON=1,TUE, WED, THU, FRI, SAT, SUN};
(1)枚举型是一个集合,集合中的元素(枚举成员)是一些命名的整型常量,元素之间用逗号, 隔开。
(2) DAY是一个标识符,可以看成这个集合的名字,是一个可选项,即是可有可无的项。
(3)第•个枚举成员的默认值为整型的0,后续枚举成员的值在前•个成员上加1。
(4)可以人为设定枚举成员的值,从而自定义某个范围内的整数.
(5)枚举型是预处理指令#define的替代。
(6)类型定义以分号;结束。
2 .使用枚举类型对变量进行声明新的数据类型定义完成后,它就可以使用了。我们已经见过最基本的数据类型,如:整型int, 单精度浮点型float,双精度浮点型double,字符型char,短整型short等等。用这些基本数据 类型声明变量通常是这样:
char a; 〃变量a的类型均为字符型char charletter;intx,
y,
z; 〃变量x,y和z的类型均为整型int int number;double m, n;
double result; 〃变量result的类型为双精度浮点型double 既然枚举也是一种数据类型,那么它和基本数据类型一样也可以对变量进行声明。
方法一:枚举类型的定义和变量的声明分开enum DAY {
MON= I, TUE, WED, THU, FRI, SAT, SUN ); enum DAY yesterday;enum DAY today;
enum DAY tomorrow; 〃变显 tomorrow 的类型为枚举型 enurn DAYenum DAY good_day. bad_day; 〃变量 good_day 和 bad_day 的类型均为枚举型 enum DAY 方法二:类型定义与变量声明同时进行:
enum 〃跟第一个定义不同的是,此处的标号DAY省略,这是允许的。 {Saturday, sunday = 0, monday, (uesday, Wednesday, thursday, friday
} workday;//变量workday的类型为枚举型enum DAYenum week { Mon= 1, Tue, Wed. Thu, Fri Sat, Sun} days; 〃变量 days 的类型为枚举型 enum week
enum BOOLEAN { false, true } end_flag, malch_flag; 〃定义枚举类型并声明了两个枚举型变 量方法三:用typedef关键字将枚举类型定义成别名,并利用该别名进行变量声明: typedef enum workday
Saturday, sunday = 0,monday, tuesday, Wednesday, thursday, friday
} workday;//此处的workday为枚举型enum workday的别名workday today, tomorrow; 〃变量 today 和 tomorrow 的类型为枚举型 workday,也即 cnum workday
enum workday 中的 workday 可以省略:
typedef enum {Saturday, sunday = 0, monday, tuesday, Wednesday, (hursday, friday
} workday;//此处的workday为枚举型enum workday的别名workday today, tomorrow; 〃变量 today 和 tomorrow 的类型为枚举型 workday,也即 enum workday
也可以用这种方式:
typedef enum workday {
Saturday, sunday = 0, monday, tuesday, Wednesday, thursday, friday );workday today, tomorrow; 〃变量 today 和 tomorrow 的类型为枚举型 workday,也即 enum workday
注意:同一个程序中不能定义同名的枚举类型,不同的枚举类型中也不能存在同名的命名常 量。错误例如如下所示:
错误声明一:存在同名的枚举类型typedef enum {
Wednesday, thursday, friday} workday;
typedef enum WEEK |
Saturday, sunday = 0, monday, } workday;错误声明二:存在同名的枚举成员
lypedef enum {
Wednesday, (hursday, friday} workday」;
typcdcf cnum WEEK {
Wednesday, sunday = 0, monday,} workday_2;
3 .使用枚举类型的变量对枚举型的变量赋值。
实例将枚举类型的赋值与基本数据类型的赋值进行了比照:
方法一:先声明变量,再对变量赋值#include<stdio.h>
/*定义枚举类型*/enum DAY { MON=1,TUE, WED, THU, FRI, SAT, SUN };
void main()(
/*使用基本数据类型声明变量,然后对变量赋值*/ int x, y, z;
x= 10; y = 20; z = 30:
/*使用枚举类型声明变量,再对枚举型变量赋值*/
enum DAY yesterday, today, tomorrow;
yesterday = MON;
today = TUE;
tomorrow = WED;
printf("%d %d %d \n", yesterday, today, tomorrow);方法二:声明变量的同时赋初值
#include <stdio.h>/*定义枚举类型*/
enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN };
界符
+
—
*
/
<
<=
>
>=
1 =
——
=
(
)
*
•
*
9
(
)
表3单词
符号的编码
单词符号
类别编码
标识符
1
常数
2
保存字
3
+
4
—
5
*
6
/
7
<
8
<=
9
>
10
>=
11
1 =
12
==
13
=
14
(
15
)
16
9
17
:
18
*
19
{
20
}
21
保存字表包括10个有代表性的保存字,界符表包括关系运算符,算术运算符,分隔符 三种,一对圆括号,加上赋值号,花括号,分号,引号.这两表的内容说明C语言的条 件语句,赋值语句,WHILE型循环语句,复合语句,过程及变量说明均可作为源程序例 子输入给词法分析程序,标识符表I中的每一项包含一个标识符,常数表C中的每一项 包含一个整常数,后两表的内容都是在词法分析过程中产生的.
如何从源程序中识别出一个个的单词符号呢?图1中的流图清晰地反映出这一过 程.
图1中,双圆圈的状态表示终态,即能到达终态就代表识别出•个单词符号,而带有火 号的终态是指处理时应回退一字符.
•算法词法分析器在扫描过程中,依次从源程序中取出源字符,根据图1的扫描过程状态转换图, 当碰到终态时,即双圆圈的状态时就得到一个单词符号,此时可以根据第一个字符判断单词 属于K, I, C, P中哪一个类,从而确定单词的“单词种别”和“单词自身的值”。
void main。
{/*使用基本数据类型声明变量同时对变量赋初值*/ int x=10, y=20, z=30;
/*使用枚举类型声明变量同时对枚举型变量赋初值*/ enum DAY yesterday = MON,
today = TUE, tomorrow = WED;
printf("%d %d %d \n", yesterday, today, tomorrow);}
方法三:定义类型的同时声明变量,然后对变量赋值。
#include <stdio.h>/*定义枚举类型,同时声明该类型的三个变量,它们都为全局变量*/
enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN } yesterday, today, tomorrow;/*定义三个具有基本数据类型的变量,它们都为全局变量号
int x, y, z;void main()
{
/*对基本数据类型的变量赋值*/
x= 10; y = 20; z = 30;
/*对枚举型的变量赋值*/
yesterday = MON;
today = TUE;
tomorrow = WED;
printf("%d %d %d \n", x, y, z); 〃输出:10 20 30
prinlf("%d %d %d \n", yesterday, today, tomorrow); 〃输出:12 3)
方法四:类型定义,变量声明,赋初值同时进行。
#include <stdio.h>/*定义枚举类型,同时声明该类型的三个变量,并赋初值。它们都为全局变量*/ enum DAY
{
MON=1,
TUE,
WED,
THU,
FRI,
SAT,
SUN)
yesterday = MON, today = TUE, tomorrow = WED;/*定义三个具有基本数据类型的变量,并赋初值。它们都为全局变量*/
int x = 10, y = 2O,z = 3O;void main()
printf("%d %d %d \n", x, y, z); 〃输出:10 20 30
prinlf("%d %d %d \n", yesterday, today, tomorrow); 〃输出:12 3)
3.1 对枚举型的变量赋整数值时,需要进行类型转换。
#includc <stdio.h>enum DAY { MON=1,TUE, WED, THU, FRI, SAT, SUN );
void main(){
enum DAY yesterday, today, tomorrow;
yesterday = TUE;
today = (enum DAY) (yesterday + 1); 〃类型转换
tomorrow = (enum DAY) 30; 〃类型转换
//tomorrow = 3; 〃错误
printf("%d %d %d \n”, yesterday, today, tomoiTow); 〃输{i| : 2 3 30 )使用枚举型变量
#include<stdio.h>enum
{
BELL= '\a',
BACKSPACE = U',
HTAB='\t;
RETURN= V,
NEWLINE = '\n;
3.2 B='\v;
SPACE='' cnum BOOLEAN { FALSE = 0, TRUE } match_flag;void main()
|
int index = 0;
int count_oLle(ter = 0;
int count_oCspacc = 0;
char str(l = 'Tm Ely efod";match_flag = FALSE;
for(; str [index] != '\0'; indcx++) if( SPACE != str[index]) count_oMet(er++;else
match_nag = (enum BOOLEAN) 1; count_oiLspace++;printf("%s %d times %c", match_flag ? "match" : "not match", count_of_space, NEWLINE); prinlf("count of letters: %d %c%c", count_of_letter. NEWLINE, RETURN);
输出:
match 2 timescount of letters: 10
Press any key (o continue.枚举类型与sizeof运算符
#include <stdio.h> cnum escapes (BELL= W,
BACKSPACE = '\b;HTAB= '\t',
RETURN =V, NEWLINE = '\n', VTAB= '\v',SPACE="
);enum BOOLEAN { FALSE = 0, TRUE } match_llag;
void main(){
printf("%d bytes \n", sizeof(enum escapes)); //4 bytes printf("%d bytes \n", sizeof(escapes)); //4 bytesprintf("%d bytes \n", sizcof(cnuni BOOLEAN)); //4 bytes printf("%d bytes \n", sizeof(BOOLEAN)); //4 bytes printf("%d bytes \n", sizeof(match_flag)); //4 bytes
printf("%d bytes \n", sizeof(SPACE)); //4 bytesprintf("%d bytes \n'\ sizeof(NEWLINE)); //4 bytes
printf("%d bytes \n,\ sizcof(FALSE)); //4 bytes printf("%d bytes \n", sizeof(0)); //4 bytes)
4 .综合举例 #includc<stdio.h>enum Season (
spring, summer=100, fall=96. winter );typedef enum
Monday, I'ucsday. Wednesday, Thursday, Friday, Saturday, Sunday }Weekday;
void main()
/* Season */
printf("%d \nH, spring); // 0
printf("%d, %c \n'\ summer, summer); // 100, d
printf("%d \n'\ fall+wintcr); // 193
Season mySeason=winter;
if(winter==mySeason)printf("mySeason is winter \n"); // mySeason is winter
int x=100;
if(x=summer)printf("x is equal to summer\n"); // x is equal to summer
printf("%d bytes\n", sizeof(spring)); // 4 bytes
/* Weekday */
printf("sizeof Weekday is: %d \n", sizeof(Weekday)); //sizeof Weekday is: 4
Weekday today = Saturday;
Weekday tomorrow:
if(today == Monday)tomorrow = Tuesday;
elsetomorrow = (Weekday) (today + 1); //remember to convert from int to Weekday
本文来自CSDN--xiaobai32966博客
空白字母与数字
图1扫描程序的状态转换图说明:这个图只是大概表达一个词法分析的思路,由于不知道加入,所以并不完全准确。
2.实验内容
流程图、程序
图2词法分析算法流图
源程序#include <stdio. h>
# include <string. h>#include <ctype. h>
#include <stdlib.h>#include <assert. h> #define LENGTH 1 10 〃定义保存字的大小
FILE * fp=NULL; 〃输出流指针FILE * fw=NULL; 〃输入流指针
char character; 〃字符char tokcn[16]; 〃字符数组,用来存放已读入的字符序列
〃编码表char* CODE[] = {*identifier*/* 标识符 */, "constant"/* 常数 */, "keyword"/* 保存字
〃保存字表 char*kLJ=l for , while , do , else , if , static , int , sizeof , break , continue };
〃标识符结构体 typedef struct (
char * I [256];〃标识符数组
int len;〃标识符数量 } identifier;〃常量结构体
typedef struct {
int cont[300];〃存放常量的数组
int len;〃常量的数目 } constnumber;〃读入一个字符,从输入流中读入一个字符到变量character中。 void getNextChar(FILE *ifp)
(if((character=getc(ifp))==EOF) exit (1);
)〃读入非空白字符,检查变量character中的字符是否为空白字符或|口|车或换行符。假设是, 〃那么调用getNextChar()读入下一个字符,直到character中的字符满足条件.
void getnbc(FILE *ifp) (
whi le(character=, | | character==, \n \ character==9) (getNextChar(ifp);
)}
〃连接字符串,把character中的字符连接到token数组的结尾。
void concat()
char * ct=&character; strcat(token, ct);〃判断是否为字母。 int letter()
return isalpha(character);)
〃判断是否为数字int digit()
(
return isdigit(character);)
〃回退字符,将刚读入的character中的字符回退到输入流中。并把character中的值置为 空。
void retract(FILE *ifp)(
(ifp->_cnt)++;
(ifp->_ptr)—;
character=,';)
〃处理保存字,对存放在token中的字符串查保存字,假设查到,那么返回该保存字的类别编码, 否那么返回0.
int reserve(char **k)(
int i;
for(i=0;i<LENGTHl;i++)if (strcmp(token, k[i])=0)
return i+1;
return 0;}
〃处理标识符,对存放在token中的字符串查找符号表,假设查到,那么返回它在符号表的位置, 〃存入常数表中,并返回它在常数表中的位置编号。
int symbol(identifier * id)(
int i;
for(i=0;i<id->lcn;i++)if (strcmp(token, id->I[i])=0)
return i+1;
if(id->len>256)assert(0);
id->I[id->len]=token;
id->len++;
return id->len;}
〃将数字字符串转化为整数。 int strtonumber()
int i;
int sum=O;
for(i=0;i<strlen (token);i++)
(sum=10*sum+(token[i], 01);
) return sum;)
〃常数存入常数表的函数,将token中的数字串(实际上是字符串),转化成标准的二进制 值(整数值)〃存入常数表中,并返回它在常数表中的位置编号。
int constant(constnumber * con)(
con->cont[con->len]=strtonumber();
con->len++; return con->len;)
〃将整数值转化为字符串char * numbertoString(int num)
(
char s[3];
int i=num/10;
while(i>0)
(char c=i+' O'; strcat(s,&c);
) return s;)
〃将结果写入到文件并且输出到屏幕。
void returntofile(int num, int val, identifier *id, constnumber *con)(
int i;
int _num=num;
char c;
c='(';
putc(c, fw);
printfc);
i= num/10;
while (i>0)
(
_num=_num-10*i;c=(i+'O');
printf("Sc",c);putc(c, fw); i=_num/10;
}
c=_num+, 0';
printfc);
putc(c, fw);
printf;
putcC ,', fw);
〃如果是标识符或常数那么放入括号内。
if(num==l) //处理标识符
(printf id->I[val-l]);
printf (")");prinlf("\n");
fputs(id->I[val-l], fw);putc(*)', fw);
putc(' \n , fw);
)
if(num-2) //处理常数
(_num=con->cont[val-1];
i=_num/10;
while(i>0) (_num=_num-10*i;
c=(i+'O');printf (飞c”, c);
putc(c, fw);i=_num/10;
}c=. num+, 0';
printf("Sc",c);printf (〃)”);
printf (*\n*);putc(c, fw);
putc
展开阅读全文