/*
 * Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *

 *  		 Crown Copyright (c) 1997
 *
 *  This TenDRA(r) Computer Program is subject to Copyright
 *  owned by the United Kingdom Secretary of State for Defence
 *  acting through the Defence Evaluation and Research Agency
 *  (DERA).  It is made available to Recipients with a
 *  royalty-free licence for its use, reproduction, transfer
 *  to other parties and amendment for any purpose not excluding
 *  product development provided that any such use et cetera
 *  shall be deemed to be acceptance of the following conditions:-
 *
 *      (1) Its Recipients shall ensure that this Notice is
 *      reproduced upon any copies or amended versions of it;
 *
 *      (2) Any amended version of it shall be clearly marked to
 *      show both the nature of and the organisation responsible
 *      for the relevant amendment or amendments;
 *
 *      (3) Its onward transfer from a recipient to another
 *      party shall be deemed to be that party's acceptance of
 *      these conditions;
 *
 *      (4) DERA gives no warranty or assurance as to its
 *      quality or suitability for any purpose and DERA accepts
 *      no liability whatsoever in relation to any use to which
 *      it may be put.
 *
 * $TenDRA: tendra/src/tools/pl/lex.c,v 1.13 2005/09/18 21:11:25 stefanf Exp $
 */


#include "config.h"
#include "cstring.h"
#include "defs.h"
#include "find_id.h"
#include "fmm.h"
#include "includes.h"
#include "msgcat.h"
#include "syntax.h"
#include "reserved.h"
#include "consfile.h"

typedef struct FStack_struct{ FILE * file; char * fname; unsigned long line;
	struct FStack_struct * next; } FStack;

static FStack * fstack = (FStack*)0;
LEX lex_v;
FILE * in_file;


char buff[BUFFLEN];
int bind = -1;

static char lexeme[BUFFLEN+1];
unsigned long cLINE = 0;
static char numstr[BUFFLEN+1];
int lnum;
long radix;


static void
push_current(void)
{
    FStack * x = xalloc(sizeof(*x));
    x->file = in_file; x->fname = file_name;
    x->line = cLINE; x->next = fstack;
    fstack = x;
}


static void
pop_current(void)
{
    in_file = fstack->file; file_name = fstack->fname;
    cLINE = fstack->line;
    fstack = fstack->next;
}


static int
directive(void)
{
    if (buff[0] == '#') {
		char c = buff[8];
		buff[8] = 0;
		if (strcmp(buff+1,"include")==0) {
			push_current();
			in_file = open_include(buff+9);
			bind = -1;
			cLINE = 0;
			return 1;
		}
		buff[8] = c;
    }
    return 0;
}


static LEX
symbol(int s)
{
    LEX l;
    l.t = s;
    return l;
}


static int
mygetc(void)
{
    while (bind<0 || buff[bind]==0) {
     	cLINE++;
		if (fgets(buff, BUFFLEN, in_file) == 0) {
			if (fstack == (FStack *)0) return -1;
			pop_current();
		} else {
			if (directive()!=1) bind = 0;
		}
	}
	return (buff[bind++]);
}


static int
string_char(int q)
{
    int c = mygetc();
    switch (c) {
	case '\\': {
	    c = mygetc();
	    if (c=='t') return '\t';
	    if (c=='n') return '\n';
	    if (c=='r') return '\r';
	    return c;
	}
	case '"': {
	    if (q) return -1;
	    break;
	}
    }
    return c;
}


static LEX
charval(void)
{
    LEX l;
    l.t = lex_character;
    l.val.v = string_char(0);
    if (mygetc()!= '\'') MSG_unmatched_quote();
    return l;
}


static LEX
stringval(void)
{
    LEX l;
    l.t = lex_qstring;
    l.val.name = "";
    for (;;) {
		int i;
		for (i=0; i<BUFFLEN; i++) {
			int c = string_char(1);
			if (c<0) {
				lexeme[i] = 0;
				/* TODO: free old string */
				l.val.name = string_concat(l.val.name, lexeme);
				return l;
			}
			lexeme[i]= (char)c;
		}
		lexeme[i] = 0;
		l.val.name = string_concat(l.val.name, lexeme);
    }
}


static int
terminator(int x, int number)
{
    switch (x) {
  	case '(': case ')': case '[': case ']': case '{':
  	case '}': case ':': case ';': case ',': case ' ':
	case 0: case '\t': case '\n': case '\r': case '\'': case '"':
	case -1: {
	    return 1;
	}
  	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9': {
	    return number;
	}
    }
    return 0;
}


static LEX
term_val(int i)
{
    LEX l;
    l.t = terminals[i].t;
    l.val.name = terminals[i].name;
    return l;
}


static LEX
cons_val(int i)
{
    LEX l;
    l.t = constructs[i].t;
    l.val.v = i;
    return l;
}


static LEX
id_val(char * id)
{
    LEX l;
    l.t = lex_ident;
    l.val.name = string_copy(id);
    return l;
}


static LEX
tok_val(Tokdec * td)
{
    LEX l;
    int st = td->sort.ressort.sort;
    l.val.tokname = td;
  again:
    switch (st) {
	case access_sort: l.t = access_tok; break;
	case alignment_sort: l.t = alignment_tok; break;
	case al_tag_sort: l.t = al_tag_tok; break;
	case bitfield_variety_sort: l.t = bitfield_variety_tok; break;
	case bool_sort: l.t = bool_tok; break;
	case error_treatment_sort: l.t = error_treatment_tok; break;
	case exp_sort: l.t =  exp_tok; break;
	case floating_variety_sort: l.t = floating_variety_tok; break;
	case label_sort: l.t = label_tok; break;
	case nat_sort: l.t = nat_tok; break;
	case ntest_sort: l.t = ntest_tok; break;
	case rounding_mode_sort: l.t =rounding_mode_tok; break;
	case shape_sort: l.t=shape_tok; break;
	case signed_nat_sort: l.t = signed_nat_tok; break;
	case string_sort: l.t = string_tok; break;
	case tag_sort: l.t = tag_tok; break;
	case transfer_mode_sort: l.t = transfer_mode_tok; break;
	case variety_sort: l.t=variety_tok; break;
	case token_sort: {
	    st = td->sort.ressort.toksort->ressort.sort;
	    goto again;
	}
	default: MSG_not_legal_sort(); break;
    }
    return l;
}


static LEX
decode(char * id)
{
    int lower = 0;
    int upper = no_of_terminals-1;
    int mid;
    int cl,cu,cm;
    Tokdec * td;

    cl = strcmp(terminals[lower].name, id);
    if (cl == 0) return term_val(lower);
    if (cl > 0) goto notterm;
    cu = strcmp(terminals[upper].name, id);
    if (cu == 0) return term_val(upper);
    if (cu < 0) goto notterm;
    while ((mid = (upper+lower)/2), upper-lower > 1) {
		cm = strcmp(terminals[mid].name, id);
		if (cm == 0) return term_val(mid);
		if ((cm ^ cl) >= 0) {
			lower = mid;
		} else if ((cm ^ cu) >= 0) {
			upper = mid;
		} else {
			break;
		}
    }

  notterm:
    lower = 0;
    upper = no_of_cons-1;
    cl = strcmp(constructs[lower].name, id);
    if (cl == 0) return cons_val(lower);
    if (cl > 0) goto notcons;
    cu = strcmp(constructs[upper].name, id);
    if (cu == 0) return cons_val(upper);
    if (cu < 0) goto notcons;
    while ((mid = (upper+lower)/2), upper-lower > 1) {
		cm = strcmp(constructs[mid].name, id);
		if (cm == 0) return cons_val(mid);
		if ((cm ^ cl) >= 0) {
			lower = mid;
		} else if ((cm ^ cu) >= 0) {
			upper = mid;
		} else {
			break;
		}
    }

  notcons:
    if (search_for_toks) {
		td = tokdecs;
		while (td != (Tokdec*)0) {
			if (strcmp(id, td->idname.id)==0) {
				return tok_val(td);
			}
			td = td->next;
		}
    }
    return id_val(id);
}


char *
fformat(char * s, int l)
{
    int i;
    for (i=0; i<l; i++) {
		char c = s[i];
		if (c>='a' && c<= 'f') {
			s[i] = (char)(c-'a'+10+'0');
		} else if (c>='A' && c<= 'F') {
			s[i] = (char)(c-'A'+10+'0');
		}
    }
    return s;
}


long
stoi(char * s, int n)
{
    long ans = 0;
    int i;
    for (i=0; i<n; i++) {
		int x = s[i];
		int d = (x>='0' && x<='9')? x-'0':
			(x>='a' && x<='f')? x-'a'+10: x-'A'+10;
		ans = ans*radix+d;
    }
    return ans;
}


static LEX
numval(int c)
{
    int isfloat = 0;
    char x;
    LEX l;
    radix = 10;
    lnum = 0;
    for (;;) {
		numstr[lnum++]= (char)c;
		switch (x=buff[bind]) {
	    case '0': case '1': case '2': case '3': case '4':
	    case '5': case '6': case '7': case '8': case '9': {
			if (x-'0'>=radix) break;
			c = mygetc();
			continue;
	    }
	    case 'a': case 'b': case 'c':
	    case 'd': case 'e': case 'f': {
			if (x-'a'+10>=radix) break;
			c = mygetc();
			continue;
	    }
	    case 'A': case 'B': case 'C':
	    case 'D': case 'E': case 'F': {
			if (x-'A'+10>=radix) break;
			c = mygetc();
			continue;
	    }
	    case 'r': case 'R': {
			if (isfloat) break;
			radix = stoi(&numstr[0], lnum);
			lnum = 0;
			IGNORE mygetc();
			switch (x=buff[bind]) {
		    case '0': case '1': case '2': case '3': case '4':
		    case '5': case '6': case '7': case '8': case '9': {
				if (x-'0' < radix) {
					c = mygetc();
					continue;
				}
				break;
		    }
		    case 'a': case 'b': case 'c':
		    case 'd': case 'e': case 'f': {
				if (x-'a'+10< radix) {
					c = mygetc();
					continue;
				}
				break;
		    }
			}
			break;
	    }
	    case '.': {
			if (isfloat) break;
			isfloat=1;
			c=mygetc();
			continue;
	    }
        }
        break;
	}
	l.t = (isfloat)?lex_float__den:lex_int__den;
	l.val.name = &numstr[0];
	return l;
}


LEX
reader(void)
{
    int c;
    int ll;

  again:
    switch (c = mygetc()) {
	case '(': return symbol(lex_ord);
  	case ')': return symbol(lex_crd);
  	case '[': return symbol(lex_osq);
  	case ']': return symbol(lex_csq);
  	case '{': return symbol(lex_ocurl);
  	case '}': return symbol(lex_ccurl);
  	case ':': return symbol(lex_colon);
  	case ';': return symbol(lex_semi);
  	case ',': return symbol(lex_comma);

  	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9': {
	    return numval(c);
	}

  	case ' ': case 0: case '\t': case '\n': case '\r': {
	    goto again;
	}

  	case '\'': return charval();
  	case '"' : return stringval();
  	case -1: return symbol(lex_eof);

	default: {
	    int number = (c=='-' || c=='.');
	    ll = 0;
	    while (ll < BUFFLEN){
     		lexeme[ll++] = (char)c;
     		if (terminator(buff[bind], number)) break;
     		number = 0;
     		c = mygetc();
	    }
	    lexeme[ll] = 0;
	    if (ll==1) {
			switch (c) {
		    case '=': return symbol(lex_equals);
		    case '|': return symbol(lex_bar);
		    case '.': return symbol(lex_point);
		    case '?': return symbol(lex_query);
		    case '#': return symbol(lex_hash);
			}
	    }
	    if (strcmp(&lexeme[0],"/*")==0) {
     		int n = 1;
     		for (;;) {
				c = mygetc();
				while (c=='*') {
					c = mygetc();
					if (c == '/') {
						if (--n == 0) goto again;
						c = mygetc();
					}
				}
				if (c=='/') {
					c = mygetc();
					if (c=='*') n++;
				}
			}
	    }
	    return decode(lexeme);
	}
    }
}


void
skip_term(int c)
{
    if (lex_v.t != c) {
		char *s = "?";
		switch (c) {
	    case lex_osq : s = "[" ; break;
	    case lex_csq : s = "]" ; break;
	    case lex_ord : s = "(" ; break;
	    case lex_crd : s = ")" ; break;
	    case lex_comma : s = "," ; break;
		}
		MSG_syntax_error(s);
    }
    lex_v = reader();
}
