#ident "@(#)parser.c	1.8 91/04/01 XGRASP"
/*-
 * parser.c - grasp language file parser.
 *
 * Copyright (c) 1991 by Patrick J. Naughton
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * This file is provided AS IS with no warranties of any kind.  The author
 * shall have no liability with respect to the infringement of copyrights,
 * trade secrets or any patents by this file or any part thereof.  In no
 * event will the author be liable for any lost revenue or profits or
 * other special, indirect and consequential damages.
 *
 * Comments and additions should be sent to the author:
 *
 *                     Patrick J. Naughton
 *                     Sun Microsystems
 *                     2550 Garcia Ave, MS 10-20
 *                     Mountain View, CA 94043
 *                     (415) 336-1080
 *
 */

#include "grasp.h"

char       *tokens[] = {
    "notoken",
    "box",
    "break",
    "call",
    "cfade",
    "cfree",
    "cgetbuf",
    "chgcolor",
    "circle",
    "clearscr",
    "cload",
    "closegl",
    "color",
    "cycle",
    "data",
    "databegin",
    "dataend",
    "dataskip",
    "dfree",
    "dload",
    "edge",
    "else",
    "endlfloat",
    "endif",
    "exec",
    "exit",
    "ffree",
    "fgaps",
    "fload",
    "float",
    "fly",
    "font",
    "fstyle",
    "getcolor",
    "getkey",
    "gosub",
    "goto",
    "if",
    "ifkey",
    "ifmem",
    "ifmouse",
    "ifvideo",
    "int",
    "line",
    "link",
    "local",
    "loop",
    "mark",
    "merge",
    "mode",
    "mouse",
    "move",
    "noise",
    "note",
    "offset",
    "opengl",
    "out",
    "palette",
    "pan",
    "pfade",
    "pfree",
    "pgetbuf",
    "pload",
    "pnewbuf",
    "point",
    "poke",
    "pokel",
    "pokew",
    "pop",
    "position",
    "psave",
    "psetbuf",
    "putoff",
    "putup",
    "rect",
    "resetgl",
    "resetscr",
    "return",
    "revpage",
    "send",
    "set",
    "setcolor",
    "setpage",
    "setrgb",
    "setupscr",
    "split",
    "spread",
    "text",
    "tile",
    "timer",
    "tran",
    "video",
    "waitkey",
    "when",
    "window",
    "fade",
    "wait",
};

static char *
sows(ptr)
    char       *ptr;
{
    int         incomment = 0;

    while (incomment || *ptr == ' ' || *ptr == '\t' || *ptr == '\r' ||
	    *ptr == ',' || *ptr == ';') {
	if (*ptr == ';')
	    incomment = 1;
	else if (*ptr == '\n')
	    break;
	ptr++;
    }
    return ptr;
}

static char *
sowsanl(ptr)
    char       *ptr;
{
    int         incomment = 0;

    while (incomment || *ptr == ' ' || *ptr == '\t' || *ptr == '\r' ||
	    *ptr == ',' || *ptr == ';' || *ptr == '\n') {
	if (*ptr == ';')
	    incomment = 1;
	else if (*ptr == '\n')
	    incomment = 0;
	ptr++;
    }
    return ptr;
}

static char *
copytoken(src, dst)
    char       *src;
    char       *dst;
{
    src = sowsanl(src);
    if (*src == '"') {
	src++;
	while (*src != '"' && *src != '\n' && *src != '\r')
	    *dst++ = *src++;
	if (*src != '\n')
	    src++;
    } else
	while (*src != ' ' && *src != '\t' && *src != '\r' &&
		*src != ',' && *src != '\n' && *src != 26) {
	    if (*src >= 'A' && *src <= 'Z')
		*dst++ = *src++ + 'a' - 'A';	/* tolower */
	    else
		*dst++ = *src++;
	}
    *dst = 0;
    src = sows(src);
    return src;
}

static int
lookuptoken(ptr)
    char       *ptr;
{
    int         i;

    if (ptr[strlen(ptr) - 1] != ':')
	for (i = 1; i < NTOKENS; i++)
	    if (!strcmp(ptr, tokens[i]))
		return i;

    return NOTOKEN;
}

static int
tokentoint(s, ret)
    char       *s;
    int        *ret;
{
    int         i;
    for (i = 0; i < strlen(s); i++)
	if (!isdigit(s[i]) && s[i] != '-')
	    return 0;
    sscanf(s, "%d", ret);
    return 1;
}

static void
addlabel(ex, s)
    ExecStruct *ex;
    char       *s;
{
    ex->label[ex->numlabels].string = strdup(s);
    ex->label[ex->numlabels].ipaddr = ex->numcodes;
    if (++(ex->numlabels) >= MAXLABELS)
	error("%s: too many labels!");
}

static void
addargcount(ex, tokenaddr)
    ExecStruct *ex;
    int         tokenaddr;
{
    ex->Code[tokenaddr].val.i = ex->numcodes - tokenaddr - 1;
}

static void
addtoken(ex, t)
    ExecStruct *ex;
    int         t;
{
    ex->Code[ex->numcodes].token = t;
    if (++(ex->numcodes) >= MAXCODES)
	error("%s: text file too large. can only have %d tokens\n", MAXCODES);
}

static void
addstring(ex, string)
    ExecStruct *ex;
    char       *string;
{
    ex->Code[ex->numcodes].val.s = strdup(string);
    addtoken(ex, STRING);
}

static void
addint(token, arg, ex, string, integer)
    int         token;
    int         arg;
    ExecStruct *ex;
    char       *string;
    int         integer;
{
    /*
     * make sure we don't convert a string that happens to parse as a number
     * such as a filename, or keyname, or video mode, into an integer
     */
    if (((token == CLOAD || token == PLOAD || token == FLOAD)
	 && (arg == 1))
	    || (token == TEXT && arg == 3)
	    || (token == VIDEO || token == IFKEY))
	addstring(ex, string);
    else {
	ex->Code[ex->numcodes].val.i = integer;
	addtoken(ex, INTEGER);
    }
}

void
parsefile(ex, ptr)
    ExecStruct *ex;
    char       *ptr;
{
    char        buffer[100];
    int         tokenaddr;

    ex->numcodes = 0;
    ex->numlabels = 0;
    do {
	int         i;
	int         token;

	ptr = copytoken(ptr, buffer);
	if (buffer[0] == 0)
	    break;
	if (buffer[strlen(buffer) - 1] == ':') {
	    buffer[strlen(buffer) - 1] = 0;
	    addlabel(ex, buffer);
	    while (*ptr != '\n' && *ptr != 26)
		ptr++;
	} else {
	    int         range;
	    int         start;

	    token = lookuptoken(buffer);
	    if (token == NOTOKEN) {
		if (tokentoint(buffer, &i))
		    addint(token, 0, ex, buffer, i);
		else
		    addstring(ex, buffer);
		tokenaddr = -1;
	    } else {
		tokenaddr = ex->numcodes;
		addtoken(ex, token);
	    }

	    range = 0;
	    while (*ptr != '\n' && *ptr != 26) {
		int         arg = ex->numcodes - tokenaddr;
		ptr = copytoken(ptr, buffer);
		if (range) {
		    if (tokentoint(buffer, &i)) {
			int         val;
			if (i >= start)
			    for (val = start; val <= i; val++)
				addint(token, arg, ex, buffer, val);
			else
			    for (val = start; val >= i; val--)
				addint(token, arg, ex, buffer, val);
			range = 0;
		    } else
			error("%s: parse error on int range\n");

		} else {
		    if (buffer[0] == '-' && buffer[1] == 0) {
			range = 1;
		    } else {
			if (tokentoint(buffer, &i)) {
			    addint(token, arg, ex, buffer, i);
			    start = i;
			} else
			    addstring(ex, buffer);
		    }
		}
	    }
	    if (tokenaddr != -1)
		addargcount(ex, tokenaddr);
	}

	if (*ptr == 26)
	    break;
	ptr++;
    } while (*ptr != 26);
}
