├── .gitignore ├── LICENSE ├── README.md ├── scripts ├── diamond.bas ├── hello_world.bas └── io.bas └── src ├── basic_io.c ├── basic_io.h ├── lexical.c ├── lexical.h ├── main.c ├── syntax.c └── syntax.h /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.o 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2009-2022 Zhang, Zepeng (redraiment@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BASIC 2 | 3 | 大学期间(2009年)的作品,学院老师筹划写“C语言教程”时,需要一个例子作为综合实验,因此我帮忙用纯ANSI C写一个 BASIC 解释器。设计思路当年也整理成了博客文章:[《BASIC解释器》](http://zzp.me/archive/basic解释器.html)。 4 | 5 | 因为文章里和后续出版的书中都没有完整的代码(按照老师的说法,不希望同学们直接Ctrl-C Ctrl-V),因此有不少同学发邮件向我询问完整的代码,所以干脆就发布到Github上供大家自由下载。 6 | 7 | # 下载编译 8 | 9 | ``` 10 | git clone https://github.com/redraiment/basic.git 11 | cd basic 12 | cc -o basic src/*.c 13 | ``` 14 | 15 | `scripts`目录下有一些简单的实例,可供参考。 16 | 17 | # BASIC语法 18 | 19 | 本BASIC解释器支持的语法主要参考高中时玩的步步高里的GVBASIC。 20 | 21 | ## 行号 22 | 23 | 与GVBASIC相同,每一行都必须有一个“整数行号”+一行“代码”组成,例如: 24 | 25 | ``` 26 | 0001 PRINT "HELLO WORLD" 27 | ``` 28 | 29 | 为了方便对齐,行号可以有任意多个前导0;行号之后必须加上一个空格。 30 | 31 | **注意**:本解释器最多支持10000行代码(可自行修改代码以支持更多行数)。 32 | 33 | ## 变量 34 | 35 | 与GVBASIC相同,本BASIC解释器也只支持固定26个变量,且变量名必须为单个大写字母(`A`到`Z`)。 36 | 37 | 变量为动态、弱类型。 38 | 39 | **变量赋值** 40 | 41 | ``` 42 | [LET] = 43 | ``` 44 | 45 | 计算等式右侧的表达式,并将结果赋值到左侧变量中。其中`LET`关键字可选。 46 | 47 | ## 输入输出 48 | 49 | **输入** 50 | 51 | ``` 52 | INPUT [, VAR ...] 53 | ``` 54 | 55 | 从标准输入(通常是键盘)中输入任意多个数据到变量中。 56 | 57 | **输出** 58 | 59 | ``` 60 | PRINT [, EXPRESSION ...] 61 | ``` 62 | 63 | 计算表达式``,并将结果输出到标准输出(通常是屏幕)。 64 | 65 | ## 表达式运算 66 | 67 | **算数运算符** 68 | 69 | * `+`:加法 70 | * `-`:减法 71 | * `*`:乘法 72 | * `/`:除法 73 | * `%`:求余 74 | * `()`:小括号提高优先级 75 | 76 | **比较运算符** 77 | 78 | * `=`:相等 79 | * `<>`:不等 80 | * `<`:小于 81 | * `>`:大于 82 | * `<=`:不大于 83 | * `>=`:不小于 84 | 85 | **逻辑运算符** 86 | 87 | * `AND`:与 88 | * `OR`:或 89 | * `NOT`:非 90 | 91 | ## 控制语句 92 | 93 | **条件语句** 94 | 95 | ``` 96 | IF THEN 97 | 98 | ... 99 | [ELSE 100 | 101 | ...] 102 | END IF 103 | ``` 104 | 105 | **FOR循环语句** 106 | 107 | ``` 108 | FOR = TO [STEP ] 109 | [SENTENCE] 110 | ... 111 | NEXT 112 | ``` 113 | 114 | * 表达式1为起点 115 | * 表达式2为终点 116 | * 步长表达式可选 117 | 118 | **WHILE循环语句** 119 | 120 | ``` 121 | WHILE 122 | [SENTENCE] 123 | ... 124 | WEND 125 | ``` 126 | 127 | 执行循环代码块,直到表达式的结果为FALSE。 128 | 129 | **跳转语句** 130 | 131 | ``` 132 | GOTO 133 | ``` 134 | 135 | 无条件跳转到指定的行号。 136 | -------------------------------------------------------------------------------- /scripts/diamond.bas: -------------------------------------------------------------------------------- 1 | 0009 N = 0 2 | 0010 WHILE N < 1 OR N > 20 3 | 0011 PRINT "请输入一个1-20之间的数" 4 | 0012 INPUT N 5 | 0013 WEND 6 | 0020 FOR I = 1 TO N 7 | 0030 L = "*" 8 | 0040 FOR J = 1 TO N - I 9 | 0050 L = " " + L 10 | 0060 NEXT 11 | 0070 FOR J = 2 TO 2 * I - 1 STEP 2 12 | 0080 L = L + "**" 13 | 0090 NEXT 14 | 0100 PRINT L 15 | 0110 NEXT 16 | 0120 I = N - 1 17 | 0130 L = "" 18 | 0140 FOR J = 1 TO N - I 19 | 0150 L = L + " " 20 | 0160 NEXT 21 | 0170 FOR J = 1 TO ((2*I) - 1) 22 | 0180 L = L + "*" 23 | 0190 NEXT 24 | 0200 PRINT L 25 | 0210 I = I - 1 26 | 0220 IF I > 0 THEN 27 | 0230 GOTO 130 28 | 0240 ELSE 29 | 0250 PRINT "By redraiment" 30 | 0260 END IF -------------------------------------------------------------------------------- /scripts/hello_world.bas: -------------------------------------------------------------------------------- 1 | 0001 PRINT "HELLO WORLD" 2 | -------------------------------------------------------------------------------- /scripts/io.bas: -------------------------------------------------------------------------------- 1 | 0001 PRINT "Please input a number: " 2 | 0002 INPUT I 3 | 0003 J = I + 1 4 | 0004 S = "" + I + " + 1 = " + J 5 | 0005 PRINT S 6 | 7 | 0006 PRINT "Please input your name: " 8 | 0007 INPUT N 9 | 0008 PRINT "Hello " + N 10 | -------------------------------------------------------------------------------- /src/basic_io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "basic_io.h" 8 | #include "lexical.h" 9 | 10 | // 代码空间 11 | CODE code[PROGRAM_SIZE] = {0}; 12 | // 代码指针 13 | int cp = 1; 14 | int code_size = 0; 15 | 16 | // 可用的内存(26个变量) 17 | VARIANT memory[MEMORY_SIZE] = {0}; 18 | 19 | void load_program(STRING filename) { 20 | FILE *fp = fopen(filename, "r"); 21 | int bg, ed; 22 | 23 | if(fp == NULL) { 24 | fprintf(stderr, "文件 %s 无法打开!\n", filename); 25 | exit(EXIT_FAILURE); 26 | } 27 | 28 | while(fscanf(fp, "%d", &code[cp].ln) != EOF) { 29 | if(code[cp].ln <= code[cp-1].ln) { 30 | fprintf(stderr, "Line %d: 标号错误!\n", cp); 31 | exit(EXIT_FAILURE); 32 | } 33 | 34 | fgets(code[cp].line, sizeof(code[cp].line), fp); 35 | for(bg = 0; isspace(code[cp].line[bg]); bg++); 36 | ed = (int)strlen(code[cp].line + bg) - 1; 37 | while(ed >= 0 && isspace(code[cp].line[ed+bg])) { 38 | ed--; 39 | } 40 | if(ed >= 0) { 41 | memmove(code[cp].line, code[cp].line + bg, ed + 1); 42 | code[cp].line[ed + 1] = 0; 43 | } else { 44 | code[cp].line[0] = 0; 45 | } 46 | 47 | cp++; 48 | if(cp >= PROGRAM_SIZE) { 49 | fprintf(stderr, "程序%s太大,代码空间不足!\n", filename); 50 | exit(EXIT_FAILURE); 51 | } 52 | } 53 | 54 | code_size = cp; 55 | cp = 1; 56 | } 57 | 58 | void exec_assignment(const STRING line) { 59 | const char *s = line; 60 | int n; 61 | 62 | if(!strnicmp(s, "LET ", 4)) { 63 | s += 4; 64 | } 65 | while(*s && isspace(*s)) { 66 | s++; 67 | } 68 | if(!isalpha(*s) || isalnum(*(s+1))) { 69 | perror("变量名错误!\n"); 70 | exit(EXIT_FAILURE); 71 | } else { 72 | n = toupper(*s) - 'A'; 73 | } 74 | 75 | do { 76 | s++; 77 | } while(*s && isspace(*s)); 78 | if(*s != '=') { 79 | fprintf(stderr, "赋值表达式 %s 语法错误!\n", line); 80 | exit(EXIT_FAILURE); 81 | } else { 82 | memory[n] = eval(s + 1); 83 | } 84 | } 85 | 86 | void exec_input(const STRING line) { 87 | const char *s = line; 88 | int n; 89 | 90 | assert(s != NULL); 91 | s += 5; 92 | 93 | while(*s) { 94 | while(*s && isspace(*s)) { 95 | s++; 96 | } 97 | if(!isalpha(*s) || isalnum(*(s+1))) { 98 | perror("变量名错误!\n"); 99 | exit(EXIT_FAILURE); 100 | } else { 101 | n = toupper(*s) - 'A'; 102 | } 103 | 104 | if(!scanf("%lf", &memory[n].i)) { 105 | int i; 106 | 107 | memory[n].type = var_string; 108 | if((memory[n].s[0] = getchar()) == '"') { 109 | for(i = 0; (memory[n].s[i]=getchar())!='"'; i++); 110 | } else { 111 | for(i = 1; !isspace(memory[n].s[i]=getchar()); i++); 112 | } 113 | memory[n].s[i] = 0; 114 | } else { 115 | memory[n].type = var_double; 116 | } 117 | 118 | do { 119 | s++; 120 | } while(*s && isspace(*s)); 121 | if(*s && *s != ',') { 122 | perror("INPUT 表达式语法错误!\n"); 123 | exit(EXIT_FAILURE); 124 | } else if(*s) { 125 | s++; 126 | } 127 | } 128 | } 129 | 130 | void exec_print(const STRING line) { 131 | STRING l; 132 | char *s, *e; 133 | VARIANT v; 134 | int c = 0; 135 | 136 | strcpy(l, line); 137 | s = l; 138 | 139 | assert(s != NULL); 140 | s += 5; 141 | 142 | for (;;) { 143 | for(e = s; *e && *e != ','; e++) { 144 | // 去除字符串 145 | if(*e == '"') { 146 | do { 147 | e++; 148 | } while(*e && *e != '"'); 149 | } 150 | } 151 | if(*e) { 152 | *e = 0; 153 | } else { 154 | e = NULL; 155 | } 156 | 157 | if(c++) putchar('\t'); 158 | v = eval(s); 159 | if(v.type == var_double) { 160 | printf("%g", v.i); 161 | } else if(v.type == var_string) { 162 | printf("%s", v.s); 163 | } 164 | 165 | if(e) { 166 | s = e + 1; 167 | } else { 168 | putchar('\n'); 169 | break; 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/basic_io.h: -------------------------------------------------------------------------------- 1 | #ifndef __BASIC_IO_H_ 2 | #define __BASIC_IO_H_ 3 | 4 | #define MEMORY_SIZE (26) 5 | #define PROGRAM_SIZE (10000) 6 | 7 | #ifndef WIN32 8 | # define stricmp strcasecmp 9 | # define strnicmp strncasecmp 10 | #endif 11 | 12 | typedef enum { 13 | var_null = 0, 14 | var_double, 15 | var_string 16 | } variant_type; 17 | typedef char STRING[128]; 18 | typedef struct { 19 | variant_type type; 20 | union { 21 | double i; 22 | STRING s; 23 | }; 24 | } VARIANT; 25 | typedef struct { 26 | int ln; // line number 27 | STRING line; 28 | } CODE; 29 | 30 | extern VARIANT memory[MEMORY_SIZE]; 31 | extern CODE code[PROGRAM_SIZE]; 32 | extern int cp; 33 | extern int code_size; 34 | 35 | void load_program(STRING filename); 36 | void exec_assignment(const STRING line); 37 | void exec_input(const STRING); 38 | void exec_print(const STRING); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/lexical.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "lexical.h" 9 | 10 | static const OPERATOR operators[] = { 11 | /* 算数运算 */ 12 | {2, 17, 1, left2right, oper_lparen}, // 左括号 13 | {2, 17, 17, left2right, oper_rparen}, // 右括号 14 | {2, 12, 12, left2right, oper_plus}, // 加 15 | {2, 12, 12, left2right, oper_minus}, // 减 16 | {2, 13, 13, left2right, oper_multiply}, // 乘 17 | {2, 13, 13, left2right, oper_divide}, // 除 18 | {2, 13, 13, left2right, oper_mod}, // 模 19 | {2, 14, 14, left2right, oper_power}, // 幂 20 | {1, 16, 15, right2left, oper_positive}, // 正号 21 | {1, 16, 15, right2left, oper_negative}, // 负号 22 | {1, 16, 15, left2right, oper_factorial}, // 阶乘 23 | /* 关系运算 */ 24 | {2, 10, 10, left2right, oper_lt}, // 小于 25 | {2, 10, 10, left2right, oper_gt}, // 大于 26 | {2, 9, 9, left2right, oper_eq}, // 等于 27 | {2, 9, 9, left2right, oper_ne}, // 不等于 28 | {2, 10, 10, left2right, oper_le}, // 不大于 29 | {2, 10, 10, left2right, oper_ge}, // 不小于 30 | /* 逻辑运算 */ 31 | {2, 5, 5, left2right, oper_and}, // 且 32 | {2, 4, 4, left2right, oper_or}, // 或 33 | {1, 15, 15, right2left, oper_not}, // 非 34 | /* 赋值 */ 35 | // BASIC 表达式中不会出现赋值 36 | {2, 2, 2, right2left, oper_assignment}, // 赋值 37 | /* 最小优先级 */ 38 | {2, 0, 0, right2left, oper_min} // 栈底 39 | }; 40 | 41 | static const char *h; 42 | static const char *e; 43 | static TOKEN before; 44 | 45 | static TOKEN next_token() { 46 | TOKEN token = {0}; 47 | STRING s; 48 | int i; 49 | 50 | if (e == NULL) { 51 | return token; 52 | } 53 | 54 | // 去掉前导空格 55 | while (*e && isspace(*e)) { 56 | e++; 57 | } 58 | 59 | if (*e == 0) { 60 | return token; 61 | } 62 | 63 | if (*e == '"') { 64 | token.type = token_operand; 65 | token.var.type = var_string; 66 | e++; 67 | for (i = 0; *e && *e != '"'; i++) { 68 | token.var.s[i] = *e; 69 | e++; 70 | } 71 | e++; 72 | } else if (isalpha(*e)) { 73 | token.type = token_operator; 74 | for (i = 0; isalnum(*e); i++) { 75 | s[i] = toupper(*e); 76 | e++; 77 | } 78 | s[i] = 0; 79 | if (!strcmp(s, "AND")) { 80 | token.ator = operators[oper_and]; 81 | } else if (!strcmp(s, "OR")) { 82 | token.ator = operators[oper_or]; 83 | } else if (!strcmp(s, "NOT")) { 84 | token.ator = operators[oper_not]; 85 | } else if (i == 1) { 86 | token.type = token_operand; 87 | token.var = memory[s[0]-'A']; 88 | if (token.var.type == var_null) { 89 | memset(&token, 0, sizeof(token)); 90 | fprintf(stderr, "变量%c未赋值!\n", s[0]); 91 | exit(EXIT_FAILURE); 92 | } 93 | } else { 94 | goto errorhandler; 95 | } 96 | } else if (isdigit(*e) || *e == '.') { 97 | token.type = token_operand; 98 | token.var.type = var_double; 99 | for (i = 0; isdigit(*e) || *e == '.'; i++) { 100 | s[i] = *e; 101 | e++; 102 | } 103 | s[i] = 0; 104 | if (sscanf(s, "%lf", &token.var.i) != 1) { 105 | // Can't Read 106 | goto errorhandler; 107 | } 108 | } else { 109 | token.type = token_operator; 110 | switch(*e) { 111 | case '(': 112 | token.ator = operators[oper_lparen]; 113 | break; 114 | case ')': 115 | token.ator = operators[oper_rparen]; 116 | break; 117 | case '+': 118 | if (before.type == token_operand 119 | ||(before.type == token_operator 120 | && before.ator.oper == oper_rparen)) { 121 | token.ator = operators[oper_plus]; 122 | } else { 123 | token.ator = operators[oper_positive]; 124 | } 125 | break; 126 | case '-': 127 | if (before.type == token_operand 128 | ||(before.type == token_operator 129 | && before.ator.oper == oper_rparen)) { 130 | token.ator = operators[oper_minus]; 131 | } else { 132 | token.ator = operators[oper_negative]; 133 | } 134 | break; 135 | case '*': 136 | token.ator = operators[oper_multiply]; 137 | break; 138 | case '/': 139 | token.ator = operators[oper_divide]; 140 | break; 141 | case '%': 142 | token.ator = operators[oper_mod]; 143 | break; 144 | case '^': 145 | token.ator = operators[oper_power]; 146 | break; 147 | case '!': 148 | token.ator = operators[oper_factorial]; 149 | break; 150 | case '=': 151 | token.ator = operators[oper_eq]; 152 | break; 153 | case '>': 154 | if (*(e+1) == '=') { 155 | token.ator = operators[oper_ge]; 156 | e++; 157 | } else { 158 | token.ator = operators[oper_gt]; 159 | } 160 | break; 161 | case '<': 162 | if (*(e+1) == '=') { 163 | token.ator = operators[oper_le]; 164 | e++; 165 | } else if (*(e+1) == '>') { 166 | token.ator = operators[oper_ne]; 167 | e++; 168 | } else { 169 | token.ator = operators[oper_lt]; 170 | } 171 | break; 172 | default: 173 | // Error 174 | goto errorhandler; 175 | } 176 | e++; 177 | } 178 | before = token; 179 | 180 | return token; 181 | 182 | errorhandler: 183 | memset(&token, 0, sizeof(token)); 184 | fprintf(stderr, "表达式 %s 前有语法错误!不可识别的运算符!\n", h); 185 | exit(EXIT_FAILURE); 186 | } 187 | 188 | PTLIST infix2postfix() { 189 | PTLIST list = NULL, tail, p; 190 | PTLIST stack = NULL; 191 | 192 | stack = (PTLIST)calloc(1, sizeof(TOKEN_LIST)); 193 | stack->next = NULL; 194 | stack->token.type = token_operator; 195 | stack->token.ator = operators[oper_min]; 196 | 197 | memset(&before, 0, sizeof(before)); 198 | for (;;) { 199 | p = (PTLIST)calloc(1, sizeof(TOKEN_LIST)); 200 | // calloc 自动初始化 201 | p->next = NULL; 202 | p->token = next_token(); 203 | if (p->token.type == token_operand) { 204 | if (!list) { 205 | list = tail = p; 206 | } else { 207 | tail->next = p; 208 | tail = p; 209 | } 210 | } else if (p->token.type == token_operator) { 211 | if (p->token.ator.oper == oper_rparen) { 212 | free(p); 213 | while (stack->token.ator.oper != oper_lparen) { 214 | p = stack; 215 | stack = stack->next; 216 | tail->next = p; 217 | tail = p; 218 | tail->next = NULL; 219 | } 220 | p = stack; 221 | stack = stack->next; 222 | free(p); 223 | } else { 224 | while (stack->token.ator.isp >= p->token.ator.icp) { 225 | tail->next = stack; 226 | stack = stack->next; 227 | tail = tail->next; 228 | tail->next = NULL; 229 | } 230 | p->next = stack; 231 | stack = p; 232 | } 233 | } else { 234 | free(p); 235 | break; 236 | } 237 | } 238 | while (stack) { 239 | p = stack; 240 | stack = stack->next; 241 | if (p->token.ator.oper != oper_min) { 242 | p->next = NULL; 243 | tail->next = p; 244 | tail = p; 245 | } else { 246 | free(p); 247 | } 248 | } 249 | 250 | return list; 251 | } 252 | 253 | VARIANT eval(const char expr[]) { 254 | PTLIST list, p; 255 | PTLIST stack = NULL, op1, op2; 256 | STRING s1, s2; 257 | STRING error_msg = "Error: "; 258 | VARIANT v; 259 | 260 | strcat(error_msg, expr); 261 | strcat(error_msg, ": "); 262 | 263 | h = e = expr; 264 | list = infix2postfix(); 265 | while (list) { 266 | p = list; 267 | list = list->next; 268 | 269 | if (p->token.type == token_operand) { 270 | p->next = stack; 271 | stack = p; 272 | continue; 273 | } 274 | 275 | assert(stack != NULL); 276 | 277 | // 操作符 278 | switch(p->token.ator.oper) { 279 | /* 算数运算 */ 280 | case oper_plus: 281 | op2 = stack; 282 | op1 = stack = stack->next; 283 | 284 | if (op1->token.var.type == var_double && 285 | op2->token.var.type == var_double) { 286 | op1->token.var.i += op2->token.var.i; 287 | } else { 288 | if (op1->token.var.type == var_double) { 289 | sprintf(s1, "%g", op1->token.var.i); 290 | } else { 291 | strcpy(s1, op1->token.var.s); 292 | } 293 | if (op2->token.var.type == var_double) { 294 | sprintf(s2, "%g", op2->token.var.i); 295 | } else { 296 | strcpy(s2, op2->token.var.s); 297 | } 298 | op1->token.type = var_string; 299 | strcat(s1, s2); 300 | strcpy(op1->token.var.s, s1); 301 | } 302 | free(op2); 303 | break; 304 | case oper_minus: 305 | op2 = stack; 306 | op1 = stack = stack->next; 307 | 308 | if (op1->token.var.type == var_double && 309 | op2->token.var.type == var_double) { 310 | op1->token.var.i -= op2->token.var.i; 311 | free(op2); 312 | } else { 313 | // Error 314 | strcat(error_msg, "字符串不支持减运算\n"); 315 | goto errorhandler; 316 | } 317 | break; 318 | case oper_multiply: 319 | op2 = stack; 320 | op1 = stack = stack->next; 321 | 322 | if (op1->token.var.type == var_double && 323 | op2->token.var.type == var_double) { 324 | op1->token.var.i *= op2->token.var.i; 325 | free(op2); 326 | } else { 327 | // Error 328 | strcat(error_msg, "字符串不支持乘运算\n"); 329 | goto errorhandler; 330 | } 331 | break; 332 | case oper_divide: 333 | op2 = stack; 334 | op1 = stack = stack->next; 335 | 336 | if (op1->token.var.type == var_double && 337 | op2->token.var.type == var_double) { 338 | if (fabs(op2->token.var.i) < 1E-6) { 339 | // Error 340 | strcat(error_msg, "除数为 0\n"); 341 | goto errorhandler; 342 | } else { 343 | op1->token.var.i /= op2->token.var.i; 344 | free(op2); 345 | } 346 | } else { 347 | // Error 348 | strcat(error_msg, "字符串不支持除运算\n"); 349 | goto errorhandler; 350 | } 351 | break; 352 | case oper_mod: 353 | op2 = stack; 354 | op1 = stack = stack->next; 355 | 356 | if (op1->token.var.type == var_double && 357 | op2->token.var.type == var_double) { 358 | if (fabs(op2->token.var.i) < 1E-6) { 359 | // Error 360 | strcat(error_msg, "除数为 0\n"); 361 | goto errorhandler; 362 | } else { 363 | int a = (int)op1->token.var.i; 364 | int b = (int)op2->token.var.i; 365 | op1->token.var.i = a % b; 366 | free(op2); 367 | } 368 | } else { 369 | // Error 370 | strcat(error_msg, "字符串不支持求模运算\n"); 371 | goto errorhandler; 372 | } 373 | break; 374 | case oper_power: 375 | op2 = stack; 376 | op1 = stack = stack->next; 377 | 378 | if (op1->token.var.type == var_double && 379 | op2->token.var.type == var_double) { 380 | op1->token.var.i = pow(op1->token.var.i, 381 | op2->token.var.i); 382 | free(op2); 383 | } else { 384 | // Error 385 | strcat(error_msg, "字符串不支持幂运算\n"); 386 | goto errorhandler; 387 | } 388 | break; 389 | case oper_positive: 390 | // Ignored 391 | break; 392 | case oper_negative: 393 | if (stack->token.var.type == var_double) { 394 | stack->token.var.i = -stack->token.var.i; 395 | } else { 396 | // 字符串取反操作 397 | STRING s; 398 | char *p = stack->token.var.s; 399 | int len = (int)strlen(p); 400 | int i; 401 | for (i = 0; i < len; i++) { 402 | s[i] = *(p + len - i - 1); 403 | } 404 | s[i] = 0; 405 | strcpy(p, s); 406 | } 407 | break; 408 | case oper_factorial: 409 | if (stack->token.var.type == var_double) { 410 | int i = (int)stack->token.var.i; 411 | stack->token.var.i = 1; 412 | while (i > 1) { 413 | stack->token.var.i *= i; 414 | i--; 415 | } 416 | } else { 417 | // Error 418 | strcat(error_msg, "字符串不支持阶乘运算\n"); 419 | goto errorhandler; 420 | } 421 | break; 422 | /* 关系运算 */ 423 | case oper_lt: 424 | case oper_gt: 425 | case oper_eq: 426 | case oper_ne: 427 | case oper_le: 428 | case oper_ge: 429 | op2 = stack; 430 | op1 = stack = stack->next; 431 | 432 | if (op1->token.var.type == var_double && 433 | op2->token.var.type == var_double) { 434 | switch(p->token.ator.oper) { 435 | case oper_lt: 436 | op1->token.var.i = op1->token.var.i < op2->token.var.i; 437 | break; 438 | case oper_gt: 439 | op1->token.var.i = op1->token.var.i > op2->token.var.i; 440 | break; 441 | case oper_eq: 442 | op1->token.var.i = op1->token.var.i == op2->token.var.i; 443 | break; 444 | case oper_ne: 445 | op1->token.var.i = op1->token.var.i != op2->token.var.i; 446 | break; 447 | case oper_le: 448 | op1->token.var.i = op1->token.var.i <= op2->token.var.i; 449 | break; 450 | case oper_ge: 451 | op1->token.var.i = op1->token.var.i >= op2->token.var.i; 452 | break; 453 | } 454 | } else { 455 | if (op1->token.var.type == var_double) { 456 | sprintf(s1, "%g", op1->token.var.i); 457 | } else { 458 | strcpy(s1, op1->token.var.s); 459 | } 460 | if (op2->token.var.type == var_double) { 461 | sprintf(s2, "%g", op2->token.var.i); 462 | } else { 463 | strcpy(s2, op2->token.var.s); 464 | } 465 | op1->token.var.type = var_double; 466 | switch(p->token.ator.oper) { 467 | case oper_lt: 468 | op1->token.var.i = strcmp(op1->token.var.s, op2->token.var.s) < 0; 469 | break; 470 | case oper_gt: 471 | op1->token.var.i = strcmp(op1->token.var.s, op2->token.var.s) > 0; 472 | break; 473 | case oper_eq: 474 | op1->token.var.i = strcmp(op1->token.var.s, op2->token.var.s) == 0; 475 | break; 476 | case oper_ne: 477 | op1->token.var.i = strcmp(op1->token.var.s, op2->token.var.s) != 0; 478 | break; 479 | case oper_le: 480 | op1->token.var.i = strcmp(op1->token.var.s, op2->token.var.s) <= 0; 481 | break; 482 | case oper_ge: 483 | op1->token.var.i = strcmp(op1->token.var.s, op2->token.var.s) >= 0; 484 | break; 485 | } 486 | } 487 | free(op2); 488 | break; 489 | /* 逻辑运算 */ 490 | case oper_and: 491 | case oper_or: { 492 | int b1 = 0, b2 = 0; 493 | 494 | op2 = stack; 495 | op1 = stack = stack->next; 496 | 497 | if (op1->token.var.type == var_double) { 498 | b1 = fabs(op1->token.var.i) > 1E-6; 499 | } else { 500 | b1 = op1->token.var.s[0] != 0; 501 | } 502 | if (op2->token.var.type == var_double) { 503 | b2 = fabs(op2->token.var.i) > 1E-6; 504 | } else { 505 | b2 = op2->token.var.s[0] != 0; 506 | } 507 | 508 | switch(p->token.ator.oper) { 509 | case oper_and: 510 | op1->token.var.i = b1 && b2; 511 | break; 512 | case oper_or: 513 | op1->token.var.i = b1 || b2; 514 | break; 515 | } 516 | 517 | free(op2); 518 | break; 519 | } 520 | case oper_not: 521 | op1 = stack; 522 | if (op1->token.var.type == var_double) { 523 | op1->token.var.i = fabs(op1->token.var.i) < 1E-6; 524 | } else { 525 | op1->token.var.i = op1->token.var.s[0] == 0; 526 | } 527 | op1->token.var.type = var_double; 528 | break; 529 | default: 530 | // Unbelievable 531 | strcat(error_msg, "不识别的运算符!\n"); 532 | goto errorhandler; 533 | break; 534 | } 535 | free(p); 536 | } 537 | 538 | assert(stack && !stack->next); 539 | v = stack->token.var; 540 | free(stack); 541 | 542 | return v; 543 | 544 | errorhandler: 545 | perror(error_msg); 546 | exit(EXIT_FAILURE); 547 | } 548 | -------------------------------------------------------------------------------- /src/lexical.h: -------------------------------------------------------------------------------- 1 | #ifndef __EXPRESSION_H_ 2 | #define __EXPRESSION_H_ 3 | 4 | #include "basic_io.h" 5 | 6 | typedef VARIANT OPERAND; 7 | typedef enum { 8 | /* 算数运算 */ 9 | oper_lparen = 0, // 左括号 10 | oper_rparen, // 右括号 11 | oper_plus, // 加 12 | oper_minus, // 减 13 | oper_multiply, // 乘 14 | oper_divide, // 除 15 | oper_mod, // 模 16 | oper_power, // 幂 17 | oper_positive, // 正号 18 | oper_negative, // 负号 19 | oper_factorial, // 阶乘 20 | /* 关系运算 */ 21 | oper_lt, // 小于 22 | oper_gt, // 大于 23 | oper_eq, // 等于 24 | oper_ne, // 不等于 25 | oper_le, // 不大于 26 | oper_ge, // 不小于 27 | /* 逻辑运算 */ 28 | oper_and, // 且 29 | oper_or, // 或 30 | oper_not, // 非 31 | /* 赋值 */ 32 | oper_assignment, // 赋值 33 | oper_min // 栈底 34 | } operator_type; 35 | typedef enum { 36 | left2right, 37 | right2left 38 | } associativity; 39 | typedef struct { 40 | int numbers; // 操作数 41 | int icp; // 优先级 42 | int isp; // 优先级 43 | associativity ass; // 结合性 44 | operator_type oper; // 操作符 45 | } OPERATOR; 46 | typedef enum { 47 | token_operand = 1, 48 | token_operator 49 | } token_type; 50 | typedef struct { 51 | token_type type; 52 | union { 53 | OPERAND var; 54 | OPERATOR ator; 55 | }; 56 | } TOKEN; 57 | typedef struct tlist { 58 | TOKEN token; 59 | struct tlist *next; 60 | } TOKEN_LIST, *PTLIST; 61 | 62 | VARIANT eval(const char[]); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "basic_io.h" 6 | #include "lexical.h" 7 | #include "syntax.h" 8 | 9 | typedef enum { 10 | key_input = 0, 11 | key_print, 12 | key_for, 13 | key_next, 14 | key_while, 15 | key_wend, 16 | key_if, 17 | key_else, 18 | key_endif, 19 | key_goto, 20 | key_let 21 | } keywords; 22 | 23 | void (*key_func[])(const STRING) = { 24 | exec_input, 25 | exec_print, 26 | exec_for, 27 | exec_next, 28 | exec_while, 29 | exec_wend, 30 | exec_if, 31 | exec_else, 32 | exec_endif, 33 | exec_goto, 34 | exec_assignment 35 | }; 36 | 37 | keywords yacc(const STRING line) { 38 | if (!strnicmp(line, "INPUT ", 6)) { 39 | return key_input; 40 | } else if (!strnicmp(line, "PRINT ", 6)) { 41 | return key_print; 42 | } else if (!strnicmp(line, "FOR ", 4)) { 43 | return key_for; 44 | } else if (!stricmp (line, "NEXT")) { 45 | return key_next; 46 | } else if (!strnicmp(line, "WHILE ", 6)) { 47 | return key_while; 48 | } else if (!stricmp(line, "WEND")) { 49 | return key_wend; 50 | } else if (!strnicmp(line, "IF ", 3)) { 51 | return key_if; 52 | } else if (!stricmp (line, "ELSE")) { 53 | return key_else; 54 | } else if (!stricmp (line, "END IF")) { 55 | return key_endif; 56 | } else if (!strnicmp(line, "GOTO ", 5)) { 57 | return key_goto; 58 | } else if (!strnicmp(line, "LET ", 4)) { 59 | return key_let; 60 | } else if (strchr(line, '=')) { 61 | return key_let; 62 | } 63 | 64 | return -1; 65 | } 66 | 67 | int main (int argc, char *argv[]) { 68 | if (argc != 2) { 69 | fprintf(stderr, "usage: %s basic_script_file\n", argv[0]); 70 | exit(EXIT_FAILURE); 71 | } 72 | 73 | load_program(argv[1]); 74 | 75 | while (cp < code_size) { 76 | (*key_func[yacc(code[cp].line)])(code[cp].line); 77 | cp++; 78 | } 79 | 80 | return EXIT_SUCCESS; 81 | } 82 | -------------------------------------------------------------------------------- /src/syntax.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "syntax.h" 8 | 9 | static struct { 10 | int id; // memory index 11 | int ln; // line number 12 | double target; // target value 13 | double step; 14 | } stack_for[MEMORY_SIZE]; 15 | static int top_for = -1; 16 | 17 | void exec_for(const STRING line) { 18 | STRING l; 19 | char *s, *t; 20 | int top = top_for + 1; 21 | 22 | if (strnicmp(line, "FOR ", 4)) { 23 | goto errorhandler; 24 | } else if (top >= MEMORY_SIZE) { 25 | fprintf(stderr, "FOR 循环嵌套过深!\n"); 26 | exit(EXIT_FAILURE); 27 | } 28 | 29 | strcpy(l, line); 30 | 31 | s = l + 4; 32 | while (*s && isspace(*s)) s++; 33 | if (isalpha(*s) && !isalnum(s[1])) { 34 | stack_for[top].id = toupper(*s) - 'A'; 35 | stack_for[top].ln = cp; 36 | } else { 37 | goto errorhandler; 38 | } 39 | 40 | do { 41 | s++; 42 | } while (*s && isspace(*s)); 43 | if (*s == '=') { 44 | s++; 45 | } else { 46 | goto errorhandler; 47 | } 48 | 49 | t = strstr(s, " TO "); 50 | if (t != NULL) { 51 | *t = 0; 52 | memory[stack_for[top].id] = eval(s); 53 | s = t + 4; 54 | } else { 55 | goto errorhandler; 56 | } 57 | 58 | t = strstr(s, " STEP "); 59 | if (t != NULL) { 60 | *t = 0; 61 | stack_for[top].target = eval(s).i; 62 | s = t + 5; 63 | stack_for[top].step = eval(s).i; 64 | if (fabs(stack_for[top].step) < 1E-6) { 65 | goto errorhandler; 66 | } 67 | } else { 68 | stack_for[top].target = eval(s).i; 69 | stack_for[top].step = 1; 70 | } 71 | 72 | if ((stack_for[top].step > 0 && 73 | memory[stack_for[top].id].i > stack_for[top].target)|| 74 | (stack_for[top].step < 0 && 75 | memory[stack_for[top].id].i < stack_for[top].target)) { 76 | while (cp < code_size && strcmp(code[cp].line, "NEXT")) { 77 | cp++; 78 | } 79 | if (cp >= code_size) { 80 | goto errorhandler; 81 | } 82 | } else { 83 | top_for++; 84 | } 85 | 86 | return; 87 | 88 | errorhandler: 89 | fprintf(stderr, "Line %d: 语法错误!\n", code[cp].ln); 90 | exit(EXIT_FAILURE); 91 | } 92 | 93 | void exec_next(const STRING line) { 94 | if (stricmp(line, "NEXT")) { 95 | fprintf(stderr, "Line %d: 语法错误!\n", code[cp].ln); 96 | exit(EXIT_FAILURE); 97 | } 98 | if (top_for < 0) { 99 | fprintf(stderr, "Line %d: NEXT 没有相匹配的 FOR!\n", code[cp].ln); 100 | exit(EXIT_FAILURE); 101 | } 102 | 103 | memory[stack_for[top_for].id].i += stack_for[top_for].step; 104 | if (stack_for[top_for].step > 0 && 105 | memory[stack_for[top_for].id].i > stack_for[top_for].target) { 106 | top_for--; 107 | } else if (stack_for[top_for].step < 0 && 108 | memory[stack_for[top_for].id].i < stack_for[top_for].target) { 109 | top_for--; 110 | } else { 111 | cp = stack_for[top_for].ln; 112 | } 113 | } 114 | 115 | static struct { 116 | int ln; 117 | int isrun; 118 | } stack_while[MEMORY_SIZE]; 119 | static int top_while = -1; 120 | 121 | void exec_while(const STRING line) { 122 | const char *s; 123 | int top = top_while + 1; 124 | VARIANT v; 125 | 126 | if (strnicmp(line, "WHILE ", 6)) { 127 | goto errorhandler; 128 | } else if (top >= MEMORY_SIZE) { 129 | fprintf(stderr, "WHILE 循环嵌套过深!\n"); 130 | exit(EXIT_FAILURE); 131 | } 132 | 133 | s = line + 6; 134 | stack_while[top].ln = cp; 135 | v = eval(s); 136 | if (v.type == var_double) { 137 | stack_while[top].isrun = fabs(v.i) > 1E-6; 138 | } else { 139 | stack_while[top].isrun = v.s[0] != 0; 140 | } 141 | 142 | if (!stack_while[top].isrun) { 143 | while (cp < code_size && stricmp(code[cp].line,"WEND")) { 144 | cp++; 145 | } 146 | if (cp >= code_size) { 147 | fprintf(stderr, "WHILE 没有对应的 WEND\n"); 148 | exit(EXIT_FAILURE); 149 | } else { 150 | cp--; 151 | } 152 | } 153 | top_while++; 154 | return; 155 | 156 | errorhandler: 157 | fprintf(stderr, "Line %d: 语法错误!\n", code[cp].ln); 158 | exit(EXIT_FAILURE); 159 | } 160 | 161 | void exec_wend(const STRING line) { 162 | if (top_while < 0) { 163 | fprintf(stderr, "WEND 没有对应的 WHILE\n"); 164 | exit(EXIT_FAILURE); 165 | } else if (stricmp(line, "WEND")) { 166 | fprintf(stderr, "Line %d: 语法错误!\n", code[cp].ln); 167 | exit(EXIT_FAILURE); 168 | } 169 | 170 | if (stack_while[top_while].isrun) { 171 | cp = stack_while[top_while].ln - 1; 172 | } 173 | top_while--; 174 | } 175 | 176 | static int stack_if[MEMORY_SIZE]; 177 | static int top_if = -1; 178 | 179 | void exec_if(const STRING line) { 180 | STRING l; 181 | char *s; 182 | int top = top_if + 1; 183 | VARIANT v; 184 | 185 | if (strnicmp(line, "IF ", 3)) { 186 | goto errorhandler; 187 | } else if (top >= MEMORY_SIZE) { 188 | fprintf(stderr, "IF 嵌套过深!\n"); 189 | exit(EXIT_FAILURE); 190 | } 191 | 192 | strcpy(l, line); 193 | s = strstr(l, "THEN"); 194 | if (!s || stricmp(s, "THEN")) { 195 | goto errorhandler; 196 | } else { 197 | *s = 0; 198 | } 199 | 200 | s = l + 3; 201 | v = eval(s); 202 | if (v.type == var_double) { 203 | stack_if[top] = fabs(v.i) > 1E-6; 204 | } else { 205 | stack_if[top] = v.s[0] != 0; 206 | } 207 | 208 | if (!stack_if[top]) { 209 | while (cp < code_size && stricmp(code[cp].line,"ELSE") 210 | && stricmp(code[cp].line,"END IF")) { 211 | cp++; 212 | } 213 | if (cp >= code_size) { 214 | fprintf(stderr, "IF 没有对应的 END IF\n"); 215 | exit(EXIT_FAILURE); 216 | } else { 217 | cp--; 218 | } 219 | } 220 | top_if++; 221 | return; 222 | 223 | errorhandler: 224 | fprintf(stderr, "Line %d: 语法错误!\n", code[cp].ln); 225 | exit(EXIT_FAILURE); 226 | } 227 | 228 | void exec_else(const STRING line) { 229 | if (top_if < 0) { 230 | fprintf(stderr, "ELSE 没有对应的 IF\n"); 231 | exit(EXIT_FAILURE); 232 | } else if (stricmp(line, "ELSE")) { 233 | fprintf(stderr, "Line %d: 语法错误!\n", code[cp].ln); 234 | exit(EXIT_FAILURE); 235 | } 236 | 237 | stack_if[top_if] = !stack_if[top_if]; 238 | if (!stack_if[top_if]) { 239 | while (cp < code_size && stricmp(code[cp].line,"END IF")) { 240 | cp++; 241 | } 242 | if (cp >= code_size) { 243 | fprintf(stderr, "IF 没有对应的 END IF\n"); 244 | exit(EXIT_FAILURE); 245 | } else { 246 | cp--; 247 | } 248 | } 249 | } 250 | 251 | void exec_endif(const STRING line) { 252 | if (top_if < 0) { 253 | fprintf(stderr, "END IF 没有对应的 IF\n"); 254 | exit(EXIT_FAILURE); 255 | } else if (stricmp(line, "END IF")) { 256 | fprintf(stderr, "Line %d: 语法错误!\n", code[cp].ln); 257 | exit(EXIT_FAILURE); 258 | } 259 | 260 | top_if--; 261 | } 262 | 263 | void exec_goto(const STRING line) { 264 | int ln; 265 | 266 | if (strnicmp(line, "GOTO ", 5)) { 267 | fprintf(stderr, "Line %d: 语法错误!\n", code[cp].ln); 268 | exit(EXIT_FAILURE); 269 | } 270 | 271 | ln = (int)eval(line + 5).i; 272 | if (ln > code[cp].ln) { 273 | while (cp < code_size && ln != code[cp].ln) { 274 | if (!strnicmp(code[cp].line, "IF ", 3)) { 275 | top_if++; 276 | stack_if[top_if] = 1; 277 | } else if (!stricmp(code[cp].line, "ELSE")) { 278 | stack_if[top_if] = 1; 279 | } else if (!stricmp(code[cp].line, "END IF")) { 280 | top_if--; 281 | } else if (!strnicmp(code[cp].line, "WHILE ", 6)) { 282 | top_while++; 283 | stack_while[top_while].isrun = 1; 284 | stack_while[top_while].ln = cp; 285 | } else if (!stricmp(code[cp].line, "WEND")) { 286 | top_while--; 287 | } else if (!strnicmp(code[cp].line, "FOR ", 4)) { 288 | int i = 4; 289 | VARIANT v; 290 | while (isspace(code[cp].line[i])) i++; 291 | v = memory[toupper(code[cp].line[i])-'A']; 292 | exec_for(code[cp].line); 293 | memory[toupper(code[cp].line[i])-'A'] = v; 294 | } else if (!stricmp(code[cp].line, "NEXT")) { 295 | top_for--; 296 | } 297 | cp++; 298 | } 299 | } else if (ln < code[cp].ln) { 300 | while (cp > 0 && ln != code[cp].ln) { 301 | if (!strnicmp(code[cp].line, "IF ", 3)) { 302 | top_if--; 303 | } else if (!stricmp(code[cp].line, "ELSE")) { 304 | stack_if[top_if] = 1; 305 | } else if (!stricmp(code[cp].line, "END IF")) { 306 | top_if++; 307 | stack_if[top_if] = 1; 308 | } else if (!strnicmp(code[cp].line, "WHILE ", 6)) { 309 | top_while--; 310 | } else if (!stricmp(code[cp].line, "WEND")) { 311 | int p = cp - 1; 312 | top_while++; 313 | while (p > 0 && strnicmp(code[p].line, "WHILE ", 6)) { 314 | p--; 315 | } 316 | stack_while[top_while].isrun = 1; 317 | stack_while[top_while].ln = p; 318 | } else if (!strnicmp(code[cp].line, "FOR ", 4)) { 319 | top_for--; 320 | } else if (!stricmp(code[cp].line, "NEXT")) { 321 | int p = cp; 322 | VARIANT v; 323 | int i = 4; 324 | 325 | while (cp > 0 && strnicmp(code[cp].line, "FOR ", 4)) { 326 | cp--; 327 | } 328 | while (isspace(code[cp].line[i])) i++; 329 | v = memory[toupper(code[cp].line[i])-'A']; 330 | exec_for(code[cp].line); 331 | memory[toupper(code[cp].line[i])-'A'] = v; 332 | cp = p; 333 | } 334 | cp--; 335 | } 336 | } else { 337 | fprintf(stderr, "Line %d: 死循环!\n", code[cp].ln); 338 | exit(EXIT_FAILURE); 339 | } 340 | 341 | if (ln == code[cp].ln) { 342 | cp--; 343 | } else { 344 | fprintf(stderr, "标号 %d 不存在!\n", ln); 345 | exit(EXIT_FAILURE); 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /src/syntax.h: -------------------------------------------------------------------------------- 1 | #ifndef __GRAMMAR_H_ 2 | #define __GRAMMAR_H_ 3 | 4 | #include "basic_io.h" 5 | #include "lexical.h" 6 | 7 | void exec_for(const STRING line); 8 | void exec_next(const STRING line); 9 | void exec_while(const STRING line); 10 | void exec_wend(const STRING line); 11 | void exec_if(const STRING line); 12 | void exec_else(const STRING line); 13 | void exec_endif(const STRING line); 14 | void exec_goto(const STRING line); 15 | 16 | #endif 17 | --------------------------------------------------------------------------------