#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "symbol.h"
#include "code.h"
#ifdef VMS
#include "math_tab.h"
#else
#include "math.tab.h"
#endif
#include "fudgit.h"
#include "head.h"

#define LQUOTE '"'
#define ANY (-1)
extern int Ft_Samples;
extern int Ft_Debug;

char Ft_Puffer[MAXMACRO+8];

static char *Pp;
static int follow(char expect, int ifyes, int ifno);

extern int Ft_autosymremove (int level);
extern void Ft_matherror (char *s1, char *s2, int lino);
extern int Ft_mathyyparse (void);
int Ft_varcpy (char *to, char *from);
extern int Ft_iolevel (void);
extern int Ft_almost (register char *str1, register char *str2);

void Ft_initmathyylex(char *str)
{
	strcpy(Ft_Puffer, str);
	Pp = Ft_Puffer;
	if (Ft_Debug & DEBUG_MATH)
		fputs(Pp, stderr);
}

int Ft_mathyyerror(char *str)
{
	extern int Ft_mathyystate;
	extern int Ft_mathyychar;

	Ft_autosymremove(0);
#ifdef YYDEBUG
	fprintf(stderr, "Cmode parser: %s. (token `%c`; current state %d)\n",
	str, Ft_mathyychar, Ft_mathyystate);
	Ft_catcher(ERRR);
#else
	Ft_matherror("Cmode parser: %s.", str, 0);
#endif
	return(ERRR);  /* DUMMY */
}

int Ft_mathyylex(void)
{
	int c;
	extern int Ft_iolevel(void);
	extern int Ft_Inproto, Ft_Indef, Ft_Inauto, Ft_Inbrace;

	while (*Pp == ' ' || *Pp == '\t')  /* remove space if any */
		Pp++;
	if (*Pp == 0)
		return(0);
	c = *Pp;
	if (c == '.' || isdigit(c)) {  /* a number  */
		double d;
		if (sscanf(Pp, "%lf", &d) != 1) {
			Ft_matherror("Wierd number near... %s", Pp, 0);
		}
		while (c == '.' || isdigit(c))
			c = *++Pp;
		if (c == 'e' || c == 'E') {
			c = *++Pp;
			if (c == '-' || c == '+')
				c = *++Pp;
			while (isdigit(c))
				c = *++Pp;
		}
		Ft_mathyylval.val = d;
		return(NUMBER);
	}
	else if (c == LQUOTE) { /* a string */
		char strbuf[TOKENSIZE+1];
		char *p;

		c = *++Pp;
		p = strbuf;
		while (c != LQUOTE) {
			if (c == '\n' || c == '\0') {
				fprintf(stderr, "Math error: Unmatched quote %c.\n", LQUOTE);
				Ft_catcher(ERRR);
			}
			if ((p - strbuf) >= TOKENSIZE) {
				*p = '\0';
				Ft_matherror("%s: String too long.", strbuf, 0);
			}
			if (c == '\\') {
#ifdef NOALERTCHAR
				static char transtab[] = "b\bf\fn\nr\rt\tv\v";
#else
				static char transtab[] = "b\bf\fn\nr\rt\ta\av\v";
#endif
				char *ch;
				extern char *strchr(const char *, int);

				c = *++Pp;
				if (islower(c) && (ch = strchr(transtab, c))) {
					*p++ = ch[1];
				}
				else {  /* put next char as is */
					*p++ = c;
				}
				c = *++Pp;
				continue;
			}
			*p++ = c;
			c = *++Pp;
		}
		*p = '\0';
		Pp++; /* swallow last quote */
		Ft_mathyylval.sym = Ft_install("", STRING, strlen(strbuf));
		strcpy(Ft_mathyylval.sym->u.str, strbuf);
		return(STRING);
	}
	else if (isalpha(c) || c == '_') {   /* a function or a variable  */
		char vname[TOKENSIZE+1];
		Symbol *s;
		int type, argno;

		type = Ft_varcpy(vname, Pp);
		Pp += strlen(vname);
		if (Ft_Inproto) {
			if (Ft_autolookup(vname, ANY))
				Ft_matherror("%s: Repeated argument name.", vname, 0);
			switch (type) {
			case VAR:
				Ft_autoinstall(vname, ARG, 0);
				return(VARARG);
			case STRVAR:
				Ft_autoinstall(vname, ARG, 0);
				return(STRVARARG);
			case VEC:
				Ft_autoinstall(vname, ARG, 0);
				if (Ft_almost(vname, "P!ARAM"))
					return(PARARG);
				return(VECARG);
			default:
				Ft_matherror("%s: Impossible case in lexi!", vname, 0);
			}
		}
		if (Ft_Inauto) {
			if (Ft_Indef && Ft_autolookup(vname, 0))
				Ft_matherror("%s: Already defined as a prototype.", vname, 0);
			switch (type) {
			case VAR:
				Ft_autoinstall(vname, AUTO, Ft_Inbrace);
				return(VARARG);
			case STRVAR:
				Ft_autoinstall(vname, AUTO, Ft_Inbrace);
				return(STRVARARG);
			case VEC:
				Ft_autoinstall(vname, AUTO, Ft_Inbrace);
				return(VECARG);
			default:
				Ft_matherror("%s: Impossible case in lexi!", vname, 0);
			}
		}
		if ((argno = Ft_autolookup(vname, ANY))) {
			Ft_mathyylval.narg = argno;
			switch (type) {
			case STRVAR: return(STRVARARG);
			case VAR: return(VARARG);
			case VEC: if (Ft_almost(vname, "P!ARAM"))
						return(PARARG);
					return(VECARG);
			default:
				Ft_matherror("%s: Impossible case in lexi!", vname, 0);
			}
		}
		if ((s = Ft_lookup(vname)) == 0) {
			switch (type) {
			case STRVAR:
				s = Ft_install(vname, UNDEFSTRVAR, 0);
				break;
			case VAR:
				s = Ft_install(vname, UNDEFVAR, 1);
				break;
			case VEC:
				s = Ft_install(vname, UNDEFVEC, Ft_Samples);
				break;
			default:
				Ft_matherror("%s: Impossible case in lexi!", vname, 0);
			}
		}
		Ft_mathyylval.sym = s;
		switch(s->type) {
			case UNDEFVAR: return(VAR);
			case UNDEFVEC: return(VEC);
			case UNDEFSTRVAR: return(STRVAR);
			case BLTINVAR: return(VAR);
			case BLTINSTRVAR: return(STRVAR);
			case BLTINCONST: return(CONST);
			case BLTINSTRCONST: return(STRCONST);
			default: return(s->type);
		}
	}
	Pp++;
	switch (c) {
	case '+':
		if(follow('+', INCR, '+') == '+')
			return(follow('=', ADDASS, '+'));
		else
			return(INCR);
	case '-':
		if(follow('-', DECR, '-') == '-')
			return(follow('=', SUBASS, '-'));
		else
			return(DECR);
	case '/':	return(follow('=', DIVASS, '/'));
	case '*':	return(follow('=', MULASS, '*'));
	case '>':	return(follow('=', GE, GT));
	case '<':	return(follow('=', LE, LT));
	case '=':	return(follow('=', EQ, '='));
	case '!':	return(follow('=', NE, NOT));
	case '|':	return(follow('|', OR, '|'));
	case '&':	return(follow('&', AND, '&'));
	default:	 return(c);
	}
}

static int follow(char expect, int ifyes, int ifno)
{
	if (*Pp == expect) {
	   Pp++;
	   return(ifyes);
	}
	return(ifno);
}

int Ft_more_input(int level, char *iprompt)
{
	int eof;
	char *cp;
	char prompt[128];
	char *cpt;
	extern char *Ft_expandedline(char *prompt, int type, int *eof);
	extern char Ft_Prompt_cm[];
	extern int Ft_Interact;

	cpt = prompt;
	if (Ft_Interact && !Ft_iolevel()) {  /* Do we build a prompt ? */
		int i = level;

		if (level>0) {
			cp = cpt;
			while (i) {
				*cp = '{';
				cp++; i--;
			}
			if (iprompt) {
				int len = strlen(iprompt);

				strcpy(cp, iprompt);
				cp += len;
			}
			strcpy(cp, "...	");
			cp += 7;
			for (i=level;i;i--,cp+=4)
				strcpy(cp, "	");
		}
		else if (iprompt) {
			cpt = iprompt;
		}
		else {
			cpt = Ft_Prompt_cm;
		}
	}
	else {
		*prompt = '\0';
	}
	if ((cp = Ft_expandedline(cpt, EXPANSION | PARENTH, &eof)) == NULL) {
		if (eof) {
			if (eof > 0)	/* not a ^D */
			fputs("Warning: Eof or Eom occurred while still in cmode.\n",
			stderr);
			return(0);
		}
		Ft_initmathyylex("\n");  /* forgive newlines in statements */
		return(ERRR);  /* this means to continue */
	}
	if (Ft_almost(cp, "fm!ode") || Ft_almost(cp, "quit")) {
		return(0);
	}
	else if (Ft_almost(cp, "cm!ode")) {
		fputs("Warning: cmode: Program already in C mode: Line ignored.\n",
		stderr);
		return(ERRR); /* We want to continue */
	}
	Ft_initmathyylex(cp);
	return(1);
}

#define isacceptable(c)  ((c) == '_')

int Ft_varcpy(char *to, char *from)
{
	int up = 0;
	int alf = 0;
	int c = *from;
	int i = 0;
	char *save = to;

	if (!to)
		to = from;
	do {  /* copy variable name  */
		if (!isdigit(c) && !isacceptable(c)) {
			up += (isupper(c) != 0);
			alf++;
		}
		*to++ = *from;
		c = *++from;
		if (++i >= TOKENSIZE) {
			*to = '\0';
			Ft_matherror("%s: Variable name too long!", save, 0);
		}
	} while (c && (isalnum(c) || isacceptable(c)));
	*to = '\0';
	if (up != 0 && up != alf) /* not all lower-upper case */
		return(STRVAR);
	if (up == 0)   /* All lower case  => variable or function */
		return(VAR);
	return(VEC);
}

void Ft_mathuser(void)  /* mostly non-linear */
{
	extern char Ft_UFunction[];

	Ft_initcode();
	Ft_initmathyylex(Ft_UFunction);
	for (;*Pp;Ft_initcode())
		Ft_mathyyparse();

	return;
}
