/*	@(#)sno1.c	1.2	*/
#include "sno.h"
#define INCR 200

/*
 *   Snobol III
 */


int 	incomp;
int     freesize;
struct  node *lookf;
struct  node *looks;
struct  node *lookend;
struct  node *lookstart;
struct  node *lookdef;
struct  node *lookret;
struct  node *lookfret;
int     cfail;
int     rfail;
struct  node *freelist, *freespace;
struct  node *namelist;
int     lc;
struct  node *schar;
FILE	*fin;
int	xargc;
char	**xargv;

char *malloc();

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

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

main (argc, argv)
	char *argv[];
{
	register struct node *a, *b, *c;
	static char stdbuf[BUFSIZ];

	setbuf (stdout, stdbuf);
	ncinit (argc, argv);
	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);
	incomp = 1;
	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;
	incomp = 0;
	while (c=execute (c));
}

struct node *
syspit()
{
	register struct node *b, *c, *d;
	int a;
	char nextchar();

	a = nextchar();
	if (a == '\n')
		return (0);
	if((a == '*') && incomp){
		while(nextchar() != '\n') ;
		return 0;
	}
	b = c = salloc();
	while (a != '\n') {
		c->p1 = d = salloc();
		c = d;
		c->ch = a;
		if (a == '\0') {
			rfail = 1;
			break;
		}
		a = nextchar();
	}
	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');
}

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

	d = f = salloc();
	while ((c = *s++)!='\0') {
		(e=salloc())->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);
}

struct node *
salloc()
{
	register struct node *f;
	register char *i;

	if (freelist==0) {
		if (--freesize < 0) {
			if ((i=malloc (INCR * sizeof (struct node))) == NULL) {
				puts ("Out of free space");
				exit (1);
			}
			freesize = INCR - 1;
			freespace = (struct node *) i;
		}
		return (freespace++);
	}
	f = freelist;
	freelist = freelist->p1;
	return (f);
}

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

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

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

struct node *
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 = salloc();
	i->p2 = 0;
	if (k)
		k->p2 = i;
	else
		namelist = i;
	j = salloc();
	i->p1 = j;
	j->p1 = copy (string);
	j->p2 = 0;
	j->typ = 0;
	return (j);
}

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

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

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

int
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;
}

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

	n = binary;
	p = salloc();
	q = salloc();
	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 = salloc();
			m->p1 = q;
			q = m;
			q->ch = '-';
		}
		p->p1 = q;
		return (p);
	}
	m = salloc();
	m->p1 = q;
	q = m;
	goto loop;
}

struct node *
add (string1, string2)
	register struct node *string1, *string2;
{
	return (binstr (strbin (string1) + strbin (string2)));
}

struct node *
sub (string1, string2)
	register struct node *string1, *string2;
{
	return (binstr (strbin (string1) - strbin (string2)));
}

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

struct node *
div (string1, string2)
	register struct node *string1, *string2;
{
	return (binstr (strbin (string1) / strbin (string2)));
}

struct node *
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;
	sfree (b);
	return (a);
}

struct node *
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;
		sfree (a);
		a = c;
	}
	sfree (a);
}

sysput (string)
	struct node *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 = strst1 ("  ");
		e = dcat (c, d);
		sysput (cat (e, b->p1));
		delete (e);
		if (b->typ==1) {
			c = strst1 ("   ");
			sysput (cat (c, b->p2));
			delete (c);
		}
		base = base->p2;
	}
}

writes (s)
	char *s;
{
	sysput (dcat (binstr (lc),dcat (strst1 ("\t"),strst1 (s))));
	fflush (stdout);
	if (cfail) {
		dump();
		fflush (stdout);
		exit (1);
	}
	while (sgetc());
	while (compile());
	fflush (stdout);
	exit (1);
}

struct node *
sgetc()
{
	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) {
		sfree (line);
		linflg++;
	} else
		line->p1 = a->p1;
	return (a);
}

ncinit (argc, argv)
	int argc;
	char *argv[];
{
	xargc = argc - 1;
	xargv = argv + 1;
	ncswitch();
}

ncswitch()
{
	if (fin && fin != stdin)
		fclose (fin);
	if (xargc > 0) {
		fin = fopen (*xargv, "r");
		if (fin == NULL) {
			fputs ("Cannot open ", stdout);
			fputs (*xargv, stdout);
			putchar ('\n');
			exit (1);
		}
		xargv++;
		xargc--;
	} else
		fin = stdin;
}

char
nextchar()
{
	register int a;

	a = getc (fin);
	if (a == EOF) {
		while (a == EOF && fin != stdin) {
			ncswitch();
			a = getc (fin);
		}
		if (a == EOF)
			a = 0;
	}
	return a;
}
