简单函数绘图语言解释器:语义分析器兼主程序

如果你要读诗,那么就要读自己的诗

:创造是极客唯一的属性

一、基本思路

语义分析是绘图语言的最后一步了。

语义分析器的核心:根据语义规则产生语义动作。
根据语法制导的基本思想,语义分析可以在语法分析过程得到实现。
对坐标设置语句(三种),语法分析识别通过之后,立刻计算表达式的值,并据此改写相应的全局变量。
对绘图语句(一种),语法分析识别通过之后,根据相应全局变量的设定值,立刻调用绘图函数绘制图形。

说明:由于本项目没有采用面向对象的设计方法,所以语义分析和语法分析是耦合在一起的,建议有兴趣的同学最好采用类来编程。

二、代码实现

语义分析头文件semantic.h

#pragma once
#ifndef SEMANTIC_H
#define SEMANTIC_H
#include"scanner.h"
#include<graphics.h>
#include<conio.h>


/*语法制导翻译的中心思想是:
识别一条合法的语句,立刻对其翻译,
故需要向语法分析器加入语义分析的相关语句和函数
*/

//全局变量区
extern struct Token token;

extern double Parameter;
extern double Origin_x , Origin_y ;
extern double Rot_ang;
extern double Scale_x , Scale_y ;

//数据结构区
typedef double(*FuncPtr)(double);

// 表达式节点
struct ExprNode
{
	enum Token_Type OpCode;//记号种类
	union {
		struct {
			struct ExprNode *Left, *Right;
		}CaseOperator;//二元运算

		struct {
			struct ExprNode *Child;
			FuncPtr MathFuncPtr;
		}CaseFunc;//函数调用

		double CaseConst;//常数,绑定右值
		double *CaseParmPtr;//参数T,绑定左值
	}Content;
};


//函数区
void Semantic(char *SrcFilePtr);//对语法分析器改进后的语义分析主程序,可以理解为解释器

//主函数:产生式(语句级)逻辑区
void Program();
void Statement();
void OriginStatment();
void RotStatement();
void ScaleStatment();
void ForStatement();

//主函数:产生式(表达式级)逻辑区
struct ExprNode * Expression();
struct ExprNode * Term();
struct ExprNode * Factor();
struct ExprNode * Component();
struct ExprNode * Atom();

//构建语法树
struct ExprNode * MakeExprNode(enum Token_Type opcode,...);

//语法分析辅助函数
void FetchToken();
void MatchToken(enum Token_Type AToken);
void SyntaxError(int case_of);

//语义分析辅助函数

//计算表达式的值
double GetExprValue(struct ExprNode *root);
//计算点的实际坐标值:获取原始坐标值,然后坐标变换(平移、尺度变换、角度变换)
void CalcCoord(struct ExprNode * x_nptr, struct ExprNode *y_nptr);
//绘制一个点
void DrawPixel(unsigned long x, unsigned long y);
//绘制图像:循环绘制由参数T指定的所有点
void DrawLoop(double Start, double End, double Step, struct ExprNode *HorPtr, struct ExprNode *VerPtr);

#endif

语义分析实现文件semantic.c

#include"semantic.h"

//全局变量区
struct Token token;


 double Parameter = 0;
 double Origin_x = 0.0, Origin_y = 0.0;
 double Rot_ang = 0.0;
 double Scale_x = 1, Scale_y = 1;
 double Draw_x, Draw_y;

//函数区
void Semantic(char *SrcFilePtr) {
	/*语法分析主程序:
	调用词法分析器的GetToken函数(封装在FetchToken中)返回记号,
	 然后使用核心产生式program()对记号流进行递归下降分析,
	 判断记号流的结构是否符合文法规则
	*/
	if (!initScanner(SrcFilePtr)) {
		printf("open Source File Error!\n");
		return;
	}
	FetchToken();//返回的记号存放在全局变量token中
	Program();//递归下降的核心产生式
	closeScanner();
}

//主函数:产生式(语句级)逻辑区
void Program() {
	while (token.type != NONTOKEN) {//词法分析器输出NONTOKEN表示已达记号流末尾
		Statement();//匹配一条语句
		MatchToken(SEMICO);
	}
}

void Statement() {
	switch (token.type)
	{
	case ORIGIN:OriginStatment(); break;
	case ROT:RotStatement(); break;
	case SCALE:ScaleStatment(); break;
	case FOR:ForStatement(); break;
	default:SyntaxError(2);
	}
}

void OriginStatment() {
	struct ExprNode *origin_x, *origin_y;
	MatchToken(ORIGIN); MatchToken(IS); MatchToken(L_BRACKET);
	origin_x = Expression(); MatchToken(COMMA);
	origin_y = Expression(); MatchToken(R_BRACKET);

	Origin_x = GetExprValue(origin_x);
	Origin_y = GetExprValue(origin_y);
}

void RotStatement() {
	struct ExprNode *rotate;
	MatchToken(ROT); MatchToken(IS); 
	rotate = Expression();

	Rot_ang = GetExprValue(rotate);
}

void ScaleStatment() {
	struct ExprNode *scale_x, *scale_y;
	MatchToken(SCALE); MatchToken(IS); MatchToken(L_BRACKET);
	scale_x = Expression(); MatchToken(COMMA);
	scale_y = Expression(); MatchToken(R_BRACKET);

	Scale_x = GetExprValue(scale_x);
	Scale_y = GetExprValue(scale_y);
}

void ForStatement() {
	struct ExprNode *start_ptr, *end_ptr, *step_ptr, *x_ptr, *y_ptr;

	MatchToken(FOR); MatchToken(T); MatchToken(FROM); start_ptr = Expression();
	MatchToken(TO); end_ptr = Expression();
	MatchToken(STEP); step_ptr = Expression();
	MatchToken(DRAW); MatchToken(L_BRACKET); x_ptr = Expression();
	MatchToken(COMMA); y_ptr = Expression();
	MatchToken(R_BRACKET);

	DrawLoop(GetExprValue(start_ptr), GetExprValue(end_ptr), GetExprValue(step_ptr),
		x_ptr,y_ptr);
}

//主函数:产生式(表达式级)逻辑区
struct ExprNode * Expression() {
	struct ExprNode *left, *right;
	enum Token_Type token_tmp;
	left = Term();
	while (token.type == PLUS || token.type == MINUS) {
		token_tmp = token.type;
		MatchToken(token_tmp);
		right = Term();
		left = MakeExprNode(token_tmp, left, right);
	}
	return left;
}

struct ExprNode * Term() {
	struct ExprNode *left, *right;
	enum Token_Type token_tmp;

	left = Factor();
	while (token.type == MUL || token.type == DIV) {
		token_tmp = token.type;
		MatchToken(token_tmp);
		right = Factor();
		left = MakeExprNode(token_tmp, left, right);
	}
	return left;
}

struct ExprNode * Factor() {
	struct ExprNode * factor_Node;
	enum Token_Type token_tmp;

	if (token.type == PLUS || token.type == MINUS) {
		struct ExprNode *ConstPtr = (struct ExprNode *)malloc(sizeof(struct ExprNode));
		ConstPtr->Content.CaseConst = 0;
		ConstPtr->OpCode = CONST_ID;

		token_tmp = token.type;
		MatchToken(token.type);

		factor_Node = Factor();
		factor_Node = MakeExprNode(token_tmp, ConstPtr, factor_Node);
	}
	else
		factor_Node = Component();

	return factor_Node;
}

struct ExprNode * Component() {
	struct ExprNode * component_Node;
	enum Token_Type token_tmp;

	component_Node = Atom();

	if (token.type == POWER) {
		struct ExprNode * component_Node_another;
		token_tmp = token.type;
		MatchToken(POWER);
		component_Node_another = Component();

		component_Node = MakeExprNode(token_tmp, component_Node, component_Node_another);
	}
	return component_Node;
}

struct ExprNode * Atom() {
	struct ExprNode *atom_Node;

	switch (token.type) {
	case CONST_ID: 
		 atom_Node = MakeExprNode(token.type, token.value); MatchToken(token.type); break;
	case T:  
		 atom_Node = MakeExprNode(token.type); MatchToken(token.type); break;
	case FUNC:  
		struct Token token_tmp = token;
		MatchToken(token.type); MatchToken(L_BRACKET); atom_Node=MakeExprNode(FUNC,token_tmp.FuncPtr,Expression());
		MatchToken(R_BRACKET); break;
	case L_BRACKET: 
		MatchToken(token.type); atom_Node = Expression(); MatchToken(R_BRACKET); break;
	default:SyntaxError(3);
	}
	return atom_Node;
}

//构建语法树
struct ExprNode * MakeExprNode(enum Token_Type opcode, ...) {//此函数参数可变
	struct ExprNode *ExprPtr = (struct ExprNode *)malloc(sizeof(struct ExprNode));
	va_list ArgPtr;
	ExprPtr->OpCode = opcode;
	va_start(ArgPtr, opcode);

	switch (opcode) {
	case CONST_ID://常数节点
		ExprPtr->Content.CaseConst = (double)va_arg(ArgPtr,double); break;
	case T://参数节点
		ExprPtr->Content.CaseParmPtr = &Parameter; break;
	case FUNC://函数节点
		ExprPtr->Content.CaseFunc.MathFuncPtr = (FuncPtr)va_arg(ArgPtr,FuncPtr);
		ExprPtr->Content.CaseFunc.Child = (struct ExprNode *)va_arg(ArgPtr,struct ExprNode *); break;
	default://二元运算节点
		ExprPtr->Content.CaseOperator.Left = (struct ExprNode *)va_arg(ArgPtr, struct ExprNode *);
		ExprPtr->Content.CaseOperator.Right = (struct ExprNode *)va_arg(ArgPtr, struct ExprNode *);
	}
	return ExprPtr;
}

//辅助函数
void FetchToken() {
	token = GetToken();
	if (token.type == ERRTOKEN)
		SyntaxError(1);
}

void MatchToken(enum Token_Type AToken) {//判断全局变量token当前类型值是否与AToken匹配
	if (token.type != AToken)
		SyntaxError(4);
	else {
		//printf("Match Token %s\n", token.lexeme);//测试语句
		FetchToken();
	}
}

void SyntaxError(int case_of) {//错误处理函数
	switch (case_of) {
	case 1://由FetchToken函数抛出:词法分析获取的记号类型为ERRTOKEN,即不构成正确的单词
		printf("[*]Error code %d: Incorrect word:%s\n", case_of,token.lexeme);
		break;
	case 2://由Statement函数抛出:不符合文法定义,句子开头的Token不正确
		printf("[*]Error code %d:Incorrect start of sentence:%s\n", case_of,token.lexeme);
		break;
	case 3://由Atom函数抛出:不符合文法定义,表达式不正确
		printf("[*]Error code %d:Incorrect Expression:%s\n", case_of, token.lexeme);
		break;
	case 4://由MatchToken函数抛出:不符合文法定义,语句错误(保留字、标点符等匹配失败)
		printf("[*]Error code %d:Incorrect Match:%s\n", case_of, token.lexeme);
		break;
	}
	system("pause");
	exit(0);//程序终止
}


//语义分析辅助函数

//计算表达式的值
double GetExprValue(struct ExprNode *root) {
	double value;
	switch (root->OpCode) {
	case CONST_ID:value = root->Content.CaseConst; break;
	case T:value = Parameter; break;
	case FUNC:value = ((FuncPtr)root->Content.CaseFunc.MathFuncPtr)(GetExprValue(root->Content.CaseFunc.Child)); break;

	default:
		switch (root->OpCode) {
		case PLUS:value = GetExprValue(root->Content.CaseOperator.Left) + GetExprValue(root->Content.CaseOperator.Right); break;
		case MINUS:value = GetExprValue(root->Content.CaseOperator.Left) - GetExprValue(root->Content.CaseOperator.Right); break;
		case MUL:value = GetExprValue(root->Content.CaseOperator.Left) * GetExprValue(root->Content.CaseOperator.Right); break;
		case DIV:value = GetExprValue(root->Content.CaseOperator.Left) / GetExprValue(root->Content.CaseOperator.Right); break;
		case POWER:value =pow(GetExprValue(root->Content.CaseOperator.Left),GetExprValue(root->Content.CaseOperator.Right)); break;
		}
	}
	return value;
}

//计算点的实际坐标值:获取原始坐标值,然后坐标变换(平移、尺度变换、角度变换)
void CalcCoord(struct ExprNode * x_nptr, struct ExprNode *y_nptr) {
	double x, y,temp;
	//获取原始坐标值
	x = GetExprValue(x_nptr);y = GetExprValue(y_nptr);

	//坐标变换
	x *= Scale_x; y *= Scale_y;

	temp = x * cos(Rot_ang) + y * sin(Rot_ang);
	y = y * cos(Rot_ang) - x * sin(Rot_ang); x = temp;

	x += Origin_x; y += Origin_y;

	//返回变换坐标值
	Draw_x = x; Draw_y = y;
}

//绘制一个点
void DrawPixel(unsigned long x, unsigned long y) {
	putpixel(x, y, RED);
}
//绘制图像:循环绘制由参数T指定的所有点
void DrawLoop(double Start, double End, double Step, struct ExprNode *HorPtr, struct ExprNode *VerPtr) {
	initgraph(640, 480);
	for (Parameter = Start; Parameter <= End; Parameter += Step) {
		CalcCoord(HorPtr, VerPtr);
		putpixel(Draw_x,Draw_y,RED);
	}
	_getch();
	closegraph();
}

解释器主程序

#include"semantic.h"

void main(int argc,char *argv[]) {
	/*char source[100];
	printf("please input the source fileName:");
	scanf("%s", source);
	*/
	Semantic(argv[1]);
	//system("pause");
}

三、运行结果

源文件内容
在这里插入图片描述
在这里插入图片描述
这里要看仔细些,因为点设定的是红色,所以不是特别明显。

扫描二维码关注公众号,回复: 8727009 查看本文章
发布了24 篇原创文章 · 获赞 19 · 访问量 6892

猜你喜欢

转载自blog.csdn.net/tch3430493902/article/details/103243234