先写一个词法分析器
把源码放在项目目录中,命名为“test.txt”,或者修改一下代码
生成词法分析结果
函数绘图语言源码示例
#include "pch.h"
#include <iostream>
#include <graphics.h> // 引用图形库头文件
#include <conio.h>
#include "pch.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define TOKEN_LEN 100000
#define TOKEN_BUF 100
enum Token_Type // 记号的类别,共22个
{
ORIGIN, SCALE, ROT, IS, // 保留字(一字一码)
TO, STEP, DRAW, FOR, FROM, // 保留字
T, // 参数
SEMICO, L_BRACKET, R_BRACKET, COMMA,// 分隔符
PLUS, MINUS, MUL, DIV, POWER, // 运算符
FUNC, // 函数(调用)
CONST_ID, // 常数
NONTOKEN, // 空记号(源程序结束)
ERRTOKEN // 出错记号(非法输入)
};
struct Token // 记号的数据结构
{
Token_Type type; // 类别
char lexeme[TOKEN_BUF]; // 属性,原始输入的字符串
double value; // 属性,若记号是常数则是常数的值
double(*FuncPtr)(double);
// 属性,若记号是函数则是函数指针
};
static Token TokenTab[] =
{
{CONST_ID, "PI", 3.1415926, NULL},
{CONST_ID, "E", 2.71828, NULL},
{T, "T", 0.0, NULL},
{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}
};
char source[100000];
static char TokenBuf[TOKEN_BUF]; //记号字符缓冲
Token token[TOKEN_LEN];//记号
/*char *enumoutput(int i)
{
char s[10];
switch (i)
{
case 1:
strcpy(s, "ORIGIN");
break;
}
return s;
}*/
int Infile()//文件读入
{
int i = 0;
FILE *fp;
char ch, fpname[100] = "test.txt";
if ((fp = fopen(fpname, "r")) == NULL)
{
printf("Can not find the file.\n");
exit(0);
}
else {
while (!feof(fp))
{
fscanf(fp, "%c", &ch);
if (ch == '\t') {//跳过/t
while (1)
{
if (EOF == fscanf(fp, "%c", &ch) || ch != '\t')break;
}
}
if (ch >= 'a'&&ch <= 'z')ch -= 32;//小写转化大写
//下面跳过注释
if (ch == '/'&&source[i-1] == '/')//找到注释//
{
while(1)
{
if (EOF == fscanf(fp, "%c", &ch) || ch == '\n' )break;//当找到换行或者文件结束时跳出
}
source[i-1] = NULL;
i--;
}
if (ch == '-'&&source[i - 1] == '-')//找到注释--
{
while (1)
{
if (EOF == fscanf(fp, "%c", &ch) || ch == '\n')break;//当找到换行或者文件结束时跳出
}
source[i - 1] = NULL;
i--;
}
//跳过注释结束
source[i] = ch;
i++;
}
source[i - 1] = '\0';
}
return i;//返回长度
}
void output(int len)
{
int i = 0;
//int length = strlen(source);
while (i<len)
{
printf("%d\t\t%s\t\t%f\t\t%x\n", token[i].type, token[i].lexeme, token[i].value, token[i].FuncPtr);
//printf("%s\t\t%s\t\t%f\t\t%x\n", enumoutput(int(token[i].type)), token[i].lexeme, token[i].value, token[i].FuncPtr);
i++;
}
printf("\n");
}
Token judgetoken(const char *IDString)//??
{
int len;
for (len = 0; len < sizeof(TokenTab) / sizeof(TokenTab[0]); len++)
{
if (strcmp(TokenTab[len].lexeme, IDString) == 0)
return TokenTab[len];
}
Token p;
memset(&p, 0, sizeof(Token));
p.type = ERRTOKEN;
return p;
}
int work()
{
int length = strlen(source);
int i = 0 , j , k = 0 , kuohaoflag = 0 ;
int token_len = 0;
for ( ; i < length ; i++)
{
if (isalpha(source[i]))//判断是否为英文字母
{
k = 0;//初始化k
j = i;
loop1:
TokenBuf[k] = source[j];
j++;
k++;
if (isalnum(source[j]))//如果下一个是字母或者数字
{
goto loop1;
}
else {
}
//判断是否token的类别
token[token_len] = judgetoken(TokenBuf);
//赋值token的类型与属性
token_len++;
memset(TokenBuf, 0x00, k);//字符串数组中所有元素全部设置为\0即可
i = j - 1;//跳过中间那些字母或数字,进入其他符号(比如+-*/空格)
}
else if (isdigit(source[i]))//若是一个数字,则一定是常量
{
j = i;
k = 0;
TokenBuf[k] = source[i];
k++;
while (1)
{
j++;
if (isdigit(source[j]))
{
TokenBuf[k] = source[j];
k++;
}
else break;
}
if (source[j] == '.')//如果是小数
{
TokenBuf[k] = source[j];//‘.’进入记号字符缓冲区
k++;
j++;
if (isdigit(source[j]))//小数点后仍是数字
{
TokenBuf[k] = source[j];
k++;
while (1)
{
j++;
if (isdigit(source[j]))
{
TokenBuf[k] = source[j];
k++;
}
else break;
}
}
else {
//小数点后不是数字,非法输入
i = j - 1;
goto loop2;
}
}
//把数字字符串转化为double
strcpy(token[token_len].lexeme, TokenBuf);
token[token_len].type = CONST_ID;
token[token_len].value = atof(TokenBuf);//字符串转化为double
token_len++;
memset(TokenBuf, 0x00, k);//字符串数组中所有元素全部设置为\0即可
i = j - 1;
}
else {//不是字母和数字,则一定是运算符或分隔符
switch (source[i])
{
case ';':
token[token_len].type = SEMICO;
strcpy(token[token_len].lexeme, ";");
token_len++;
break;
case ',':
token[token_len].type = COMMA;
strcpy(token[token_len].lexeme, ",");
token_len++;
break;
case '+':
token[token_len].type = PLUS;
strcpy(token[token_len].lexeme, "+");
token_len++;
break;
case '-':
token[token_len].type = MINUS;
strcpy(token[token_len].lexeme, "-");
token_len++;
break;
case '*':
if (source[i + 1] == '*')
{
token[token_len].type = POWER;
strcpy(token[token_len].lexeme, "**");
token_len++;
i++;
}
else {
token[token_len].type = MUL;
strcpy(token[token_len].lexeme, "*");
token_len++;
}
break;
case '/':
token[token_len].type = DIV;
strcpy(token[token_len].lexeme, "/");
token_len++;
break;
case '(':
token[token_len].type = L_BRACKET;
strcpy(token[token_len].lexeme, "(");
token_len++;
kuohaoflag++;
break;
case ')':
token[token_len].type = R_BRACKET;
strcpy(token[token_len].lexeme, ")");
token_len++;
kuohaoflag--;
break;
case ' ':
break;
case '\n':
break;
default :
loop2:
token[token_len].type = ERRTOKEN;
strcpy(token[token_len].lexeme, "ERRTOKEN");
token_len++;
break;
}
}
}
//最后面代码分析结束时加入程序结束的空记号
token[token_len].type = NONTOKEN;
strcpy(token[token_len].lexeme, "NONTOKEN");
token_len++;
return token_len;
}
int main()
{
//int length;
Infile();
int i=work();
output(i);
}