如果你要读诗,那么就要读自己的诗
:创造是极客唯一的属性
一、基本思路
语义分析是绘图语言的最后一步了。
语义分析器的核心:根据语义规则产生语义动作。
根据语法制导的基本思想,语义分析可以在语法分析过程得到实现。
对坐标设置语句(三种),语法分析识别通过之后,立刻计算表达式的值,并据此改写相应的全局变量。
对绘图语句(一种),语法分析识别通过之后,根据相应全局变量的设定值,立刻调用绘图函数绘制图形。
说明:由于本项目没有采用面向对象的设计方法,所以语义分析和语法分析是耦合在一起的,建议有兴趣的同学最好采用类来编程。
二、代码实现
语义分析头文件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");
}
三、运行结果
源文件内容
这里要看仔细些,因为点设定的是红色,所以不是特别明显。