basic.c
· 13 KiB · C
Eredeti
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <time.h>
/* ---- constants ---- */
#define MAXLINES 1000
#define MAXBUF 256
#define MAXVARS 256
#define MAXSTACK 256
#define MAXDATA 1024
/* ---- program storage ---- */
typedef struct { int num; char text[MAXBUF]; } Line;
Line program[MAXLINES];
int prog_cnt = 0;
/* ---- variables (numeric only for now) ---- */
char var_names[MAXVARS][3];
double var_vals[MAXVARS];
int var_cnt = 0;
/* ---- DATA pool ---- */
double data_pool[MAXDATA];
int data_count = 0;
int data_pos = 0;
/* ---- GOSUB stack (BAKSTK) ---- */
int call_stack[MAXSTACK];
int call_sp = 0;
/* ---- FOR/NEXT stack ---- */
typedef struct { int var_idx; double limit; double step; int line_idx; } ForEntry;
ForEntry for_stack[MAXSTACK];
int for_sp = 0;
/* ---- globals ---- */
double fac;
int cur_line_idx; /* index into program[] */
int running;
/* ============================================================
FIND_LINE - like SRCHLN
============================================================ */
int find_line(int num) {
for (int i = 0; i < prog_cnt; i++)
if (program[i].num == num) return i;
return -1;
}
/* ============================================================
PTRGET - find or create variable
============================================================ */
int ptrget(const char *name) {
char n[3] = {0};
n[0] = toupper(name[0]);
if (isalnum(name[1])) n[1] = toupper(name[1]);
for (int i = 0; i < var_cnt; i++)
if (strcmp(n, var_names[i]) == 0) return i;
strcpy(var_names[var_cnt], n);
var_vals[var_cnt] = 0.0;
return var_cnt++;
}
/* ============================================================
EXPRESSION PARSER (EVAL / OPRND)
Supports: numbers, variables, +,-,*,/,^,(),ABS,SQR,RND,INT
============================================================ */
double expr(char **p);
static void skip_ws(char **p) { while (**p == ' ') (*p)++; }
static double primary(char **p) {
skip_ws(p);
double x = 0;
/* Unary minus */
if (**p == '-') { (*p)++; return -primary(p); }
/* Parenthesis */
if (**p == '(') {
(*p)++;
x = expr(p);
skip_ws(p);
if (**p == ')') (*p)++;
return x;
}
/* String check: ABS, SQR, RND, INT */
if (strncasecmp(*p, "ABS(", 4) == 0) { *p+=4; x=fabs(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; }
if (strncasecmp(*p, "SQR(", 4) == 0) { *p+=4; x=sqrt(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; }
if (strncasecmp(*p, "INT(", 4) == 0) { *p+=4; x=floor(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; }
if (strncasecmp(*p, "RND(", 4) == 0) { *p+=4; expr(p); skip_ws(p); if(**p==')')(*p)++; return (double)rand()/RAND_MAX; }
if (strncasecmp(*p, "SIN(", 4) == 0) { *p+=4; x=sin(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; }
if (strncasecmp(*p, "COS(", 4) == 0) { *p+=4; x=cos(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; }
if (strncasecmp(*p, "LOG(", 4) == 0) { *p+=4; x=log(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; }
if (strncasecmp(*p, "EXP(", 4) == 0) { *p+=4; x=exp(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; }
/* Number literal */
if (isdigit(**p) || **p == '.') {
char *end; x = strtod(*p, &end); *p = end; return x;
}
/* Variable */
if (isalpha(**p)) {
char name[3] = {0};
name[0] = toupper(*(*p)++);
if (isalnum(**p)) name[1] = toupper(*(*p)++);
return var_vals[ptrget(name)];
}
return 0;
}
static double power(char **p) {
double x = primary(p);
skip_ws(p);
if (**p == '^') { (*p)++; x = pow(x, primary(p)); }
return x;
}
static double term(char **p) {
double x = power(p);
skip_ws(p);
while (**p == '*' || **p == '/') {
char op = *(*p)++;
double y = power(p);
x = (op == '*') ? x*y : x/y;
skip_ws(p);
}
return x;
}
double expr(char **p) {
double x = term(p);
skip_ws(p);
while (**p == '+' || **p == '-') {
char op = *(*p)++;
double y = term(p);
x = (op == '+') ? x+y : x-y;
skip_ws(p);
}
return fac = x;
}
/* Relational: returns 1/0 */
double rel_expr(char **p) {
double x = expr(p);
skip_ws(p);
if (strncmp(*p,"<>",2)==0) { *p+=2; return x != expr(p); }
if (strncmp(*p,"<=",2)==0) { *p+=2; return x <= expr(p); }
if (strncmp(*p,">=",2)==0) { *p+=2; return x >= expr(p); }
if (**p=='<') { (*p)++; return x < expr(p); }
if (**p=='>') { (*p)++; return x > expr(p); }
if (**p=='=') { (*p)++; return x == expr(p); }
return x;
}
/* ============================================================
COLLECT DATA (FDTLP)
============================================================ */
void collect_data() {
data_count = 0; data_pos = 0;
for (int i = 0; i < prog_cnt; i++) {
char *p = program[i].text;
while (*p && strncasecmp(p,"DATA",4)!=0) p++;
if (!*p) continue;
p += 4;
while (*p) {
skip_ws(&p);
if (*p == ',') p++;
skip_ws(&p);
if (!*p) break;
char *end; double v = strtod(p,&end);
if (end != p) { data_pool[data_count++] = v; p = end; }
else break;
}
}
}
/* ============================================================
INSERT LINE (PROMPT / TOKENIZE)
============================================================ */
void insert_line(int num, char *text) {
/* Replace existing */
for (int i = 0; i < prog_cnt; i++) {
if (program[i].num == num) {
if (!text || !*text) {
/* Delete */
for (int j = i; j < prog_cnt-1; j++) program[j]=program[j+1];
prog_cnt--; return;
}
strncpy(program[i].text, text, MAXBUF-1);
return;
}
}
if (!text || !*text) return;
/* Insert sorted */
program[prog_cnt].num = num;
strncpy(program[prog_cnt].text, text, MAXBUF-1);
prog_cnt++;
/* Bubble sort by line number */
for (int i = prog_cnt-1; i > 0 && program[i].num < program[i-1].num; i--) {
Line tmp = program[i]; program[i] = program[i-1]; program[i-1] = tmp;
}
}
/* ============================================================
EXECUTE ONE LINE (NEWSTT / EXEC / FNCTAB)
============================================================ */
void execute_line(char *stmt);
void run_from(int idx);
void execute_stmt(char *p) {
skip_ws(&p);
/* REM / ' */
if (strncasecmp(p,"REM",3)==0 || *p=='\'') return;
/* PRINT */
if (strncasecmp(p,"PRINT",5)==0) {
p += 5;
while (*p) {
skip_ws(&p);
if (!*p) break;
if (*p == '"') {
p++;
while (*p && *p!='"') putchar(*p++);
if (*p=='"') p++;
} else if (*p==';') { p++; continue; }
else if (*p==',') { printf("\t"); p++; continue; }
else { printf("%g", rel_expr(&p)); }
skip_ws(&p);
}
printf("\n");
return;
}
/* INPUT */
if (strncasecmp(p,"INPUT",5)==0) {
p += 5; skip_ws(&p);
if (*p=='"') { p++; while(*p && *p!='"') putchar(*p++); if(*p=='"')p++; if(*p==',')p++; printf("? "); }
else printf("? ");
char name[3]={0}; name[0]=toupper(*p++); if(isalnum(*p)) name[1]=toupper(*p++);
double v; scanf("%lf",&v); getchar();
var_vals[ptrget(name)] = v;
return;
}
/* LET or assignment */
if (strncasecmp(p,"LET",3)==0) p+=3;
/* Detect assignment: name = */
if (isalpha(*p)) {
char name[3]={0}; char *save=p;
name[0]=toupper(*p++); if(isalnum(*p)) name[1]=toupper(*p++);
skip_ws(&p);
if (*p=='=') {
p++; var_vals[ptrget(name)] = expr(&p); return;
}
p = save;
}
/* IF */
if (strncasecmp(p,"IF",2)==0) {
p+=2; skip_ws(&p);
double cond = rel_expr(&p);
skip_ws(&p);
if (strncasecmp(p,"THEN",4)==0) p+=4;
skip_ws(&p);
if (cond) execute_stmt(p);
return;
}
/* GOTO */
if (strncasecmp(p,"GOTO",4)==0) {
p+=4; int tgt=(int)expr(&p);
int idx=find_line(tgt);
if (idx<0) { printf("?Undefined line %d\n",tgt); running=0; return; }
cur_line_idx = idx; running = 2; return;
}
/* GOSUB */
if (strncasecmp(p,"GOSUB",5)==0) {
p+=5; int tgt=(int)expr(&p);
int idx=find_line(tgt);
if (idx<0) { printf("?Undefined line %d\n",tgt); running=0; return; }
call_stack[call_sp++] = cur_line_idx+1;
cur_line_idx = idx; running = 2; return;
}
/* RETURN */
if (strncasecmp(p,"RETURN",6)==0) {
if (call_sp<=0) { printf("?RETURN without GOSUB\n"); running=0; return; }
cur_line_idx = call_stack[--call_sp]; running = 2; return;
}
/* FOR */
if (strncasecmp(p,"FOR",3)==0) {
p+=3; skip_ws(&p);
char name[3]={0}; name[0]=toupper(*p++); if(isalnum(*p)) name[1]=toupper(*p++);
skip_ws(&p); if(*p=='=') p++;
double start=expr(&p);
skip_ws(&p); if(strncasecmp(p,"TO",2)==0) p+=2;
double limit=expr(&p);
double step=1.0;
skip_ws(&p); if(strncasecmp(p,"STEP",4)==0) { p+=4; step=expr(&p); }
int vi=ptrget(name); var_vals[vi]=start;
for_stack[for_sp].var_idx=vi;
for_stack[for_sp].limit=limit;
for_stack[for_sp].step=step;
for_stack[for_sp].line_idx=cur_line_idx;
for_sp++;
return;
}
/* NEXT */
if (strncasecmp(p,"NEXT",4)==0) {
if (for_sp<=0) { printf("?NEXT without FOR\n"); running=0; return; }
ForEntry *fe = &for_stack[for_sp-1];
var_vals[fe->var_idx] += fe->step;
if ((fe->step>0 && var_vals[fe->var_idx] <= fe->limit) ||
(fe->step<0 && var_vals[fe->var_idx] >= fe->limit)) {
cur_line_idx = fe->line_idx; running=2;
} else { for_sp--; }
return;
}
/* READ */
if (strncasecmp(p,"READ",4)==0) {
p+=4; skip_ws(&p);
while(*p) {
skip_ws(&p);
if(*p==',') p++;
skip_ws(&p);
if(!*p) break;
char name[3]={0}; name[0]=toupper(*p++); if(isalnum(*p)) name[1]=toupper(*p++);
if (data_pos>=data_count) { printf("?Out of DATA\n"); running=0; return; }
var_vals[ptrget(name)] = data_pool[data_pos++];
}
return;
}
/* RESTORE */
if (strncasecmp(p,"RESTORE",7)==0) { data_pos=0; return; }
/* DATA */
if (strncasecmp(p,"DATA",4)==0) return; /* handled at collect time */
/* END / STOP */
if (strncasecmp(p,"END",3)==0 || strncasecmp(p,"STOP",4)==0) { running=0; return; }
/* LIST */
if (strncasecmp(p,"LIST",4)==0) {
for(int i=0;i<prog_cnt;i++) printf("%d %s\n",program[i].num,program[i].text);
return;
}
/* RUN */
if (strncasecmp(p,"RUN",3)==0) {
collect_data(); data_pos=0; call_sp=0; for_sp=0; var_cnt=0;
run_from(0); return;
}
/* NEW */
if (strncasecmp(p,"NEW",3)==0) { prog_cnt=0; var_cnt=0; call_sp=0; for_sp=0; data_count=0; printf("Ok\n"); return; }
/* CLEAR */
if (strncasecmp(p,"CLEAR",5)==0) { var_cnt=0; call_sp=0; for_sp=0; data_pos=0; return; }
/* DIM stub */
if (strncasecmp(p,"DIM",3)==0) return;
printf("?Syntax error\n");
}
/* Execute multiple statements on one line (: separated) */
void execute_line(char *text) {
char buf[MAXBUF]; strncpy(buf,text,MAXBUF-1);
char *tok = strtok(buf,":");
while (tok && running) {
execute_stmt(tok);
tok = strtok(NULL,":");
}
}
/* RUN loop from line index */
void run_from(int idx) {
running = 1;
cur_line_idx = idx;
while (running && cur_line_idx < prog_cnt) {
int save = cur_line_idx;
execute_line(program[cur_line_idx].text);
if (running == 2) { running = 1; } /* GOTO/GOSUB already set cur_line_idx */
else if (running) cur_line_idx++;
}
running = 0;
}
/* ============================================================
MAIN - READY loop
============================================================ */
int main() {
srand((unsigned)time(NULL));
printf("MS BASIC-80 C Port\nOk\n");
char input[MAXBUF];
while (1) {
printf("> ");
if (!fgets(input,MAXBUF,stdin)) break;
input[strcspn(input,"\n")] = 0;
if (!*input) continue;
/* Check for line number */
char *p = input;
skip_ws(&p);
if (isdigit(*p)) {
int num = (int)strtol(p,&p,10);
skip_ws(&p);
insert_line(num, *p ? p : "");
} else {
/* Direct statement */
if (strncasecmp(p,"RUN",3)==0) { collect_data(); data_pos=0; call_sp=0; for_sp=0; var_cnt=0; run_from(0); }
else if (strncasecmp(p,"LIST",4)==0) { for(int i=0;i<prog_cnt;i++) printf("%d %s\n",program[i].num,program[i].text); }
else if (strncasecmp(p,"NEW",3)==0) { prog_cnt=0; var_cnt=0; call_sp=0; for_sp=0; data_count=0; }
else execute_stmt(p);
}
printf("Ok\n");
}
return 0;
}
| 1 | #include <stdio.h> |
| 2 | #include <stdlib.h> |
| 3 | #include <string.h> |
| 4 | #include <ctype.h> |
| 5 | #include <math.h> |
| 6 | #include <time.h> |
| 7 | |
| 8 | /* ---- constants ---- */ |
| 9 | #define MAXLINES 1000 |
| 10 | #define MAXBUF 256 |
| 11 | #define MAXVARS 256 |
| 12 | #define MAXSTACK 256 |
| 13 | #define MAXDATA 1024 |
| 14 | |
| 15 | /* ---- program storage ---- */ |
| 16 | typedef struct { int num; char text[MAXBUF]; } Line; |
| 17 | Line program[MAXLINES]; |
| 18 | int prog_cnt = 0; |
| 19 | |
| 20 | /* ---- variables (numeric only for now) ---- */ |
| 21 | char var_names[MAXVARS][3]; |
| 22 | double var_vals[MAXVARS]; |
| 23 | int var_cnt = 0; |
| 24 | |
| 25 | /* ---- DATA pool ---- */ |
| 26 | double data_pool[MAXDATA]; |
| 27 | int data_count = 0; |
| 28 | int data_pos = 0; |
| 29 | |
| 30 | /* ---- GOSUB stack (BAKSTK) ---- */ |
| 31 | int call_stack[MAXSTACK]; |
| 32 | int call_sp = 0; |
| 33 | |
| 34 | /* ---- FOR/NEXT stack ---- */ |
| 35 | typedef struct { int var_idx; double limit; double step; int line_idx; } ForEntry; |
| 36 | ForEntry for_stack[MAXSTACK]; |
| 37 | int for_sp = 0; |
| 38 | |
| 39 | /* ---- globals ---- */ |
| 40 | double fac; |
| 41 | int cur_line_idx; /* index into program[] */ |
| 42 | int running; |
| 43 | |
| 44 | /* ============================================================ |
| 45 | FIND_LINE - like SRCHLN |
| 46 | ============================================================ */ |
| 47 | int find_line(int num) { |
| 48 | for (int i = 0; i < prog_cnt; i++) |
| 49 | if (program[i].num == num) return i; |
| 50 | return -1; |
| 51 | } |
| 52 | |
| 53 | /* ============================================================ |
| 54 | PTRGET - find or create variable |
| 55 | ============================================================ */ |
| 56 | int ptrget(const char *name) { |
| 57 | char n[3] = {0}; |
| 58 | n[0] = toupper(name[0]); |
| 59 | if (isalnum(name[1])) n[1] = toupper(name[1]); |
| 60 | for (int i = 0; i < var_cnt; i++) |
| 61 | if (strcmp(n, var_names[i]) == 0) return i; |
| 62 | strcpy(var_names[var_cnt], n); |
| 63 | var_vals[var_cnt] = 0.0; |
| 64 | return var_cnt++; |
| 65 | } |
| 66 | |
| 67 | /* ============================================================ |
| 68 | EXPRESSION PARSER (EVAL / OPRND) |
| 69 | Supports: numbers, variables, +,-,*,/,^,(),ABS,SQR,RND,INT |
| 70 | ============================================================ */ |
| 71 | double expr(char **p); |
| 72 | |
| 73 | static void skip_ws(char **p) { while (**p == ' ') (*p)++; } |
| 74 | |
| 75 | static double primary(char **p) { |
| 76 | skip_ws(p); |
| 77 | double x = 0; |
| 78 | /* Unary minus */ |
| 79 | if (**p == '-') { (*p)++; return -primary(p); } |
| 80 | /* Parenthesis */ |
| 81 | if (**p == '(') { |
| 82 | (*p)++; |
| 83 | x = expr(p); |
| 84 | skip_ws(p); |
| 85 | if (**p == ')') (*p)++; |
| 86 | return x; |
| 87 | } |
| 88 | /* String check: ABS, SQR, RND, INT */ |
| 89 | if (strncasecmp(*p, "ABS(", 4) == 0) { *p+=4; x=fabs(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; } |
| 90 | if (strncasecmp(*p, "SQR(", 4) == 0) { *p+=4; x=sqrt(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; } |
| 91 | if (strncasecmp(*p, "INT(", 4) == 0) { *p+=4; x=floor(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; } |
| 92 | if (strncasecmp(*p, "RND(", 4) == 0) { *p+=4; expr(p); skip_ws(p); if(**p==')')(*p)++; return (double)rand()/RAND_MAX; } |
| 93 | if (strncasecmp(*p, "SIN(", 4) == 0) { *p+=4; x=sin(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; } |
| 94 | if (strncasecmp(*p, "COS(", 4) == 0) { *p+=4; x=cos(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; } |
| 95 | if (strncasecmp(*p, "LOG(", 4) == 0) { *p+=4; x=log(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; } |
| 96 | if (strncasecmp(*p, "EXP(", 4) == 0) { *p+=4; x=exp(expr(p)); skip_ws(p); if(**p==')')(*p)++; return x; } |
| 97 | /* Number literal */ |
| 98 | if (isdigit(**p) || **p == '.') { |
| 99 | char *end; x = strtod(*p, &end); *p = end; return x; |
| 100 | } |
| 101 | /* Variable */ |
| 102 | if (isalpha(**p)) { |
| 103 | char name[3] = {0}; |
| 104 | name[0] = toupper(*(*p)++); |
| 105 | if (isalnum(**p)) name[1] = toupper(*(*p)++); |
| 106 | return var_vals[ptrget(name)]; |
| 107 | } |
| 108 | return 0; |
| 109 | } |
| 110 | |
| 111 | static double power(char **p) { |
| 112 | double x = primary(p); |
| 113 | skip_ws(p); |
| 114 | if (**p == '^') { (*p)++; x = pow(x, primary(p)); } |
| 115 | return x; |
| 116 | } |
| 117 | |
| 118 | static double term(char **p) { |
| 119 | double x = power(p); |
| 120 | skip_ws(p); |
| 121 | while (**p == '*' || **p == '/') { |
| 122 | char op = *(*p)++; |
| 123 | double y = power(p); |
| 124 | x = (op == '*') ? x*y : x/y; |
| 125 | skip_ws(p); |
| 126 | } |
| 127 | return x; |
| 128 | } |
| 129 | |
| 130 | double expr(char **p) { |
| 131 | double x = term(p); |
| 132 | skip_ws(p); |
| 133 | while (**p == '+' || **p == '-') { |
| 134 | char op = *(*p)++; |
| 135 | double y = term(p); |
| 136 | x = (op == '+') ? x+y : x-y; |
| 137 | skip_ws(p); |
| 138 | } |
| 139 | return fac = x; |
| 140 | } |
| 141 | |
| 142 | /* Relational: returns 1/0 */ |
| 143 | double rel_expr(char **p) { |
| 144 | double x = expr(p); |
| 145 | skip_ws(p); |
| 146 | if (strncmp(*p,"<>",2)==0) { *p+=2; return x != expr(p); } |
| 147 | if (strncmp(*p,"<=",2)==0) { *p+=2; return x <= expr(p); } |
| 148 | if (strncmp(*p,">=",2)==0) { *p+=2; return x >= expr(p); } |
| 149 | if (**p=='<') { (*p)++; return x < expr(p); } |
| 150 | if (**p=='>') { (*p)++; return x > expr(p); } |
| 151 | if (**p=='=') { (*p)++; return x == expr(p); } |
| 152 | return x; |
| 153 | } |
| 154 | |
| 155 | /* ============================================================ |
| 156 | COLLECT DATA (FDTLP) |
| 157 | ============================================================ */ |
| 158 | void collect_data() { |
| 159 | data_count = 0; data_pos = 0; |
| 160 | for (int i = 0; i < prog_cnt; i++) { |
| 161 | char *p = program[i].text; |
| 162 | while (*p && strncasecmp(p,"DATA",4)!=0) p++; |
| 163 | if (!*p) continue; |
| 164 | p += 4; |
| 165 | while (*p) { |
| 166 | skip_ws(&p); |
| 167 | if (*p == ',') p++; |
| 168 | skip_ws(&p); |
| 169 | if (!*p) break; |
| 170 | char *end; double v = strtod(p,&end); |
| 171 | if (end != p) { data_pool[data_count++] = v; p = end; } |
| 172 | else break; |
| 173 | } |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | /* ============================================================ |
| 178 | INSERT LINE (PROMPT / TOKENIZE) |
| 179 | ============================================================ */ |
| 180 | void insert_line(int num, char *text) { |
| 181 | /* Replace existing */ |
| 182 | for (int i = 0; i < prog_cnt; i++) { |
| 183 | if (program[i].num == num) { |
| 184 | if (!text || !*text) { |
| 185 | /* Delete */ |
| 186 | for (int j = i; j < prog_cnt-1; j++) program[j]=program[j+1]; |
| 187 | prog_cnt--; return; |
| 188 | } |
| 189 | strncpy(program[i].text, text, MAXBUF-1); |
| 190 | return; |
| 191 | } |
| 192 | } |
| 193 | if (!text || !*text) return; |
| 194 | /* Insert sorted */ |
| 195 | program[prog_cnt].num = num; |
| 196 | strncpy(program[prog_cnt].text, text, MAXBUF-1); |
| 197 | prog_cnt++; |
| 198 | /* Bubble sort by line number */ |
| 199 | for (int i = prog_cnt-1; i > 0 && program[i].num < program[i-1].num; i--) { |
| 200 | Line tmp = program[i]; program[i] = program[i-1]; program[i-1] = tmp; |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | /* ============================================================ |
| 205 | EXECUTE ONE LINE (NEWSTT / EXEC / FNCTAB) |
| 206 | ============================================================ */ |
| 207 | void execute_line(char *stmt); |
| 208 | void run_from(int idx); |
| 209 | |
| 210 | void execute_stmt(char *p) { |
| 211 | skip_ws(&p); |
| 212 | /* REM / ' */ |
| 213 | if (strncasecmp(p,"REM",3)==0 || *p=='\'') return; |
| 214 | /* PRINT */ |
| 215 | if (strncasecmp(p,"PRINT",5)==0) { |
| 216 | p += 5; |
| 217 | while (*p) { |
| 218 | skip_ws(&p); |
| 219 | if (!*p) break; |
| 220 | if (*p == '"') { |
| 221 | p++; |
| 222 | while (*p && *p!='"') putchar(*p++); |
| 223 | if (*p=='"') p++; |
| 224 | } else if (*p==';') { p++; continue; } |
| 225 | else if (*p==',') { printf("\t"); p++; continue; } |
| 226 | else { printf("%g", rel_expr(&p)); } |
| 227 | skip_ws(&p); |
| 228 | } |
| 229 | printf("\n"); |
| 230 | return; |
| 231 | } |
| 232 | /* INPUT */ |
| 233 | if (strncasecmp(p,"INPUT",5)==0) { |
| 234 | p += 5; skip_ws(&p); |
| 235 | if (*p=='"') { p++; while(*p && *p!='"') putchar(*p++); if(*p=='"')p++; if(*p==',')p++; printf("? "); } |
| 236 | else printf("? "); |
| 237 | char name[3]={0}; name[0]=toupper(*p++); if(isalnum(*p)) name[1]=toupper(*p++); |
| 238 | double v; scanf("%lf",&v); getchar(); |
| 239 | var_vals[ptrget(name)] = v; |
| 240 | return; |
| 241 | } |
| 242 | /* LET or assignment */ |
| 243 | if (strncasecmp(p,"LET",3)==0) p+=3; |
| 244 | /* Detect assignment: name = */ |
| 245 | if (isalpha(*p)) { |
| 246 | char name[3]={0}; char *save=p; |
| 247 | name[0]=toupper(*p++); if(isalnum(*p)) name[1]=toupper(*p++); |
| 248 | skip_ws(&p); |
| 249 | if (*p=='=') { |
| 250 | p++; var_vals[ptrget(name)] = expr(&p); return; |
| 251 | } |
| 252 | p = save; |
| 253 | } |
| 254 | /* IF */ |
| 255 | if (strncasecmp(p,"IF",2)==0) { |
| 256 | p+=2; skip_ws(&p); |
| 257 | double cond = rel_expr(&p); |
| 258 | skip_ws(&p); |
| 259 | if (strncasecmp(p,"THEN",4)==0) p+=4; |
| 260 | skip_ws(&p); |
| 261 | if (cond) execute_stmt(p); |
| 262 | return; |
| 263 | } |
| 264 | /* GOTO */ |
| 265 | if (strncasecmp(p,"GOTO",4)==0) { |
| 266 | p+=4; int tgt=(int)expr(&p); |
| 267 | int idx=find_line(tgt); |
| 268 | if (idx<0) { printf("?Undefined line %d\n",tgt); running=0; return; } |
| 269 | cur_line_idx = idx; running = 2; return; |
| 270 | } |
| 271 | /* GOSUB */ |
| 272 | if (strncasecmp(p,"GOSUB",5)==0) { |
| 273 | p+=5; int tgt=(int)expr(&p); |
| 274 | int idx=find_line(tgt); |
| 275 | if (idx<0) { printf("?Undefined line %d\n",tgt); running=0; return; } |
| 276 | call_stack[call_sp++] = cur_line_idx+1; |
| 277 | cur_line_idx = idx; running = 2; return; |
| 278 | } |
| 279 | /* RETURN */ |
| 280 | if (strncasecmp(p,"RETURN",6)==0) { |
| 281 | if (call_sp<=0) { printf("?RETURN without GOSUB\n"); running=0; return; } |
| 282 | cur_line_idx = call_stack[--call_sp]; running = 2; return; |
| 283 | } |
| 284 | /* FOR */ |
| 285 | if (strncasecmp(p,"FOR",3)==0) { |
| 286 | p+=3; skip_ws(&p); |
| 287 | char name[3]={0}; name[0]=toupper(*p++); if(isalnum(*p)) name[1]=toupper(*p++); |
| 288 | skip_ws(&p); if(*p=='=') p++; |
| 289 | double start=expr(&p); |
| 290 | skip_ws(&p); if(strncasecmp(p,"TO",2)==0) p+=2; |
| 291 | double limit=expr(&p); |
| 292 | double step=1.0; |
| 293 | skip_ws(&p); if(strncasecmp(p,"STEP",4)==0) { p+=4; step=expr(&p); } |
| 294 | int vi=ptrget(name); var_vals[vi]=start; |
| 295 | for_stack[for_sp].var_idx=vi; |
| 296 | for_stack[for_sp].limit=limit; |
| 297 | for_stack[for_sp].step=step; |
| 298 | for_stack[for_sp].line_idx=cur_line_idx; |
| 299 | for_sp++; |
| 300 | return; |
| 301 | } |
| 302 | /* NEXT */ |
| 303 | if (strncasecmp(p,"NEXT",4)==0) { |
| 304 | if (for_sp<=0) { printf("?NEXT without FOR\n"); running=0; return; } |
| 305 | ForEntry *fe = &for_stack[for_sp-1]; |
| 306 | var_vals[fe->var_idx] += fe->step; |
| 307 | if ((fe->step>0 && var_vals[fe->var_idx] <= fe->limit) || |
| 308 | (fe->step<0 && var_vals[fe->var_idx] >= fe->limit)) { |
| 309 | cur_line_idx = fe->line_idx; running=2; |
| 310 | } else { for_sp--; } |
| 311 | return; |
| 312 | } |
| 313 | /* READ */ |
| 314 | if (strncasecmp(p,"READ",4)==0) { |
| 315 | p+=4; skip_ws(&p); |
| 316 | while(*p) { |
| 317 | skip_ws(&p); |
| 318 | if(*p==',') p++; |
| 319 | skip_ws(&p); |
| 320 | if(!*p) break; |
| 321 | char name[3]={0}; name[0]=toupper(*p++); if(isalnum(*p)) name[1]=toupper(*p++); |
| 322 | if (data_pos>=data_count) { printf("?Out of DATA\n"); running=0; return; } |
| 323 | var_vals[ptrget(name)] = data_pool[data_pos++]; |
| 324 | } |
| 325 | return; |
| 326 | } |
| 327 | /* RESTORE */ |
| 328 | if (strncasecmp(p,"RESTORE",7)==0) { data_pos=0; return; } |
| 329 | /* DATA */ |
| 330 | if (strncasecmp(p,"DATA",4)==0) return; /* handled at collect time */ |
| 331 | /* END / STOP */ |
| 332 | if (strncasecmp(p,"END",3)==0 || strncasecmp(p,"STOP",4)==0) { running=0; return; } |
| 333 | /* LIST */ |
| 334 | if (strncasecmp(p,"LIST",4)==0) { |
| 335 | for(int i=0;i<prog_cnt;i++) printf("%d %s\n",program[i].num,program[i].text); |
| 336 | return; |
| 337 | } |
| 338 | /* RUN */ |
| 339 | if (strncasecmp(p,"RUN",3)==0) { |
| 340 | collect_data(); data_pos=0; call_sp=0; for_sp=0; var_cnt=0; |
| 341 | run_from(0); return; |
| 342 | } |
| 343 | /* NEW */ |
| 344 | if (strncasecmp(p,"NEW",3)==0) { prog_cnt=0; var_cnt=0; call_sp=0; for_sp=0; data_count=0; printf("Ok\n"); return; } |
| 345 | /* CLEAR */ |
| 346 | if (strncasecmp(p,"CLEAR",5)==0) { var_cnt=0; call_sp=0; for_sp=0; data_pos=0; return; } |
| 347 | /* DIM stub */ |
| 348 | if (strncasecmp(p,"DIM",3)==0) return; |
| 349 | |
| 350 | printf("?Syntax error\n"); |
| 351 | } |
| 352 | |
| 353 | /* Execute multiple statements on one line (: separated) */ |
| 354 | void execute_line(char *text) { |
| 355 | char buf[MAXBUF]; strncpy(buf,text,MAXBUF-1); |
| 356 | char *tok = strtok(buf,":"); |
| 357 | while (tok && running) { |
| 358 | execute_stmt(tok); |
| 359 | tok = strtok(NULL,":"); |
| 360 | } |
| 361 | } |
| 362 | |
| 363 | /* RUN loop from line index */ |
| 364 | void run_from(int idx) { |
| 365 | running = 1; |
| 366 | cur_line_idx = idx; |
| 367 | while (running && cur_line_idx < prog_cnt) { |
| 368 | int save = cur_line_idx; |
| 369 | execute_line(program[cur_line_idx].text); |
| 370 | if (running == 2) { running = 1; } /* GOTO/GOSUB already set cur_line_idx */ |
| 371 | else if (running) cur_line_idx++; |
| 372 | } |
| 373 | running = 0; |
| 374 | } |
| 375 | |
| 376 | /* ============================================================ |
| 377 | MAIN - READY loop |
| 378 | ============================================================ */ |
| 379 | int main() { |
| 380 | srand((unsigned)time(NULL)); |
| 381 | printf("MS BASIC-80 C Port\nOk\n"); |
| 382 | char input[MAXBUF]; |
| 383 | while (1) { |
| 384 | printf("> "); |
| 385 | if (!fgets(input,MAXBUF,stdin)) break; |
| 386 | input[strcspn(input,"\n")] = 0; |
| 387 | if (!*input) continue; |
| 388 | /* Check for line number */ |
| 389 | char *p = input; |
| 390 | skip_ws(&p); |
| 391 | if (isdigit(*p)) { |
| 392 | int num = (int)strtol(p,&p,10); |
| 393 | skip_ws(&p); |
| 394 | insert_line(num, *p ? p : ""); |
| 395 | } else { |
| 396 | /* Direct statement */ |
| 397 | if (strncasecmp(p,"RUN",3)==0) { collect_data(); data_pos=0; call_sp=0; for_sp=0; var_cnt=0; run_from(0); } |
| 398 | else if (strncasecmp(p,"LIST",4)==0) { for(int i=0;i<prog_cnt;i++) printf("%d %s\n",program[i].num,program[i].text); } |
| 399 | else if (strncasecmp(p,"NEW",3)==0) { prog_cnt=0; var_cnt=0; call_sp=0; for_sp=0; data_count=0; } |
| 400 | else execute_stmt(p); |
| 401 | } |
| 402 | printf("Ok\n"); |
| 403 | } |
| 404 | return 0; |
| 405 | } |