Ostatnio aktywny 1 month ago

's Avatar zdebel zrewidował ten Gist 1 month ago. Przejdź do rewizji

1 file changed, 1078 insertions

basic.c(stworzono plik)

@@ -0,0 +1,1078 @@
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 ---- */
24 + typedef struct { int num; char text[MAXBUF]; } Line;
25 + Line program[MAXLINES];
26 + int prog_cnt = 0;
27 +
28 + /* ---- Variable types ---- */
29 + #define TYPE_INT 2
30 + #define TYPE_SNG 4
31 + #define TYPE_STR 3
32 +
33 + typedef struct {
34 + char name[MAXSTR];
35 + int type;
36 + double num;
37 + char str[MAXSTR];
38 + } Var;
39 +
40 + Var variables[MAXVARS];
41 + int var_cnt = 0;
42 +
43 + /* ---- Arrays ---- */
44 + typedef 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 +
54 + Arr arrays[MAXARR];
55 + int arr_cnt = 0;
56 + int option_base = 0;
57 +
58 + /* ---- DATA pool (FDTLP) ---- */
59 + char data_pool[MAXDATA][MAXSTR];
60 + int data_count = 0;
61 + int data_pos = 0;
62 +
63 + /* ---- GOSUB stack (BAKSTK) ---- */
64 + int call_stack[MAXSTACK];
65 + int call_sp = 0;
66 +
67 + /* ---- FOR/NEXT stack ---- */
68 + typedef struct {
69 + int var_idx;
70 + double limit;
71 + double step;
72 + int line_idx;
73 + } ForEntry;
74 + ForEntry for_stack[MAXSTACK];
75 + int for_sp = 0;
76 +
77 + /* ---- WHILE/WEND stack ---- */
78 + int while_stack[MAXSTACK];
79 + int while_sp = 0;
80 +
81 + /* ---- ON ERROR ---- */
82 + int on_error_line = -1;
83 + int err_number = 0;
84 + int err_line = 0;
85 +
86 + /* ---- Globals ---- */
87 + double fac;
88 + int cur_line_idx;
89 + int running;
90 + int tron_flag = 0;
91 + int null_count = 0; /* NULL() */
92 + char last_inkey = 0;
93 + int cont_idx = -1; /* for CONT */
94 +
95 + /* ---- File handles (OPEN/CLOSE) ---- */
96 + FILE *file_handles[MAXFILES];
97 + char file_modes[MAXFILES]; /* 'I','O','A','R' */
98 +
99 + /* ============================================================
100 + UTILS
101 + ============================================================ */
102 + static void skip_ws(char **p) { while (**p == ' ' || **p == '\t') (*p)++; }
103 +
104 + static 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 + ============================================================ */
112 + int 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 +
118 + void 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 + ============================================================ */
141 + int 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 +
151 + int is_str_var(const char *name) {
152 + return name[strlen(name)-1] == '$';
153 + }
154 +
155 + /* ============================================================
156 + ARRAY MANAGEMENT (DIM / SBSCPT)
157 + ============================================================ */
158 + Arr *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 +
164 + Arr *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 + ============================================================ */
183 + double expr(char **p);
184 + void run_from(int idx);
185 + void execute_stmt(char *p);
186 + void basic_error(int code, char *msg);
187 +
188 + static char str_result[MAXSTR]; /* scratch for string functions */
189 +
190 + static 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 */
197 + static 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 +
214 + static 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 +
301 + static 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 + }
306 + static 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 + }
311 + static 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 + }
316 + static 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 + }
326 + static 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 + }
337 + double expr(char **p) { return fac=and_or(p); }
338 +
339 + /* String expression: result in out */
340 + static 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 + ============================================================ */
373 + void 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 + ============================================================ */
400 + void 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 + ============================================================ */
410 + static 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 + ============================================================ */
421 + static 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 + ============================================================ */
440 + void 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 */
1023 + void 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 +
1041 + void 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 + ============================================================ */
1056 + int 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 + }
Nowsze Starsze