Cygwin环境的熟悉和lex的使用1

实验内容

Exp1

读懂exam1.l和exam2.l两个例子,使用cygwin下的flex工具将exam1.l和exam2.l编译并调试通过。并且修改exam2.l,在其基础上增加如下记号:

  • 左右大小括号:{ } ( )

  • 将关系算符改写成C中的形式

  • 分号、赋值号:; =

  • 关键字:if else

  • 双斜线表示的注释://

  • 算术运算符号:+ - * /

  • 将标识符改为可含有下划线,并且可以以下划线开头

  • 将注释内容忽略

Exp e1

在实验1所改写的程序的基础上增加识别string记号。string是字符串,如果”出现在字符串中,则必须转义,写成\”形式;如果\出现在字符串中,也必须转义,写成\形式。

在cygwin下用flex和gcc工具将实验调试通过,并写出测试例测试正确性。同时该实验必须满足如下要求:

  1. string是字符串,它是以双引号括起的一串字符。

  2. 双引号内的字符有如下要求:

  • 不能包含单独的”或者\,除非用\进行转义。例如字符串内的”写成\”,而\写成\。

  • 字符串内可以出现转义字符。转义字符可简化表示为\c,其中c为任意字母或(反斜杠),”(双引号),’(单引号)三个符号中的一个。

  • 字符串内不可包含实体的换行。(可以包含\n,但是如果两个“”中的字符串出现在两行中,即包含了实体换行,则不应识别为字符串)

实验过程

Exp1

添加记号名

1
2
3
4
5
6
7
8
9
10
11
12
13
#define NEWLINE     23
#define ERRORCHAR 24

#define LEFTBRACKET 25 //bracket 为小括号 : ()
#define RIGHTBRACKET 26
#define SEMICOLON 27 //分号
#define ASSIGN 28 //赋值
#define ARITHMETIC 29 //算术
#define NOTE 30 注释
#define IF 31
#define ELSE 32
#define LEFTBRACES 33 //braces 为大括号 : { }
#define RIGHTBRACES 34

定义动作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<INITIAL>"<"            {return (RELOP);}
<INITIAL>"<=" {return (RELOP);}
<INITIAL>"==" {return (RELOP);}
<INITIAL>"!=" {return (RELOP);}
<INITIAL>">" {return (RELOP);}
<INITIAL>">=" {return (RELOP);}

<INITIAL>"(" {return (LEFTBRACKET);}
<INITIAL>")" {return (RIGHTBRACKET);}
<INITIAL>"{" {return (LEFTBRACES);}
<INITIAL>"}" {return (RIGHTBRACES);}
<INITIAL>";" {return (SEMICOLON);}
<INITIAL>"=" {return (ASSIGN);}
<INITIAL>"+" {return (ARITHMETIC);}
<INITIAL>"-" {return (ARITHMETIC);}
<INITIAL>"*" {return (ARITHMETIC);}
<INITIAL>"/" {return (ARITHMETIC);}
<INITIAL>. {return ERRORCHAR;}

更改writeout函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 NUMBER: fprintf(yyout, "(NUM, \"%s\") ", yytext);break;
case ID: fprintf(yyout, "(ID, \"%s\") ", yytext);break;
case LEFTBRACKET: fprintf(yyout, "(LEFTBRACKET, \"%s\") ", yytext);break;
case RIGHTBRACKET: fprintf(yyout, "(RIGHTBRACKET, \"%s\") ", yytext);break;
case LEFTBRACES: fprintf(yyout, "(LEFTBRACES, \"%s\") ", yytext);break;
case RIGHTBRACES: fprintf(yyout, "(RIGHTBRACES, \"%s\") ", yytext);break;
case SEMICOLON: fprintf(yyout, "(SEMICOLON, \"%s\") ", yytext);break;
case ASSIGN: fprintf(yyout, "(ASSIGN, \"%s\") ", yytext);break;
case ARITHMETIC: fprintf(yyout, "(ARITHMETIC, \"%s\") ", yytext);break;
case NOTE: fprintf(yyout, "(NOTE, \"%s\") ", yytext);break;
case IF: fprintf(yyout, "(IF, \"%s\") ", yytext);break;
case ELSE: fprintf(yyout, "(ELSE, \"%s\") ", yytext);break;
case NEWLINE: fprintf(yyout, "\n");break;
default:break;
}
return;
}

Exp e1

增加String记号

1
2
#define STRING 35
#define STRERROR 36

进行正规定义

1
2
allow    ((\\\\)|(\\\")|(\\\')|(\\{letter})|(\\{digit}))*
str ({allow}|[^\"\'\n\\])*\"

增加引用状态

1
2
3
4
5
<INITIAL>"\""           {BEGIN QUOTE;}
<QUOTE>"\"" {BEGIN INITIAL;}
<QUOTE>"\n" {BEGIN INITIAL;}
<QUOTE>{str} {BEGIN INITIAL; return (STRING);}
<QUOTE>[^\"\n] {;}
  • QUOTE状态以双引号开始,双引号结束,当匹配到除字符串str和单引号、回车之外的任意字符,返回ERRORCHAR
  • 当QUOTE状态下,遇到回车则进入INITIAL状态

实验结果

编译运行

1
2
3
flex exam2.letter
gcc lex.yy.c -lfl
./a.out test2.p

  • 对于不满足string条件的STRERROR直接不输出

  • 对于string6 , 在本分析器中当遇到第一个双引号时,进入QUOTE状态,之后string6”被识别为str类型,但虽然str以双引号结尾,但不将双引号作为字符串的一部分,(如果string6”被作为字符串的话,string6””应该被识别为str类型)

    之后,QUOTE状态结束,遇到双引号又进入QUOTE状态,遇到回车,结束QUOTE状态

  • 对于string2 , string3:

allow ((\\\\)|(\\\")|(\\\')|(\\{letter})|(\\{digit}))*

​ “ \ ”、“ \” ”属于allow范围,因此包含在字符串内

  • 对于string4:

    “\n \’ \0 \a”均属于allow范围,包含在字符串内

  • 对于string5,string7,string,8,没有匹配到合适的str类型,因此匹配到的STRERROR类型不输出,最后不会有输出结果