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

#include "symbol.h"
#include "code.h"
#include "math.tab.h"
#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 = *++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)) {   /* 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;
}
