/* bobscn.c - a lexical scanner */
/*
        Copyright (c) 1991, by David Michael Betz
        All rights reserved.

        Alterations and additions, 1994, by G.C.Wraith
*/

#include <setjmp.h>
#include <string.h>
#include "bob.h"
#include "bobcom.h"

/* useful definitions */
#define LSIZE   256

/* RISC OS */
extern int errorline;
extern char errormesg[100];
extern int Taskwin_query(void);
extern void throwback(void);
extern void taskw_throw(void);

/* keyword table */
static struct { char *kt_keyword; int kt_token; } ktab[] = {
{ "class",      T_CLASS         },
{ "static",     T_STATIC        },
{ "if",         T_IF            },
{ "else",       T_ELSE          },
{ "while",      T_WHILE         },
{ "return",     T_RETURN        },
{ "for",        T_FOR           },
{ "break",      T_BREAK         },
{ "continue",   T_CONTINUE      },
{ "do",         T_DO            },
{ "new",        T_NEW           },
{ "nil",        T_NIL           },
{ "local",      T_LOCAL         },     /* RISC OS */
{ "switch",     T_SWITCH        },     /* RISC OS */
{ "case",       T_CASE          },     /* RISC OS */
{ "default",    T_DEFAULT       },     /* RISC OS */
{ "until",      T_UNTIL         },     /* RISC OS */
{ "repeat",     T_DO            },     /* RISC OS */
{ "in",         T_IN            },     /* RISC OS */
{ "put",        T_PUT           },     /* RISC OS */
{ "vector",     T_VECTOR        },     /* RISC OS */
{ "enum",       T_ENUM          },     /* RISC OS */
{ NULL,         0               }};

/* token name table */
static char *t_names[] = {
"<string>",
"<identifier>",
"<number>",
"class",
"static",
"if",
"else",
"while",
"return",
"for",
"break",
"continue",
"do",
"new",
"nil",
"local",      /* RISC OS */
"switch",     /* RISC OS */
"case",       /* RISC OS */
"default",    /* RISC OS */
"until",      /* RISC OS */
"in",         /* RISC OS */
"put",        /* RISC OS */
"vector",     /* RISC OS */
"enum",       /* RISC OS */
"<=",
"==",
"!=",
">=",
"<<",
">>",
"&&",
"||",
"++",
"--",
"+=",
"-=",
"*=",
"/=",
"%=",
"&=",
"|=",
"^=",
"<<=",
">>=",
"::",
"->",
"<real>"};

/* global variables */
int t_value;            /* numeric value */
float r_value;          /* real value */
char t_token[TKNSIZE+1];/* token string */

/* local variables */
#ifdef __STDC__
static int (*getcf)(void *);    /* getc function */
#else
static int (*getcf)();          /* getc function */
#endif
static void *getcd;     /* getc data */
static int savetkn;     /* look ahead token */
static int savech;      /* look ahead character */
static int lastch;      /* last input character */
static char line[LSIZE];/* last input line */
static char *lptr;      /* line pointer */
static int lnum;        /* line number */

/* prototypes */
#ifdef __STDC__
static int rtoken(void);
static int getstring(void);
static int getcharacter(void);
static int literalch(void);
static int getid(int ch);
static int getnumber(int ch);
static int skipspaces(void);
static int isidchar(int ch);
static int getch(void);
int ishexdigit(int);     /* RISC OS */
static int gethexnumber(int);   /* RISC OS */
#endif

/* init_scanner - initialize the scanner */
int init_scanner(int (*gf)(void *),void *gd)
{
    /* remember the getc function and data */
    getcf = gf; getcd = gd;

    /* setup the line buffer */
    lptr = line; *lptr = '\0';
    lnum = 0;

    /* no lookahead yet */
    savetkn = T_NOTOKEN;
    savech = '\0';

    /* no last character */
    lastch = '\0';
    return (0);
}

/* token - get the next token */
int token(void)
{
    int tkn;

    if ((tkn = savetkn) != T_NOTOKEN)
        savetkn = T_NOTOKEN;
    else
        tkn = rtoken();
    return (tkn);
}

/* stoken - save a token */
int stoken(int tkn)
{
  savetkn = tkn;
  return (0);
}

/* tkn_name - get the name of a token */
char *tkn_name(int tkn)
{
    static char tname[2];
    if (tkn == T_EOF)
        return ("<eof>");
    else if (tkn >= _TMIN && tkn <= _TMAX)
        return (t_names[tkn-_TMIN]);
    tname[0] = tkn;
    tname[1] = '\0';
    return (tname);
}

/* rtoken - read the next token */
static int rtoken(void)
{
    int ch,ch2;

    /* check the next character */
    for (;;)
        switch (ch = skipspaces()) {
        case EOF:       return (T_EOF);
        case '"':       return (getstring());
        case '\'':      return (getcharacter());
        case '<':       switch (ch = getch()) {
                        case '=':
                            return (T_LE);
                        case '<':
                            if ((ch = getch()) == '=')
                                return (T_SHLEQ);
                            savech = ch;
                            return (T_SHL);
                        default:
                            savech = ch;
                            return ('<');
                        }
        case '=':       if ((ch = getch()) == '=')
                            return (T_EQ);
                        savech = ch;
                        return ('=');
        case '!':       if ((ch = getch()) == '=')
                            return (T_NE);
                        savech = ch;
                        return ('!');
        case '>':       switch (ch = getch()) {
                        case '=':
                            return (T_GE);
                        case '>':
                            if ((ch = getch()) == '=')
                                return (T_SHREQ);
                            savech = ch;
                            return (T_SHR);
                        default:
                            savech = ch;
                            return ('>');
                        }
        case '&':       switch (ch = getch()) {
                        case '&':
                            return (T_AND);
                        case '=':
                            return (T_ANDEQ);
                        default:
                            if (ishexdigit(ch))
                               return(gethexnumber(ch));
                            savech = ch;
                            return ('&');
                        }
        case '|':       switch (ch = getch()) {
                        case '|':
                            return (T_OR);
                        case '=':
                            return (T_OREQ);
                        default:
                            savech = ch;
                            return ('|');
                        }
        case '^':       if ((ch = getch()) == '=')
                            return (T_XOREQ);
                        savech = ch;
                        return ('^');
        case '+':       switch (ch = getch()) {
                        case '+':
                            return (T_INC);
                        case '=':
                            return (T_ADDEQ);
                        default:
                            savech = ch;
                            return ('+');
                        }
        case '-':       switch (ch = getch()) {
                        case '-':
                            return (T_DEC);
                        case '=':
                            return (T_SUBEQ);
                        case '>':
                            return (T_MEMREF);
                        default:
                            savech = ch;
                            return ('-');
                        }
        case '*':       if ((ch = getch()) == '=')
                            return (T_MULEQ);
                        savech = ch;
                        return ('*');
        case '/':       switch (ch = getch()) {
                        case '=':
                            return (T_DIVEQ);
                        case '/':
                            while ((ch = getch()) != EOF)
                                if (ch == '\n')
                                    break;
                            break;
                        case '*':
                            ch = ch2 = EOF;
                            for (; (ch2 = getch()) != EOF; ch = ch2)
                                if (ch == '*' && ch2 == '/')
                                    break;
                            break;
                        default:
                            savech = ch;
                            return ('/');
                        }
                        break;
        case ':':       if ((ch = getch()) == ':')
                            return (T_CC);
                        savech = ch;
                        return (':');
        default:        if (isdigit(ch))
                            return (getnumber(ch));
                        else if (isidchar(ch))
                            return (getid(ch));
                        else {
                            t_token[0] = ch;
                            t_token[1] = '\0';
                            return (ch);
                        }
        }
}

/* getstring - get a string */
static int getstring(void)
{
    char *p;
    int ch;

    /* get the string */
    p = t_token;
    while ((ch = literalch()) != EOF && ch != '"')
        *p++ = ch;
    if (ch == EOF)
        savech = EOF;
    *p = '\0';
    return (T_STRING);
}

/* getcharacter - get a character constant */
static int getcharacter(void)
{
    t_value = literalch();
    t_token[0] = t_value;
    t_token[1] = '\0';
    if (getch() != '\'')
        parse_error("Expecting a closing single quote");
    return (T_NUMBER);
}

/* literalch - get a character from a literal string */
static int literalch(void)
{
    int ch;
    if ((ch = getch()) == '\\')
        switch (ch = getch()) {
        case 'n':  ch = '\n'; break;
        case 't':  ch = '\t'; break;
#ifdef RISCOS
case '0':  ch = '\0'; break;
#endif
        case EOF:  ch = '\\'; savech = EOF; break;
        }
    return (ch);
}

/* getid - get an identifier */
static int getid(int ch)
{
    char *p;
    int i;

    /* get the identifier */
    p = t_token; *p++ = ch;
    while ((ch = getch()) != EOF && isidchar(ch))
        *p++ = ch;
    savech = ch;
    *p = '\0';

    /* check to see if it is a keyword */
    for (i = 0; ktab[i].kt_keyword != NULL; ++i)
        if (strcmp(ktab[i].kt_keyword,t_token) == 0)
            return (ktab[i].kt_token);
    return (T_IDENTIFIER);
}

/* getnumber - get a number */
static int getnumber(int ch)
{
    char *p;
    float denom;

    /* get the number */
    p = t_token; *p++ = ch; t_value = ch - '0';
    while ((ch = getch()) != EOF && isdigit(ch)) {
        t_value = t_value * 10 + ch - '0';
        *p++ = ch;
    }
    if (ch != '.')
    {
     savech = ch;
     *p = '\0';
     return (T_NUMBER);
    }
    else
    {
     *p++ = ch;
     r_value = t_value;
     denom = 10.0;
     while ((ch = getch()) != EOF && isdigit(ch)) {
        r_value +=  (ch - '0')/denom;
        denom *= 10.0;
        *p++ = ch;
      }
     savech = ch;
     *p = '\0';
     return (T_REAL);
    }
}

int ishexdigit(int ch)
{ return(isdigit(ch) || ((ch >= 'a') && (ch <= 'f'))); }

/* get a hexadecimal number */
static int gethexnumber(int ch)
{
 char *p;
 p = t_token; *p++ = ch;
 t_value = isdigit(ch)?(ch-'0'):(ch-'W');
 while ((ch = getch()) != EOF && ishexdigit(ch)) {
    t_value = (t_value<<4) + (isdigit(ch)?(ch-'0'):(ch-'W'));
    *p++ = ch;
 }
 savech = ch;
 *p = '\0';
 return (T_NUMBER);
}


/* skipspaces - skip leading spaces */
static int skipspaces(void)
{
    int ch;
    while ((ch = getch()) != '\0' && isspace(ch))
        ;
    return (ch);
}

/* isidchar - is this an identifier character */
static int isidchar(int ch)
{
    return (isupper(ch)
         || islower(ch)
         || isdigit(ch)
         || ch == '_'
         || ch == '@'
         || ch == '$');
}

/* getch - get the next character */
static int getch(void)
{
    int ch;
    
    /* check for a lookahead character */
    if ((ch = savech) != '\0')
        savech = '\0';

    /* check for a buffered character */
    else {
        while ((ch = *lptr++) == '\0') {

            /* check for being at the end of file */
            if (lastch == EOF)
                return (EOF);

            /* read the next line */
            lptr = line;
            while ((lastch = (*getcf)(getcd)) != EOF && lastch != '\n')
                *lptr++ = lastch;
            *lptr++ = '\n'; *lptr = '\0';
            lptr = line;
            ++lnum;
        }
    }

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

/* parse_error - report an error in the current line */
int parse_error(char *msg)
{
    extern jmp_buf error_trap;
    char buf[LSIZE];
    char *src,*dst;
    {
    /*  redisplay the line with the error  */
     sprintf(buf,">>> %s <<<\n>>> in line %d <<<\n%s",msg,lnum,line);
     osputs(buf); 

   /*   point to the position immediately following the error */
     for (src = line, dst = buf; src < lptr-1; ++src)
         *dst++ = (*src == '\t' ? '\t' : ' ');
     *dst++ = '^'; *dst++ = '\n'; *dst = '\0';
     osputs(buf); }

    /* invoke the error trap */
    longjmp(error_trap,1);
    return 0;
}
