#include #include #include #include #include #include /* ================================================================ MS BASIC-80 Full C Port Ported from Z80 assembly disassembly (FNCTAB/WORDS/EVAL/NEWSTT) ================================================================ */ #define MAXLINES 2000 #define MAXBUF 512 #define MAXVARS 512 #define MAXARR 64 #define MAXARRLEN 1024 #define MAXSTACK 256 #define MAXDATA 2048 #define MAXSTR 256 #define MAXFILES 8 /* ---- Program storage ---- */ typedef struct { int num; char text[MAXBUF]; } Line; Line program[MAXLINES]; int prog_cnt = 0; /* ---- Variable types ---- */ #define TYPE_INT 2 #define TYPE_SNG 4 #define TYPE_STR 3 typedef struct { char name[MAXSTR]; int type; double num; char str[MAXSTR]; } Var; Var variables[MAXVARS]; int var_cnt = 0; /* ---- Arrays ---- */ typedef struct { char name[MAXSTR]; int type; int dims[4]; int ndims; int base; /* OPTION BASE */ double *nums; char (*strs)[MAXSTR]; } Arr; Arr arrays[MAXARR]; int arr_cnt = 0; int option_base = 0; /* ---- DATA pool (FDTLP) ---- */ char data_pool[MAXDATA][MAXSTR]; int data_count = 0; int data_pos = 0; /* ---- GOSUB stack (BAKSTK) ---- */ int call_stack[MAXSTACK]; int call_sp = 0; /* ---- FOR/NEXT stack ---- */ typedef struct { int var_idx; double limit; double step; int line_idx; } ForEntry; ForEntry for_stack[MAXSTACK]; int for_sp = 0; /* ---- WHILE/WEND stack ---- */ int while_stack[MAXSTACK]; int while_sp = 0; /* ---- ON ERROR ---- */ int on_error_line = -1; int err_number = 0; int err_line = 0; /* ---- Globals ---- */ double fac; int cur_line_idx; int running; int tron_flag = 0; int null_count = 0; /* NULL() */ char last_inkey = 0; int cont_idx = -1; /* for CONT */ /* ---- File handles (OPEN/CLOSE) ---- */ FILE *file_handles[MAXFILES]; char file_modes[MAXFILES]; /* 'I','O','A','R' */ /* ============================================================ UTILS ============================================================ */ static void skip_ws(char **p) { while (**p == ' ' || **p == '\t') (*p)++; } static char *strupr_local(char *s) { for (char *p = s; *p; p++) *p = toupper(*p); return s; } /* ============================================================ LINE MANAGEMENT (SRCHLN / INSERT) ============================================================ */ int find_line(int num) { for (int i = 0; i < prog_cnt; i++) if (program[i].num == num) return i; return -1; } void insert_line(int num, char *text) { for (int i = 0; i < prog_cnt; i++) { if (program[i].num == num) { if (!text || !*text) { for (int j = i; j < prog_cnt-1; j++) program[j] = program[j+1]; prog_cnt--; return; } strncpy(program[i].text, text, MAXBUF-1); return; } } if (!text || !*text) return; program[prog_cnt].num = num; strncpy(program[prog_cnt].text, text, MAXBUF-1); prog_cnt++; for (int i = prog_cnt-1; i > 0 && program[i].num < program[i-1].num; i--) { Line tmp = program[i]; program[i] = program[i-1]; program[i-1] = tmp; } } /* ============================================================ VARIABLE MANAGEMENT (PTRGET / GETVAR) ============================================================ */ int ptrget(const char *name) { for (int i = 0; i < var_cnt; i++) if (strcasecmp(name, variables[i].name) == 0) return i; strncpy(variables[var_cnt].name, name, MAXSTR-1); variables[var_cnt].type = TYPE_SNG; variables[var_cnt].num = 0; variables[var_cnt].str[0] = 0; return var_cnt++; } int is_str_var(const char *name) { return name[strlen(name)-1] == '$'; } /* ============================================================ ARRAY MANAGEMENT (DIM / SBSCPT) ============================================================ */ Arr *find_arr(const char *name) { for (int i = 0; i < arr_cnt; i++) if (strcasecmp(name, arrays[i].name) == 0) return &arrays[i]; return NULL; } Arr *make_arr(const char *name, int *dims, int ndims) { Arr *a = &arrays[arr_cnt++]; strncpy(a->name, name, MAXSTR-1); a->ndims = ndims; a->base = option_base; int total = 1; for (int i = 0; i < ndims; i++) { a->dims[i] = dims[i]+1; total *= (dims[i]+1); } a->type = is_str_var(name) ? TYPE_STR : TYPE_SNG; if (a->type == TYPE_STR) a->strs = calloc(total, sizeof(*a->strs)); else a->nums = calloc(total, sizeof(double)); return a; } /* ============================================================ EXPRESSION PARSER (EVAL / OPRND / PRITAB) Full: + - * / ^ unary- relational AND OR NOT Functions: ABS SQR RND SIN COS TAN ATN LOG EXP INT FIX SGN LEN VAL ASC CHR$ STR$ LEFT$ RIGHT$ MID$ SPACE$ HEX$ OCT$ CINT CSNG CDBL INSTR STRING$ FRE POS ============================================================ */ double expr(char **p); void run_from(int idx); void execute_stmt(char *p); void basic_error(int code, char *msg); static char str_result[MAXSTR]; /* scratch for string functions */ static double parse_string_fn(char **p) { /* Returns 0, result in str_result */ str_result[0] = 0; return 0; } /* Parse a string literal or string expression, result -> str_result */ static void parse_str_expr(char **p, char *out) { skip_ws(p); if (**p == '"') { (*p)++; int i=0; while (**p && **p != '"') out[i++] = *(*p)++; out[i]=0; if(**p=='"')(*p)++; } else if (isalpha(**p)) { char name[MAXSTR]={0}; char *q=*p; int i=0; while (isalnum(**p) || **p=='$' || **p=='_') name[i++]=*(*p)++; name[i]=0; if (is_str_var(name)) { strcpy(out, variables[ptrget(name)].str); } else { *p=q; sprintf(out,"%g",expr(p)); } } else { sprintf(out,"%g",expr(p)); } } static double primary(char **p) { skip_ws(p); double x = 0; if (**p == '-') { (*p)++; return -primary(p); } if (**p == '+') { (*p)++; return primary(p); } if (**p == '(') { (*p)++; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return x; } /* Functions */ #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;} MATHFN1("ABS",fabs) MATHFN1("SQR",sqrt) MATHFN1("SIN",sin) MATHFN1("COS",cos) MATHFN1("TAN",tan) MATHFN1("ATN",atan) MATHFN1("LOG",log) MATHFN1("EXP",exp) MATHFN1("INT",floor) MATHFN1("FIX",trunc) #undef MATHFN1 if (strncasecmp(*p,"SGN(",4)==0){ *p+=4; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return (x>0)-(x<0); } 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; } if (strncasecmp(*p,"CINT(",5)==0){ *p+=5; x=round(expr(p)); skip_ws(p); if(**p==')')(*p)++; return (int)x; } if (strncasecmp(*p,"CSNG(",5)==0){ *p+=5; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return (float)x; } if (strncasecmp(*p,"CDBL(",5)==0){ *p+=5; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return x; } if (strncasecmp(*p,"FRE(",4)==0) { *p+=4; expr(p); skip_ws(p); if(**p==')')(*p)++; return 60000; } if (strncasecmp(*p,"POS(",4)==0) { *p+=4; expr(p); skip_ws(p); if(**p==')')(*p)++; return 0; } 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); } 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]; } 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); } if (strncasecmp(*p,"PEEK(",5)==0){ *p+=5; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return 0; } /* stub */ if (strncasecmp(*p,"INP(",4)==0) { *p+=4; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return 0; } /* stub */ if (strncasecmp(*p,"LPOS(",5)==0){ *p+=5; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return 0; } if (strncasecmp(*p,"INSTR(",6)==0) { *p+=6; double start=1; char h[MAXSTR],n[MAXSTR]; /* INSTR([start,] hay, needle) */ char *save=*p; double t=expr(p); skip_ws(p); if (**p==',') { start=t; (*p)++; parse_str_expr(p,h); } else { *p=save; parse_str_expr(p,h); } skip_ws(p); if(**p==',')(*p)++; parse_str_expr(p,n); skip_ws(p); if(**p==')')(*p)++; char *found=strstr(h+(int)start-1,n); return found ? (found-h+1) : 0; } /* NOT */ if (strncasecmp(*p,"NOT",3)==0 && !isalnum((*p)[3])) { *p+=3; return ~(int)primary(p); } /* Number literal */ if (isdigit(**p) || **p=='.') { char *end; x=strtod(*p,&end); *p=end; return x; } /* Hex &H / Octal &O */ if (**p=='&') { (*p)++; if (toupper(**p)=='H') { (*p)++; x=(double)strtol(*p,p,16); return x; } if (toupper(**p)=='O') { (*p)++; x=(double)strtol(*p,p,8); return x; } x=(double)strtol(*p,p,8); return x; } /* Variable or array element */ if (isalpha(**p)) { char name[MAXSTR]={0}; int i=0; while (isalnum(**p)||**p=='$'||**p=='_') name[i++]=*(*p)++; name[i]=0; skip_ws(p); /* Array element */ if (**p=='(') { (*p)++; int idx[4]={0}; int nd=0; while (**p && **p!=')') { idx[nd++]=(int)expr(p); skip_ws(p); if(**p==',')(*p)++; } if(**p==')')(*p)++; Arr *a = find_arr(name); if (!a) { /* auto-dim 0..10 */ int d=10; a=make_arr(name,&d,1); } int flat=0; if (nd>=1) flat=idx[0]-option_base; if (nd>=2) flat=flat*(a->dims[1])+idx[1]-option_base; if (flat<0) flat=0; if (a->type==TYPE_STR) { /* str arrays not returned as number */ return 0; } else return a->nums[flat]; } return variables[ptrget(name)].num; } return 0; } static double power_e(char **p) { double x=primary(p); skip_ws(p); if (**p=='^') { (*p)++; x=pow(x,primary(p)); } return x; } static double term(char **p) { double x=power_e(p); skip_ws(p); 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); } return x; } static double add_sub(char **p) { double x=term(p); skip_ws(p); while(**p=='+'||**p=='-') { char op=*(*p)++; double y=term(p); x=(op=='+')?x+y:x-y; skip_ws(p); } return x; } static double relational(char **p) { double x=add_sub(p); skip_ws(p); if (strncmp(*p,"<>",2)==0){*p+=2;return x!=add_sub(p);} if (strncmp(*p,"<=",2)==0){*p+=2;return x<=add_sub(p);} if (strncmp(*p,">=",2)==0){*p+=2;return x>=add_sub(p);} if (**p=='<'){(*p)++;return x'){(*p)++;return x>add_sub(p);} if (**p=='='){(*p)++;return x==add_sub(p);} return x; } static double and_or(char **p) { double x=relational(p); skip_ws(p); while(1){ if (strncasecmp(*p,"AND",3)==0&&!isalnum((*p)[3])){ *p+=3; x=(int)x&(int)relational(p); } else if(strncasecmp(*p,"OR",2)==0&&!isalnum((*p)[2])){ *p+=2; x=(int)x|(int)relational(p); } else if(strncasecmp(*p,"XOR",3)==0&&!isalnum((*p)[3])){ *p+=3; x=(int)x^(int)relational(p); } else break; skip_ws(p); } return x; } double expr(char **p) { return fac=and_or(p); } /* String expression: result in out */ static void str_expr(char **p, char *out) { skip_ws(p); /* CHR$ */ 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; } /* STR$ */ if (strncasecmp(*p,"STR$(",5)==0){ *p+=5; double v=expr(p); skip_ws(p); if(**p==')')(*p)++; sprintf(out,"%g",v); return; } /* LEFT$ */ 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; } /* RIGHT$ */ 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; } /* MID$ */ 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; } /* SPACE$ */ 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; } /* STRING$ */ 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; } /* HEX$ */ if (strncasecmp(*p,"HEX$(",5)==0){ *p+=5; int v=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; sprintf(out,"%X",v); return; } /* OCT$ */ if (strncasecmp(*p,"OCT$(",5)==0){ *p+=5; int v=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; sprintf(out,"%o",v); return; } /* MKI$ MKS$ MKD$ */ 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; } 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; } 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; } /* INKEY$ */ if (strncasecmp(*p,"INKEY$",6)==0){ *p+=6; out[0]=last_inkey; out[1]=0; last_inkey=0; return; } /* Fallthrough: parse as string var or literal */ parse_str_expr(p, out); } /* ============================================================ COLLECT DATA (FDTLP) ============================================================ */ void collect_data() { data_count=0; data_pos=0; for (int i=0;i0 && cur_line_idx=0) { cur_line_idx=find_line(on_error_line); running=2; return; } running=0; } /* ============================================================ PRINT USING (USING / PUFOUT) - basic implementation ============================================================ */ static void print_using(char *fmt, double val) { /* Handle # . , + - $ simple format chars */ char buf[MAXBUF]; int w=0,d=0; for (char *f=fmt;*f;f++) if(*f=='#') w++; else if(*f=='.') { for(char *g=f+1;*g=='#';g++) d++; break; } if (d>0) printf("%*.*f",w,d,val); else printf("%*g",w,val); } /* ============================================================ WRITE statement (csv output) ============================================================ */ static void do_write(char **p, FILE *fp) { while (**p) { skip_ws(p); if (**p=='"' || (isalpha(**p) && is_str_var(*p))) { char s[MAXSTR]; str_expr(p,s); fprintf(fp,"\"%s\"",s); } else { fprintf(fp,"%g",expr(p)); } skip_ws(p); if (**p==',') { fprintf(fp,","); (*p)++; } else break; } fprintf(fp,"\n"); } /* ============================================================ EXECUTE ONE STATEMENT (NEWSTT / EXEC / FNCTAB) ============================================================ */ void execute_stmt(char *p) { skip_ws(&p); if (!*p) return; /* REM / ' */ if (strncasecmp(p,"REM",3)==0||*p=='\'') return; /* PRINT / LPRINT */ if (strncasecmp(p,"LPRINT",6)==0||strncasecmp(p,"PRINT",5)==0) { FILE *fp=stdout; int is_lp=(strncasecmp(p,"LPRINT",6)==0); p += is_lp?6:5; /* PRINT USING */ if (strncasecmp(p," USING",6)==0||strncasecmp(p,"USING",5)==0) { if(*p==' ')p++; p+=5; skip_ws(&p); char fmt[MAXBUF]; str_expr(&p,fmt); skip_ws(&p); if(*p==';')p++; double v=expr(&p); print_using(fmt,v); fprintf(fp,"\n"); return; } int newline=1; while (*p) { skip_ws(&p); if (!*p) break; if (*p==';') { p++; newline=0; continue; } if (*p==',') { fprintf(fp,"\t"); p++; newline=0; continue; } /* TAB( */ if (strncasecmp(p,"TAB(",4)==0) { p+=4; int n=(int)expr(&p); skip_ws(&p); if(*p==')')p++; for(int i=0;i=0 && file_handles[file_no]) { fgets(ibuf,MAXBUF,file_handles[file_no]); } else { fgets(ibuf,MAXBUF,stdin); } ibuf[strcspn(ibuf,"\n")]=0; int vi=ptrget(name); if (is_str_var(name)) { strncpy(variables[vi].str,ibuf,MAXSTR-1); variables[vi].type=TYPE_STR; } else { variables[vi].num=atof(ibuf); variables[vi].type=TYPE_SNG; } skip_ws(&p); } return; } /* LET / assignment */ if (strncasecmp(p,"LET",3)==0&&!isalnum(p[3])) p+=3; if (isalpha(*p)) { char name[MAXSTR]={0}; char *save=p; int i=0; while(isalnum(*p)||*p=='$') name[i++]=*p++; name[i]=0; skip_ws(&p); /* Array assignment */ if (*p=='(') { (*p==0); p++; int idx[4]={0}; int nd=0; while(*p&&*p!=')') { idx[nd++]=(int)expr(&p); skip_ws(&p); if(*p==',')p++; } if(*p==')')p++; skip_ws(&p); if(*p=='=')p++; Arr *a=find_arr(name); if(!a){int d=10;a=make_arr(name,&d,1);} int flat=idx[0]-option_base; if (nd>=2) flat=flat*(a->dims[1])+idx[1]-option_base; if (flat<0) flat=0; if (a->type==TYPE_STR){char s[MAXSTR];str_expr(&p,s);strncpy(a->strs[flat],s,MAXSTR-1);} else a->nums[flat]=expr(&p); return; } if (*p=='=') { p++; int vi=ptrget(name); if (is_str_var(name)) { char s[MAXSTR]; str_expr(&p,s); strncpy(variables[vi].str,s,MAXSTR-1); variables[vi].type=TYPE_STR; } else { variables[vi].num=expr(&p); variables[vi].type=TYPE_SNG; } return; } p=save; } /* IF / THEN / ELSE */ if (strncasecmp(p,"IF",2)==0&&!isalnum(p[2])) { p+=2; skip_ws(&p); double cond=expr(&p); skip_ws(&p); if (strncasecmp(p,"THEN",4)==0&&!isalnum(p[4])) p+=4; skip_ws(&p); /* THEN line_number */ if (isdigit(*p)) { int tgt=(int)strtol(p,&p,10); if (cond) { int idx=find_line(tgt); if(idx>=0){cur_line_idx=idx;running=2;} } else { /* find ELSE */ skip_ws(&p); if (strncasecmp(p,"ELSE",4)==0) { p+=4; skip_ws(&p); if(isdigit(*p)){ int t=(int)strtol(p,&p,10); int idx=find_line(t); if(idx>=0){cur_line_idx=idx;running=2;}} else execute_stmt(p); } } return; } char *else_p=NULL; /* find ELSE in remainder */ char *scan=p; int depth=0; while(*scan){ if(*scan=='(')depth++; else if(*scan==')')depth--; if(depth==0&&strncasecmp(scan,"ELSE",4)==0&&!isalnum(scan[4])&&!isalnum(scan[-1])){ else_p=scan; break; } scan++; } 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); } else if (else_p) { execute_stmt(else_p+4); } return; } /* GOTO */ if (strncasecmp(p,"GOTO",4)==0&&!isalnum(p[4])) { p+=4; int tgt=(int)expr(&p); int idx=find_line(tgt); if(idx<0){basic_error(8,"Undefined line");return;} cur_line_idx=idx; running=2; return; } /* GOSUB */ if (strncasecmp(p,"GOSUB",5)==0&&!isalnum(p[5])) { p+=5; int tgt=(int)expr(&p); int idx=find_line(tgt); if(idx<0){basic_error(8,"Undefined line");return;} call_stack[call_sp++]=cur_line_idx+1; cur_line_idx=idx; running=2; return; } /* RETURN */ if (strncasecmp(p,"RETURN",6)==0&&!isalnum(p[6])) { if(call_sp<=0){basic_error(3,"RETURN without GOSUB");return;} cur_line_idx=call_stack[--call_sp]; running=2; return; } /* ON GOTO / ON GOSUB / ON ERROR */ if (strncasecmp(p,"ON",2)==0&&!isalnum(p[2])) { p+=2; skip_ws(&p); if (strncasecmp(p,"ERROR",5)==0) { p+=5; skip_ws(&p); if (strncasecmp(p,"GOTO",4)==0){p+=4; on_error_line=(int)expr(&p);} else if(strncasecmp(p,"RESUME",6)==0){p+=6;skip_ws(&p);if(strncasecmp(p,"NEXT",4)==0)p+=4;} return; } double n=expr(&p); skip_ws(&p); int is_sub=(strncasecmp(p,"GOSUB",5)==0); p += is_sub?5:4; int targets[64]; int tc=0; while(*p){skip_ws(&p);if(!isdigit(*p))break;targets[tc++]=(int)strtol(p,&p,10);skip_ws(&p);if(*p==',')p++;} int ni=(int)n-1; if(ni>=0&&ni=0){ if(is_sub){call_stack[call_sp++]=cur_line_idx+1;} cur_line_idx=idx; running=2; } } return; } /* FOR */ if (strncasecmp(p,"FOR",3)==0&&!isalnum(p[3])) { p+=3; skip_ws(&p); char name[MAXSTR]={0}; int i=0; while(isalnum(*p)||*p=='$') name[i++]=*p++; name[i]=0; skip_ws(&p); if(*p=='=')p++; double start=expr(&p); skip_ws(&p); if(strncasecmp(p,"TO",2)==0&&!isalnum(p[2]))p+=2; double limit=expr(&p); double step=1.0; skip_ws(&p); if(strncasecmp(p,"STEP",4)==0&&!isalnum(p[4])){p+=4;step=expr(&p);} int vi=ptrget(name); variables[vi].num=start; variables[vi].type=TYPE_SNG; for_stack[for_sp].var_idx=vi; for_stack[for_sp].limit=limit; for_stack[for_sp].step=step; for_stack[for_sp].line_idx=cur_line_idx; for_sp++; return; } /* NEXT */ if (strncasecmp(p,"NEXT",4)==0&&!isalnum(p[4])) { if(for_sp<=0){basic_error(1,"NEXT without FOR");return;} ForEntry *fe=&for_stack[for_sp-1]; variables[fe->var_idx].num+=fe->step; if((fe->step>0&&variables[fe->var_idx].num<=fe->limit)|| (fe->step<0&&variables[fe->var_idx].num>=fe->limit)){ cur_line_idx=fe->line_idx; running=2; } else for_sp--; return; } /* WHILE */ if (strncasecmp(p,"WHILE",5)==0&&!isalnum(p[5])) { p+=5; if (!expr(&p)) { /* skip to WEND */ int depth=1; while(++cur_line_idx0){ if(strstr(program[cur_line_idx].text,"WHILE"))depth++; if(strstr(program[cur_line_idx].text,"WEND"))depth--; } } else { while_stack[while_sp++]=cur_line_idx; } return; } /* WEND */ if (strncasecmp(p,"WEND",4)==0&&!isalnum(p[4])) { if(while_sp<=0){basic_error(23,"WEND without WHILE");return;} cur_line_idx=while_stack[--while_sp]-1; running=2; return; } /* DATA */ if (strncasecmp(p,"DATA",4)==0) return; /* READ */ if (strncasecmp(p,"READ",4)==0&&!isalnum(p[4])) { p+=4; while(*p){ skip_ws(&p); if(*p==',')p++; skip_ws(&p); if(!*p)break; char name[MAXSTR]={0}; int i=0; while(isalnum(*p)||*p=='$') name[i++]=*p++; name[i]=0; if(!name[0])break; if(data_pos>=data_count){basic_error(4,"Out of DATA");return;} int vi=ptrget(name); if(is_str_var(name)){strncpy(variables[vi].str,data_pool[data_pos++],MAXSTR-1);variables[vi].type=TYPE_STR;} else{variables[vi].num=atof(data_pool[data_pos++]);variables[vi].type=TYPE_SNG;} } return; } /* RESTORE */ if (strncasecmp(p,"RESTORE",7)==0) { p+=7; skip_ws(&p); if(isdigit(*p)){int tgt=(int)strtol(p,&p,10);(void)tgt;} /* stub: restore to line */ data_pos=0; return; } /* DIM */ if (strncasecmp(p,"DIM",3)==0&&!isalnum(p[3])) { p+=3; while(*p){ skip_ws(&p); if(*p==',')p++; skip_ws(&p); if(!*p)break; char name[MAXSTR]={0}; int i=0; while(isalnum(*p)||*p=='$') name[i++]=*p++; name[i]=0; skip_ws(&p); int dims[4]={10,10,10,10}; int ndims=0; if(*p=='('){ p++; while(*p&&*p!=')'){skip_ws(&p);dims[ndims++]=(int)expr(&p);skip_ws(&p);if(*p==',')p++;} if(*p==')')p++; } if(ndims==0)ndims=1; make_arr(name,dims,ndims); } return; } /* SWAP */ if (strncasecmp(p,"SWAP",4)==0&&!isalnum(p[4])) { p+=4; skip_ws(&p); char n1[MAXSTR]={0}; int i=0; while(isalnum(*p)||*p=='$') n1[i++]=*p++; n1[i]=0; skip_ws(&p); if(*p==',')p++; skip_ws(&p); char n2[MAXSTR]={0}; i=0; while(isalnum(*p)||*p=='$') n2[i++]=*p++; n2[i]=0; int v1=ptrget(n1),v2=ptrget(n2); double tmp=variables[v1].num; variables[v1].num=variables[v2].num; variables[v2].num=tmp; char ts[MAXSTR]; strcpy(ts,variables[v1].str); strcpy(variables[v1].str,variables[v2].str); strcpy(variables[v2].str,ts); return; } /* POKE */ if (strncasecmp(p,"POKE",4)==0&&!isalnum(p[4])) { p+=4; expr(&p); skip_ws(&p); if(*p==',')p++; expr(&p); /* stub */ return; } /* WAIT */ if (strncasecmp(p,"WAIT",4)==0&&!isalnum(p[4])) { p+=4; expr(&p); skip_ws(&p); if(*p==',')p++; expr(&p); return; } /* OUT */ if (strncasecmp(p,"OUT",3)==0&&!isalnum(p[3])) { p+=3; expr(&p); skip_ws(&p); if(*p==',')p++; expr(&p); return; } /* LSET / RSET */ if (strncasecmp(p,"LSET",4)==0&&!isalnum(p[4])) { p+=4; skip_ws(&p); char name[MAXSTR]={0}; int i=0; while(isalnum(*p)||*p=='$') name[i++]=*p++; name[i]=0; skip_ws(&p); if(*p=='=')p++; char s[MAXSTR]; str_expr(&p,s); int vi=ptrget(name); strncpy(variables[vi].str,s,MAXSTR-1); variables[vi].type=TYPE_STR; return; } if (strncasecmp(p,"RSET",4)==0&&!isalnum(p[4])) { p+=4; skip_ws(&p); char name[MAXSTR]={0}; int i=0; while(isalnum(*p)||*p=='$') name[i++]=*p++; name[i]=0; skip_ws(&p); if(*p=='=')p++; char s[MAXSTR]; str_expr(&p,s); int vi=ptrget(name); strncpy(variables[vi].str,s,MAXSTR-1); variables[vi].type=TYPE_STR; return; } /* WRITE */ if (strncasecmp(p,"WRITE",5)==0&&!isalnum(p[5])) { p+=5; skip_ws(&p); FILE *fp=stdout; if(*p=='#'){p++;int fn=(int)expr(&p);skip_ws(&p);if(*p==',')p++;if(fn>=0&&fn=0&&fn=0&&fn=0) printf("%d %s\n",program[idx].num,program[idx].text); return; } /* NULL */ if (strncasecmp(p,"NULL",4)==0&&!isalnum(p[4])) { p+=4; null_count=(int)expr(&p); return; } /* CALL */ if (strncasecmp(p,"CALL",4)==0&&!isalnum(p[4])) { p+=4; expr(&p); return; /* stub */ } /* CHAIN */ if (strncasecmp(p,"CHAIN",5)==0&&!isalnum(p[5])) { p+=5; skip_ws(&p); char fname[MAXBUF]; str_expr(&p,fname); execute_stmt("NEW"); execute_stmt("LOAD"); return; } /* SYSTEM */ if (strncasecmp(p,"SYSTEM",6)==0&&!isalnum(p[6])) { printf("\n"); exit(0); } /* END / STOP */ if (strncasecmp(p,"END",3)==0&&!isalnum(p[3])) { cont_idx=cur_line_idx; running=0; return; } 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; } /* CONT */ if (strncasecmp(p,"CONT",4)==0&&!isalnum(p[4])) { if(cont_idx<0){printf("?Can't continue\n");return;} run_from(cont_idx); return; } /* LIST [n[-m]] */ if (strncasecmp(p,"LIST",4)==0&&!isalnum(p[4])) { p+=4; skip_ws(&p); int n1=0,n2=999999; 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;}} for(int i=0;i=n1&&program[i].num<=n2) printf("%d %s\n",program[i].num,program[i].text); return; } /* RUN */ if (strncasecmp(p,"RUN",3)==0&&!isalnum(p[3])) { p+=3; skip_ws(&p); int start=0; if(isdigit(*p)){int n=(int)strtol(p,&p,10);start=find_line(n);if(start<0)start=0;} collect_data(); data_pos=0; call_sp=0; for_sp=0; while_sp=0; var_cnt=0; arr_cnt=0; run_from(start); return; } /* NEW */ if (strncasecmp(p,"NEW",3)==0&&!isalnum(p[3])) { 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; return; } /* CLEAR */ if (strncasecmp(p,"CLEAR",5)==0&&!isalnum(p[5])) { var_cnt=0; arr_cnt=0; call_sp=0; for_sp=0; while_sp=0; data_pos=0; return; } /* MERGE */ if (strncasecmp(p,"MERGE",5)==0&&!isalnum(p[5])) { p+=5; skip_ws(&p); char fname[MAXBUF]; str_expr(&p,fname); FILE *f=fopen(fname,"r"); if(!f)return; char line[MAXBUF]; while(fgets(line,MAXBUF,f)){ line[strcspn(line,"\n")]=0; char *q=line; skip_ws(&q); if(isdigit(*q)){int num=(int)strtol(q,&q,10);skip_ws(&q);insert_line(num,q);} } fclose(f); return; } /* GET / PUT (file) */ if (strncasecmp(p,"GET",3)==0&&!isalnum(p[3])) { p+=3; /* stub */ return; } if (strncasecmp(p,"PUT",3)==0&&!isalnum(p[3])) { p+=3; /* stub */ return; } /* FIELD */ if (strncasecmp(p,"FIELD",5)==0&&!isalnum(p[5])) { return; /* stub */ } /* RESET */ if (strncasecmp(p,"RESET",5)==0&&!isalnum(p[5])) { return; /* stub */ } printf("?Syntax error: %.40s\n",p); } /* Execute : separated stmts on a line */ void execute_line(char *text) { char buf[MAXBUF]; strncpy(buf,text,MAXBUF-1); buf[MAXBUF-1]=0; char *p=buf; while(*p && running!=0) { char stmt[MAXBUF]; int i=0; int in_q=0; while(*p){ if(*p=='"') in_q=!in_q; if(*p==':' && !in_q){ p++; break; } stmt[i++]=*p++; } stmt[i]=0; int prev_running=running; execute_stmt(stmt); if(running==2) return; /* jump happened */ } } void run_from(int idx) { running=1; cur_line_idx=idx; while(running==1 && cur_line_idx>=0 && cur_line_idx "); fflush(stdout); if(!fgets(input,MAXBUF,stdin)) break; input[strcspn(input,"\n")]=0; if(!*input) continue; char *p=input; skip_ws(&p); if(isdigit(*p)){ int num=(int)strtol(p,&p,10); skip_ws(&p); insert_line(num,*p?p:""); } else { execute_stmt(p); } if(!running) printf("Ok\n"); } return 0; }