编译原理的词法分析,主要是根据自己的规则,识别出相对应的规则,根据自己的需求,输出你想要的内容
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
//记号类型序号
enum Token_Type {
ORIGIN, SCALE, ROT, IS, TO, //保留字
STEP, DRAW, FOR, FROM, //保留字
T, //参数
ID, //标识符(已没用了)
SEMICO, L_BRACKET, R_BRACKET, COMMA, //分隔符
PLUS, MINUS, MUL, DIV, POWER, //运算符
FUNC, //函数
CONST_ID, //数值常数
ERRTOKEN, //错误单词
NONTOKEN //表示词法分析结束的专用记号
};
//记号变量类型
struct Token {
Token_Type type; //记号类别号
char *lexeme; //标识符的字符串
double value; //数值常数的值
double (*FuncPtr)(double); //函数的指针
};
// 预定义单词表
#define NUMKEYWORDS 18
struct Token TokenTab[NUMKEYWORDS] = {
//语言中使用的符号常数
{CONST_ID, "PI", 3.1415926, NULL},
{CONST_ID, "E", 2.71828, NULL},
//语言中惟一的变量
{T, "T", 0.0, NULL},
//语言允许用的数学函数名(函数原型放在 math.h 中)
{FUNC, "SIN", 0.0, sin},
{FUNC, "COS", 0.0, cos},
{FUNC, "TAN", 0.0, tan},
{FUNC, "LN", 0.0, log},
{FUNC, "EXP", 0.0, exp},
{FUNC, "SQRT", 0.0, sqrt},
//语句关键字
{ORIGIN, "ORIGIN", 0.0, NULL},
{SCALE, "SCALE", 0.0, NULL},
{ROT, "ROT", 0.0, NULL},
{IS, "IS", 0.0, NULL},
{FOR, "FOR", 0.0, NULL},
{FROM, "FROM", 0.0, NULL},
{TO, "TO", 0.0, NULL},
{STEP, "STEP", 0.0, NULL},
{DRAW, "DRAW", 0.0, NULL}
};
Token iskeywords(const char* id);//判断标识符是否语言的预留单词
void main(int argc, char *argv[]) {
FILE *InFile; //源文件
int Char; //当前输入字符
Token token; //记号变量
int c; //临时变量(暂存已读入的输入字符)
char *bufc,*bufval; //临时变量(以字符串形式暂存读入的单字符,暂存标识符、数值常量的当前串)
if (argc < 2) { //程序执行时有 1 个命令行参数(即需要进行词法分析的文件名)
printf("请输入源文件名!\n" );
return;
}
InFile = fopen (argv[1], "r"); //以只读方式,打开源文件
printf("记号类别 字符串 常数值 函数指针\n");
printf("__________________________________________\n");
while(1) {
//初始化记号变量
//token.type=NONTOKEN;//记号类别号放在后面处理
token.lexeme="";
token.value=0;
token.FuncPtr=NULL;
Char = getc (InFile); // 从源文件读入一个字符
if (Char == EOF) {
token.type=NONTOKEN;
break; //读到文件终止符,词法分析结束
}
//消除注释内容
if(Char=='-' || Char=='/' ) { //当前输入字符是 - 或 /,说明可能遇到注释引导符了
c=Char; //暂存已读的输入字符
Char = getc (InFile); //向前多读 1 个输入字符
if (Char == c) { //多读的输入字符与暂存字符相同,说明是注释引导符 -- 或 //
//逐一读入注释文本(以换行符或文件终止符为结束),不作任何处理,即跳过了这些注释内容
while (Char != '\n' && Char != EOF)
Char = getc (InFile);
} else { //多读的字符不是 - 或 /,说明遇到的不是注释引导符
ungetc (Char, InFile); //将多读的这个字符退回输入流
Char=c; //当前输入字符恢复为暂存字符(暂存的字符 - 或 / 是运算符,需作为记号处理)
}
}
if(isspace(Char)) continue; //过滤空白符(空格、制表符、换行符)
if(Char=='-') { //减号
token.type=MINUS;
token.lexeme="-";
} else if(Char=='/' ) { //除号
token.type=DIV;
token.lexeme="/";
} else if(Char=='*' ) { //当前输入字符是 *,说明已读出乘号或遇到乘方算符的首字母
c=Char; //暂存已读的 *
Char = getc (InFile); //向前多读 1 个输入字符
if (Char == c) { //多读的字符也是 *,说明已读出了乘方算符
token.type=POWER;
token.lexeme="**";
} else { //多读的字符不是 *,说明是乘号
ungetc (Char, InFile); //将多读的字符退回输入流
token.type=MUL;
token.lexeme="*";
}
} else if(Char==',' ) { //逗号
token.type=COMMA;
token.lexeme=",";
} else if(Char==';' ) { //分号
token.type=SEMICO;
token.lexeme=";";
} else if(Char=='(' ) { //左括号
token.type=L_BRACKET;
token.lexeme="(";
} else if(Char=='+' ) { //加号
token.type=PLUS;
token.lexeme="+";
} else if(Char==')' ) { //右括号
token.type=R_BRACKET;
token.lexeme=")";
} else if(isalpha(Char)) { //当前输入字符是字母,说明遇到了标识符的首字母
//将首字母以字符串的形式,保存到临时串
bufc=(char*)malloc(2);
bufc[0]=toupper(Char);
bufc[1]='\0';
bufval=bufc;
//循环读取后续的输入字符
for(;;){
Char = getc (InFile); //读入 1 个输入字符
if (isalnum(Char)) { //当前输入字符仍然是字母或数字,说明还在标识符中
//将读入的输入字符,以字符串的形式,连接到临时串后
bufc=(char*)malloc(2);
bufc[0]=toupper(Char);
bufc[1]='\0';
strcat(bufval,bufc);//将字符串bufval追加到bufc后面
} else //当前输入字符不再是字母或数字,说明标识符已经读完,并且多读了一个不属于标识符的输入
break;
}
ungetc (Char, InFile);//将多读的字符退回到输入流中
//token.type=ID; //记号类别 = 标识符
//token.lexeme = bufval;//记号值 = 临时串值
token=iskeywords(bufval); //判定是否语言的关键字,结果以记号变量形式返回
} else if(isdigit(Char)) { //当前输入字符是数字,说明遇到了数值常量的首个数字字母
bufc=(char*)malloc(2);
bufc[0]=Char;
bufc[1]='\0';
bufval=bufc;
//逐个字符处理数值常量的整数部分
for (;;) {
Char = getc (InFile);
if (isdigit(Char)) {
bufc=(char*)malloc(2);
bufc[0]=Char;
bufc[1]='\0';
strcat(bufval,bufc);
} else
break;
}
if (Char == '.') { //当前输入字符是 .,说明遇到了数值常量的小数部分
bufc=(char*)malloc(2);
bufc[0]=Char;
bufc[1]='\0';
strcat(bufval,bufc);
//逐个字符处理数值常量的小数部分
for (;;) {
Char = getc (InFile);
if (isdigit(Char)) {
bufc=(char*)malloc(2);
bufc[0]=Char;
bufc[1]='\0';
strcat(bufval,bufc);
} else
break;
}
}
ungetc (Char, InFile);//将多读的、不属于数值常量的字符,退回输入流
token.type=CONST_ID; //记号类别 = 常数
token.value=atof(bufval); //记号值 = 临时串转换成的浮点数
} else if (Char == EOF ){ //文件终止符(处理源文件最后一行是注释的情况)
token.type=NONTOKEN;
break;
} else { //当前输入字符不是以上情况,说明该字符不在本语言的字母表中,出现词法错误
token.type=ERRTOKEN;
}
//putc(Char,stdout);//逐个字符地显示处理后的源文件
//输出记号信息
if(token.type==CONST_ID)
printf(" %4d%12s%12f\n",token.type,"",token.value);
else if(token.type==FUNC)
printf(" %4d%12s%12f%12x\n",token.type,"",token.value,token.FuncPtr);
else
printf(" %4d%12s\n",token.type, token.lexeme);
};
printf(" %4d%12s\n",token.type, token.lexeme);//显示分析结束记号
printf("______________________________\n");
fclose (InFile); //关闭源文件(即关闭词法分析器)
}
//判断标识符是否语言的预留单词
Token iskeywords(const char* id){
int i;
Token token;
token.lexeme="";
token.value=0;
token.FuncPtr=NULL;
for(i=0;i<NUMKEYWORDS;i++) if( strcmp(TokenTab[i].lexeme, id)==0) break;
if(i<NUMKEYWORDS) token=TokenTab[i]; else token.type = ERRTOKEN;
return token;
}