/*	This file contains stuff about user procedure calls and
* variable assignment and lookup.
*
*	The Children's Museum, Boston, Mass.
*	Written by Douglas B. Klunder
*/

#include "logo.h"
extern struct plist *pcell;
extern int *stkbase;
extern int stkbi;
extern int *newstk;
extern int newsti;
extern int argno;
extern int yylval;
extern int yychar;
extern short yyerrflag;
static struct alist *globvars;
extern struct stkframe *fbr;
extern struct plist *proclist;
extern struct alist *locptr;
extern struct alist *newloc;

struct alist *loclk1();
struct alist *look1();
struct object *look();

go(linenum)	/* LOGO go */
register struct object *linenum;
{
	register struct lincell *lptr;
	register numline;

	if (pcell==NULL) {	/* not in procedure */
		printf("Go can only be used within a procedure.\n");
		errhand();
	}
	linenum = numconv(linenum,"Go");
	if (!intp(linenum)) ungood("Go",linenum);
	numline = linenum->obint;
	mfree(linenum);
/*	Search for saved line no. */
	for (lptr=pcell->plines;lptr;lptr=lptr->nextline) {
		if (lptr->linenum==numline)
		{	/* line found, so adjust pseudo-code
			* pointers to continue execution at
			* right place
			*/
			stkbase=lptr->base;
			stkbi=lptr->index;
			return;
		}
	}
	/* no match */
	printf("There is no line %d.\n",numline);
	errhand();
}

struct object *lnamep(name)	/* namep */
register struct object *name;
{	/* check for both local and global definitions */
	register char *nstr;

	if (!stringp(name)) ungood("Namep",name);
	nstr = name->obstr;
	if (loclk1(nstr) || look1(nstr)) {
		mfree(name);
		return(true());
	}
	mfree(name);
	return(false());
}

loccreate(varname,lptr)		/* create new local variable cell, with name
				* but without value */
register struct object *varname;
register struct alist **lptr;
{
	register struct alist *temp1,*temp2;
	char ch;

	if (pcell==NULL) {	/* not in procedure */
		printf("Local can only be used within a procedure.\n");
		errhand();
	}
	if (!stringp(varname)) ungood("Local",varname);
	if ((ch = varname->obstr[0]) == '\0') {
		printf("Variable name can't be empty.\n");
		errhand();
	}
	if (ch<'a' || ch>'z') {
		printf("Variable name %s must start with a letter.\n",
				varname->obstr);
		errhand();
	}
	if (*lptr==NULL) {	/* first cell */
		*lptr=(temp1=(struct alist *)ckzmalloc(sizeof(*temp1)));
	} else {
		for (temp1= *lptr;temp1;temp1=temp1->next) {
			if (!strcmp(temp1->name->obstr,varname->obstr))
			{	/* name already present */
				nputs(varname->obstr);
				printf(" is already defined as a local variable.\n");
				errhand();
			}
			temp2=temp1;
		}
		/* create new cell at end of string */
		temp2->next=(struct alist *)ckzmalloc(sizeof(*temp2));
		temp1=temp2->next;
	}
	temp1->next=NULL;
	temp1->name=varname;
	temp1->val=(struct object *)-1;
}

struct object *cmlocal(arg)
struct object *arg;
{
	loccreate(globcopy(arg),&locptr);
	mfree(arg);
	return ((struct object *)(-1));
}

struct alist *loclk2(str,lap)	/* look for local definition of variable
				* return cell pointer if found */
		/* BH 5/19/81 was loclk1 but now subprocedure */
register char *str;
register struct alist *lap;
{
	while (lap) {
		if (!strcmp(str,lap->name->obstr)) return(lap);
		lap=lap->next;
	}
	return(NULL);
}

struct alist *loclk1(str)	/* look for local definition of variable
				 * WITH DYNAMIC SCOPE!! BH 5/19/81 */
register char *str;
{
	register struct stkframe *skp;
	register struct alist *lap;

	if (lap = loclk2(str,locptr)) return(lap);
		/* found in innermost active procedure */
	for (skp = fbr; skp; skp = skp->prevframe) {
		/* else try other active procedures */
		if (skp->loclist)
			if ((lap = loclk2(str,skp->loclist)) != NULL)
				return(lap);
	}
	return(NULL);
}

struct object *alllk(str)	/* return value of variable */
register struct object *str;
{	/* look both locally and globally */
	register struct alist *ap;
	register char *strnm;

	if (!stringp(str)) ungood("Thing",str);
	strnm = str->obstr;
	if ((ap=loclk1(strnm))==NULL) return(look(str));
	if (ap->val==(struct object *)-1) {
		nputs(strnm);
		puts(" has no value.");
		errhand();
	}
	mfree(str);
	return(localize(ap->val));
}

newfr()		/* create new stack frame to accommodate procedure */
{
	register int *temp;

	temp=(int *)ckmalloc(PSTKSIZ*sizeof(int));
	*temp=(int)newstk;
	*(newstk+PSTKSIZ-1)=(int)temp;
	newstk=temp;
	newsti=1;
}

struct plist *proclook(name)	/* check if procedure already in memory */
register char *name;
{
	register struct plist *here;

	for (here=proclist;here;here=here->after)
		if (!strcmp(name,here->procname->obstr)) return(here);
	return(NULL);
}

argassign(argval)	/* assign value to next unfilled input */
register struct object *argval;
{
	register struct alist *temp1;

	for (temp1=newloc;temp1->val!=(struct object *)-1;temp1=temp1->next) {
		if (!stringp(temp1->name)) {
			printf("Argassign bug trap, newloc messed up.\n");
			return;
		}
	}
	temp1->val=globcopy(argval);
	mfree(argval);
	if (--argno==0) {	/* all inputs filled, so save unparsed token */
		fbr->oldyyl=yylval;
		fbr->oldyyc=yychar;
		if (yyerrflag) return;
		yychar= -1;
	}
}

assign(name,val)	/* make */
register struct object *name,*val;
{
	register struct alist *ap;
	register char *namestr;
	char *tmp,ch;

	if (!stringp(name)) ungood("Make",name);
	namestr = name->obstr;
	for(tmp=namestr;*tmp;tmp++){
		if((*tmp<'a' || *tmp>'z') && (*tmp <'0' || *tmp>'9')) {
			pf1("Cannot assign value to %l\n",name);
			errhand();
		}
	}
	if ((ap=loclk1(namestr))) {	/* local definition */
		if (ap->val != (struct object *)-1) lfree(ap->val);
		mfree(name);
		ap->val=globcopy(val);
		mfree(val);
		return;
	}
	else if ((ap=look1(namestr))==0)
	{	/* new variable, so allocate cell */
		if ((ch = namestr[0]) == '\0') {
			printf("Variable name can't be empty.\n");
			errhand();
		}
		if (ch<'a' || ch>'z') {
			printf("Variable name %s must start with a letter.\n",
					namestr);
			errhand();
		}
		ap=(struct alist *)ckmalloc(sizeof(*ap));
		ap->name = globcopy(name);
		ap->next=globvars;
		globvars=ap;
		mfree(name);
	} else {	/* old global definition */
		lfree(ap->val);
		mfree(name);
	}
	ap->val=globcopy(val);
	mfree(val);
}

struct object *look(str)	/* return value of globally defined variable */
register struct object *str;
{
	register struct alist *ap;
	register char *strtxt;

	if (!stringp(str)) ungood("Thing",str);
	strtxt = str->obstr;
	ap=look1(strtxt);
	if (ap==NULL) {
		nputs(strtxt);
		printf(" has no value.\n");
		errhand();
	}
	mfree(str);
	return(localize(ap->val));
}

struct alist *look1(str)	/* return pointer to right variable cell */
register char *str;
{
	register struct alist *ap;

	for(ap=globvars; ap != 0; ap=ap->next)
		if (!strcmp(str,ap->name->obstr)) return(ap);
	return(0);
}
