Cygwin环境的熟悉和lex的使用2
一.目的:
熟悉cygwin环境的使用,学习使用lex写简单的词法分析程序,会在cygwin环境下使用flex调试lex写的程序。
二. 内容:
在上一篇“Cygwin环境的熟悉和lex的使用1”所改写的程序的基础上增加string记号。string是字符串,如果”出现在字符串中,则必须转义,写成\”形式;如果\出现在字符串中,也必须转义,写成\\形式。
三.要求:
在cygwin下用flex和gcc工具将实验调试通过,并写出测试例测试正确性。同时该实验必须满足如下要求:
1. string是字符串,它是以双引号括起的一串字符。
2. 双引号内的字符可包括:
除”和\外的所有可打印字符(可打印字符请查ASCII码表)
空格和制表符。
转义字符。转义字符至少应该包括:\n,\t,\”,\\,\^c, \ddd,其中c表示任意可打印字符,d表示数字。
3. 字符串内不可包含换行。
四.源代码:
%{
#include <stdio.h>
#define LT 1
#define LE 2
#define GT 3
#define GE 4
#define EQ 5
#define NE 6
#define ADD 7
#define JIAN 8
#define CHEN 9
#define CHU 10
#define WHILE 18
#define DO 19
#define IF 11
#define ELSE 12
#define STRING 16
#define ID 20
#define NUMBER 21
#define RELOP 22
#define NEWLINE 23
#define ERRORCHAR 24
int yylval;
%}
delim [ \t \n]
ws {delim}+
letter [A-Za-z]
digit [0-9]
id (_)*{letter}(_)*({letter}|{digit})*
number {digit}+(\.{digit}+)?(E[+-]?{digit}+)?
string \"[^\"]+\"
/* 状态(或条件)定义可以定义在这里
* INITIAL是一个默认的状态,不需要定义
*/
%s COMMENT
%s ZHUSHI
%%
<INITIAL>"/*" {BEGIN COMMENT;ECHO;}
<COMMENT>"*/" {BEGIN INITIAL;ECHO;}
<COMMENT>.|\n {ECHO;}
<INITIAL>"\\" {BEGIN ZHUSHI;;}
<ZHUSHI>"\t" {BEGIN INITIAL;;}
<ZHUSHI>.|\n {;}
/* ECHO是一个宏,相当于 fprintf(yyout, "%s", yytext)*/
<INITIAL>{ws} {;}
<INITIAL>while {return (WHILE);}
<INITIAL>if {return (IF);}
<INITIAL>else {return (ELSE);}
<INITIAL>do {return (DO);}
<INITIAL>{id} {yylval = installID ();return (ID);}
<INITIAL>{number} {yylval = installNum ();return (NUMBER);}
<INITIAL>{string} {yylval = installSTRING ();return (STRING);}
<INITIAL>"<" {yylval = LT;return (RELOP);}
<INITIAL>"<=" {yylval = LE;return (RELOP);}
<INITIAL>"=" {yylval = EQ;return (RELOP);}
<INITIAL>"!=" {yylval = NE;return (RELOP);}
<INITIAL>">" {yylval = GT;return (RELOP);}
<INITIAL>">=" {yylval = GE;return (RELOP);}
<INITIAL>"{" {return (RELOP);}
<INITIAL>"}" {return (RELOP);}
<INITIAL>"(" {return (RELOP);}
<INITIAL>")" {return (RELOP);}
<INITIAL>";" {return (RELOP);}
<INITIAL>"+" {yylval = ADD;return (RELOP);}
<INITIAL>"-" {yylval = JIAN;return (RELOP);}
<INITIAL>"*" {yylval = CHEN;return (RELOP);}
<INITIAL>"/" {yylval = CHU;return (RELOP);}
<INITIAL>. {return ERRORCHAR;}
%%
int installID () {
/* 把词法单元装入符号表并返回指针。*/
return ID;
}
int installNum () {
/* 类似上面的过程,但词法单元不是标识符而是数 */
return NUMBER;
}
int installSTRING () {
/* 类似上面的过程,但词法单元不是标识符而是数 */
return STRING;
}
int yywrap (){
return 1;
}
void writeout(int c){
switch(c){
case ERRORCHAR: fprintf(yyout, "(ERRORCHAR, \"%s\") ", yytext);break;
case RELOP: fprintf(yyout, "(RELOP, \"%s\") ", yytext);break;
case WHILE: fprintf(yyout, "(WHILE, \"%s\") ", yytext);break;
case DO: fprintf(yyout, "(DO, \"%s\") ", yytext);break;
case IF: fprintf(yyout, "(IF, \"%s\") ", yytext);break;
case ELSE: fprintf(yyout, "(ELSE, \"%s\") ", yytext);break;
case STRING: fprintf(yyout, "(STRING, \"%s\") ", yytext);break;
case NUMBER: fprintf(yyout, "(NUM, \"%s\") ", yytext);break;
case ID: fprintf(yyout, "(ID, \"%s\") ", yytext);break;
case NEWLINE: fprintf(yyout, "\n");break;
default:break;
}
return;
}
int main (int argc, char ** argv){
int c,j=0;
if (argc>=2){
if ((yyin = fopen(argv[1], "r")) == NULL){
printf("Can't open file %s\n", argv[1]);
return 1;
}
if (argc>=3){
yyout=fopen(argv[2], "w");
}
}
while (c = yylex()){
writeout(c);
j++;
if (j%5 == 0) writeout(NEWLINE);
}
if(argc>=2){
fclose(yyin);
if (argc>=3) fclose(yyout);
}
return 0;
}
Test.p:
while _a >= -1.2E-2 do b_x<=2 {if(c!=5)c=(d+e)*f; else c=(e-f)/2;} "sfjjjg1323\n"
/*请注意*/ \\ 请注意:测试文件的格式必须符合要求,比如,该文件要求的格式是UNIX格式。
五.结果及分析:
(WHILE,while),(ID,_a),( RELOP,>=),( NUM,-1.2E-2),(DO,do),(ID,b_x),( RELOP,<=), ( NUM,2), ( RELOP,{), ( RELOP,(), (ID,c), ( RELOP,!=), (NUM,5), ( RELOP,)), (ID,c), ( RELOP,==), ( RELOP,(), (ID,d), ( RELOP,+), (ID,e), ( RELOP,*), ( ID,f), ( RELOP,;), ( ELSE,else), (ID,c), ( RELOP,=), ( RELOP,(), (ID,e), ( RELOP,-), (ID,f), ( RELOP,)), ( RELOP,/), ( NUM,2), ( RELOP,;), (RELOP,}), (STRING, sfjjjg1323\n),
词法分析器从第一个字符开始扫描,将相应的字符与匹配的类别输出在屏幕上,忽略空格和换行字符,将注释也忽略,能识别字符串(除”和\外的所有可打印字符,空格和制表符,转义字符【转义字符至少应该包括:\n,\t,\”,\\,\^c, \ddd,其中c表示任意可打印字符,d表示数字】,字符串内不可包含换行)。