#include #include // #include // #include #include #include #include #include "basic.h" struct label { char name[LAB_LEN]; char *p; /* points to place to go in source file*/ }; struct for_stack { int *var; /* counter variable */ int target; /* target value */ char *loc; }; struct label label_table[NUM_LAB]; char *gstack[SUB_NEST]; /* stack for gosub */ struct for_stack fstack[FOR_NEST]; /* stack for FOR/NEXT loop */ int ftos; /* index to top of FOR stack */ int gtos; /* index to top of GOSUB stack */ char *program, *prog_ip; /* holds expression to be analyzed */ char *interp_name; jmp_buf e_buf; /* hold environment for longjmp() */ char *find_label(); void scan_labels(); struct for_stack fpop(); char *gpop(); int get_next_label(char *); void fpush(struct for_stack); void gosub(), greturn(), gpush(char*), label_init(); void exec_if(), exec_for(), next(), exec_goto(); void exec_exec(), exec_load(); void exec_list(); /* initialize variables */ void init() { program = NULL; prog_ip = NULL; label_init(); } /* Load program and scans labels */ void load_program (char *p_buf) { if (program != NULL) { free(program); } program = p_buf; scan_labels(); /* find the labels in the program */ } /* Insert line at the right place of program */ void insert_line(char *l, int n) { char *a, *b, *p; int i_n, t, k; if (program == NULL) { load_program(strdup(l)); } else { int prog_l = strlen(program); // lookup insert position // find smallest label above line number a = (n == 0 ? program : program + prog_l); i_n = -1; b = program + prog_l; for (t = 0; t < NUM_LAB; t++) { if (label_table[t].name[0]) { k = atoi(label_table[t].name); if (k == n) { a = label_table[t].p; while (*a != '\n' && a > program) a--; // back to beginning of line if (*a == '\n') a++; } if (k > n && (k < i_n || i_n == -1)) { i_n = k; b = label_table[t].p; while (*b != '\n' && b > program) b--; // back to beginning of line if (*b == '\n') b++; if (b < a) a = b; } } } p = malloc(prog_l + strlen(l) + 3); strncpy(p, program, (a - program)); // copy [0, a[ p[a - program] = 0; // if l is an empty line, do not copy. for (i_n = 0; l[i_n]; i_n++) { if (!isdigit(l[i_n]) && l[i_n] != '\n' && l[i_n] != '\r') { strcpy(p + (a - program), l); // copy l break; } } strcat(p + (a - program), b); // copy [b, end[ load_program(p); // reparse labels } } /* start program execution at given entry point (not necessarily in loaded program) return values : - 0 : return on end of input - 1 : return on error - 2 : return on "END" statement - 3 : return on "LOAD" statement */ int start(char *entry) { ftos = 0; /* initialize the FOR stack index */ gtos = 0; /* initialize the GOSUB stack index */ prog_ip = entry; if(setjmp(e_buf)) return 1; /* initialize the long jump buffer */ do { token_type = get_token(); /* check for assignment statement */ if(token_type==VARIABLE) { putback(); /* return the var to the input stream */ assignment(); /* must be assignment statement */ } else /* is command */ switch(tok) { case LIST: exec_list(); break; case PRINT: exec_print(); break; case GOTO: exec_goto(); break; case IF: exec_if(); break; case FOR: exec_for(); break; case NEXT: next(); break; case INPUT: input(); break; case GOSUB: gosub(); break; case RETURN: greturn(); break; case EXEC: exec_exec(); break; case LOAD: exec_load(); return 3; case RUN: // (re)start program at begining ftos = gtos = 0; prog_ip = program; break; case END: // end program execution return 2; } } while (prog_ip != NULL && tok != FINISHED); return 0; } /* display an error message */ void serror(int error) { static char *e[]= { "syntax error", "unbalanced parentheses", "no expression present", "equals sign expected", "not a variable", "Label table full", "duplicate label", "undefined label", "THEN expected", "TO expected", "too many nested FOR loops", "NEXT without FOR", "too many nested GOSUBs", "RETURN without GOSUB" }; printf ("[near '%s' %d %d] E: %s\n", token, tok, token_type, e[error]); longjmp(e_buf, 1); /* return to save point */ exit(0); } /* Find all labels. */ void scan_labels() { int addr; label_init(); /* zero all labels */ if (program == 0) return; /* if no program is loaded, nothing to do. */ prog_ip = program; /* prog_ip will go through the program */ /* if the first token in the file is a label */ get_token(); if(token_type==NUMBER) { strcpy(label_table[0].name,token); label_table[0].p=prog_ip; } find_eol(); do { get_token(); if(token_type==NUMBER) { addr = get_next_label(token); if(addr==-1 || addr==-2) { (addr==-1) ?serror(5):serror(6); } strncpy(label_table[addr].name, token, LAB_LEN); label_table[addr].name[LAB_LEN-1] = 0; // null terminate label_table[addr].p = prog_ip; /* current point in program */ } /* if not on a blank line, find next line */ if(tok!=EOL) find_eol(); } while(tok!=FINISHED); } /* Return index of next free position in label array. A -1 is returned if the array is full. A -2 is returned when duplicate label is found. */ int get_next_label(char *s) { register int t; for(t=0;t", *token)) { serror(0); /* not a legal operator */ return; } op=*token; get_exp(&y); /* get right expression */ /* determine the outcome */ cond = 0; switch(op) { case '<': if(x': if(x>y) cond=1; break; case '=': if(x==y) cond=1; break; } if(cond) { /* is true so process target of IF */ get_token(); if(tok!=THEN) { serror(8); return; }/* else program execution starts on next line */ } else find_eol(); /* find start of next line */ } /* Execute a FOR loop. */ void exec_for() { struct for_stack i; int value; get_token(); /* read the control variable */ if(!isalpha(*token)) { serror(4); return; } i.var=find_var(token); /* save its index */ get_token(); /* read the equals sign */ if(*token!='=') { serror(3); return; } get_exp(&value); /* get initial value */ *(i.var) = value; get_token(); if(tok!=TO) serror(9); /* read and discard the TO */ get_exp(&i.target); /* get target value */ /* if loop can execute at least once, push info on stack */ if(value>=*(i.var)) { i.loc = prog_ip; fpush(i); } else /* otherwise, skip loop code altogether */ while(tok!=NEXT) get_token(); } /* Execute a NEXT statement. */ void next() { struct for_stack i; i = fpop(); /* read the loop info */ (*(i.var))++; if(*(i.var)>i.target) return; /* all done */ fpush(i); /* otherwise, restore the info */ prog_ip = i.loc; /* loop */ } /* Push function for the FOR stack. */ void fpush(struct for_stack i) { if(ftos>FOR_NEST) serror(10); fstack[ftos]=i; ftos++; } struct for_stack fpop() { ftos--; if(ftos<0) serror(11); return(fstack[ftos]); } /* Execute a GOSUB command. */ void gosub() { char *loc; get_token(); /* find the label to call */ loc = find_label(token); if(loc=='\0') serror(7); /* label not defined */ else { gpush(prog_ip); /* save place to return to */ prog_ip = loc; /* start program running at that loc */ } } /* Return from GOSUB. */ void greturn() { prog_ip = gpop(); } /* GOSUB stack push function. */ void gpush(char *s) { gtos++; if(gtos==SUB_NEST) { serror(12); return; } gstack[gtos]=s; } /* GOSUB stack pop function. */ char *gpop() { if(gtos==0) { serror(13); return 0; } return(gstack[gtos--]); } /* EXEC function Forks and runs another instance of the interpreter */ void exec_exec() { /* TODO int pid; get_token(); if (token_type==QUOTE) { pid = fork(); if (pid < 0) { printf("Error: could not fork.\n"); } else { if (pid == 0) { execlp(interp_name, interp_name, token, 0); printf("Error: could not exec.\n"); exit(0); } else { wait(0); } } get_token(); } else { serror(0); } */ printf("EXEC not available.\n"); } /* LOAD function loads a program into interpreter and ends anything currently executing */ void exec_load() { char *p_buf; get_token(); if (token_type==QUOTE) { if (!(p_buf = load_file(token))) { printf("Error: could not load file %s\n", token); } else { load_program(p_buf); } } else { serror(0); } } /* LIST function dumps program source text */ void exec_list() { get_token(); if (program == NULL) { printf("No program loaded.\n"); } else { printf("%s", program); } }