词法分析器只做token识别,代码详细注释

时间:2021-12-16 09:05:15

mian.c

#include "globals.h"

/**创建只扫描的编译器 **/
#define NO_PARSE TRUE

#include "scan.h"///词法分析,核心过程为扫描函数 //如果NO_PARSE为true,调用头文件scan.h

/** 分配全局变量 */
int lineno = 0;
FILE * source; ///源代码文件
FILE * listing; ///显示分析过程的文件,这里重定向到stdout
FILE * code; ///目标汇编代码文件

/** 分配并初始化追踪标志 / 直接运行main.c看不到结果  需将下列部分变量的值从FALSE改为TRUE,表示分部执行相应操作 */
int EchoSource = TRUE; ///将TINY源程序回显到带有行号的列表-->改为true才能执行
int TraceScan = TRUE; ///当扫描程序识别出记号时,就显示每个记号的信息

main( int argc, char * argv[] )
{
char pgm[120]; /** 源代码文件名称 */
if (argc != 2)
{ fprintf(stderr,"usage: %s <filename>\n",argv[0]);
exit(1); ///如果argv不为2,打印显示信息并退出【意为如果输入不是:tiny 文件名.文件类型 假如多输入了个1就表示有三个参数,则错误】
}
strcpy(pgm,argv[1]) ; ///复制argv[1]地址以null为退出字符的存储器区块到另一个存储器区块品pgm内
if (strchr (pgm, '.') == NULL)
strcat(pgm,".tny"); ///把.tny文件所指字符串添加到pgm结尾处并添加'\0'
source = fopen(pgm,"r"); ///以只读的方式打开pgm文件,并将指向pgm文件的指针返回给source
if (source==NULL)
{ fprintf(stderr,"File %s not found\n",pgm);
exit(1); /// 如果源文件没有被找到,输出文件没有被找到
}
listing = stdout; /** 发清单消息到屏幕 */
fprintf(listing,"\nTINY COMPILATION: %s\n",pgm); ///应答显示语句


while (getToken()!=ENDFILE); ///如果输入流没有结束就继续进行循环,直至结束

fclose(source); ///关闭源代码文件
return 0;
}
globals.h
///全局定义.包括了数据类型的定义和整个编译器均使用的全程变量,任何代码文件都包含了globals.h头文件

#ifndef _GLOBALS_H_
#define _GLOBALS_H_ ///宏定义

#include <stdio.h>
#include <string.h> ///头文件引用

#ifndef FALSE
#define FALSE 0 //定义FALSE为0
#endif

#ifndef TRUE
#define TRUE 1 //定义TRUE为1
#endif

///定义了关键字个数8个
#define MAXRESERVED 8

typedef enum
/* book-keeping tokens */
{ENDFILE,ERROR,
/* 保留字 */
IF,THEN,ELSE,END,REPEAT,UNTIL,READ,WRITE,
/* 标识符 */
ID,NUM,
/* 特殊符号 */
ASSIGN,EQ,LT,PLUS,MINUS,TIMES,OVER,LPAREN,RPAREN,SEMI
} TokenType; /// 定义了关键字,运算符等内容的枚举值

extern FILE* source; /* 源代码文本文件 */ ///extern用在变量或函数前,表示变量或函数的定义在其他文件中
extern FILE* listing; /// 显示分析过程的文件的地址 */


extern int lineno; /* 源代码行数清单 */

/* EchoSource = TRUE causes the source program to
* be echoed to the listing file with line numbers
* during parsing
*/
extern int EchoSource;

/* TraceScan = TRUE causes token information to be
* printed to the listing file as each token is
* recognized by the scanner
*/
extern int TraceScan;

#endif

scan.h
///对于tiny编译器的扫描程序接口

#ifndef _SCAN_H_
#define _SCAN_H_

/** maxtokenlen是token的最大大小*/
#define MAXTOKENLEN 40

/** tokenString数组保存每个token */
extern char tokenString[MAXTOKENLEN+1];

/** 函数getToken返回源程序中的下一个token*/
TokenType getToken(void);


#endif

scan.c

#include "globals.h"
#include "scan.h"


/**定义的状态 */
typedef enum
{ START,///初始状态
INASSIGN,///进入到赋值状态
INCOMMENT,///进入到注释状态
INNUM,///进入到数字状态
INID,///进入到标志符状态
DONE ///状态结束
}StateType; /**每当语法分析程序需要一个单词时,就调用该子程序,得到 (类别码,单词的值)*/




/** 语义标识符和保留字*/
char tokenString[MAXTOKENLEN+1];




/** BUFLEN = 源代码的输入缓冲长度 */
#define BUFLEN 256


static char lineBuf[BUFLEN]; /** 当前行 */
static int linepos = 0; /** 在linebuf中的当前位置*/
static int bufsize = 0; /** 缓冲区的字符串当前大小*/
static int EOF_flag = FALSE; /** 如果读入下一个字符出错,设置EOF_flag为假。*/


/** getNextChar获取下一个非空白字符开始从lineBuf,如果读完,则读入新行*/
static int getNextChar(void)
{ if (!(linepos < bufsize))
{ lineno++;
if (fgets(lineBuf,BUFLEN-1,source))
{ if (EchoSource) fprintf(listing,"%4d: %s",lineno,lineBuf);
bufsize = strlen(lineBuf);
linepos = 0;
return lineBuf[linepos++];
}
else
{ EOF_flag = TRUE;
return EOF;
}
}
else return lineBuf[linepos++];
}



/** 如果读入下一个字符出错,在linebuf中回退一个字符 。*/
static void ungetNextChar(void)
{ if (!EOF_flag) linepos-- ;}


/** 保留字的查找表 */
static struct
{ char* str;
TokenType tok;
} reservedWords[MAXRESERVED]
= {{"if",IF},{"then",THEN},{"else",ELSE},{"end",END},
{"repeat",REPEAT},{"until",UNTIL},{"read",READ},
{"write",WRITE}};




/** 标识符是否是保留字*/
static TokenType reservedLookup (char * s)
{ int i;
for (i=0;i<MAXRESERVED;i++)
if (!strcmp(s,reservedWords[i].str))
return reservedWords[i].tok;
return ID;
}


/****************************************/
/**********扫描仪的主要功能*************/
/****************************************/




/** 函数gettoken返回源文件中下一个标记 */
TokenType getToken(void)
{ /** 存入tokenstring的位置 */
int tokenStringIndex = 0;
/** 保存当前要返回的token; */
TokenType currentToken;
/** 当前状态 */
StateType state = START;
/** 表示保存到tokenstring的flag */
int save;
while (state != DONE)
{ int c = getNextChar(); /**从输入buf中读入一个字符*/
save = TRUE;
switch (state)
{ case START:
if (isdigit(c)) /**判断字母*/
state = INNUM;
else if (isalpha(c))
state = INID;
else if (c == ':')
state = INASSIGN;
else if ((c == ' ') || (c == '\t') || (c == '\n'))
save = FALSE;
else if (c == '{')
{ save = FALSE;
state = INCOMMENT;
}
else
{ state = DONE;
switch (c)
{ case EOF:
save = FALSE;
currentToken = ENDFILE;
break;
case '=':
currentToken = EQ;
break;
case '<':
currentToken = LT;
break;
case '+':
currentToken = PLUS;
break;
case '-':
currentToken = MINUS;
break;
case '*':
currentToken = TIMES;
break;
case '/':
currentToken = OVER;
break;
case '(':
currentToken = LPAREN;
break;
case ')':
currentToken = RPAREN;
break;
case ';':
currentToken = SEMI;
break;
default:
currentToken = ERROR;
break;
}
}
break;
case INCOMMENT:
save = FALSE;
if (c == EOF)
{ state = DONE;
currentToken = ENDFILE;
}
else if (c == '}') state = START;
break;
case INASSIGN:
state = DONE;
if (c == '=')
currentToken = ASSIGN;
else
{ /** 在输入中备份 */
ungetNextChar();
save = FALSE;
currentToken = ERROR;
}
break;
case INNUM:
if (!isdigit(c))
{ /** 在输入中备份*/
ungetNextChar();
save = FALSE;
state = DONE;
currentToken = NUM;
}
break;
case INID:
if (!isalpha(c))
{ /** 在输入中备份*/
ungetNextChar();
save = FALSE;
state = DONE;
currentToken = ID;
}
break;
case DONE:
default: /** 应该不会执行 */
fprintf(listing,"Scanner Bug: state= %d\n",state);
state = DONE;
currentToken = ERROR;
break;
}
if ((save) && (tokenStringIndex <= MAXTOKENLEN))
tokenString[tokenStringIndex++] = (char) c;
if (state == DONE)
{ tokenString[tokenStringIndex] = '\0';
if (currentToken == ID)
currentToken = reservedLookup(tokenString);
}
}
if (TraceScan) {
fprintf(listing,"\t%d: ",lineno);
printToken(currentToken,tokenString);
}
return currentToken;
} /* end getToken */


///此函数输出一个标号和一个词素
void printToken( TokenType token, const char* tokenString )
{ switch (token)
{ case IF:
case THEN:
case ELSE:
case END:
case REPEAT:
case UNTIL:
case READ:
case WRITE:
fprintf(listing,
"reserved word: %s\n",tokenString);
break;
case ASSIGN: fprintf(listing,":=\n"); break;
case LT: fprintf(listing,"<\n"); break;
case EQ: fprintf(listing,"=\n"); break;
case LPAREN: fprintf(listing,"(\n"); break;
case RPAREN: fprintf(listing,")\n"); break;
case SEMI: fprintf(listing,";\n"); break;
case PLUS: fprintf(listing,"+\n"); break;
case MINUS: fprintf(listing,"-\n"); break;
case TIMES: fprintf(listing,"*\n"); break;
case OVER: fprintf(listing,"/\n"); break;
case ENDFILE: fprintf(listing,"EOF\n"); break;
case NUM:
fprintf(listing,
"NUM, val= %s\n",tokenString);
break;
case ID:
fprintf(listing,
"ID, name= %s\n",tokenString);
break;
case ERROR:
fprintf(listing,
"ERROR: %s\n",tokenString);
break;
default: /* should never happen */
fprintf(listing,"Unknown token: %d\n",token);
}
}


---->>>>>>用C语言的编译工具创建一个tiny项目,在创建这几个文件,在生成的tiny软件目录,写一个需要识别的txt文本文件,用控制台来到当前目录,输入tiny + 文件名.txt即可看到识别出的效果

效果图:
词法分析器只做token识别,代码详细注释