/* xlread - xlisp expression input routine */

#ifdef AZTEC
#include "a:stdio.h"
#else
#include "stdio.h"
#include "ctype.h"  /* dont need for CI-C86 compiler */
#endif

#include "xlisp.h"

/* global variables */
struct node *oblist;

/* external variables */
extern struct node *xlstack;
extern int (*xlgetc)();
extern int xlplevel;

/* local variables */
static int savech;

/* forward declarations (the extern hack is for decusc) */
extern struct node *parse();
extern struct node *plist();
extern struct node *pstring();
extern struct node *pnumber();
extern struct node *pquote();
extern struct node *pname();

/* xlread - read an xlisp expression */
struct node *xlread()
{
    /* initialize */
    savech = -1;
    xlplevel = 0;

    /* parse an expression */
    return (parse());
}

/* parse - parse an xlisp expression */
static struct node *parse()
{
    int ch;

    /* keep looking for a node skipping comments */
    while (TRUE)

	/* check next character for type of node */
	switch (ch = nextch()) {
	case '\'':			/* a quoted expression */
		return (pquote());
	case '(':			/* a sublist */
		return (plist());
	case ')':			/* closing paren - shouldn't happen */
		xlfail("extra right paren");
	case '.':			/* dot - shouldn't happen */
		xlfail("misplaced dot");
	case ';':			/* a comment */
		pcomment();
		break;
	case '"':			/* a string */
		return (pstring());
	default:
		if (isdigit(ch))	/* a number */
		    return (pnumber(1));
		else if (issym(ch))	/* a name */
		    return (pname());
		else
		    xlfail("invalid character");
	}
}

/* pcomment - parse a comment */
static pcomment()
{
    /* skip to end of line */
    while (getch() != '\n')
	;
}

/* plist - parse a list */
static struct node *plist()
{
    struct node *oldstk,val,*lastnptr,*nptr;
    int ch;

    /* increment the nesting level */
    xlplevel += 1;

    /* create a new stack frame */
    oldstk = xlsave(&val,NULL);

    /* skip the opening paren */
    savech = -1;

    /* keep appending nodes until a closing paren is found */
    for (lastnptr = NULL; (ch = nextch()) > 0 && ch != ')'; lastnptr = nptr) {

	/* check for a dotted pair */
	if (ch == '.') {

	    /* skip the dot */
	    savech = -1;

	    /* make sure there's a node */
	    if (lastnptr == NULL)
		xlfail("invalid dotted pair");

	    /* parse the expression after the dot */
	    lastnptr->n_listnext = parse();

	    /* make sure its followed by a close paren */
	    if (nextch() != ')')
		xlfail("invalid dotted pair");

	    /* done with this list */
	    break;
	}

	/* allocate a new node and link it into the list */
	nptr = newnode(LIST);
	if (lastnptr == NULL)
	    val.n_ptr = nptr;
	else
	    lastnptr->n_listnext = nptr;

	/* initialize the new node */
	nptr->n_listvalue = parse();
    }

    /* skip the closing paren */
    savech = -1;

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* decrement the nesting level */
    xlplevel -= 1;

    /* return successfully */
    return (val.n_ptr);
}

/* pstring - parse a string */
static struct node *pstring()
{
    struct node *oldstk,val;
    char sbuf[STRMAX+1];
    int ch,i,d1,d2,d3;

    /* create a new stack frame */
    oldstk = xlsave(&val,NULL);

    /* skip the opening quote */
    savech = -1;

    /* loop looking for a closing quote */
    for (i = 0; i < STRMAX && (ch = getch()) != '"'; i++) {
	switch (ch) {
	case '\\':
		switch (ch = getch()) {
		case 'e':
			ch = '\033';
			break;
		case 'n':
			ch = '\n';
			break;
		case 'r':
			ch = '\r';
			break;
		case 't':
			ch = '\t';
			break;
		default:
			if (ch >= '0' && ch <= '7') {
			    d1 = ch - '0';
			    d2 = getch() - '0';
			    d3 = getch() - '0';
			    ch = (d1 << 6) + (d2 << 3) + d3;
			}
			break;
		}
	}
	sbuf[i] = ch;
    }
    sbuf[i] = 0;

    /* initialize the node */
    val.n_ptr = newnode(STR);
    val.n_ptr->n_str = strsave(sbuf);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the new string */
    return (val.n_ptr);
}

/* pnumber - parse a number */
static struct node *pnumber(sign)
  int sign;
{
    struct node *val;
    int ch,ival;

    /* loop looking for digits */
    for (ival = 0; isdigit(ch = thisch()); savech = -1)
	ival = ival * 10 + ch - '0';

    /* make sure the number terminated correctly */
    if (issym(ch))
	xlfail("badly formed number");

    /* initialize the node */
    val = newnode(INT);
    val->n_int = sign * ival;

    /* return the new number */
    return (val);
}

/* xlenter - enter a symbol into the symbol table */
struct node *xlenter(sname)
  char *sname;
{
    struct node *sptr;

    /* check for nil */
    if (strcmp(sname,"nil") == 0)
	return (NULL);

    /* check for the oblist being undefined */
    if (oblist == NULL) {
	oblist = newnode(SYM);
	oblist->n_symname = strsave("oblist");
	oblist->n_symvalue = newnode(LIST);
	oblist->n_symvalue->n_listvalue = oblist;
    }

    /* check for symbol already in table */
    for (sptr = oblist->n_symvalue; sptr != NULL; sptr = sptr->n_listnext)
	if (sptr->n_listvalue == NULL)
	    printf("bad oblist\n");
	else if (sptr->n_listvalue->n_symname == NULL)
	    printf("bad oblist symbol\n");
	else
	if (strcmp(sptr->n_listvalue->n_symname,sname) == 0)
	    return (sptr->n_listvalue);

    /* enter a new symbol and link it into the symbol list */
    sptr = newnode(LIST);
    sptr->n_listnext = oblist->n_symvalue;
    oblist->n_symvalue = sptr;
    sptr->n_listvalue = newnode(SYM);
    sptr->n_listvalue->n_symname = strsave(sname);

    /* return the new symbol */
    return (sptr->n_listvalue);
}

/* pquote - parse a quoted expression */
static struct node *pquote()
{
    struct node *oldstk,val;

    /* create a new stack frame */
    oldstk = xlsave(&val,NULL);

    /* skip the quote character */
    savech = -1;

    /* allocate two nodes */
    val.n_ptr = newnode(LIST);
    val.n_ptr->n_listvalue = xlenter("quote");
    val.n_ptr->n_listnext = newnode(LIST);

    /* initialize the second to point to the quoted expression */
    val.n_ptr->n_listnext->n_listvalue = parse();

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the quoted expression */
    return (val.n_ptr);
}

/* pname - parse a symbol name */
static struct node *pname()
{
    char sname[STRMAX+1];
    int ch,i;

    /* get the first character */
    ch = sname[0] = getch();

    /* check for signed number */
    if (ch == '+' || ch == '-') {
	if (isdigit(thisch()))
	    return (pnumber(ch == '+' ? 1 : -1));
    }

    /* get symbol name */
    for (i = 1; i < STRMAX && issym(thisch()); i++)
	sname[i] = getch();
    sname[i] = 0;

    /* initialize value */
    return (xlenter(sname));
}

/* nextch - look at the next non-blank character */
static int nextch()
{
    /* look for a non-blank character */
    while (isspace(thisch()))
	savech = -1;

    /* return the character */
    return (thisch());
}

/* thisch - look at the current character */
static int thisch()
{
    /* return and save the current character */
    return (savech = getch());
}

/* getch - get the next character */
static int getch()
{
    int ch;

    /* check for a saved character */
    if ((ch = savech) >= 0)
	savech = -1;
    else
	ch = (*xlgetc)();

    /* check for the abort character */
    if (ch == EOF)
	if (xlplevel > 0) {
	    putchar('\n');
	    xltin(FALSE);
	    xlfail("input aborted");
	}
	else
	    exit();

    /* return the character */
    return (ch);
}

/* issym - check whether a character if valid in a symbol name */
static int issym(ch)
  int ch;
{
    if (isspace(ch) ||
    	ch <  ' ' ||
    	ch == '(' ||
    	ch == ')' ||
    	ch == ';' || 
    	ch == '.' ||
    	ch == '"' ||
    	ch == '\'')
	return (FALSE);
    else
	return (TRUE);
}

