题2: 设计一个词法扫描器。词法扫描器的功能是输入源程序,输出单词符号。写出关键字集合和程序流程图。例如源程序为C语言。输入如下一段:
main() {
int a,b;
a = 10;
b = a + 20;
}
输出如图:
(2,”main”)
(5,”(“)
(5,”)“)
(5,”{“)
(1,”int”)
(2,”a”)
(5,”,”)
(2,”b”)
(5,”;”)
(2,”a”)
(4,”=”)
(3,”10”)
(5,”;”)
(2,”b”)
(4,”=”)
(2,”a”)
(4,”+”)
(3,”20”)
(5,”;”)
(5,” ) “ )
词法扫描器 c c++
答案:1 悬赏:0 手机版
解决时间 2021-05-05 06:18
- 提问者网友:回忆在搜索
- 2021-05-04 22:19
最佳答案
- 五星知识达人网友:往事埋风中
- 2021-05-04 22:39
类似的词法分析器,你可以在这基础上加上一些功能。mlex.c内容如下:#include <stdio.h>#include <stdlib.h>#include <string.h>#include <conio.h>#include <assert.h>#include "lex.h"
char strToken[30]; //字符数组,存放构成单词符号的字符串。int lvalue; //单词的内码值keyword keywords[]={ "switch",$SWITCH, "case",$CASE, "if",$IF, "else",$ELSE, "do",$DO, "while",$WHILE, "stop",$STOP, "end",$END, "var",$VAR, "dim",$DIM, "char",$CHAR, 0,0,}; //关键字表symbol ctable[200]; //常数表symbol vtable[200]; //符号表(标识符)char inbuf[300]; //扫描缓冲区int sp; //起点指示器int ep; //扫描指示器int lineno; //行计数器FILE *fsrc;
char GetChar(){ //从扫描缓冲区第一个缓冲区从inbuf中读一个字符,实现双输入缓冲。 //[0]到inbuf[149],第二个缓冲区从inbuf[150]到inbuf[299], //每次从源文件读一个缓冲区大小的内容,分析完当前工作缓冲区内容时把源文件读入 //另一个缓冲区,另一个缓冲区成为工作缓冲区 int i = 0; char ch; if ((ep == 150)||(ep == 300)) { ep = ep%300; while (((inbuf[i+ep]=fgetc(fsrc))!=NULL) && (i<150)) i++; } ch = inbuf[ep]; ep++; return ch;}char GetBC(){ //从扫描缓冲区读字符,过滤空格、制表和回车符号 char ch = GetChar(); while ((ch==' ') || (ch=='\n') || (ch=='\t')) { if (ch=='\n') lineno++; ch = GetChar(); } return ch;}void Concat(char str[], char ch){ //将字符ch添加到符号串str的尾部 str[strlen(str)] = ch;}bool isLetter(char ch){ //判断ch是否字母 if (((ch>='A')&&(ch<='Z')) || ((ch>='a')&&(ch<='z'))) return true; return false;}bool isDigit(char ch){ //判断ch是否数字 if ((ch>='0')&&(ch<='9')) return true; return false;}
int Reserve(char* str){ //如str是保留字,返回保留字种别号 //否则返回0 int i = 0; while (keywords[i].code != 0) { if (strcmp(str,keywords[i].name) == 0) return keywords[i].code; i++; } return 0;}void Retract(){ //从扫描输入缓冲区回退一个字符 if (ep==0) ep = 299; else ep--;}
int InsertID(char *str){ //如符号表中已有str标识符,返回记录号, //否则将str加到符号表中,返回记录号 int i = 0; while ((vtable[i].number!=0) && (i<200)) { if (strcmp(str,vtable[i].name) == 0) return vtable[i].number; i++; } if (i<200) { vtable[i].number = i+1; strcpy(vtable[i].name, str); return vtable[i].number; } return 0;}int InsertConst(char *str){ //如常数表中已有str常数,返回记录号, //否则将str加到常数表中,返回记录号 int i = 0; while ((ctable[i].number!=0) && (i<200)) { if (strcmp(str,ctable[i].name) == 0) return ctable[i].number; i++; } if (i<200) { ctable[i].number = i+1; strcpy(ctable[i].name, str); return ctable[i].number; } return 0;}void ProcError(){ printf("%i 行有错误\n", lineno);}int Lex(){ //从扫描缓冲区中识别出一个单词,返回单词种别号, //构成的单词的符号串保留在strToken中, //单词的值保留在lvalue中。strToken和lvalue为全局变量。 int code; char ch;
memset(strToken, 0, 30); ch = GetBC(); sp = ep-1; lvalue = 0; if (isLetter(ch)) { while (isLetter(ch) || isDigit(ch)) { Concat(strToken, ch); ch = GetChar(); } Retract(); code = Reserve(strToken); if (code == 0) { lvalue = InsertID(strToken); return $ID; } return code; } else if (isDigit(ch)) { while (isDigit(ch)) { Concat(strToken, ch);ch = GetChar(); } if (ch == '.' ) { Concat(strToken, ch); if(!isDigit(GetChar())) {ProcError(); return $END;} Retract(); while(isDigit(GetChar())) { Retract(); Concat(strToken,GetChar()); Retract(); GetChar(); } InsertConst(strToken); return $FLOAT; } Retract(); lvalue = InsertConst(strToken); return $INT; }
else if (ch == '=') { ch = GetChar(); if (ch == '=') return $EQ; Retract(); return $ASSIGN; } else if (ch == '+') return $PLUS; else if (ch == ';') return $SEMICOLON; else if (ch == '(') return $LPAR; else if (ch == ')') return $RPAR; else if (ch == '{') return $LBRACE; else if (ch == '}') return $RBRACE;
else if (ch == '*') { ch = GetChar(); if (ch == '*') return $POWER; Retract(); return $STAR; } else if (ch == EOF) return $END; else { ProcError(); return $END; }}void main(){ token aToken; int i = 0; char strName[256]; FILE *fcon = fopen("const.txt","w+"); FILE *ftoken = fopen("token.txt","w+"); FILE *fsym = fopen("symbol.txt","w+"); printf("请输入源文件名(enter结束):\n"); scanf("%s",strName); fsrc = fopen(strName,"r"); if (fsrc == NULL) { printf("找不到源文件src.txt\n"); exit(1); } if ((ftoken == NULL) || (fcon == NULL) || \ (fsym == NULL)) { printf("不能创建输出文件const.txt,token.txt,symbol.txt\n"); exit(1); } while ((i<150) && ((inbuf[i]=fgetc(fsrc)) != EOF)) i++;
ep = 0; lineno = 1; aToken.label = 0; aToken.code = Lex(); aToken.addr = lvalue; printf("单词序列:\n"); while (aToken.code != $END) { //单词输出到文件 fprintf(ftoken, "(%3d,%3d)", aToken.code, aToken.addr); //单词输出到屏幕 printf("(%3d,%3d) ", aToken.code, aToken.addr); aToken.label++; aToken.code = Lex(); aToken.addr = lvalue; } i = 0; printf("\n符号表:\n"); while (vtable[i].number != 0) { fprintf(fsym, "(%3d,%s)", vtable[i].number, vtable[i].name); printf("(%3d,%s)", vtable[i].number, vtable[i].name); i++; } i = 0; printf("\n常数表:\n"); while (ctable[i].number != 0) { fprintf(fcon, "(%3d,%s)", ctable[i].number, ctable[i].name); printf("(%3d,%s)", ctable[i].number, ctable[i].name); i++; } fclose(fsrc); fclose(fsym); fclose(fcon); fclose(ftoken); printf("\n词法分析结束,按任意键\n"); getch();}
lex.h头文件内容如下:typedef struct token{ int label; char name[30];//组成单词的字符串 int code; int addr;}token;
typedef struct symbol{ int number; char name[30];//组成标识符或常数的字符串} symbol;
typedef struct keyword{ char name[30]; //组成关键字的字符串 int code;} keyword;
//定义一些常量enum TVAL {$WHILE=1,$IF,$ELSE,$SWITCH,$CASE,$DO,$STOP,$END,$ID,$INT,$EQ,$ASSIGN,$PLUS, \ $STAR,$POWER,$COMMA,$LPAR,$RPAR,$SEMICOLON,$LBRACE, \ $RBRACE,$VAR,$DIM,$FLOAT,$CHAR};
char strToken[30]; //字符数组,存放构成单词符号的字符串。int lvalue; //单词的内码值keyword keywords[]={ "switch",$SWITCH, "case",$CASE, "if",$IF, "else",$ELSE, "do",$DO, "while",$WHILE, "stop",$STOP, "end",$END, "var",$VAR, "dim",$DIM, "char",$CHAR, 0,0,}; //关键字表symbol ctable[200]; //常数表symbol vtable[200]; //符号表(标识符)char inbuf[300]; //扫描缓冲区int sp; //起点指示器int ep; //扫描指示器int lineno; //行计数器FILE *fsrc;
char GetChar(){ //从扫描缓冲区第一个缓冲区从inbuf中读一个字符,实现双输入缓冲。 //[0]到inbuf[149],第二个缓冲区从inbuf[150]到inbuf[299], //每次从源文件读一个缓冲区大小的内容,分析完当前工作缓冲区内容时把源文件读入 //另一个缓冲区,另一个缓冲区成为工作缓冲区 int i = 0; char ch; if ((ep == 150)||(ep == 300)) { ep = ep%300; while (((inbuf[i+ep]=fgetc(fsrc))!=NULL) && (i<150)) i++; } ch = inbuf[ep]; ep++; return ch;}char GetBC(){ //从扫描缓冲区读字符,过滤空格、制表和回车符号 char ch = GetChar(); while ((ch==' ') || (ch=='\n') || (ch=='\t')) { if (ch=='\n') lineno++; ch = GetChar(); } return ch;}void Concat(char str[], char ch){ //将字符ch添加到符号串str的尾部 str[strlen(str)] = ch;}bool isLetter(char ch){ //判断ch是否字母 if (((ch>='A')&&(ch<='Z')) || ((ch>='a')&&(ch<='z'))) return true; return false;}bool isDigit(char ch){ //判断ch是否数字 if ((ch>='0')&&(ch<='9')) return true; return false;}
int Reserve(char* str){ //如str是保留字,返回保留字种别号 //否则返回0 int i = 0; while (keywords[i].code != 0) { if (strcmp(str,keywords[i].name) == 0) return keywords[i].code; i++; } return 0;}void Retract(){ //从扫描输入缓冲区回退一个字符 if (ep==0) ep = 299; else ep--;}
int InsertID(char *str){ //如符号表中已有str标识符,返回记录号, //否则将str加到符号表中,返回记录号 int i = 0; while ((vtable[i].number!=0) && (i<200)) { if (strcmp(str,vtable[i].name) == 0) return vtable[i].number; i++; } if (i<200) { vtable[i].number = i+1; strcpy(vtable[i].name, str); return vtable[i].number; } return 0;}int InsertConst(char *str){ //如常数表中已有str常数,返回记录号, //否则将str加到常数表中,返回记录号 int i = 0; while ((ctable[i].number!=0) && (i<200)) { if (strcmp(str,ctable[i].name) == 0) return ctable[i].number; i++; } if (i<200) { ctable[i].number = i+1; strcpy(ctable[i].name, str); return ctable[i].number; } return 0;}void ProcError(){ printf("%i 行有错误\n", lineno);}int Lex(){ //从扫描缓冲区中识别出一个单词,返回单词种别号, //构成的单词的符号串保留在strToken中, //单词的值保留在lvalue中。strToken和lvalue为全局变量。 int code; char ch;
memset(strToken, 0, 30); ch = GetBC(); sp = ep-1; lvalue = 0; if (isLetter(ch)) { while (isLetter(ch) || isDigit(ch)) { Concat(strToken, ch); ch = GetChar(); } Retract(); code = Reserve(strToken); if (code == 0) { lvalue = InsertID(strToken); return $ID; } return code; } else if (isDigit(ch)) { while (isDigit(ch)) { Concat(strToken, ch);ch = GetChar(); } if (ch == '.' ) { Concat(strToken, ch); if(!isDigit(GetChar())) {ProcError(); return $END;} Retract(); while(isDigit(GetChar())) { Retract(); Concat(strToken,GetChar()); Retract(); GetChar(); } InsertConst(strToken); return $FLOAT; } Retract(); lvalue = InsertConst(strToken); return $INT; }
else if (ch == '=') { ch = GetChar(); if (ch == '=') return $EQ; Retract(); return $ASSIGN; } else if (ch == '+') return $PLUS; else if (ch == ';') return $SEMICOLON; else if (ch == '(') return $LPAR; else if (ch == ')') return $RPAR; else if (ch == '{') return $LBRACE; else if (ch == '}') return $RBRACE;
else if (ch == '*') { ch = GetChar(); if (ch == '*') return $POWER; Retract(); return $STAR; } else if (ch == EOF) return $END; else { ProcError(); return $END; }}void main(){ token aToken; int i = 0; char strName[256]; FILE *fcon = fopen("const.txt","w+"); FILE *ftoken = fopen("token.txt","w+"); FILE *fsym = fopen("symbol.txt","w+"); printf("请输入源文件名(enter结束):\n"); scanf("%s",strName); fsrc = fopen(strName,"r"); if (fsrc == NULL) { printf("找不到源文件src.txt\n"); exit(1); } if ((ftoken == NULL) || (fcon == NULL) || \ (fsym == NULL)) { printf("不能创建输出文件const.txt,token.txt,symbol.txt\n"); exit(1); } while ((i<150) && ((inbuf[i]=fgetc(fsrc)) != EOF)) i++;
ep = 0; lineno = 1; aToken.label = 0; aToken.code = Lex(); aToken.addr = lvalue; printf("单词序列:\n"); while (aToken.code != $END) { //单词输出到文件 fprintf(ftoken, "(%3d,%3d)", aToken.code, aToken.addr); //单词输出到屏幕 printf("(%3d,%3d) ", aToken.code, aToken.addr); aToken.label++; aToken.code = Lex(); aToken.addr = lvalue; } i = 0; printf("\n符号表:\n"); while (vtable[i].number != 0) { fprintf(fsym, "(%3d,%s)", vtable[i].number, vtable[i].name); printf("(%3d,%s)", vtable[i].number, vtable[i].name); i++; } i = 0; printf("\n常数表:\n"); while (ctable[i].number != 0) { fprintf(fcon, "(%3d,%s)", ctable[i].number, ctable[i].name); printf("(%3d,%s)", ctable[i].number, ctable[i].name); i++; } fclose(fsrc); fclose(fsym); fclose(fcon); fclose(ftoken); printf("\n词法分析结束,按任意键\n"); getch();}
lex.h头文件内容如下:typedef struct token{ int label; char name[30];//组成单词的字符串 int code; int addr;}token;
typedef struct symbol{ int number; char name[30];//组成标识符或常数的字符串} symbol;
typedef struct keyword{ char name[30]; //组成关键字的字符串 int code;} keyword;
//定义一些常量enum TVAL {$WHILE=1,$IF,$ELSE,$SWITCH,$CASE,$DO,$STOP,$END,$ID,$INT,$EQ,$ASSIGN,$PLUS, \ $STAR,$POWER,$COMMA,$LPAR,$RPAR,$SEMICOLON,$LBRACE, \ $RBRACE,$VAR,$DIM,$FLOAT,$CHAR};
我要举报
如以上问答信息为低俗、色情、不良、暴力、侵权、涉及违法等信息,可以点下面链接进行举报!
大家都在看
推荐资讯