题目
为函数绘图语言编写一个解释器,解释器接受用绘图语言编写的源程序,经语法和语义分析之后,将源程序所规定的图形显示在显示屏(或窗口)中。
语法和语义
- 循环绘图(FOR-DRAW)
- 比例设置(SCALE)
- 角度旋转(ROT)
- 坐标平移(ORIGIN)
- 注释 (-- 或 //)
循环绘图
- 语法
FOR T FROM 起点 TO 终点 STEP 步长 DRAW(横坐标, 纵坐标); - 语义
令T从起点到终点、每次改变一个步长,绘制出由(横坐标,纵坐标)所规定的点的轨迹。 - 举例
FOR T FROM 0 TO 2*PI STEP PI/50 DRAW (cos(T), sin(T)); - 说明
该语句的作用是令T从0到2*PI、步长 PI/50,绘制出各个点的坐标(cos(T),sin(T)),即一个单位圆。 - 注意
由于绘图系统的默认值是
ORIGIN IS (0,0);
ROT IS 0;
SCALE IS (1, 1);
所以实际绘制出的图形是在屏幕左上角的一个点。
比例设置
- 语法
SCALE IS (横坐标比例因子,纵坐标比例因子); - 语义
设置横坐标和纵坐标的比例,并分别按照比例因子进行缩放。
角度旋转
- 语法
ROT IS 角度; - 语义
逆时针旋转角度所规定的弧度值。具体计算公式:
旋转后X=旋转前XCOS(角度)+旋转前YSIN(角度)
旋转后Y=旋转前YCOS(角度)-旋转前XSIN(角度)
坐标平移
- 语法
ORIGIN IS (横坐标,纵坐标); - 语义
将坐标系的原点平移到横坐标和纵坐标规定的点处。
注释
- 语法
// This is a comment line
或
– 此行是注释 - 语义
// 或 – 之后,直到行尾,均是注释
代码
C语言
主函数
/****************************************************************************************\
*
* <1> 各类语句可以按任意次序书写,且语句以分号结尾。源程序中的语句以它们出现的先后顺序处理。
* <2> ORIGIN、ROT和SCALE 语句只影响其后的绘图语句,且遵循最后出现的语句有效的原则。
* 例如,若有下述ROT语句序列: ROT IS 0.7 ;ROT IS 1.57 ;
* 则随后的绘图语句将按1.57而不是0.7弧度旋转。
* <3> 无论ORIGIN、ROT和SCALE语句的出现顺序如何,图形的变换顺序总是:比例变换→旋转变换→平移变换
* <4> 语言对大小写不敏感,例如for、For、FOR等,均被认为是同一个保留字。
* <5> 语句中表达式的值均为双精度类型,旋转角度单位为弧度且为逆时针旋转,平移单位为点。
*
*
\****************************************************************************************/
#include "CMPL_H.h"
extern struct Token token;
int main(int argc, char **argv)
{
InitScanner(argv[1]);
static TCHAR szClassName[] = TEXT("Drawbmp");
HINSTANCE hInstance; HWND hwnd;
MSG msg; WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
// wndclass.hbrBackground= (HBRUSH)(COLOR_WINDOW+1); //
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szClassName;
RegisterClass(&wndclass);
hwnd = CreateWindow(
szClassName,
TEXT("Good Job!"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
980, //窗口宽度
720, //窗口高度
NULL, //父窗口句柄
NULL, //窗口菜单句柄
hInstance,
NULL
);
switch (argc) {
case 1:
MessageBox(hwnd, TEXT("you need to drag txtfile on this program."),TEXT("Usage"),MB_OK | MB_ICONWARNING);
return 0;
// help();
// break;
case 2:
break;
case 3:
// InitScanner(argv[1]);
if (!strcmp(argv[2], "-w")) {
PrintToken();
}
else if (!strcmp(argv[2], "-s")) {
ExpressionTest();
}
else {
help();
}
break;
default:
help();
}
ShowWindow (hwnd, 1); UpdateWindow (hwnd);
while( GetMessage(&msg, NULL, 0, 0) ){
TranslateMessage(&msg);
DispatchMessage (&msg);
}
CloseScanner();
return 0;
}
功能函数
#include "CMPL_H.h"
struct Token token;
static int line = 1;
static char TokenBuf[20] = {0};
double Parameter=0; // 为参数T分配的变量
double Origin_x=0.0, Origin_y=0.0;// 用于记录平移距离
double Rot_ang=0.0; // 用于记录旋转角度
double Scale_x=1, Scale_y=1; // 用于记录比例因子
HDC hdc;
static FILE *fp = NULL;
static char * TTP[] = {
"ORIGIN","SCALE","ROT", "IS", // 保留字(一字一码)
"TO", "STEP", "DRAW","FOR", "FROM", // 保留字
"T", // 参数
";", "(", ")", ",", // 分隔符
"+", "-", "*", "/", "**", // 运算符
"FUNC", // 函数(调用)
"NUMBER", // 常数
"ERRTOKEN",
};
static struct Token TokenTab[18] =
{
{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}
};
void PrintToken()
{
if (fp) {
rewind(fp);
TCHAR prtokn[50];
HANDLE hdlWrite;
AllocConsole();
hdlWrite = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsole(hdlWrite, TEXT("type string value pointer \n"), 49, NULL, NULL);
WriteConsole(hdlWrite, TEXT("-----------------------------------------------\n"), 49, NULL, NULL);
while (1) {
token = GetToken();
if (token.type != NONTOKEN) {
sprintf(prtokn, "%4d %12s %12f %12p\n", token.type, token.lexeme, token.value, token.Fp);
WriteConsole(hdlWrite, prtokn, 45, NULL, NULL);
}
else break;
}
WriteConsole(hdlWrite, TEXT("-----------------------------------------------\n"), 49, NULL, NULL);
CloseHandle(hdlWrite);
}
}
void Parser()
{
if (fp) {
rewind(fp);
FetchToken(); // 获取第一个记号
Program(); // 递归下降分析
}
}
void ExpressionTest()
{
if (fp) {
rewind(fp);
HANDLE hdlWrite;
AllocConsole();
hdlWrite = GetStdHandle(STD_OUTPUT_HANDLE);
struct ExprNode *x_ptr;
double x;
FetchToken();
while (token.type != NONTOKEN) {
x_ptr = Expression();
}
PrintSyntaxTree(hdlWrite, x_ptr, 0);
// x = GetExprValue(x_ptr);
// printf("x = %f ", x);-
CloseHandle(hdlWrite);
}
}
void PrintSyntaxTree(HANDLE hdlWrite, struct ExprNode *root, int indent)
{
char prtokn[10];
int i = indent; int len = 8;
while (i-- > 0) {
WriteConsole(hdlWrite, TEXT(" "), 1, NULL, NULL);
}
if (root) {
switch (root->OpCode) {
case CONST_ID:
sprintf(prtokn, " %f\n", root->Content.CaseConst);
WriteConsole(hdlWrite, prtokn, len, NULL, NULL);
// printf(" %f\n",root->Content.CaseConst);
break;
case T:
WriteConsole(hdlWrite, TEXT(" T\n"), len, NULL, NULL);
// printf(" T\n");
break;
case FUNC:
sprintf(prtokn, " %p\n", root->Content.CaseFunc.MathFuncPtr);
WriteConsole(hdlWrite, prtokn, len, NULL, NULL);
// printf(" %p\n",root->Content.CaseFunc.MathFuncPtr);
PrintSyntaxTree(hdlWrite, root->Content.CaseFunc.Child, 1+indent);
break;
default:
WriteConsole(hdlWrite, TTP[root->OpCode], len, NULL, NULL);
// printf(" %s\n",TTP[root->OpCode]);
PrintSyntaxTree(hdlWrite, root->Content.CaseOperator.Left, 1+indent);
PrintSyntaxTree(hdlWrite, root->Content.CaseOperator.Right, 1+indent);
}
}
}
int InitScanner(char *fname)
{
fp = fopen(fname, "r");
if (fp) { return 1; }
else { fclose(fp);printf("Open Source File Error ! \n"); return 0; }
}
void CloseScanner()
{
fclose(fp);
}
static struct Token GetToken()
{
struct Token token = {ERRTOKEN, NULL, 0.0, NULL};
token.lexeme = TokenBuf;
char ch;
int i=0, j;
while ((ch = fgetc(fp)) != EOF){
if (isalpha(ch)) {
TokenBuf[i++] = toupper(ch);
if (!isalpha(fgetc(fp))) {
if (!feof(fp)) {
fseek(fp, -1L, 1);
}
TokenBuf[i] = '\0';
for (j = 0; j < 18; j++) {
if (!strcmp(TokenBuf, TokenTab[j].lexeme)){
return TokenTab[j];
}
}
token.type = T;
return token;
}
else {
if (!feof(fp)) {
fseek(fp, -1L, 1);
}
}
}
else if (isdigit(ch)) {
TokenBuf[i++] = ch;
if (!isdigit(ch=fgetc(fp)) && ch != '.') {
if (!feof(fp)) {
fseek(fp, -1L, 1);
}
TokenBuf[i] = '\0';
token.value = atof(TokenBuf);
token.type = CONST_ID;
return token;
}
else {
if (!feof(fp)) {
fseek(fp, -1L, 1);
}
}
}
else {
switch (ch) {
case ';':
token.type = SEMICO;
TokenBuf[i++] = ch;
TokenBuf[i] = '\0';
return token;
case '+':
token.type = PLUS;
TokenBuf[i++] = ch;
TokenBuf[i] = '\0';
return token;
case ',':
token.type = COMMA;
TokenBuf[i++] = ch;
TokenBuf[i] = '\0';
return token;
case '(':
token.type = L_BRACKET;
TokenBuf[i++] = ch;
TokenBuf[i] = '\0';
return token;
case ')':
token.type = R_BRACKET;
TokenBuf[i++] = ch;
TokenBuf[i] = '\0';
return token;
case '.':
if(isdigit(fgetc(fp)) && isdigit(TokenBuf[i-1])) {
fseek(fp, -1L, 1);
TokenBuf[i++] = ch;
break;
}
else {
fseek(fp, -1L, 1);
TokenBuf[i++] = ch;
TokenBuf[i] = '\0';
return token;
}
case '*':
if (fgetc(fp) == '*') {
token.type = POWER;
TokenBuf[i++] = ch;
TokenBuf[i++] = ch;
TokenBuf[i] = '\0';
return token;
}
else {
if (!feof(fp)) {
fseek(fp, -1L, 1);
}
token.type = MUL;
TokenBuf[i++] = ch;
TokenBuf[i] = '\0';
return token;
}
case '-':
if (fgetc(fp) == '-') {
while ((ch = fgetc(fp)) != '\n' && ch != EOF);
line++;
break;
}
else {
if (!feof(fp)) {
fseek(fp, -1L, 1);
}
token.type = MINUS;
TokenBuf[i++] = ch;
TokenBuf[i] = '\0';
return token;
}
case '/':
if (fgetc(fp) == '/') {
while ((ch = fgetc(fp)) != '\n' && ch != EOF);
line++;
break;
}
else {
if (!feof(fp)) {
fseek(fp, -1L, 1);
}
token.type = DIV;
TokenBuf[i++] = ch;
TokenBuf[i] = '\0';
return token;
}
case '\n': line++;
case '\t':
case ' ':
break;
default : return token;
}
}
}
token.type = NONTOKEN;
return token;
}
static void FetchToken()
{
token = GetToken();
if (token.type == ERRTOKEN) SyntaxError(1);
}
static void SyntaxError (int case_of)
{
printf("[SYNTAXERR]line %d: %4d %12s %12f %12x\n",
line, token.type, token.lexeme, token.value, token.Fp);
// exit(1);
}
static void Program()
{
while (token.type != NONTOKEN) {
Statement();
MatchToken(SEMICO);
}
}
static void Statement(void)
{
switch (token.type){
case ORIGIN:
OriginStatement();
break;
case ROT:
RotStatement();
break;
case SCALE:
ScaleStatement();
break;
case FOR:
ForStatement();
break;
default:
SyntaxError(1);
}
}
static void MatchToken(enum Token_Type AToken)
{
if (AToken == token.type) {
FetchToken();
// printf("matchtoken %12s\n",token.type==FUNC || token.type==T ?token.lexeme : TTP[token.type]);
}
else {
printf("[ERROR]line %d: expected '%s' before '%s' token.\n", line, TTP[AToken],
token.type==FUNC || token.type==T ?token.lexeme : TTP[token.type]);
FetchToken();
}
}
static void OriginStatement(void)
{
struct ExprNode *x_ptr, *y_ptr;
double x, y;
MatchToken (ORIGIN);
MatchToken (IS);
MatchToken (L_BRACKET); x_ptr = Expression(); //PrintSyntaxTree(x_ptr, 0);
x = GetExprValue(x_ptr); // 获取横坐标的平移值
MatchToken (COMMA); y_ptr = Expression();
y = GetExprValue(y_ptr); // 获取纵坐标的平移值
MatchToken (R_BRACKET);
SetOrigin(x, y); // 置坐标平移全程量
}
static void RotStatement(void)
{
double rot;
struct ExprNode *r_ptr;
MatchToken (ROT);
MatchToken (IS);
r_ptr = Expression(); rot = GetExprValue(r_ptr);
SetRot(rot);
}
static void ScaleStatement(void)
{
double x, y;
struct ExprNode *x_ptr, *y_ptr;
MatchToken (SCALE);
MatchToken (IS);
MatchToken (L_BRACKET); x_ptr = Expression();
x = GetExprValue(x_ptr);
MatchToken (COMMA); y_ptr = Expression();
y = GetExprValue(y_ptr);
MatchToken (R_BRACKET);
SetScale(x, y);
}
static void ForStatement(void)
{
double Start, End, Step;
struct ExprNode *start_ptr, *end_ptr, *step_ptr,
*x_ptr, *y_ptr;
MatchToken (FOR); MatchToken(T);
MatchToken (FROM); start_ptr = Expression();
Start = GetExprValue(start_ptr);
MatchToken (TO); end_ptr = Expression();
End = GetExprValue(end_ptr);
MatchToken (STEP); step_ptr = Expression();
Step = GetExprValue(step_ptr);
MatchToken (DRAW);
MatchToken (L_BRACKET); x_ptr = Expression();
MatchToken (COMMA); y_ptr = Expression();
MatchToken (R_BRACKET);
DrawLoop (Start, End, Step, x_ptr, y_ptr);
}
static 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;
}
static 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;
}
static struct ExprNode * Factor()
{
struct ExprNode *left, *right;
enum Token_Type token_tmp;
token_tmp = token.type;
switch (token_tmp) {
case PLUS:
MatchToken(token_tmp);
left = Factor();
break;
case MINUS:
left = MakeExprNode(CONST_ID, 0.0);
MatchToken(token_tmp);
right = Factor();
left = MakeExprNode(token_tmp,left,right);
break;
default :
left = Component();
}
return left;
}
static struct ExprNode *Component()
{
struct ExprNode *left, *right;
enum Token_Type token_tmp;
left = Atom();
while (token.type == POWER) {
token_tmp = token.type;
MatchToken(token_tmp);
right =Component();
left = MakeExprNode(token_tmp,left,right);
}
return left;
}
static struct ExprNode *Atom()
{
struct ExprNode *left;
FuncPtr fup_tmp = token.Fp;
switch (token.type) {
case CONST_ID:
left = MakeExprNode(CONST_ID,token.value);
MatchToken(CONST_ID);
break;
case T:
left = MakeExprNode(T);
MatchToken(T);
break;
case FUNC:
MatchToken(FUNC);
MatchToken(L_BRACKET);
left = Expression();
left = MakeExprNode(FUNC, fup_tmp, left);
MatchToken(R_BRACKET);
break;
case L_BRACKET:
MatchToken(L_BRACKET);
left = Expression();
MatchToken(R_BRACKET);
break;
default:
printf("[ERROR]line %d: expected %s before '%s' token.\n", line, "Expression", TTP[token.type]);
return NULL;
// exit(1);
}
return left;
}
static struct ExprNode * MakeExprNode(enum Token_Type opcode, ...)
{
va_list ap;
va_start(ap, opcode);
struct ExprNode *ExprPtr = (struct ExprNode *)malloc(sizeof(struct ExprNode));
ExprPtr->OpCode = opcode;
switch(opcode)
{
case CONST_ID: // 常数节点
ExprPtr->Content.CaseConst = (double)va_arg(ap, double);
break;
case T: // 参数节点
ExprPtr->Content.CaseParmPtr = &Parameter;
break;
case FUNC: // 函数调用节点
ExprPtr->Content.CaseFunc.MathFuncPtr = (FuncPtr)va_arg(ap, FuncPtr);
ExprPtr->Content.CaseFunc.Child = (struct ExprNode *)va_arg(ap, struct ExprNode *);
break;
default: // 二元运算节点
ExprPtr->Content.CaseOperator.Left = (struct ExprNode *)va_arg(ap, struct ExprNode *);
ExprPtr->Content.CaseOperator.Right = (struct ExprNode *)va_arg(ap, struct ExprNode *);
break;
}
va_end(ap);
return ExprPtr;
}
static double GetExprValue(struct ExprNode * root)
{
if (root) {
switch (root -> OpCode) {
case PLUS :
return GetExprValue(root->Content.CaseOperator.Left )
+ GetExprValue(root->Content.CaseOperator.Right);
case MINUS :
return GetExprValue(root->Content.CaseOperator.Left )
- GetExprValue(root->Content.CaseOperator.Right);
case MUL :
return GetExprValue(root->Content.CaseOperator.Left )
* GetExprValue(root->Content.CaseOperator.Right);
case DIV :
return GetExprValue(root->Content.CaseOperator.Left )
/ GetExprValue(root->Content.CaseOperator.Right);
case POWER :
return pow( GetExprValue(root->Content.CaseOperator.Left),
GetExprValue(root->Content.CaseOperator.Right) );
case FUNC :
return (* root->Content.CaseFunc.MathFuncPtr)
( GetExprValue(root->Content.CaseFunc.Child) );
case CONST_ID :
return root->Content.CaseConst ;
case T :
return *(root->Content.CaseParmPtr);
default :
return 0.0 ;
}
}
return 0.0;
}
static void CalcCoord(struct ExprNode *x_nptr, struct ExprNode *y_nptr, double *x_val, double *y_val)
{
double local_x, local_y, temp;
local_x = GetExprValue(x_nptr); // 计算点的原始坐标
local_y = GetExprValue(y_nptr); //printf("\nori%f,%f\n", local_x, local_y);
local_x *= Scale_x; // 比例变换
local_y *= Scale_y; //printf("\nsca%f,%f\n", local_x, local_y);
temp = local_x*cos(Rot_ang)+local_y*sin(Rot_ang);
local_y = local_y*cos(Rot_ang)-local_x*sin(Rot_ang); //printf("\nrot%f\n", Rot_ang);
local_x = temp; // 旋转变换
local_x += Origin_x; // 平移变换
local_y += Origin_y;
*x_val = local_x; // 返回变换后点的坐标
*y_val = local_y;
}
static void DrawPixel(unsigned long x, unsigned long y)
{
// printf("\ndraw%u,%u\n", x, y);
SetPixel(hdc, x, y, RGB(0, 0, 255));
}
static void DrawLoop(double Start, double End, double Step, struct ExprNode *HorPtr, struct ExprNode *VerPtr)
{
double x, y;
for (Parameter=Start; Parameter<=End; Parameter+=Step) {
CalcCoord(HorPtr, VerPtr, &x, &y); // 计算实际坐标
DrawPixel((unsigned long)x, (unsigned long)y); // 根据坐标绘制点
}
}
static void SetOrigin(double x, double y)
{
Origin_x = x;
Origin_y = y;
}
static void SetScale(double x, double y)
{
Scale_x = x;
Scale_y = y;
}
static void SetRot(double r)
{
Rot_ang = r;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
RECT rect;
switch (message){
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
GetClientRect (hwnd, &rect) ;
// Rectangle(hdc,10,10,100,100);
// MessageBox(hwnd, TEXT("you need to drag txtfile on this program."),TEXT("Usage"),MB_OK | MB_ICONWARNING);
Parser();
EndPaint (hwnd, &ps) ;
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam) ;
}
return 0;
}
void help(void)
{
int len = 58;
HANDLE hdlWrite;
AllocConsole();
hdlWrite = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsole(hdlWrite, TEXT("*******************************************************\n"),len, NULL, NULL);
WriteConsole(hdlWrite, TEXT(" Usage : draw filename.txt [-w|-s] \n"),len, NULL, NULL);
WriteConsole(hdlWrite, TEXT(" OR drag txtfile on this program directly \n\n"),len, NULL, NULL);
WriteConsole(hdlWrite, TEXT(" -w: Scan and Print tokens. \n"),len, NULL, NULL);
WriteConsole(hdlWrite, TEXT(" -s: Print Syntax Tree \n"),len, NULL, NULL);
WriteConsole(hdlWrite, TEXT("default: draw your image. \n"),len, NULL, NULL);
WriteConsole(hdlWrite, TEXT("\n******************************************************\n"),len, NULL, NULL);
CloseHandle(hdlWrite);
}
头文件
#ifndef CMPL_H
#define CMPL_H
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include <malloc.h>
#include <stdarg.h>
#include <stdlib.h>
typedef double (* FuncPtr)(double);
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, // 常数
ERRTOKEN, // 出错记号(非法输入)
NONTOKEN, // 空记号(源程序结束)
};
struct Token // 记号的数据结构
{
enum Token_Type type; // 类别
char * lexeme; // 属性,原始输入的字符串
double value; // 属性,若记号是常数则是常数的值
FuncPtr Fp; // 属性,若记号是函数则是函数指针
};
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;
};
int InitScanner(char *fname);
static struct Token GetToken();
void CloseScanner();
void PrintToken(void);
static struct ExprNode * Expression();
static struct ExprNode * Term();
static struct ExprNode * Factor();
static struct ExprNode * Component();
static struct ExprNode * Atom();
void Parser(void);
void PrintSyntaxTree(HANDLE hdlWrite, struct ExprNode *root, int indent);
static void Program();
static void Statement();
static void OriginStatement();
static void RotStatement();
static void ScaleStatement();
static void ForStatement();
static void SyntaxError (int case_of);
static void MatchToken(enum Token_Type AToken);
static void FetchToken();
static struct ExprNode * MakeExprNode(enum Token_Type opcode, ...);
void ExpressionTest(void);
static void SetRot(double r);
static void SetScale(double x, double y);
static void SetOrigin(double x, double y);
static void DrawLoop(double Start, double End, double Step, struct ExprNode *HorPtr, struct ExprNode *VerPtr);
static void DrawPixel(unsigned long x, unsigned long y);
static void CalcCoord(struct ExprNode *x_nptr, struct ExprNode *y_nptr, double *x_val, double *y_val);
static double GetExprValue(struct ExprNode * root);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void help(void);
#endif
测试用例
// Manual:
// 循环绘图(FOR-DRAW) 左上角为原点
// 比例设置(SCALE) x方向从左向右增长
// 角度旋转(ROT) y方向从上到下增长(与一般的坐标系方向相反)
// 坐标平移(ORIGIN)
// 支持函数 SIN sin
// COS cos
// TAN tan
// LN log
// EXP exp
// SQRT sqrt
// 运算符 + - * / ** //a ** b = pow(a, b)
// 注释 (-- 或 //)
// 默认值:
// origin is (0, 0)
// rot is 0;
// scale is (1, 1)
//
-----------------------------------------------------------------------
// 函数绘图源程序举例
// --------------- 函数f(t)=t的图形
// origin is (100, 300); -- 设置原点的偏移量
// rot is 0; -- 设置旋转角度(不旋转)
// scale is (1, 1); -- 设置横坐标和纵坐标的比例
// for T from 0 to 200 step 1 draw (t, 0);
// -- 横坐标的轨迹(纵坐标为0)
// for T from 0 to 150 step 1 draw (0, -t);
// -- 纵坐标的轨迹(横坐标为0)
// for T from 0 to 120 step 1 draw (t, -t);
// -- 函数f(t)=t的轨迹
//
------------------------------------------------------------------------
// 1.循环绘图(FOR-DRAW )语句
// 语法:FOR T FROM 起点 TO 终点 STEP 步长 DRAW(横坐标, 纵坐标);
// 语义:令T从起点到终点、每次改变一个步长,绘制出由(横坐标,纵坐标)所规定的点的轨迹。
// 举例:FOR T FROM 0 TO 2*PI STEP PI/50 DRAW (cos(T), sin(T));
// 说明:该语句的作用是令T从0到2*PI、步长 PI/50,绘制出各个点的坐标(cos(T),sin(T)),即一个单位圆。
//
// 注意:
// 由于绘图系统的默认值是
// ORIGIN IS (0,0);
// ROT IS 0;
// SCALE IS (1, 1);
// 所以实际绘制出的图形是在屏幕左上角的一个点。
//
// 2.比例设置(SCALE)语句
// 语法:SCALE IS (横坐标比例因子,纵坐标比例因子);
// 语义:设置横坐标和纵坐标的比例,并分别按照比例因子进行缩放。
// 举例:SCALE IS (100, 100);
// 说明:将横坐标和纵坐标的比例设置为1:1,且放大100倍。
// 若: SCALE IS (100, 100/3);
// 则:横坐标和纵坐标的比例为3:1。
//
// 3.坐标平移(ORIGIN)语句
// 语法:ORIGIN IS (横坐标,纵坐标);
// 语义:将坐标系的原点平移到横坐标和纵坐标规定的点处。
// 举例:ORIGIN IS (360, 240);
// 说明:将原点从(0, 0)平移到(360, 240) 处。
//
// 4.角度旋转(ROT)语句
// 语法:ROT IS 角度;
// 语义:逆时针旋转角度所规定的弧度值。
// 举例:ROT IS PI/2;
// 说明:逆时针旋转PI/2,即逆时针旋转90度。
// 5. 注释语句
// 注释的作用:便于理解;屏蔽暂时不需要的语句。
// 语法:// This is a comment line
// -- 此行是注释
//
// 语义:// 或 -- 之后,直到行尾,均是注释
----------------------------------------------------------------
// 运行方法:将此txt文件拖到draw.exe 上即可运行。
//origin is (450, 350);
//scale is (50, 50);
//for t from 0 to 2*pi step pi/100 draw(cos(t), sin(t));
//scale is (100, 100);
//for t from 0 to 2*pi step pi/200 draw(cos(t), sin(t));
//scale is (10, 10);
//for t from -10 to 10 step 0.001 draw(t, sin(t));
//for t from -10 to 10 step 0.001 draw(sin(t), t);
//origin is (450, 350); -- 设置原点的偏移量
//rot is pi/6; -- 设置旋转角度
//scale is (2, 1); -- 设置横、纵坐标比例
//for t from -100 to 100 step 1 draw (t, 0); -- 横坐标
//for t from -100 to 100 step 1 draw (0, t); -- 纵坐标
//scale is (200, 100); -- 设置横、纵坐标比例
//for t from 0 to 2*pi step pi/50 draw (cos(t),sin(t));
----------------------------------------------------------------------
origin is (450,350);
scale is (200,200);
for t from 0 to 2*pi step pi/500 draw(cos(t), sin(t));
origin is (450,350);
scale is (150,150);
for t from pi/12 to 11*pi/12 step pi/500 draw(cos(t), sin(t));
origin is (330,350);
scale is (100,100);
for t from pi/4+pi to 3*pi/4+pi step pi/500 draw(cos(t), sin(t));
scale is (100-20*sqrt(2),100-20*sqrt(2));
for t from pi/4+pi to 3*pi/4+pi step pi/500 draw(cos(t), sin(t));
origin is (570,350);
scale is (100,100);
for t from pi/4+pi to 3*pi/4+pi step pi/500 draw(cos(t), sin(t));
scale is (100-20*sqrt(2),100-20*sqrt(2));
for t from pi/4+pi to 3*pi/4+pi step pi/500 draw(cos(t), sin(t));
origin is (320+50*sqrt(2),360-50*sqrt(2));
scale is (10*sqrt(2),10*sqrt(2));
for t from -pi/4 to 3*pi/4 step pi/500 draw(cos(t), sin(t));
origin is (560+50*sqrt(2),360-50*sqrt(2));
scale is (10*sqrt(2),10*sqrt(2));
for t from -pi/4 to 3*pi/4 step pi/500 draw(cos(t), sin(t));
origin is (340-50*sqrt(2),360-50*sqrt(2));
scale is (10*sqrt(2),10*sqrt(2));
for t from pi/4 to 5*pi/4 step pi/500 draw(cos(t), sin(t));
origin is (580-50*sqrt(2),360-50*sqrt(2));
scale is (10*sqrt(2),10*sqrt(2));
for t from pi/4 to 5*pi/4 step pi/500 draw(cos(t), sin(t));
origin is (330-(100-10*sqrt(2))/2,350-(100-10*sqrt(2))/2*sqrt(3));
scale is (10*sqrt(2),10*sqrt(2));
for t from 0 to 2*pi step pi/500 draw(cos(t), sin(t));
origin is (570-(100-10*sqrt(2))/2,350-(100-10*sqrt(2))/2*sqrt(3));
scale is (10*sqrt(2),10*sqrt(2));
for t from 0 to 2*pi step pi/500 draw(cos(t), sin(t));
origin is (315,350);
scale is (50,20);
for t from 0 to 2*pi step pi/500 draw(cos(t), sin(t));
origin is (585,350);
for t from 0 to 2*pi step pi/500 draw(cos(t), sin(t));
origin is (455-100*sqrt(2),370-100*sqrt(2));
scale is (30,30);
rot is 3*pi/4;
for t from 0 to 3*pi/4 step pi/500 draw(cos(t), sin(t));
origin is (445+100*sqrt(2),370-100*sqrt(2));
scale is (30,30);
rot is pi;
for t from 0 to 3*pi/4 step pi/500 draw(cos(t), sin(t));
说明
链接:代码及程序(windows平台)
提取码:0soo
下载后直接将README.txt拖到draw_0_3.exe即可运行。
课程作业,未优化,未完成,代码稀烂,仅作记录