Última actividad 1 month ago

's Avatar zdebel revisó este gist 1 month ago. Ir a la revisión

1 file changed, 1095 insertions

basic.c(archivo creado)

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