Scaner
扫描器实验报告
by Sandy & Sabrina
目录
扫描器实验报告... 1
一. 实验目标... 2
二. 实验要求... 2
三. 实验分析... 2
1. 正则表达式... 2
2. NFA. 3
3. 状态转换表... 3
4. DFA. 4
四. 实验步骤... 4
1. 为了便于测试,首先编写扫描器的输入输出部分。... 4
2. 添加了一部分TokenType和关键字。... 5
3. 扫描器的核心代码中,表驱动的实现。... 6
4. 检查错误代码如下: 6
五. 运行界面... 7
1. 正常运行界面... 7
2. 丢失右半边 ’}’ 时,显示ERROE. 8
3. 丢失右半边引号时,显示ERROR. 8
4. 检测到错误字符时,显示ERROR. 8
六. 备注说明... 8
1. 测试代码... 8
2. 关于TokenType. 9
3. 使用语言和编译环境说明... 9
一. 实验目标
设计一个简单的扫描器,要求实现以下目标:
- 输入为一个源代码文件,要求输出为token数据流。
- 扫描器要实现最长可能的匹配。例如:一个字符串‘:=’应该被识别为‘ass-symbol’而不是‘:’和‘=’。
- token要表示为(Kind,Value)的形式。我们用一下的标识符来表示 不同类型的token。
KEY表示关键字
SYM表示特殊符号
ID表示识别码
NUM表示数字串
STR表示字符串
- 检查词汇错误:给出意义错误消息以及错误发生的行数。词汇错误的种类有:
⑴ 非法字符,也就是说,扫描器可以识别一个不在TINY+字母表中的字符。例如$就是一个非法字符。
⑵ 一个字符串的右半边引号丢失,例如 ‘scanner。
⑶ 一个评论的右半边大括号丢失,例如{this is an example。
二. 实验要求
1. 用C或C++编写程序。
2. 在8个学时内完成该实验,提交报告和源程序。
三. 实验分析
1. 正则表达式
ID=letter (letter | digit)*
NUM=digitdigit*
STRING=' any character except '
letter=a|…|z|A|…|Z
digit=0|…|9
2.NFA
ID:
Figure 3-1 ID的NFA
NUM:
Figure 3-2 NUM的NFA
STRING:
Figure 3-3STRING的NFA
Figure 3-4 部分符号的NFA
3. 状态转换表
blank |
digit |
letter |
{ |
} |
; |
: |
+ |
- |
* |
/ |
( |
) |
> |
< |
= |
, |
‘ |
EOF |
Other |
|
START |
START |
INNUM |
INID |
INCOMMENT |
ERROR |
DONE |
INASSIGN |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
INGREATER |
INSMAUER |
DONE |
DONE |
INSTRING |
DONE |
ERROR |
INCOMMENT |
INCOMMENT |
INCOMMENT |
INCOMMENT |
INCOMMENT |
START |
INCOMMENT |
INCOMMENT |
INCOMMENT |
INCOMMENT |
INCOMMENT |
INCOMMENT |
INCOMMENT |
INCOMMENT |
INCOMMENT |
INCOMMENT |
INCOMMENT |
INCOMMENT |
INCOMMENT |
ERROR |
INCOMMENT |
INNUM |
DONE |
INNUM |
ERROR |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
INID |
DONE |
INID |
INID |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
INASSIGN |
ERROR |
ERROR |
ERROR |
ERROR |
ERROR |
ERROR |
ERROR |
ERROR |
ERROR |
ERROR |
ERROR |
ERROR |
ERROR |
ERROR |
ERROR |
DONE |
ERROR |
ERROR |
ERROR |
ERROR |
INGREATER |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
INSMALLER |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
DONE |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
INSTRING |
DONE |
ERROR |
INSTRING |
DONE |
||||||||||||||||||||
ERROR |
4.DFA
Figure 3-5 所有字符的DFA
PS: 1、上图中三个ERROR为同一个,由于不方便才画成三个。
2、表示该状态转换不需要保存当前字符。
四. 实验步骤
1. 为了便于测试,首先编写扫描器的输入输出部分。
输入输出部分主要沿用UTIL.h和UTIL.C两个文件,并做了点修改。
在void printToken( TokenTypetoken, const char*tokenString )中添加了新的关键字、特殊字符和STR的输出。
MAIN文件主要沿用书上的代码,只是将其中的一部分BOOL值作了修改。
/* set NO_PARSE to TRUE to get a scanner-only compiler */
#define NO_PARSE TRUE//FALSE
/* set NO_ANALYZE to TRUE to get a parser-only compiler */
#define NO_ANALYZE TRUE//FALSE
/* set NO_CODE to TRUE to get a compiler that does not
* generate code
*/
#define NO_CODE TRUE//FALSE
int EchoSource = TRUE;//FALSE;
intTraceScan = TRUE;//FALSE;
同时,在MAIN文件中修改了如下一段代码:
Debug时 |
Release版 |
// char pgm[120]; /* source code file name */ /* if (argc != 2) { fprintf(stderr,”usage: %s <filename>\n”,argv[0]); exit(1); } strcpy(pgm,argv[1]) ; if (strchr (pgm, ‘.’) == NULL) strcat(pgm,”.tny”); */ char *pgm = “test.txt”; |
char pgm[120]; /* source code file name */ if (argc != 2) { fprintf(stderr,”usage: %s <filename>\n”,argv[0]); exit(1); } strcpy(pgm,argv[1]) ; if (strchr (pgm, ‘.’) == NULL) strcat(pgm,”.tny”); // char *pgm = “test.txt”; |
Table 4-1 测试版和release版的输入修改
char *pgm = "test.txt";只是为便于测试,绑定了一个测试文件,真正运行时将这句代码注释,而运行上面那部分代码,从而只要将文件拖入执行文件中,便可运行生成token序列串。
2. 添加了一部分TokenType和关键字。
typedef enum
/* book-keepingtokens */
{ENDFILE,ERROR,
/* reserved words*/
/*add true false or and not int bool string while do*/ IF,THEN,ELSE,END,REPEAT,UNTIL,READ,WRITE,OR,AND,NOT,INT,BOOL,STRING,WHILE,DO,BOOL_TRUE,BOOL_FALSE,
/* multicharactertokens */
ID,NUM,STR,
/* specialsymbols */
/*add > <= >= ,*/
ASSIGN,EQ,LT,PLUS,MINUS,TIMES,OVER,LPAREN,RPAREN,SEMI,MT,LOE,MOE,COMMA
} TokenType;
/* lookup table of reserved words */
/*add true false or and not int bool string while do*/
static struct{
char* str;
TokenType tok;
}reservedWords[MAXRESERVED]
={ {"if",IF},{"then",THEN},{"else",ELSE},{"end",END},
{"repeat",REPEAT},{"until",UNTIL},{"read",READ},
{"write",WRITE},{"true",BOOL_TRUE},{"false",BOOL_FALSE},
{"or",OR},{"and",AND},{"not",NOT},{"int",INT},{"bool",BOOL},
{"string",STRING},{"while",WHILE},{"do",DO}};
3. 扫描器的核心代码中,表驱动的实现。
typedef enum
{START,INASSIGN,INCOMMENT,INNUM,INID, INGREATER , INSMALLER , INSTRING ,DONE,S_ERROR}
StateType;
/*This is a transformation table used to store all thetransformer in the DFA*/
static struct{
StateType state;
int save;
}TTable[11][13];
由状态转换表可知,一开始的表较大,经过改进后,将一部分标识符合并,从而节省了一部分空间。详见状态转换表。
表驱动的关键代码为:
/*get the column number of symbols in the transformationtable*/
sym= symNum(c);
/*check if the current char need to save in the currenttoken*/
save= TTable[state][sym].save;
/*state change*/
state= TTable[state][sym].state;
save = TTable[state][sym].save;用来记录该字符是否需要保存到当前token内;
state = TTable[state][sym].state;记录跳转后的状态。
另外,设了个int型变量originState,用来记录原来的状态,用来判断Tokentype。
自定义函数static int symNum(char c) 获得状态转换表中的字符c的列数;
static void TTableInit() 初始化状态转换表。
4. 检查错误代码如下:
if(c == '\n'&& (originState == INSTRING ||originState == INCOMMENT)){
state= S_ERROR;
if(originState== INCOMMENT)
c= '}';
tokenString[tokenStringIndex++]= c;
break;
}
若扫到换行还没有’或者}来终止INSTRING或者INCOMMENT的状态,则认为出错。
五. 运行界面
1. 正常运行界面
Figure 5-1 正常运行界面
2. 丢失右半边’}’ 时,显示ERROE
Figure 5-2 丢失右半边‘}’时显示ERROR
3. 丢失右半边引号时,显示ERROR
Figure 5-3 丢失右半边引号时显示ERROR
4. 检测到错误字符时,显示ERROR
Figure 5-4 4. 检测到错误字符时显示ERROR
六. 备注说明
1.测试代码
string str;
int x, fact;
str:= 'sample program in TINY+ language-computes factorial' ;
read x;
if x>0 and x<100 then {don't computeif x<=0}
fact:=1;
whilex>0 do
fact:=fact*x;
x:=x-1
end;
writefact
end
2.关于TokenType
我们是在TINY本来的TokenType的基础上加上STR、MT等类型,没有把所有特殊符号合并成SYM,也没有把所有关键字合并成KEY,每种特殊符号和关键字都有自己特定的TokenType,这样是因为不想修改太多的源程序,还考虑到以后做语法分析等,可以会更加便利。
3.使用语言和编译环境说明
本程序使用的是C语言,使用编译环境为VS2008。
下载请点击 https://files.cnblogs.com/sandywong/Scaner.rar