📄 scan.cpp
字号:
// scan.cpp: implementation of the Cscan class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "face.h"
#include "scan.h"
#include "global.h"
#include "MainFrm.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
Cscan::Cscan()
{
//在构造函数中初始化变量值:
lineno = 0;
linepos = 0;
bufsize = 0;
Tokennum=0;
EOF_flag = FALSE;
Error = false;
}
Cscan::~Cscan()
{
}
/****************************************************/
/* 文件 zscanner.cpp */
/* 说明 TINY编译器的词法扫描器实现 */
/* 主题 编译器结构:原理和实例 */
/****************************************************/
/*******************************************************************/
/* 函数名 getNextChar */
/* 功 能 取得下一非空字符函数 */
/* 说 明 该函数从输入缓冲区lineBuf中取得下一个非空字符 */
/* 如果lineBuf中的字串已经读完,则从源代码文件中读入一新行 */
/*******************************************************************/
int Cscan::getNextChar(void)
{
/* 当前代码输入行缓冲器lineBuf已经耗尽 */
if (!(linepos < bufsize))
{
/* 源代码行号lineno加1 */
lineno++;
/* 从源文件source中读入BUFLEN-2(254)个字符到行缓冲区lineBuf中 *
* fgets在的lineBuf末尾保留换行符.并在末尾加了一个NULL字符表示结束 */
if (fgets(lineBuf,BUFLEN-1,source))
{
/* 如果源文件追踪标志EchoSource为TRUE *
* 将源程序行号lineno及行内容lineBuf在词法扫描时写入列表文件listing */
if (EchoSource) fprintf(listing,"%4d: %s",lineno,lineBuf);
/* 取得当前输入源代码行的实际长度,送给变量bufsize */
bufsize = strlen(lineBuf);
/* 输入行缓冲区lineBuf中当前字符位置linepos指向lineBuf开始位置 */
linepos = 0;
/* 取得输入行缓冲区lineBuf中下一字符 */
return lineBuf[linepos++];
}
else
{
/* 未能成功读入新的代码行,fget函数返回值为NULL *
* 已经到源代码文件末尾,设置EOF_flag标志为TRUE */
EOF_flag = TRUE;
/* 函数返回EOF */
return EOF;
}
}
/* 行输入缓冲区lineBuf中字符还未读完,直接取其中下一字符,函数返回所取字符 */
else return lineBuf[linepos++];
}
/********************************************************/
/* 函数名 ungetNextChar */
/* 功 能 字符回退函数 */
/* 说 明 该过程在行输入缓冲区lineBuf中回退一个字符 */
/* 用于超前读字符后不匹配时候的回退 */
/********************************************************/
void Cscan::ungetNextChar(void)
{
/* 如果EOF_flag标志为FALSE,不是处于源文件末尾 *
* 输入行缓冲区lineBuf中当前字符位置linepos减1 */
if (!EOF_flag)
linepos-- ;
}
/*****************************************************************/
/* 函数名 ChainToFile */
/* 功 能 将链表中的Token结点依次存入文件中 */
/* 说 明 参数p是指针变量,指向Token链表的表头 */
/*****************************************************************/
void Cscan::ChainToFile (ChainNodeType *Chainhead)
{
int num=1;
FILE *fp;
ChainNodeType *currentP=Chainhead;
/*创建一个新的文件"Tokenlist",以存储Token序列*/
fp=fopen("c:\\Tokenlist","wb+");
if (fp==NULL)
{ printf("cannot create file Tokenlist!\n");
exit(0);
}
fp=fopen("c:\\Tokenlist","ab"); /*按追加方式打开文件*/
if (fp==NULL)
{ printf("cannot open file Tokenlist!\n");
exit(0);
}
/*从表头到表尾,依次将所有的Token写入文件*/
do
{ fwrite(currentP,TOKENLEN,1,fp);
currentP=currentP->nextToken;
num++;
}
while (currentP!=NULL);
fclose(fp); /*关闭文件*/
}
/****************************************************************/
/* 函数名 printToken */
/* 功 能 输出单词函数 */
/* 说 明 该函数将单词及其词元写入列表文件listing */
/* 参数token指定了输出单词,参数tokenString指定了其词元 */
/****************************************************************/
void Cscan::printToken( TokenType token )
{
/* 对函数参数token给定单词进行分类处理 */
switch (token.Lex)
{
/* 单词token为保留字,将保留字词元以指定格式写入列表文件listing */
case PROGRAM:
case PROCEDURE:
case TYPE:
case VAR:
case IF:
case THEN:
case ELSE:
case FI:
case WHILE:
case DO:
case ENDWH:
case BEGIN:
case END:
case READ:
case WRITE:
case ARRAY:
case OF:
case RECORD:
case RETURN:
/*后来添加的*/
case INTEGER:
case CHAR1:
fprintf(listing,
"reserved word: %s\n",token.Sem);
break;
/*特殊符号 */
/* 单词token为特殊符号:ASSIGN (赋值),将":="写入文件listing */
case ASSIGN: fprintf(listing,":=\n"); break;
/* 单词token为特殊符号:LT (小于),将"<"写入文件listing */
case LT: fprintf(listing,"<\n"); break;
/* 单词token为特殊符号:EQ (等于),将"="写入文件listing */
case EQ: fprintf(listing,"=\n"); break;
/* 单词token为特殊符号:PLUS (加号),将"+"写入文件listing */
case PLUS: fprintf(listing,"+\n"); break;
/* 单词token为特殊符号;MINUS (减号),将"-"写入文件listing */
case MINUS: fprintf(listing,"-\n"); break;
/* 单词token为特殊符号:TIMES (乘号),将"*"写入文件listing */
case TIMES: fprintf(listing,"*\n"); break;
/* 单词token为特殊符号:OVER (除号),将"/"写入文件listing */
case OVER: fprintf(listing,"/\n"); break;
/* 单词token为特殊符号:LPAREN (左括号),将"("写入文件listing */
case LPAREN: fprintf(listing,"(\n"); break;
/* 单词token为特殊符号:RPAREN (右括号),将")"写入文件listing */
case RPAREN: fprintf(listing,")\n"); break;
/* 单词token为特殊符号:LMIDPAREN (左中括号),将"["写入文件listing */
case LMIDPAREN: fprintf(listing,"[\n"); break;
/* 单词token为特殊符号:RMIDPAREN (右中括号),将"]"写入文件listing */
case RMIDPAREN: fprintf(listing,"]\n"); break;
/* 单词token为特殊符号:SEMI (分号),将";"写入文件listing */
case SEMI: fprintf(listing,";\n"); break;
/* 单词token为特殊符号:COLON (冒号),将":"写入文件listing */
case COLON: fprintf(listing,":\n"); break;
/* 单词token为特殊符号:COMMA (逗号),将","写入文件listing */
case COMMA: fprintf(listing,",\n"); break;
/* 单词token为特殊符号:DOT (程序结束符),将"."写入文件listing */
case DOT: fprintf(listing,".\n"); break;
/* 单词token为特殊符号:UNDERANGE (下标界限),将".."写入文件listing */
case UNDERANGE: fprintf(listing,";\n"); break;
/* 单词token为簿记单词符号:ENDFILE (文件结尾),将EOF写入文件listing */
case ENDFILE1: fprintf(listing,"EOF\n"); break;
/* 单词token为多字符单词符号:INTC (数字),将数值写入文件listing */
case INTC:
fprintf(listing,
"INTC, val= %s\n",token.Sem);
break;
/* 单词token为多字符单词符号:CHARC (字符),将字符写入文件listing */
case CHARC:
fprintf(listing,
"CHARC, letter= %s\n",token.Sem);
break;
/* 单词token为多字符单词符号:ID (标识符),将标识符名写入文件listing */
case ID:
fprintf(listing,
"ID, name= %s\n",token.Sem);
break;
/* 单词token为簿记单词符号:ERROR (错误),将错误信息写入文件listing */
case ERROR1:
fprintf(listing, "ERROR: %s\n",token.Sem);
break;
/* 单词token为其他未知单词,未知信息写入文件listing,此种情况不应发生 */
default:
fprintf(listing,"Unknown token: %d\n",token.Lex);
}
}
/*****************************************************************/
/* 函数名 printTokenlist */
/* 功 能 将文件"Tokenlist"中保存的Token序列写入列表文件listing中*/
/* 一般,listing指向标准输出。 */
/* 说 明 */
/*****************************************************************/
void Cscan::printTokenlist()
{ int num;
TokenType currentToken;
//输出信息部分:
if (Error)
fprintf(listing,">>词法分析有错,以ERROR打头的为错误的Token:\n");
else fprintf(listing,">>词法分析无错:Token序列如下:\n");
//fprintf(listing, " \n>>scanner result :\n");
/*按只读方式打开文件*/
FILE *fp=fopen("c:\\Tokenlist","rb");
if (fp==NULL)
{ fprintf(listing,"cannot create file Tokenlist!\n");
exit(0);
}
/*从文件中依次读Token,并输出到listing指向的列表文件中去*/
for (num=1;num<=Tokennum;num++)
{/*从文件中读一个Token存入变量currentToken中*/
fread(¤tToken,TOKENLEN,1,fp);
fprintf(listing,"\t%d: ",currentToken.lineshow);
printToken(currentToken);
/*文件指针移到下一个Token的位置*/
fseek(fp,num*TOKENLEN,0);
}
fclose(fp); /*关闭文件*/
}
/**************************************************************/
/* 函数名 reservedLookup */
/* 功 能 保留字查找函数 */
/* 说 明 使用线性查找,查看一个标识符是否是保留字 */
/* 标识符如果在保留字表中则返回相应单词,否则返回单词ID */
/**************************************************************/
LexType Cscan::reservedLookup (char * s)
{
int i;
/* 在保留字表中查找,MAXRESERVED已经定义为8,为保留字数 */
for (i=0;i<MAXRESERVED;i++)
/* 线性查保留字表,察看函数参数s指定标识符是否在表中 *
* 当两字符串匹配的时候,函数strcmp返回值为0(FALSE) */
if (!strcmp(s,reservedWords[i].str))
/* 字符串s与保留字表中某一表项匹配,函数返回对应保留字单词 */
return( reservedWords[i].tok);
/* 字符串s未在保留字表中找到,函数返回标识符单词ID */
return ID;
}
/****************************************
********* 词法扫描器基本函数 **********
****************************************/
/************************************************************/
/* 函数名 getTokenlist */
/* 功 能 取得单词函数 */
/* 说 明 函数从源文件字符串序列中获取下一个单词符号 */
/* 使用确定性有限自动机DFA,采用直接转向法 */
/* 超前读字符,对保留字采用查表方式识别 */
/* 产生词法错误时候,仅仅略过产生错误的字符,不加改正 */
/************************************************************/
void Cscan::getTokenList(CString pgm)
{
/***************************************/
//在构造函数中初始化变量值:
lineno = 0;
linepos = 0;
bufsize = 0;
Tokennum=0;
EOF_flag = FALSE;
Error = false;
/**************************************/
source =fopen(pgm ,"r");
if (source==NULL)
{
AfxGetApp()->m_pMainWnd->MessageBox("未找到源文件");
Error = true;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -