%{
/* lexer.l: the nawm tokenizer */

/* Copyright (C) 1999 by the Massachusetts Institute of Technology.
 *
 * 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, and that the name of M.I.T. not be used in
 * advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nawm.h"
#include "lang.h"
#include "parser.h"

int nawm_input(char *buf, int bufsize);
void *search_keys(char *name, int *kind);

#undef YY_INPUT
#define YY_INPUT(buf, result, size) { result = nawm_input(buf, size); }

#undef yywrap
#define yywrap() 1

char *process_string(char *str);
varbinding *mkvarbinding(char *, int, void *);

extern int yyparse(void);
extern dtype rettype;

extern Window root;
extern Display *dpy;

int kind, lineno;

typedef struct _lexscope {
  struct _lexscope *parent;
  int numvars;
  varbinding *vars;
  dtype *vtypes;
} lexscope;
static lexscope *scopestack;

%}

%%
include			{ return INCLUDE; }
option			{ return OPTION; }

begin			{ return BEGIN_; }
end			{ return END; }

command			{ return COMMAND; }
function		{ return FUNCTION; }

mode			{ return MODE; }

int			{ yylval.i = T_INT; return DTYPE; }
string			{ yylval.i = T_STR; return DTYPE; }
window			{ yylval.i = T_WIN; return DTYPE; }

key			{ return KEYPRESS; }
keypress		{ return KEYPRESS; }
keyrelease		{ return KEYRELEASE; }
button			{ return BUTTONPRESS; }
buttonpress		{ return BUTTONPRESS; }
buttonrelease		{ return BUTTONRELEASE; }
motion			{ return MOTION; }
enter			{ return ENTER; }
leave			{ return LEAVE; }

if			{ return IF; }
else			{ return ELSE; }
for			{ return FOR; }
in			{ return IN; }
while			{ return WHILE; }
do			{ return DO; }
break			{ return BREAK; }
continue		{ return CONTINUE; }
return			{ return RETURN; }
del			{ return DEL; }

[A-Za-z_][A-Za-z_0-9]*	{ yylval.n = lookup(yytext, &kind, 0);
			  if (yylval.n)
			    return kind;
			  else
			    {
			      yylval.s = xstrdup(yytext);
			      return SYM;
			    }
			}
[0-9]+			{ yylval.i = atoi(yytext); return NUM; }
\"([^"\\]|\\.)*\"	{ yylval.s = process_string(yytext); return STR; }

"+"			{ yylval.i = PLUS; return ADDOP; }
"-"			{ yylval.i = MINUS; return ADDOP; }
"*"			{ yylval.i = TIMES; return MULTOP; }
"/"			{ yylval.i = DIVIDE; return MULTOP; }
"%"			{ yylval.i = REMAINDER; return MULTOP; }
"=="			{ yylval.i = EQUAL; return COMPOP; }
"!="			{ yylval.i = NOTEQUAL; return COMPOP; }
"<="			{ yylval.i = LESSEQUAL; return COMPOP; }
"<"			{ yylval.i = LESS; return COMPOP; }
">="			{ yylval.i = GREATEREQUAL; return COMPOP; }
">"			{ yylval.i = GREATER; return COMPOP; }
"="			{ yylval.i = ASSIGN; return ASSIGNOP; }
"&&"			{ yylval.i = AND; return BOOLOP; }
"||"			{ yylval.i = OR; return BOOLOP; }
"!"			{ yylval.i = NOT; return UNOP; }

"("			{ return '('; }
")"			{ return ')'; }
"["			{ return '['; }
"]"			{ return ']'; }
"{"			{ return '{'; }
"}"			{ return '}'; }
"."			{ return '.'; }
","			{ return ','; }
";"			{ return ';'; }

[ \t]*			{ ; }
#.*$			{ ; }
\n			{ lineno++; }

.			{ die("Bad character `%c' in input at line %d.", *yytext, lineno); }

%%
char *pstr;
extern FILE *yyin;

void initlex(void)
{
  scopestack = NULL;
  pushlexscope(0);
}

void parse_file(char *name)
{
  lineno = 1;
  rettype = -1;
  if (!strcmp(name, "-"))
    yyin = stdin;
  else
    yyin = fopen(name, "r");
  if (!yyin)
    {
      die("Can't open file `%s'\n", name);
      return;
    }
  pstr = NULL;
  yyparse();
  if (strcmp(name, "-"))
    fclose(yyin);
  lineno = 0;
}

void parse_string(char *s)
{
  lineno = 0;
  rettype = -1;
  pstr = s;
  yyparse();
  lineno = 0;
}

int nawm_input(char *buf, int bufsize)
{
  int bytesread;

  if (pstr)
    {
      if (*pstr)
	{
	  buf[bufsize - 1] = '\0';
	  strncpy(buf, pstr, bufsize - 1);
	  bytesread = strlen(buf);
	  pstr += bytesread;
	}
      else
	bytesread = 0;
    }
  else
    bytesread = fread(buf, 1, bufsize, yyin);
  return bytesread;
}

char *process_string(char *str)
{
  char *newstr, *s, *d;
  int len = strlen(str) - 2;

  newstr = xmalloc(len + 1);
  for (s = str + 1, d = newstr; s < str + len + 1; s++, d++)
    {
      if (*s == '\\')
	{
	  switch (*++s)
	    {
	    case '\'':
	    case '"':
	    case '?':
	    case '\\':
	      *d = *s;
	      break;

	    case 'a':
	      *d = '\a';
	      break;
	    case 'b':
	      *d = '\b';
	      break;
	    case 'f':
	      *d = '\f';
	      break;
	    case 'n':
	      *d = '\n';
	      break;
	    case 'r':
	      *d = '\r';
	      break;
	    case 't':
	      *d = '\t';
	      break;
	    case 'v':
	      *d = '\v';
	      break;

	    case 'x':
#define xdigit(x) ( isdigit(x) ? x - '0' : x - 'a' + 10 )
	      if (isxdigit(s[1]) && isxdigit(s[2]))
		*d = xdigit(*++s) * 16 + xdigit(*++s);
	      else
		die("bad hex constant in \"%s\"", str);
	      break;

	    default:
#define isodigit(x) ( isdigit(x) && x != '8' && x != '9' )
	      if (!isodigit(s[0]) || !isodigit(s[1]) || !isodigit(s[2]))
		die("bad escape in \"%s\"", str);
	      else
		*d = xdigit(*++s) * 64 + xdigit(*++s) * 8 + xdigit(*++s);
	      break;
	    }
	  }
      else
	*d = *s;
    }

  *d = '\0';
  return newstr;
}


varbinding *mkvarbinding(char *name, int type, void *data)
{
  varbinding *b = xmalloc(sizeof(varbinding));

  b->name = name;
  b->type = type;
  b->data = data;
  b->next = NULL;
  return b;
}


void pushlexscope(int nestable)
{
  lexscope *tmp;

  tmp = xmalloc(sizeof(lexscope));
  tmp->numvars = nestable ? 0 : -1;
  tmp->vars = NULL;
  tmp->vtypes = malloc(0);
  tmp->parent = scopestack;
  scopestack = tmp;
}

dtype *getscopevartypes(void)
{
  return scopestack->vtypes;
}

int getscopenumvars(void)
{
  return scopestack->numvars;
}

void poplexscope(void)
{
  lexscope *tmp = scopestack;
  varbinding *v, *vtmp;

  scopestack = scopestack->parent;
  free(tmp->vtypes);
  for (v = tmp->vars; v; v = vtmp)
    {
      free(v->name);
      vtmp = v->next;
      free(v);
    }
  free(tmp);
}


variable *mkvar(dtype type)
{
  variable *v = xmalloc(sizeof(variable));

  v->type = type;
  if (scopestack->numvars == -1)
    {
      v->slot = -1;
      v->data = initial_value(type);
    }
  else
    {
      scopestack->vtypes = xrealloc(scopestack->vtypes,
				    (scopestack->numvars + 1) *
				    sizeof(dtype));
      scopestack->vtypes[scopestack->numvars] = type;
      v->slot = scopestack->numvars++;
    }
  return v;
}


void define(int type, char *name, void *data)
{
  varbinding *var;
  int tmp;

  if (!scopestack)
    die("Definition not allowed here.\n");

  if (search_builtins(name, &tmp))
    die("Attempted to redefine builtin `%s'\n", name);

  for (var = scopestack->vars; var; var = var->next)
    {
      if (!strcmp(var->name, name))
	die("Attempted to redefine %s `%s'.",
	    var->type == VAR ? "variable" :
	    var->type == FUN ? "function" : "command", name);
    }

  var = mkvarbinding(xstrdup(name), type, data);
  var->next = scopestack->vars;
  scopestack->vars = var;
}

void *lookup(char *name, int *type, int norecurse)
{
  lexscope *scope;
  varbinding *b;
  void *ans;

  ans = search_builtins(name, type);
  if (ans)
    return ans;

  for (scope = scopestack; scope; scope = scope->parent)
    {
      for (b = scope->vars; b; b = b->next)
	{
	  if (!strcmp(b->name, name))
	    {
	      *type = b->type;
	      return b->data;
	    }
	}
      if (norecurse)
	break;
    }
  return NULL;
}

char *nameof(void *data)
{
  lexscope *scope;
  char *ans;
  varbinding *b;

  ans = nameof_builtin(data);
  if (ans)
    return ans;

  for (scope = scopestack; scope; scope = scope->parent)
    {
      for (b = scope->vars; b; b = b->next)
	{
	  if (b->data == data)
	    return b->name;
	}
    }
  return "[unknown symbol]";
}
