#include "sno.h"

/*
 *   Snobol III
 */


int	freesize;
struct node *freespace &end;
int	*fault -1;

mes(s) {
	sysput(strstr(s));
}

init(s, t) {
	register struct node *a, *b;

	a = strstr(s);
	b = look(a);
	delete(a);
	b->typ = t;
	return(b);
}

main(argc, argv)
char *argv[];
{
	extern fin, fout;
	register struct node *a, *b, *c;

	if(argc > 1) {
		fin = open(argv[1], 0);
		if(fin < 0) {
			mes("cannot open input");
			exit();
		}
	}
	fout = dup(1);
	lookf = init("f", 0);
	looks = init("s", 0);
	lookend = init("end", 0);
	lookstart = init("start", 0);
	lookdef = init("define", 0);
	lookret = init("return", 0);
	lookfret = init("freturn", 0);
	init("syspit", 3);
	init("syspot", 4);
	a = c = compile();
	while (lookend->typ != 2) {
		a->p1 = b = compile();
		a = b;
	}
	cfail = 1;
	a->p1 = 0;
	if (lookstart->typ == 2)
		c = lookstart->p2;
	while (c=execute(c));
	flush();
}

syspit() {
	extern fin;
	register struct node *b, *c, *d;
	int a;

	if ((a=getchar())=='\n')
		return(0);
	b = c = alloc();
	while(a != '\n') {
		c->p1 = d = alloc();
		c = d;
	l:
		c->ch = a;
		if(a == '\0') {
			if(fin) {
				close(fin);
				fin = 0;
				a = getchar();
				goto l;
			}
			rfail = 1;
			break;
		}
		a = getchar();
	}
	b->p2 = c;
	if(rfail) {
		delete(b);
		b = 0;
	}
	return(b);
}

syspot(string)
struct node *string;
{
	register struct node *a, *b, *s;

	s = string;
	if (s!=0) {
		a = s;
		b = s->p2;
		while(a != b) {
			a = a->p1;
			putchar(a->ch);
		}
	}
	putchar('\n');
}

strstr(s)
char s[];
{
	int c;
	register struct node *e, *f, *d;

	d = f = alloc();
	while ((c = *s++)!='\0') {
		(e=alloc())->ch = c;
		f->p1 = e;
		f = e;
	}
	d->p2 = e;
	return(d);
}

class(c) {
	switch (c) {
		case ')':  return(1);
		case '(':  return(2);
		case '\t':
		case ' ': return(3);
		case '+':  return(4);
		case '-':  return(5);
		case '*': return(6);
		case '/':  return(7);
		case '$':  return(8);
		case '"':
		case '\'': return(9);
		case '=':  return(10);
		case ',':  return(11);
	}
	return(0);
}

alloc() {
	register struct node *f;
	register int i;
	extern fout;

	if (freelist==0) {
		if (--freesize < 20) {
			if ((i=sbrk(1200)) == -1) {
				flush();
				write (fout, "Out of free space\n", 18);
				exit();
			}
			freesize =+ 200;
		}
		return(freespace++);
	}
	f = freelist;
	freelist = freelist->p1;
	return(f);
}

free(pointer)
struct node *pointer;
{
	pointer->p1 = freelist;
	freelist = pointer;
}

nfree()
{
	register int i;
	register struct node *a;

	i = freesize;
	a = freelist;
	while(a) {
		a = a->p1;
		i++;
	}
	return(i);
}

look(string)
struct node *string;
{
	register struct node *i, *j, *k;

	k = 0;
	i = namelist;
	while (i) {
		j = i->p1;
		if (equal(j->p1, string) == 0)
			return(j);
		i = (k=i)->p2;
	}
	i = alloc();
	i->p2 = 0;
	if (k)
		k->p2 = i;
	else
		namelist = i;
	j = alloc();
	i->p1 = j;
	j->p1 = copy(string);
	j->p2 = 0;
	j->typ = 0;
	return(j);
}

copy(string)
struct node *string;
{
	register struct node *j, *l, *m;
	struct node *i, *k;

	if (string == 0)
		return(0);
	i = l = alloc();
	j = string;
	k = string->p2;
	while(j != k) {
		m = alloc();
		m->ch = (j=j->p1)->ch;
		l->p1 = m;
		l = m;
	}
	i->p2 = l;
	return(i);
}

equal(string1, string2)
struct node *string1, *string2;
{
	register struct node *i, *j, *k;
	struct node *l;
	int n, m;

	if (string1==0) {
		if (string2==0)
			return(0);
		return(-1);
	}
	if (string2==0)
		return(1);
	i = string1;
	j = string1->p2;
	k = string2;
	l = string2->p2;
	for(;;) {
		m = (i=i->p1)->ch;
		n = (k=k->p1)->ch;
		if (m>n)
			return(1);
		if (m<n)
			return(-1);
		if (i==j) {
			if (k==l)
				return(0);
			return(-1);
		}
		if (k==l)
			return(1);
	}
}

strbin(string)
struct node *string;
{
	int n, m, sign;
	register struct node *p, *q, *s;

	s = string;
	n = 0;
	if (s==0)
		return(0);
	p = s->p1;
	q = s->p2;
	sign = 1;
	if (class(p->ch)==5) { /* minus */
		sign = -1;
		if (p==q)
			return(0);
		p = p->p1;
	}
loop:
	m = p->ch - '0';
	if (m>9 | m<0)
		writes("bad integer string");
	n = n * 10 + m;
	if (p==q)
		return(n*sign);
	p = p->p1;
	goto loop;
}

binstr(binary) {
	int n, sign;
	register struct node *m, *p, *q;

	n = binary;
	p = alloc();
	q = alloc();
	sign = 1;
	if (binary<0) {
		sign = -1;
		n = -binary;
	}
	p->p2 = q;
loop:
	q->ch = n%10+'0';
	n = n / 10;
	if (n==0) {
		if (sign<0) {
			m = alloc();
			m->p1 = q;
			q = m;
			q->ch = '-';
		}
		p->p1 = q;
		return(p);
	}
	m = alloc();
	m->p1 = q;
	q = m;
	goto loop;
}

add(string1, string2) {
	return(binstr(strbin(string1) + strbin(string2)));
}

sub(string1, string2) {
	return(binstr(strbin(string1) - strbin(string2)));
}

mult(string1, string2) {
	return(binstr(strbin(string1) * strbin(string2)));
}

div(string1, string2) {
	return(binstr(strbin(string1) / strbin(string2)));
}

cat(string1, string2) 
struct node *string1, *string2;
{
	register struct node *a, *b;

	if (string1==0)
		return(copy(string2));
	if (string2==0)
		return(copy(string1));
	a = copy(string1);
	b = copy(string2);
	a->p2->p1 = b->p1;
	a->p2 = b->p2;
	free(b);
	return(a);
}

dcat(a,b)
struct node *a, *b;
{
	register struct node *c;

	c = cat(a,b);
	delete(a);
	delete(b);
	return(c);
}

delete(string)
struct node *string;
{
	register struct node *a, *b, *c;

	if (string==0)
		return;
	a = string;
	b = string->p2;
	while(a != b) {
		c = a->p1;
		free(a);
		a = c;
	}
	free(a);
}

sysput(string) {
	syspot(string);
	delete(string);
}

dump()
{
	dump1(namelist);
}

dump1(base)
struct node *base;
{
	register struct node *b, *c, *e;
	struct node *d;

	while (base) {
		b = base->p1;
		c = binstr(b->typ);
		d = strstr("  ");
		e = dcat(c, d);
		sysput(cat(e, b->p1));
		delete(e);
		if (b->typ==1) {
			c = strstr("   ");
			sysput(cat(c, b->p2));
			delete(c);
		}
		base = base->p2;
	}
}

writes(s) {

	sysput(dcat(binstr(lc),dcat(strstr("\t"),strstr(s))));
	flush();
	if (cfail) {
		dump();
		flush();
		exit();
	}
	while(getc());
	while (compile());
	flush();
	exit();
}

getc() {
	register struct node *a;
	static struct node *line;
	static linflg;

	while (line==0) {
		line = syspit();
		if(rfail) {
			cfail++;
			writes("eof on input");
		}
		lc++;
	}
	if (linflg) {
		line = 0;
		linflg = 0;
		return(0);
	}
	a = line->p1;
	if (a==line->p2) {
		free(line);
		linflg++;
	} else
		line->p1 = a->p1;
	return(a);
}
