#include "linetab.h" lineref_t *linerefs[32769]; int refcounts[32769]; int lines_exist[32769]; unsigned char last_cmd, on_op; int last_cmd_pos; void add_lineref(unsigned short from, unsigned short pos) { lineref_t *p; int c; unsigned short to; to = fp2int(program + pos); if(to > 32767) return; p = linerefs[to]; c = refcounts[to]; if(c) { p = realloc(p, sizeof(lineref_t) * (c + 1)); } else { p = malloc(sizeof(lineref_t)); } if(!p) die("Out of memory."); linerefs[to] = p; linerefs[to][c].lineno = from; linerefs[to][c].pos = pos; linerefs[to][c].cmd = last_cmd; c++; refcounts[to] = c; } /* makes sure a numeric constant isn't start of an expression. */ int is_standalone_num(unsigned short pos) { if(program[pos] != OP_NUMCONST) return 0; switch(program[pos + 7]) { case OP_EOS: case OP_EOL: case OP_COMMA: return 1; default: return 0; } } CALLBACK(start_stmt) { lines_exist[lineno] = 1; } CALLBACK(got_cmd) { last_cmd = tok; last_cmd_pos = pos; on_op = 0; } void computed_msg(unsigned short lineno) { static int last_lineno = -1; char *cmd; /* avoid duplicate warnings */ if(lineno == last_lineno) return; last_lineno = lineno; switch(last_cmd) { case CMD_GOTO: cmd = "GOTO"; break; case CMD_GO_TO: cmd = "GO TO"; break; case CMD_GOSUB: cmd = "GOSUB"; break; case CMD_RESTORE: cmd = "RESTORE"; break; case CMD_TRAP: cmd = "TRAP"; break; case CMD_ON: if(on_op == OP_GOSUB) cmd = "ON/GOSUB"; else cmd = "ON/GOTO"; break; case CMD_LIST: cmd = "LIST"; break; default: /* should never happen! */ cmd = "???"; break; } fprintf(stderr, "%s: Warning: Computed %s at line %d.\n", self, cmd, lineno); } CALLBACK(got_var) { switch(last_cmd) { /* any use of a variable in the arguments to these means we can't renumber that argument. */ case CMD_GOTO: case CMD_GO_TO: case CMD_GOSUB: case CMD_RESTORE: case CMD_TRAP: case CMD_LIST: computed_msg(lineno); break; case CMD_ON: /* vars are OK in ON, before the GOTO or GOSUB */ if(on_op) computed_msg(lineno); break; default: break; } } CALLBACK(got_exp) { unsigned char last_tok = program[pos - 1]; int standalone; if(last_cmd == CMD_ON) { if(tok == OP_GOTO || tok == OP_GOSUB) on_op = tok; } if(tok != OP_NUMCONST) return; /* beware: standalone only means nothing *follows* the constant in the same expression. still have to check last_tok to see what came before. */ standalone = is_standalone_num(pos); switch(last_cmd) { /* these take a single argument */ case CMD_GOTO: case CMD_GO_TO: case CMD_GOSUB: case CMD_RESTORE: case CMD_TRAP: if((pos == last_cmd_pos + 1) && standalone) { add_lineref(lineno, pos + 1); } else { computed_msg(lineno); } break; case CMD_IF: /* this only applies to bare line numbers, like IF A THEN 1000, not IF A THEN GOTO 1000 (or anything else after THEN). */ if(last_tok == OP_THEN) { add_lineref(lineno, pos + 1); } break; case CMD_ON: { /* takes arbitrary number of arguments */ switch(last_tok) { case OP_GOTO: case OP_GOSUB: case OP_COMMA: if(standalone) add_lineref(lineno, pos + 1); else computed_msg(lineno); break; default: break; } } break; case CMD_LIST: { /* takes one or two arguments */ switch(last_tok) { case CMD_LIST: case OP_COMMA: if(standalone) add_lineref(lineno, pos + 1); else computed_msg(lineno); break; default: break; } } default: break; } } void build_ref_table(void) { on_start_stmt = start_stmt; on_cmd_token = got_cmd; on_exp_token = got_exp; on_var_token = got_var; walk_all_code(); on_start_stmt = on_cmd_token = on_exp_token = on_var_token = 0; } void free_ref_table(void) { int i; for(i = 0; i < 32768; i++) if(linerefs[i]) free(linerefs[i]); }