#include <tce/syscall.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <setjmp.h>
#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) + 4);
strncpy(p, program, (a - program)); // copy [0, a[
char *p2 = p + (a - program);
if (*(p2-1) != '\n') *(p2++) = '\n';
*p2 = 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(p2, l); // copy l
break;
}
}
if (p2[strlen(p2)-1] != '\n') strcat(p2, "\n");
strcat(p2, b); // copy [b, end[
if (p2[strlen(p2)-1] != '\n') strcat(p2, "\n");
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 COLOR:
color();
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<NUM_LAB;++t) {
if(label_table[t].name[0]==0) return t;
if(!strcmp(label_table[t].name,s)) return -2; /* dup */
}
return -1;
}
/* Find location of given label. A null is returned if
label is not found; otherwise a pointer to the position
of the label is returned.
*/
char *find_label(char *s)
{
register int t;
for(t=0; t<NUM_LAB; ++t)
if(!strcmp(label_table[t].name,s)) return label_table[t].p;
return '\0'; /* error condition */
}
/* Execute a GOTO statement. */
void exec_goto()
{
char *loc;
get_token(); /* get label to go to */
/* find the location of the label */
loc = find_label(token);
if(loc=='\0')
serror(7); /* label not defined */
else prog_ip=loc; /* start program running at that loc */
}
/* Initialize the array that holds the labels.
By convention, a null label name indicates that
array position is unused.
*/
void label_init()
{
register int t;
for(t=0; t<NUM_LAB; ++t) label_table[t].name[0]='\0';
}
/* Execute an IF statement. */
void exec_if()
{
int x , y, cond;
char op;
get_exp(&x); /* get left expression */
get_token(); /* get the operator */
if(!strchr("=<>", *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<y) cond=1;
break;
case '>':
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() {
int pid;
get_token();
if (token_type==QUOTE) {
char *args[2];
args[0] = token;
args[1] = 0;
pid = run(interp_name, args, term.fd);
if (pid <= 0) {
printf("Error: could not run '%s'.\n", interp_name);
} else {
int ret = waitpid(pid, 1);
printf("[exited: %i]\n", ret);
}
get_token();
} else {
serror(0);
}
}
/* 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);
}
}