summaryrefslogblamecommitdiff
path: root/src/user/app/kbasic/control.c
blob: 87731c492c775d350095d74d69b3153ec1caf134 (plain) (tree)
1
2
3
4

                        

                   



























































































                                                                                                            
                                                   

                                                                              




                                                    


                                                                                   
                                                         


                                      



                                                                

































                                                                                   


                                                


























































































































































































































































































































                                                                                             



                                






                                                                            
                        

                                                      




                            





























                                                                                 
#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);
	}
}