Última atividade 1 month ago

basic.c Bruto
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/* ================================================================
9 MS BASIC-80 Full C Port
10 Ported from Z80 assembly disassembly (FNCTAB/WORDS/EVAL/NEWSTT)
11 ================================================================ */
12
13#define MAXLINES 2000
14#define MAXBUF 512
15#define MAXVARS 512
16#define MAXARR 64
17#define MAXARRLEN 1024
18#define MAXSTACK 256
19#define MAXDATA 2048
20#define MAXSTR 256
21#define MAXFILES 8
22
23/* ---- Program storage ---- */
24typedef struct { int num; char text[MAXBUF]; } Line;
25Line program[MAXLINES];
26int prog_cnt = 0;
27
28/* ---- Variable types ---- */
29#define TYPE_INT 2
30#define TYPE_SNG 4
31#define TYPE_STR 3
32
33typedef struct {
34 char name[MAXSTR];
35 int type;
36 double num;
37 char str[MAXSTR];
38} Var;
39
40Var variables[MAXVARS];
41int var_cnt = 0;
42
43/* ---- Arrays ---- */
44typedef struct {
45 char name[MAXSTR];
46 int type;
47 int dims[4];
48 int ndims;
49 int base; /* OPTION BASE */
50 double *nums;
51 char (*strs)[MAXSTR];
52} Arr;
53
54Arr arrays[MAXARR];
55int arr_cnt = 0;
56int option_base = 0;
57
58/* ---- DATA pool (FDTLP) ---- */
59char data_pool[MAXDATA][MAXSTR];
60int data_count = 0;
61int data_pos = 0;
62
63/* ---- GOSUB stack (BAKSTK) ---- */
64int call_stack[MAXSTACK];
65int call_sp = 0;
66
67/* ---- FOR/NEXT stack ---- */
68typedef struct {
69 int var_idx;
70 double limit;
71 double step;
72 int line_idx;
73} ForEntry;
74ForEntry for_stack[MAXSTACK];
75int for_sp = 0;
76
77/* ---- WHILE/WEND stack ---- */
78int while_stack[MAXSTACK];
79int while_sp = 0;
80
81/* ---- ON ERROR ---- */
82int on_error_line = -1;
83int err_number = 0;
84int err_line = 0;
85
86/* ---- Globals ---- */
87double fac;
88int cur_line_idx;
89int running;
90int tron_flag = 0;
91int null_count = 0; /* NULL() */
92char last_inkey = 0;
93int cont_idx = -1; /* for CONT */
94
95/* ---- File handles (OPEN/CLOSE) ---- */
96FILE *file_handles[MAXFILES];
97char file_modes[MAXFILES]; /* 'I','O','A','R' */
98
99/* ============================================================
100 UTILS
101 ============================================================ */
102static void skip_ws(char **p) { while (**p == ' ' || **p == '\t') (*p)++; }
103
104static char *strupr_local(char *s) {
105 for (char *p = s; *p; p++) *p = toupper(*p);
106 return s;
107}
108
109/* ============================================================
110 LINE MANAGEMENT (SRCHLN / INSERT)
111 ============================================================ */
112int find_line(int num) {
113 for (int i = 0; i < prog_cnt; i++)
114 if (program[i].num == num) return i;
115 return -1;
116}
117
118void insert_line(int num, char *text) {
119 for (int i = 0; i < prog_cnt; i++) {
120 if (program[i].num == num) {
121 if (!text || !*text) {
122 for (int j = i; j < prog_cnt-1; j++) program[j] = program[j+1];
123 prog_cnt--; return;
124 }
125 strncpy(program[i].text, text, MAXBUF-1);
126 return;
127 }
128 }
129 if (!text || !*text) return;
130 program[prog_cnt].num = num;
131 strncpy(program[prog_cnt].text, text, MAXBUF-1);
132 prog_cnt++;
133 for (int i = prog_cnt-1; i > 0 && program[i].num < program[i-1].num; i--) {
134 Line tmp = program[i]; program[i] = program[i-1]; program[i-1] = tmp;
135 }
136}
137
138/* ============================================================
139 VARIABLE MANAGEMENT (PTRGET / GETVAR)
140 ============================================================ */
141int ptrget(const char *name) {
142 for (int i = 0; i < var_cnt; i++)
143 if (strcasecmp(name, variables[i].name) == 0) return i;
144 strncpy(variables[var_cnt].name, name, MAXSTR-1);
145 variables[var_cnt].type = TYPE_SNG;
146 variables[var_cnt].num = 0;
147 variables[var_cnt].str[0] = 0;
148 return var_cnt++;
149}
150
151int is_str_var(const char *name) {
152 return name[strlen(name)-1] == '$';
153}
154
155/* ============================================================
156 ARRAY MANAGEMENT (DIM / SBSCPT)
157 ============================================================ */
158Arr *find_arr(const char *name) {
159 for (int i = 0; i < arr_cnt; i++)
160 if (strcasecmp(name, arrays[i].name) == 0) return &arrays[i];
161 return NULL;
162}
163
164Arr *make_arr(const char *name, int *dims, int ndims) {
165 Arr *a = &arrays[arr_cnt++];
166 strncpy(a->name, name, MAXSTR-1);
167 a->ndims = ndims; a->base = option_base;
168 int total = 1;
169 for (int i = 0; i < ndims; i++) { a->dims[i] = dims[i]+1; total *= (dims[i]+1); }
170 a->type = is_str_var(name) ? TYPE_STR : TYPE_SNG;
171 if (a->type == TYPE_STR) a->strs = calloc(total, sizeof(*a->strs));
172 else a->nums = calloc(total, sizeof(double));
173 return a;
174}
175
176/* ============================================================
177 EXPRESSION PARSER (EVAL / OPRND / PRITAB)
178 Full: + - * / ^ unary- relational AND OR NOT
179 Functions: ABS SQR RND SIN COS TAN ATN LOG EXP INT FIX SGN
180 LEN VAL ASC CHR$ STR$ LEFT$ RIGHT$ MID$ SPACE$
181 HEX$ OCT$ CINT CSNG CDBL INSTR STRING$ FRE POS
182 ============================================================ */
183double expr(char **p);
184void run_from(int idx);
185void execute_stmt(char *p);
186void basic_error(int code, char *msg);
187
188static char str_result[MAXSTR]; /* scratch for string functions */
189
190static double parse_string_fn(char **p) {
191 /* Returns 0, result in str_result */
192 str_result[0] = 0;
193 return 0;
194}
195
196/* Parse a string literal or string expression, result -> str_result */
197static void parse_str_expr(char **p, char *out) {
198 skip_ws(p);
199 if (**p == '"') {
200 (*p)++; int i=0;
201 while (**p && **p != '"') out[i++] = *(*p)++;
202 out[i]=0; if(**p=='"')(*p)++;
203 } else if (isalpha(**p)) {
204 char name[MAXSTR]={0}; char *q=*p;
205 int i=0;
206 while (isalnum(**p) || **p=='$' || **p=='_') name[i++]=*(*p)++;
207 name[i]=0;
208 if (is_str_var(name)) {
209 strcpy(out, variables[ptrget(name)].str);
210 } else { *p=q; sprintf(out,"%g",expr(p)); }
211 } else { sprintf(out,"%g",expr(p)); }
212}
213
214static double primary(char **p) {
215 skip_ws(p);
216 double x = 0;
217 if (**p == '-') { (*p)++; return -primary(p); }
218 if (**p == '+') { (*p)++; return primary(p); }
219 if (**p == '(') { (*p)++; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return x; }
220
221 /* Functions */
222 #define MATHFN1(nm,fn) if(strncasecmp(*p,nm"(",strlen(nm)+1)==0){*p+=strlen(nm)+1;x=fn(expr(p));skip_ws(p);if(**p==')')(*p)++;return x;}
223 MATHFN1("ABS",fabs) MATHFN1("SQR",sqrt) MATHFN1("SIN",sin)
224 MATHFN1("COS",cos) MATHFN1("TAN",tan) MATHFN1("ATN",atan)
225 MATHFN1("LOG",log) MATHFN1("EXP",exp) MATHFN1("INT",floor)
226 MATHFN1("FIX",trunc)
227 #undef MATHFN1
228
229 if (strncasecmp(*p,"SGN(",4)==0){ *p+=4; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return (x>0)-(x<0); }
230 if (strncasecmp(*p,"RND(",4)==0){ *p+=4; double a=expr(p); skip_ws(p); if(**p==')')(*p)++; (void)a; return (double)rand()/RAND_MAX; }
231 if (strncasecmp(*p,"CINT(",5)==0){ *p+=5; x=round(expr(p)); skip_ws(p); if(**p==')')(*p)++; return (int)x; }
232 if (strncasecmp(*p,"CSNG(",5)==0){ *p+=5; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return (float)x; }
233 if (strncasecmp(*p,"CDBL(",5)==0){ *p+=5; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return x; }
234 if (strncasecmp(*p,"FRE(",4)==0) { *p+=4; expr(p); skip_ws(p); if(**p==')')(*p)++; return 60000; }
235 if (strncasecmp(*p,"POS(",4)==0) { *p+=4; expr(p); skip_ws(p); if(**p==')')(*p)++; return 0; }
236 if (strncasecmp(*p,"LEN(",4)==0) { *p+=4; char tmp[MAXSTR]; parse_str_expr(p,tmp); skip_ws(p); if(**p==')')(*p)++; return strlen(tmp); }
237 if (strncasecmp(*p,"ASC(",4)==0) { *p+=4; char tmp[MAXSTR]; parse_str_expr(p,tmp); skip_ws(p); if(**p==')')(*p)++; return tmp[0]; }
238 if (strncasecmp(*p,"VAL(",4)==0) { *p+=4; char tmp[MAXSTR]; parse_str_expr(p,tmp); skip_ws(p); if(**p==')')(*p)++; return atof(tmp); }
239 if (strncasecmp(*p,"PEEK(",5)==0){ *p+=5; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return 0; } /* stub */
240 if (strncasecmp(*p,"INP(",4)==0) { *p+=4; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return 0; } /* stub */
241 if (strncasecmp(*p,"LPOS(",5)==0){ *p+=5; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return 0; }
242
243 if (strncasecmp(*p,"INSTR(",6)==0) {
244 *p+=6; double start=1; char h[MAXSTR],n[MAXSTR];
245 /* INSTR([start,] hay, needle) */
246 char *save=*p; double t=expr(p); skip_ws(p);
247 if (**p==',') { start=t; (*p)++; parse_str_expr(p,h); }
248 else { *p=save; parse_str_expr(p,h); }
249 skip_ws(p); if(**p==',')(*p)++;
250 parse_str_expr(p,n);
251 skip_ws(p); if(**p==')')(*p)++;
252 char *found=strstr(h+(int)start-1,n);
253 return found ? (found-h+1) : 0;
254 }
255
256 /* NOT */
257 if (strncasecmp(*p,"NOT",3)==0 && !isalnum((*p)[3])) { *p+=3; return ~(int)primary(p); }
258
259 /* Number literal */
260 if (isdigit(**p) || **p=='.') {
261 char *end; x=strtod(*p,&end); *p=end; return x;
262 }
263
264 /* Hex &H / Octal &O */
265 if (**p=='&') {
266 (*p)++;
267 if (toupper(**p)=='H') { (*p)++; x=(double)strtol(*p,p,16); return x; }
268 if (toupper(**p)=='O') { (*p)++; x=(double)strtol(*p,p,8); return x; }
269 x=(double)strtol(*p,p,8); return x;
270 }
271
272 /* Variable or array element */
273 if (isalpha(**p)) {
274 char name[MAXSTR]={0}; int i=0;
275 while (isalnum(**p)||**p=='$'||**p=='_') name[i++]=*(*p)++;
276 name[i]=0;
277 skip_ws(p);
278 /* Array element */
279 if (**p=='(') {
280 (*p)++;
281 int idx[4]={0}; int nd=0;
282 while (**p && **p!=')') {
283 idx[nd++]=(int)expr(p); skip_ws(p);
284 if(**p==',')(*p)++;
285 }
286 if(**p==')')(*p)++;
287 Arr *a = find_arr(name);
288 if (!a) { /* auto-dim 0..10 */ int d=10; a=make_arr(name,&d,1); }
289 int flat=0;
290 if (nd>=1) flat=idx[0]-option_base;
291 if (nd>=2) flat=flat*(a->dims[1])+idx[1]-option_base;
292 if (flat<0) flat=0;
293 if (a->type==TYPE_STR) { /* str arrays not returned as number */ return 0; }
294 else return a->nums[flat];
295 }
296 return variables[ptrget(name)].num;
297 }
298 return 0;
299}
300
301static double power_e(char **p) {
302 double x=primary(p); skip_ws(p);
303 if (**p=='^') { (*p)++; x=pow(x,primary(p)); }
304 return x;
305}
306static double term(char **p) {
307 double x=power_e(p); skip_ws(p);
308 while(**p=='*'||**p=='/') { char op=*(*p)++; double y=power_e(p); x=(op=='*')?x*y:(y?x/y:(basic_error(11,"Division by zero"),1.0)); skip_ws(p); }
309 return x;
310}
311static double add_sub(char **p) {
312 double x=term(p); skip_ws(p);
313 while(**p=='+'||**p=='-') { char op=*(*p)++; double y=term(p); x=(op=='+')?x+y:x-y; skip_ws(p); }
314 return x;
315}
316static double relational(char **p) {
317 double x=add_sub(p); skip_ws(p);
318 if (strncmp(*p,"<>",2)==0){*p+=2;return x!=add_sub(p);}
319 if (strncmp(*p,"<=",2)==0){*p+=2;return x<=add_sub(p);}
320 if (strncmp(*p,">=",2)==0){*p+=2;return x>=add_sub(p);}
321 if (**p=='<'){(*p)++;return x<add_sub(p);}
322 if (**p=='>'){(*p)++;return x>add_sub(p);}
323 if (**p=='='){(*p)++;return x==add_sub(p);}
324 return x;
325}
326static double and_or(char **p) {
327 double x=relational(p); skip_ws(p);
328 while(1){
329 if (strncasecmp(*p,"AND",3)==0&&!isalnum((*p)[3])){ *p+=3; x=(int)x&(int)relational(p); }
330 else if(strncasecmp(*p,"OR",2)==0&&!isalnum((*p)[2])){ *p+=2; x=(int)x|(int)relational(p); }
331 else if(strncasecmp(*p,"XOR",3)==0&&!isalnum((*p)[3])){ *p+=3; x=(int)x^(int)relational(p); }
332 else break;
333 skip_ws(p);
334 }
335 return x;
336}
337double expr(char **p) { return fac=and_or(p); }
338
339/* String expression: result in out */
340static void str_expr(char **p, char *out) {
341 skip_ws(p);
342 /* CHR$ */
343 if (strncasecmp(*p,"CHR$(",5)==0){ *p+=5; int c=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; out[0]=c; out[1]=0; return; }
344 /* STR$ */
345 if (strncasecmp(*p,"STR$(",5)==0){ *p+=5; double v=expr(p); skip_ws(p); if(**p==')')(*p)++; sprintf(out,"%g",v); return; }
346 /* LEFT$ */
347 if (strncasecmp(*p,"LEFT$(",6)==0){ *p+=6; char s[MAXSTR]; parse_str_expr(p,s); skip_ws(p); if(**p==',')(*p)++; int n=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; strncpy(out,s,n); out[n]=0; return; }
348 /* RIGHT$ */
349 if (strncasecmp(*p,"RIGHT$(",7)==0){ *p+=7; char s[MAXSTR]; parse_str_expr(p,s); skip_ws(p); if(**p==',')(*p)++; int n=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; int l=strlen(s); int st=l-n; if(st<0)st=0; strcpy(out,s+st); return; }
350 /* MID$ */
351 if (strncasecmp(*p,"MID$(",5)==0){ *p+=5; char s[MAXSTR]; parse_str_expr(p,s); skip_ws(p); if(**p==',')(*p)++; int st=(int)expr(p)-1; skip_ws(p); int n=strlen(s); if(**p==','){ (*p)++; n=(int)expr(p); } skip_ws(p); if(**p==')')(*p)++; if(st<0)st=0; strncpy(out,s+st,n); out[n]=0; return; }
352 /* SPACE$ */
353 if (strncasecmp(*p,"SPACE$(",7)==0){ *p+=7; int n=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; memset(out,' ',n); out[n]=0; return; }
354 /* STRING$ */
355 if (strncasecmp(*p,"STRING$(",8)==0){ *p+=8; int n=(int)expr(p); skip_ws(p); if(**p==',')(*p)++; char tmp[MAXSTR]; if(**p=='"'){ parse_str_expr(p,tmp); memset(out,tmp[0],n); } else { int c=(int)expr(p); memset(out,c,n); } out[n]=0; skip_ws(p); if(**p==')')(*p)++; return; }
356 /* HEX$ */
357 if (strncasecmp(*p,"HEX$(",5)==0){ *p+=5; int v=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; sprintf(out,"%X",v); return; }
358 /* OCT$ */
359 if (strncasecmp(*p,"OCT$(",5)==0){ *p+=5; int v=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; sprintf(out,"%o",v); return; }
360 /* MKI$ MKS$ MKD$ */
361 if (strncasecmp(*p,"MKI$(",5)==0){ *p+=5; int v=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; memcpy(out,&v,2); out[2]=0; return; }
362 if (strncasecmp(*p,"MKS$(",5)==0){ *p+=5; float v=(float)expr(p); skip_ws(p); if(**p==')')(*p)++; memcpy(out,&v,4); out[4]=0; return; }
363 if (strncasecmp(*p,"MKD$(",5)==0){ *p+=5; double v=expr(p); skip_ws(p); if(**p==')')(*p)++; memcpy(out,&v,8); out[8]=0; return; }
364 /* INKEY$ */
365 if (strncasecmp(*p,"INKEY$",6)==0){ *p+=6; out[0]=last_inkey; out[1]=0; last_inkey=0; return; }
366 /* Fallthrough: parse as string var or literal */
367 parse_str_expr(p, out);
368}
369
370/* ============================================================
371 COLLECT DATA (FDTLP)
372 ============================================================ */
373void collect_data() {
374 data_count=0; data_pos=0;
375 for (int i=0;i<prog_cnt;i++) {
376 char *p=program[i].text;
377 while (*p) {
378 if (strncasecmp(p,"DATA",4)==0) {
379 p+=4;
380 while (*p && *p!='\n') {
381 skip_ws(&p);
382 if (*p==',') { p++; continue; }
383 if (!*p) break;
384 char *dst=data_pool[data_count];
385 if (*p=='"') { p++; int i2=0; while(*p&&*p!='"') dst[i2++]=*p++; dst[i2]=0; if(*p=='"')p++; }
386 else { int i2=0; while(*p&&*p!=','&&*p!='\n') dst[i2++]=*p++; dst[i2]=0; }
387 data_count++;
388 skip_ws(&p);
389 }
390 break;
391 }
392 p++;
393 }
394 }
395}
396
397/* ============================================================
398 ERROR HANDLING (ERRORREPORT)
399 ============================================================ */
400void basic_error(int code, char *msg) {
401 fprintf(stderr,"?%s error in %d\n", msg, (prog_cnt>0 && cur_line_idx<prog_cnt)?program[cur_line_idx].num:0);
402 err_number=code;
403 if (on_error_line>=0) { cur_line_idx=find_line(on_error_line); running=2; return; }
404 running=0;
405}
406
407/* ============================================================
408 PRINT USING (USING / PUFOUT) - basic implementation
409 ============================================================ */
410static void print_using(char *fmt, double val) {
411 /* Handle # . , + - $ simple format chars */
412 char buf[MAXBUF]; int w=0,d=0;
413 for (char *f=fmt;*f;f++) if(*f=='#') w++; else if(*f=='.') { for(char *g=f+1;*g=='#';g++) d++; break; }
414 if (d>0) printf("%*.*f",w,d,val);
415 else printf("%*g",w,val);
416}
417
418/* ============================================================
419 WRITE statement (csv output)
420 ============================================================ */
421static void do_write(char **p, FILE *fp) {
422 while (**p) {
423 skip_ws(p);
424 if (**p=='"' || (isalpha(**p) && is_str_var(*p))) {
425 char s[MAXSTR]; str_expr(p,s);
426 fprintf(fp,"\"%s\"",s);
427 } else {
428 fprintf(fp,"%g",expr(p));
429 }
430 skip_ws(p);
431 if (**p==',') { fprintf(fp,","); (*p)++; }
432 else break;
433 }
434 fprintf(fp,"\n");
435}
436
437/* ============================================================
438 EXECUTE ONE STATEMENT (NEWSTT / EXEC / FNCTAB)
439 ============================================================ */
440void execute_stmt(char *p) {
441 skip_ws(&p);
442 if (!*p) return;
443
444 /* REM / ' */
445 if (strncasecmp(p,"REM",3)==0||*p=='\'') return;
446
447 /* PRINT / LPRINT */
448 if (strncasecmp(p,"LPRINT",6)==0||strncasecmp(p,"PRINT",5)==0) {
449 FILE *fp=stdout; int is_lp=(strncasecmp(p,"LPRINT",6)==0);
450 p += is_lp?6:5;
451 /* PRINT USING */
452 if (strncasecmp(p," USING",6)==0||strncasecmp(p,"USING",5)==0) {
453 if(*p==' ')p++; p+=5; skip_ws(&p);
454 char fmt[MAXBUF]; str_expr(&p,fmt);
455 skip_ws(&p); if(*p==';')p++;
456 double v=expr(&p);
457 print_using(fmt,v);
458 fprintf(fp,"\n"); return;
459 }
460 int newline=1;
461 while (*p) {
462 skip_ws(&p);
463 if (!*p) break;
464 if (*p==';') { p++; newline=0; continue; }
465 if (*p==',') { fprintf(fp,"\t"); p++; newline=0; continue; }
466 /* TAB( */
467 if (strncasecmp(p,"TAB(",4)==0) { p+=4; int n=(int)expr(&p); skip_ws(&p); if(*p==')')p++; for(int i=0;i<n;i++) fputc(' ',fp); newline=0; continue; }
468 /* SPC( */
469 if (strncasecmp(p,"SPC(",4)==0) { p+=4; int n=(int)expr(&p); skip_ws(&p); if(*p==')')p++; for(int i=0;i<n;i++) fputc(' ',fp); newline=0; continue; }
470 newline=1;
471 if (*p=='"'||(isalpha(*p)&&is_str_var(p))) {
472 char s[MAXSTR]; str_expr(&p,s); fprintf(fp,"%s",s);
473 } else {
474 double v=expr(&p);
475 if (v==(long)v) fprintf(fp,"%.0f",v); else fprintf(fp,"%g",v);
476 fputc(' ',fp);
477 }
478 skip_ws(&p);
479 }
480 if (newline) fprintf(fp,"\n");
481 return;
482 }
483
484 /* INPUT / LINE INPUT */
485 if (strncasecmp(p,"LINE INPUT",10)==0) {
486 p+=10; skip_ws(&p); if(*p=='#'){p++; expr(&p); skip_ws(&p); if(*p==',')p++;} /* file# stub */
487 char name[MAXSTR]={0}; int i=0; skip_ws(&p);
488 while(isalnum(*p)||*p=='$') name[i++]=*p++;
489 name[i]=0;
490 char line[MAXBUF]; if(!fgets(line,MAXBUF,stdin)) line[0]=0;
491 line[strcspn(line,"\n")]=0;
492 int vi=ptrget(name); strncpy(variables[vi].str,line,MAXSTR-1); variables[vi].type=TYPE_STR;
493 return;
494 }
495 if (strncasecmp(p,"INPUT",5)==0) {
496 p+=5; skip_ws(&p);
497 int file_no=-1;
498 if (*p=='#') { p++; file_no=(int)expr(&p); skip_ws(&p); if(*p==',')p++; }
499 char prompt[MAXBUF]="? ";
500 if (*p=='"') { p++; int i=0; while(*p&&*p!='"') prompt[i++]=*p++; prompt[i]=0; if(*p=='"')p++; if(*p==';'||*p==',')p++; strcat(prompt,"? "); }
501 while (*p) {
502 skip_ws(&p);
503 if (*p==',') { p++; continue; }
504 char name[MAXSTR]={0}; int i=0;
505 while(isalnum(*p)||*p=='$') name[i++]=*p++;
506 name[i]=0;
507 if (!name[0]) break;
508 printf("%s",prompt); prompt[0]=0; strcpy(prompt,"");
509 char ibuf[MAXBUF];
510 if (file_no>=0 && file_handles[file_no]) { fgets(ibuf,MAXBUF,file_handles[file_no]); }
511 else { fgets(ibuf,MAXBUF,stdin); }
512 ibuf[strcspn(ibuf,"\n")]=0;
513 int vi=ptrget(name);
514 if (is_str_var(name)) { strncpy(variables[vi].str,ibuf,MAXSTR-1); variables[vi].type=TYPE_STR; }
515 else { variables[vi].num=atof(ibuf); variables[vi].type=TYPE_SNG; }
516 skip_ws(&p);
517 }
518 return;
519 }
520
521 /* LET / assignment */
522 if (strncasecmp(p,"LET",3)==0&&!isalnum(p[3])) p+=3;
523 if (isalpha(*p)) {
524 char name[MAXSTR]={0}; char *save=p; int i=0;
525 while(isalnum(*p)||*p=='$') name[i++]=*p++;
526 name[i]=0;
527 skip_ws(&p);
528 /* Array assignment */
529 if (*p=='(') {
530 (*p==0); p++;
531 int idx[4]={0}; int nd=0;
532 while(*p&&*p!=')') { idx[nd++]=(int)expr(&p); skip_ws(&p); if(*p==',')p++; }
533 if(*p==')')p++;
534 skip_ws(&p); if(*p=='=')p++;
535 Arr *a=find_arr(name);
536 if(!a){int d=10;a=make_arr(name,&d,1);}
537 int flat=idx[0]-option_base;
538 if (nd>=2) flat=flat*(a->dims[1])+idx[1]-option_base;
539 if (flat<0) flat=0;
540 if (a->type==TYPE_STR){char s[MAXSTR];str_expr(&p,s);strncpy(a->strs[flat],s,MAXSTR-1);}
541 else a->nums[flat]=expr(&p);
542 return;
543 }
544 if (*p=='=') {
545 p++;
546 int vi=ptrget(name);
547 if (is_str_var(name)) {
548 char s[MAXSTR]; str_expr(&p,s);
549 strncpy(variables[vi].str,s,MAXSTR-1);
550 variables[vi].type=TYPE_STR;
551 } else {
552 variables[vi].num=expr(&p);
553 variables[vi].type=TYPE_SNG;
554 }
555 return;
556 }
557 p=save;
558 }
559
560 /* IF / THEN / ELSE */
561 if (strncasecmp(p,"IF",2)==0&&!isalnum(p[2])) {
562 p+=2; skip_ws(&p);
563 double cond=expr(&p);
564 skip_ws(&p);
565 if (strncasecmp(p,"THEN",4)==0&&!isalnum(p[4])) p+=4;
566 skip_ws(&p);
567 /* THEN line_number */
568 if (isdigit(*p)) {
569 int tgt=(int)strtol(p,&p,10);
570 if (cond) { int idx=find_line(tgt); if(idx>=0){cur_line_idx=idx;running=2;} }
571 else {
572 /* find ELSE */
573 skip_ws(&p);
574 if (strncasecmp(p,"ELSE",4)==0) { p+=4; skip_ws(&p);
575 if(isdigit(*p)){ int t=(int)strtol(p,&p,10); int idx=find_line(t); if(idx>=0){cur_line_idx=idx;running=2;}}
576 else execute_stmt(p);
577 }
578 }
579 return;
580 }
581 char *else_p=NULL;
582 /* find ELSE in remainder */
583 char *scan=p; int depth=0;
584 while(*scan){
585 if(*scan=='(')depth++; else if(*scan==')')depth--;
586 if(depth==0&&strncasecmp(scan,"ELSE",4)==0&&!isalnum(scan[4])&&!isalnum(scan[-1])){ else_p=scan; break; }
587 scan++;
588 }
589 if (cond) { char tmp[MAXBUF]; if(else_p){int len=else_p-p;strncpy(tmp,p,len);tmp[len]=0;execute_stmt(tmp);}else execute_stmt(p); }
590 else if (else_p) { execute_stmt(else_p+4); }
591 return;
592 }
593
594 /* GOTO */
595 if (strncasecmp(p,"GOTO",4)==0&&!isalnum(p[4])) {
596 p+=4; int tgt=(int)expr(&p);
597 int idx=find_line(tgt);
598 if(idx<0){basic_error(8,"Undefined line");return;}
599 cur_line_idx=idx; running=2; return;
600 }
601
602 /* GOSUB */
603 if (strncasecmp(p,"GOSUB",5)==0&&!isalnum(p[5])) {
604 p+=5; int tgt=(int)expr(&p);
605 int idx=find_line(tgt);
606 if(idx<0){basic_error(8,"Undefined line");return;}
607 call_stack[call_sp++]=cur_line_idx+1;
608 cur_line_idx=idx; running=2; return;
609 }
610
611 /* RETURN */
612 if (strncasecmp(p,"RETURN",6)==0&&!isalnum(p[6])) {
613 if(call_sp<=0){basic_error(3,"RETURN without GOSUB");return;}
614 cur_line_idx=call_stack[--call_sp]; running=2; return;
615 }
616
617 /* ON GOTO / ON GOSUB / ON ERROR */
618 if (strncasecmp(p,"ON",2)==0&&!isalnum(p[2])) {
619 p+=2; skip_ws(&p);
620 if (strncasecmp(p,"ERROR",5)==0) {
621 p+=5; skip_ws(&p);
622 if (strncasecmp(p,"GOTO",4)==0){p+=4; on_error_line=(int)expr(&p);}
623 else if(strncasecmp(p,"RESUME",6)==0){p+=6;skip_ws(&p);if(strncasecmp(p,"NEXT",4)==0)p+=4;}
624 return;
625 }
626 double n=expr(&p); skip_ws(&p);
627 int is_sub=(strncasecmp(p,"GOSUB",5)==0);
628 p += is_sub?5:4;
629 int targets[64]; int tc=0;
630 while(*p){skip_ws(&p);if(!isdigit(*p))break;targets[tc++]=(int)strtol(p,&p,10);skip_ws(&p);if(*p==',')p++;}
631 int ni=(int)n-1;
632 if(ni>=0&&ni<tc){
633 int idx=find_line(targets[ni]);
634 if(idx>=0){
635 if(is_sub){call_stack[call_sp++]=cur_line_idx+1;}
636 cur_line_idx=idx; running=2;
637 }
638 }
639 return;
640 }
641
642 /* FOR */
643 if (strncasecmp(p,"FOR",3)==0&&!isalnum(p[3])) {
644 p+=3; skip_ws(&p);
645 char name[MAXSTR]={0}; int i=0;
646 while(isalnum(*p)||*p=='$') name[i++]=*p++;
647 name[i]=0;
648 skip_ws(&p); if(*p=='=')p++;
649 double start=expr(&p);
650 skip_ws(&p); if(strncasecmp(p,"TO",2)==0&&!isalnum(p[2]))p+=2;
651 double limit=expr(&p);
652 double step=1.0;
653 skip_ws(&p); if(strncasecmp(p,"STEP",4)==0&&!isalnum(p[4])){p+=4;step=expr(&p);}
654 int vi=ptrget(name); variables[vi].num=start; variables[vi].type=TYPE_SNG;
655 for_stack[for_sp].var_idx=vi;
656 for_stack[for_sp].limit=limit;
657 for_stack[for_sp].step=step;
658 for_stack[for_sp].line_idx=cur_line_idx;
659 for_sp++;
660 return;
661 }
662
663 /* NEXT */
664 if (strncasecmp(p,"NEXT",4)==0&&!isalnum(p[4])) {
665 if(for_sp<=0){basic_error(1,"NEXT without FOR");return;}
666 ForEntry *fe=&for_stack[for_sp-1];
667 variables[fe->var_idx].num+=fe->step;
668 if((fe->step>0&&variables[fe->var_idx].num<=fe->limit)||
669 (fe->step<0&&variables[fe->var_idx].num>=fe->limit)){
670 cur_line_idx=fe->line_idx; running=2;
671 } else for_sp--;
672 return;
673 }
674
675 /* WHILE */
676 if (strncasecmp(p,"WHILE",5)==0&&!isalnum(p[5])) {
677 p+=5;
678 if (!expr(&p)) {
679 /* skip to WEND */
680 int depth=1;
681 while(++cur_line_idx<prog_cnt&&depth>0){
682 if(strstr(program[cur_line_idx].text,"WHILE"))depth++;
683 if(strstr(program[cur_line_idx].text,"WEND"))depth--;
684 }
685 } else {
686 while_stack[while_sp++]=cur_line_idx;
687 }
688 return;
689 }
690
691 /* WEND */
692 if (strncasecmp(p,"WEND",4)==0&&!isalnum(p[4])) {
693 if(while_sp<=0){basic_error(23,"WEND without WHILE");return;}
694 cur_line_idx=while_stack[--while_sp]-1;
695 running=2; return;
696 }
697
698 /* DATA */
699 if (strncasecmp(p,"DATA",4)==0) return;
700
701 /* READ */
702 if (strncasecmp(p,"READ",4)==0&&!isalnum(p[4])) {
703 p+=4;
704 while(*p){
705 skip_ws(&p); if(*p==',')p++; skip_ws(&p); if(!*p)break;
706 char name[MAXSTR]={0}; int i=0;
707 while(isalnum(*p)||*p=='$') name[i++]=*p++;
708 name[i]=0; if(!name[0])break;
709 if(data_pos>=data_count){basic_error(4,"Out of DATA");return;}
710 int vi=ptrget(name);
711 if(is_str_var(name)){strncpy(variables[vi].str,data_pool[data_pos++],MAXSTR-1);variables[vi].type=TYPE_STR;}
712 else{variables[vi].num=atof(data_pool[data_pos++]);variables[vi].type=TYPE_SNG;}
713 }
714 return;
715 }
716
717 /* RESTORE */
718 if (strncasecmp(p,"RESTORE",7)==0) {
719 p+=7; skip_ws(&p);
720 if(isdigit(*p)){int tgt=(int)strtol(p,&p,10);(void)tgt;} /* stub: restore to line */
721 data_pos=0; return;
722 }
723
724 /* DIM */
725 if (strncasecmp(p,"DIM",3)==0&&!isalnum(p[3])) {
726 p+=3;
727 while(*p){
728 skip_ws(&p); if(*p==',')p++; skip_ws(&p); if(!*p)break;
729 char name[MAXSTR]={0}; int i=0;
730 while(isalnum(*p)||*p=='$') name[i++]=*p++;
731 name[i]=0;
732 skip_ws(&p);
733 int dims[4]={10,10,10,10}; int ndims=0;
734 if(*p=='('){
735 p++;
736 while(*p&&*p!=')'){skip_ws(&p);dims[ndims++]=(int)expr(&p);skip_ws(&p);if(*p==',')p++;}
737 if(*p==')')p++;
738 }
739 if(ndims==0)ndims=1;
740 make_arr(name,dims,ndims);
741 }
742 return;
743 }
744
745 /* SWAP */
746 if (strncasecmp(p,"SWAP",4)==0&&!isalnum(p[4])) {
747 p+=4; skip_ws(&p);
748 char n1[MAXSTR]={0}; int i=0; while(isalnum(*p)||*p=='$') n1[i++]=*p++; n1[i]=0;
749 skip_ws(&p); if(*p==',')p++; skip_ws(&p);
750 char n2[MAXSTR]={0}; i=0; while(isalnum(*p)||*p=='$') n2[i++]=*p++; n2[i]=0;
751 int v1=ptrget(n1),v2=ptrget(n2);
752 double tmp=variables[v1].num; variables[v1].num=variables[v2].num; variables[v2].num=tmp;
753 char ts[MAXSTR]; strcpy(ts,variables[v1].str); strcpy(variables[v1].str,variables[v2].str); strcpy(variables[v2].str,ts);
754 return;
755 }
756
757 /* POKE */
758 if (strncasecmp(p,"POKE",4)==0&&!isalnum(p[4])) {
759 p+=4; expr(&p); skip_ws(&p); if(*p==',')p++; expr(&p); /* stub */
760 return;
761 }
762
763 /* WAIT */
764 if (strncasecmp(p,"WAIT",4)==0&&!isalnum(p[4])) { p+=4; expr(&p); skip_ws(&p); if(*p==',')p++; expr(&p); return; }
765
766 /* OUT */
767 if (strncasecmp(p,"OUT",3)==0&&!isalnum(p[3])) { p+=3; expr(&p); skip_ws(&p); if(*p==',')p++; expr(&p); return; }
768
769 /* LSET / RSET */
770 if (strncasecmp(p,"LSET",4)==0&&!isalnum(p[4])) {
771 p+=4; skip_ws(&p); char name[MAXSTR]={0}; int i=0;
772 while(isalnum(*p)||*p=='$') name[i++]=*p++;
773 name[i]=0; skip_ws(&p); if(*p=='=')p++;
774 char s[MAXSTR]; str_expr(&p,s);
775 int vi=ptrget(name); strncpy(variables[vi].str,s,MAXSTR-1); variables[vi].type=TYPE_STR;
776 return;
777 }
778 if (strncasecmp(p,"RSET",4)==0&&!isalnum(p[4])) {
779 p+=4; skip_ws(&p); char name[MAXSTR]={0}; int i=0;
780 while(isalnum(*p)||*p=='$') name[i++]=*p++;
781 name[i]=0; skip_ws(&p); if(*p=='=')p++;
782 char s[MAXSTR]; str_expr(&p,s);
783 int vi=ptrget(name); strncpy(variables[vi].str,s,MAXSTR-1); variables[vi].type=TYPE_STR;
784 return;
785 }
786
787 /* WRITE */
788 if (strncasecmp(p,"WRITE",5)==0&&!isalnum(p[5])) {
789 p+=5; skip_ws(&p);
790 FILE *fp=stdout; if(*p=='#'){p++;int fn=(int)expr(&p);skip_ws(&p);if(*p==',')p++;if(fn>=0&&fn<MAXFILES&&file_handles[fn])fp=file_handles[fn];}
791 do_write(&p,fp); return;
792 }
793
794 /* OPEN */
795 if (strncasecmp(p,"OPEN",4)==0&&!isalnum(p[4])) {
796 p+=4; skip_ws(&p);
797 char fname[MAXBUF]; str_expr(&p,fname); skip_ws(&p);
798 char mode_str[8]="O";
799 if(strncasecmp(p,"FOR",3)==0&&!isalnum(p[3])){
800 p+=3; skip_ws(&p);
801 if(strncasecmp(p,"INPUT",5)==0){strcpy(mode_str,"r");p+=5;}
802 else if(strncasecmp(p,"OUTPUT",6)==0){strcpy(mode_str,"w");p+=6;}
803 else if(strncasecmp(p,"APPEND",6)==0){strcpy(mode_str,"a");p+=6;}
804 skip_ws(&p);
805 }
806 if(strncasecmp(p,"AS",2)==0&&!isalnum(p[2])){p+=2;skip_ws(&p);}
807 if(*p=='#')p++;
808 int fn=(int)expr(&p);
809 if(fn>=0&&fn<MAXFILES){ if(file_handles[fn])fclose(file_handles[fn]); file_handles[fn]=fopen(fname,mode_str); }
810 return;
811 }
812
813 /* CLOSE */
814 if (strncasecmp(p,"CLOSE",5)==0&&!isalnum(p[5])) {
815 p+=5; skip_ws(&p);
816 if(!*p){for(int i=0;i<MAXFILES;i++){if(file_handles[i])fclose(file_handles[i]);file_handles[i]=NULL;}return;}
817 while(*p){skip_ws(&p);if(*p=='#')p++;int fn=(int)expr(&p);if(fn>=0&&fn<MAXFILES&&file_handles[fn]){fclose(file_handles[fn]);file_handles[fn]=NULL;}skip_ws(&p);if(*p==',')p++;}
818 return;
819 }
820
821 /* LOAD */
822 if (strncasecmp(p,"LOAD",4)==0&&!isalnum(p[4])) {
823 p+=4; skip_ws(&p); char fname[MAXBUF]; str_expr(&p,fname);
824 FILE *f=fopen(fname,"r"); if(!f){fprintf(stderr,"?File not found: %s\n",fname);return;}
825 char line[MAXBUF];
826 while(fgets(line,MAXBUF,f)){
827 line[strcspn(line,"\n")]=0;
828 char *q=line; skip_ws(&q);
829 if(isdigit(*q)){int num=(int)strtol(q,&q,10);skip_ws(&q);insert_line(num,q);}
830 }
831 fclose(f); collect_data(); return;
832 }
833
834 /* SAVE */
835 if (strncasecmp(p,"SAVE",4)==0&&!isalnum(p[4])) {
836 p+=4; skip_ws(&p); char fname[MAXBUF]; str_expr(&p,fname);
837 FILE *f=fopen(fname,"w"); if(!f){fprintf(stderr,"?Cannot save\n");return;}
838 for(int i=0;i<prog_cnt;i++) fprintf(f,"%d %s\n",program[i].num,program[i].text);
839 fclose(f); return;
840 }
841
842 /* KILL */
843 if (strncasecmp(p,"KILL",4)==0&&!isalnum(p[4])) {
844 p+=4; skip_ws(&p); char fname[MAXBUF]; str_expr(&p,fname); remove(fname); return;
845 }
846
847 /* NAME */
848 if (strncasecmp(p,"NAME",4)==0&&!isalnum(p[4])) {
849 p+=4; skip_ws(&p); char old[MAXBUF],new_[MAXBUF];
850 str_expr(&p,old); skip_ws(&p);
851 if(strncasecmp(p,"AS",2)==0)p+=2; skip_ws(&p);
852 str_expr(&p,new_); rename(old,new_); return;
853 }
854
855 /* FILES */
856 if (strncasecmp(p,"FILES",5)==0&&!isalnum(p[5])) {
857 system("ls"); return; /* stub */
858 }
859
860 /* WIDTH */
861 if (strncasecmp(p,"WIDTH",5)==0&&!isalnum(p[5])) {
862 p+=5; expr(&p); return; /* stub */
863 }
864
865 /* TRON / TROFF */
866 if (strncasecmp(p,"TRON",4)==0&&!isalnum(p[4])) { tron_flag=1; return; }
867 if (strncasecmp(p,"TROFF",5)==0&&!isalnum(p[5])) { tron_flag=0; return; }
868
869 /* ERASE */
870 if (strncasecmp(p,"ERASE",5)==0&&!isalnum(p[5])) {
871 p+=5; skip_ws(&p); char name[MAXSTR]={0}; int i=0;
872 while(isalnum(*p)||*p=='$') name[i++]=*p++;
873 name[i]=0;
874 for(int j=0;j<arr_cnt;j++) if(strcasecmp(arrays[j].name,name)==0){
875 if(arrays[j].nums)free(arrays[j].nums);
876 if(arrays[j].strs)free(arrays[j].strs);
877 for(int k=j;k<arr_cnt-1;k++) arrays[k]=arrays[k+1]; arr_cnt--; break;
878 }
879 return;
880 }
881
882 /* RANDOMIZE */
883 if (strncasecmp(p,"RANDOMIZE",9)==0&&!isalnum(p[9])) {
884 p+=9; skip_ws(&p);
885 if(*p) srand((unsigned)expr(&p)); else srand((unsigned)time(NULL));
886 return;
887 }
888
889 /* OPTION BASE */
890 if (strncasecmp(p,"OPTION",6)==0&&!isalnum(p[6])) {
891 p+=6; skip_ws(&p);
892 if(strncasecmp(p,"BASE",4)==0){p+=4;option_base=(int)expr(&p);}
893 return;
894 }
895
896 /* DEF FN */
897 if (strncasecmp(p,"DEF",3)==0&&!isalnum(p[3])) {
898 /* stub - user functions not yet supported */
899 return;
900 }
901
902 /* DEFSTR/DEFINT/DEFSNG/DEFDBL */
903 if (strncasecmp(p,"DEFSTR",6)==0||strncasecmp(p,"DEFINT",6)==0||
904 strncasecmp(p,"DEFSNG",6)==0||strncasecmp(p,"DEFDBL",6)==0) return; /* stub */
905
906 /* RESUME */
907 if (strncasecmp(p,"RESUME",6)==0&&!isalnum(p[6])) {
908 p+=6; skip_ws(&p);
909 if(strncasecmp(p,"NEXT",4)==0) cur_line_idx++;
910 err_number=0; on_error_line=-1; running=1; return;
911 }
912
913 /* ERROR */
914 if (strncasecmp(p,"ERROR",5)==0&&!isalnum(p[5])) {
915 p+=5; int code=(int)expr(&p); basic_error(code,"User"); return;
916 }
917
918 /* DELETE */
919 if (strncasecmp(p,"DELETE",6)==0&&!isalnum(p[6])) {
920 p+=6; skip_ws(&p); int n1=(int)strtol(p,&p,10); int n2=n1;
921 if(*p=='-'){p++;n2=(int)strtol(p,&p,10);}
922 for(int i=n1;i<=n2;i++) insert_line(i,"");
923 return;
924 }
925
926 /* RENUM */
927 if (strncasecmp(p,"RENUM",5)==0&&!isalnum(p[5])) {
928 p+=5; int start=10, step=10;
929 skip_ws(&p); if(isdigit(*p)){start=(int)strtol(p,&p,10);skip_ws(&p);if(*p==','){p++;step=(int)strtol(p,&p,10);}}
930 for(int i=0;i<prog_cnt;i++) program[i].num=start+i*step;
931 return;
932 }
933
934 /* AUTO */
935 if (strncasecmp(p,"AUTO",4)==0&&!isalnum(p[4])) { printf("[AUTO mode not supported interactively]\n"); return; }
936
937 /* EDIT */
938 if (strncasecmp(p,"EDIT",4)==0&&!isalnum(p[4])) {
939 p+=4; skip_ws(&p); int n=(int)strtol(p,&p,10);
940 int idx=find_line(n);
941 if(idx>=0) printf("%d %s\n",program[idx].num,program[idx].text);
942 return;
943 }
944
945 /* NULL */
946 if (strncasecmp(p,"NULL",4)==0&&!isalnum(p[4])) { p+=4; null_count=(int)expr(&p); return; }
947
948 /* CALL */
949 if (strncasecmp(p,"CALL",4)==0&&!isalnum(p[4])) { p+=4; expr(&p); return; /* stub */ }
950
951 /* CHAIN */
952 if (strncasecmp(p,"CHAIN",5)==0&&!isalnum(p[5])) {
953 p+=5; skip_ws(&p); char fname[MAXBUF]; str_expr(&p,fname);
954 execute_stmt("NEW"); execute_stmt("LOAD");
955 return;
956 }
957
958 /* SYSTEM */
959 if (strncasecmp(p,"SYSTEM",6)==0&&!isalnum(p[6])) { printf("\n"); exit(0); }
960
961 /* END / STOP */
962 if (strncasecmp(p,"END",3)==0&&!isalnum(p[3])) { cont_idx=cur_line_idx; running=0; return; }
963 if (strncasecmp(p,"STOP",4)==0&&!isalnum(p[4])) { printf("Break in %d\n", program[cur_line_idx].num); cont_idx=cur_line_idx+1; running=0; return; }
964
965 /* CONT */
966 if (strncasecmp(p,"CONT",4)==0&&!isalnum(p[4])) {
967 if(cont_idx<0){printf("?Can't continue\n");return;}
968 run_from(cont_idx); return;
969 }
970
971 /* LIST [n[-m]] */
972 if (strncasecmp(p,"LIST",4)==0&&!isalnum(p[4])) {
973 p+=4; skip_ws(&p);
974 int n1=0,n2=999999;
975 if(isdigit(*p)){n1=(int)strtol(p,&p,10);n2=n1;skip_ws(&p);if(*p=='-'){p++;if(isdigit(*p))n2=(int)strtol(p,&p,10);else n2=999999;}}
976 for(int i=0;i<prog_cnt;i++) if(program[i].num>=n1&&program[i].num<=n2) printf("%d %s\n",program[i].num,program[i].text);
977 return;
978 }
979
980 /* RUN */
981 if (strncasecmp(p,"RUN",3)==0&&!isalnum(p[3])) {
982 p+=3; skip_ws(&p); int start=0;
983 if(isdigit(*p)){int n=(int)strtol(p,&p,10);start=find_line(n);if(start<0)start=0;}
984 collect_data(); data_pos=0; call_sp=0; for_sp=0; while_sp=0; var_cnt=0; arr_cnt=0;
985 run_from(start); return;
986 }
987
988 /* NEW */
989 if (strncasecmp(p,"NEW",3)==0&&!isalnum(p[3])) {
990 prog_cnt=0; var_cnt=0; arr_cnt=0; call_sp=0; for_sp=0; while_sp=0; data_count=0; on_error_line=-1;
991 return;
992 }
993
994 /* CLEAR */
995 if (strncasecmp(p,"CLEAR",5)==0&&!isalnum(p[5])) {
996 var_cnt=0; arr_cnt=0; call_sp=0; for_sp=0; while_sp=0; data_pos=0; return;
997 }
998
999 /* MERGE */
1000 if (strncasecmp(p,"MERGE",5)==0&&!isalnum(p[5])) {
1001 p+=5; skip_ws(&p); char fname[MAXBUF]; str_expr(&p,fname);
1002 FILE *f=fopen(fname,"r"); if(!f)return;
1003 char line[MAXBUF]; while(fgets(line,MAXBUF,f)){
1004 line[strcspn(line,"\n")]=0; char *q=line; skip_ws(&q);
1005 if(isdigit(*q)){int num=(int)strtol(q,&q,10);skip_ws(&q);insert_line(num,q);}
1006 } fclose(f); return;
1007 }
1008
1009 /* GET / PUT (file) */
1010 if (strncasecmp(p,"GET",3)==0&&!isalnum(p[3])) { p+=3; /* stub */ return; }
1011 if (strncasecmp(p,"PUT",3)==0&&!isalnum(p[3])) { p+=3; /* stub */ return; }
1012
1013 /* FIELD */
1014 if (strncasecmp(p,"FIELD",5)==0&&!isalnum(p[5])) { return; /* stub */ }
1015
1016 /* RESET */
1017 if (strncasecmp(p,"RESET",5)==0&&!isalnum(p[5])) { return; /* stub */ }
1018
1019 printf("?Syntax error: %.40s\n",p);
1020}
1021
1022/* Execute : separated stmts on a line */
1023void execute_line(char *text) {
1024 char buf[MAXBUF]; strncpy(buf,text,MAXBUF-1); buf[MAXBUF-1]=0;
1025 char *p=buf;
1026 while(*p && running!=0) {
1027 char stmt[MAXBUF]; int i=0;
1028 int in_q=0;
1029 while(*p){
1030 if(*p=='"') in_q=!in_q;
1031 if(*p==':' && !in_q){ p++; break; }
1032 stmt[i++]=*p++;
1033 }
1034 stmt[i]=0;
1035 int prev_running=running;
1036 execute_stmt(stmt);
1037 if(running==2) return; /* jump happened */
1038 }
1039}
1040
1041void run_from(int idx) {
1042 running=1; cur_line_idx=idx;
1043 while(running==1 && cur_line_idx>=0 && cur_line_idx<prog_cnt){
1044 if(tron_flag) printf("[%d]",program[cur_line_idx].num);
1045 int save=cur_line_idx;
1046 execute_line(program[cur_line_idx].text);
1047 if(running==2){ running=1; }
1048 else if(running==1) cur_line_idx++;
1049 }
1050 running=0;
1051}
1052
1053/* ============================================================
1054 MAIN - READY loop (asm READY/PROMPT)
1055 ============================================================ */
1056int main() {
1057 srand((unsigned)time(NULL));
1058 memset(file_handles,0,sizeof(file_handles));
1059 on_error_line=-1;
1060 printf("MS BASIC-80 C Port\nOk\n");
1061 char input[MAXBUF];
1062 while(1){
1063 printf("> ");
1064 fflush(stdout);
1065 if(!fgets(input,MAXBUF,stdin)) break;
1066 input[strcspn(input,"\n")]=0;
1067 if(!*input) continue;
1068 char *p=input; skip_ws(&p);
1069 if(isdigit(*p)){
1070 int num=(int)strtol(p,&p,10); skip_ws(&p);
1071 insert_line(num,*p?p:"");
1072 } else {
1073 execute_stmt(p);
1074 }
1075 if(!running) printf("Ok\n");
1076 }
1077 return 0;
1078}
1079