%{
/*
 * mgllex.l  -  Lexer for Bootrom Menu Generation Language
 *
 * Copyright (C) 1997-2003 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: mgllex.l,v 1.4 2003/01/25 23:29:44 gkminix Exp $
 */

#include "mknbi.h"
#include "mgl.h"
#include "gencode.h"
#include "y.tab.h"



/*
 ********************************************************************
 *
 * Definition of builtin data types. We need this here for the definition
 * of internal functions.
 */

/* No type - used to catch errors in type definitions */
struct typesdef none_type = {
	EXPR_NONE,   0, { { 0, 0, -1 } },		NULL
};

/* Integer type */
struct typesdef int_type = {
	EXPR_NUM,    2, { { -MAX_INT, MAX_INT, -1 } },	&none_type
};

/* String type */
struct typesdef string_type = {
	EXPR_STRING, MAX_STR_LEN, { { 0, 0, -1 } },	&int_type
};

/* Type for string index */
struct typesdef strindex_type = {
	EXPR_NUM,    2, { { 0, 255, -1 } },		& string_type
};

/* Character type */
struct typesdef char_type = {
	EXPR_CHAR,   1, { { 0, MAX_CHAR, -1 } },	&strindex_type
};

/* Boolean type */
struct typesdef bool_type = {
	EXPR_BOOL,   1, { { 0, 1, -1 } },		&char_type
};

/* Table of names for predefined types */
struct {
	char            *name;
	struct typesdef *t;
} predeftypes[] = {
	{ "integer",	&int_type    },
	{ "string",	&string_type },
	{ "char",	&char_type   },
	{ "boolean",	&bool_type   },
	{ "logical",	&bool_type   },
	{ NULL,		NULL         }
};



/*
 ********************************************************************
 *
 * List of internally defined constants
 */
struct {
	char            *name;
	char            *strval;
	int              numval;
	struct typesdef *t;
} predefconsts[] = {
	{ "maxint",		NULL, MAX_INT,	&int_type  },
	{ "true",		NULL, 1,	&bool_type },
	{ "false",		NULL, 0,	&bool_type },
	{ "BOOTP_HOSTNAME",	NULL, 12,	&int_type  },
	{ "BOOTP_DOMAIN",	NULL, 15,	&int_type  },
	{ "BOOTP_ROOTPATH",	NULL, 17,	&int_type  },
	{ NULL,			NULL, 0,	NULL       }
};



/*
 ********************************************************************
 *
 * List of internally defined variables.
 */
struct {
	char            *name;
        long             addr;
        struct typesdef *t;
} predefvars[] = {
	{ "servername", ADDR_SERVERNAME, &string_type },
	{ "hostname",   ADDR_HOSTNAME,   &string_type },
	{ NULL,         0,               NULL         }
};



/*
 ********************************************************************
 *
 * Definition of internal functions. To add a new internal function,
 * simply assign a new CMD_* value to pass to the runtime module, and
 * add a new entry here. You might optionally add a collapsing function
 * into mglparse.y for constant arguments.
 */
struct {
	char           *name;
	struct funcdef  f;
} functab[] = {
	/* This is a general scalar operation - int types are just dummies */
	{ "pred",	{ 0, 0, CMD_PRED, 1, &int_type,
				{ &int_type },    { ATTR_NONE  } } },
	/* This is a general scalar operation - int types are just dummies */
	{ "succ",	{ 0, 0, CMD_SUCC, 1, &int_type,
				{ &int_type },    { ATTR_NONE  } } },
	/* This is a general scalar operation - int types are just dummies */
	{ "ord",	{ 0, 0, CMD_ORD, 1, &int_type,
				{ &int_type },    { ATTR_NONE  } } },
	{ "odd",	{ 0, 0, CMD_ODD, 1, &bool_type,
				{ &int_type },    { ATTR_NONE  } } },
	{ "chr",	{ 0, 0, CMD_CHR, 1, &char_type,
				{ &int_type },    { ATTR_NONE  } } },
	{ "strlen",	{ 0, 0, CMD_STRLEN, 1, &int_type,
				{ &string_type }, { ATTR_CONST } } },
	{ "strsub",	{ 0, 0, CMD_STRSUB, 3, &string_type,
				{ &string_type, &int_type, &int_type },
				{ ATTR_CONST, ATTR_NONE, ATTR_NONE } } },
	{ "abs",	{ 0, 0, CMD_ABS, 1, &int_type,
				{ &int_type },    { ATTR_NONE  } } },
	{ "sqr",	{ 0, 0, CMD_SQR, 1, &int_type,
				{ &int_type },    { ATTR_NONE  } } },
	{ "cls",	{ 0, 0, CMD_CLS, 0, NULL,
				{ NULL },         { ATTR_NONE  } } },
	{ NULL,		{ 0, 0, CMD_NONE, 0, NULL,
				{ NULL },         { ATTR_NONE  } } }
};



/*
 ********************************************************************
 *
 * Some symbols are used internally by the lexer, and just return a
 * token to the parser. This way, we can redefine the symbols in the
 * user program, and still use them as tokens in the parser.
 */
struct {
	char *name;
	int   token;
} specialtab[] = {
	{ "at",		AT      },
	{ "from",	FROM    },
	{ "gateway",	GATEWAY },
	{ "get",	GET     },
	{ "gotoxy",	GOTOXY  },
	{ "load",	LOAD    },
	{ "menu",	MENU    },
	{ "print",	PRINT   },
	{ "timeout",	TIMEOUT },
	{ NULL,         0       }
};



/*
 ********************************************************************
 *
 * Global variables
 */
char *curfile;			/* Name of current input file */



/*
 ********************************************************************
 *
 * String buffer for C style strings
 */
static char string_buf[MAX_STR_LEN + 1];	/* string buffer */
static char *string_pos;			/* current string position */
static int string_flag;				/* flag if string too long */



/*
 ********************************************************************
 *
 * Routine to add a character to the constant string
 */
static void add_string(c)
char c;
{
  if (string_pos >= &string_buf[MAX_STR_LEN]) {
	if (!string_flag) {
		warning("string too long, truncating");
		string_flag++;
	}
  } else
	*string_pos++ = c;
}
%}



	/*
	 ********************************************************************
	 *
	 * Define some generally useful regular expressions
	 */
ws		[ \t]+
comment		#.*
id		[a-zA-Z][a-zA-Z0-9_]*
intnum		[0-9]+
hexnum		[0-9a-fA-F]+
nl		\n
cr		\r



	/*
	 ********************************************************************
	 *
	 * Always use a pointer for yytext
	 */
%pointer



	/*
	 ********************************************************************
	 *
	 * Define an exclusive start state for scanning C style strings
	 */
%x Cstring Comment
%%



	/*
	 ********************************************************************
	 *
	 * Skip all whitespace and comments
	 */
{ws}		;
{comment}	;

"(*"		{ BEGIN Comment; }	/* Switch to comment mode */
<Comment>.	;			/* Throw away any comment text */
<Comment>{nl}	{ lineno++; }		/* We still have to count line numbers */
<Comment>"*)"	{ BEGIN INITIAL; }	/* Return to regular mode */



	/*
	 ********************************************************************
	 *
	 * Parse an IP number
	 */
{intnum}\.{intnum}\.{intnum}\.{intnum} {
#ifdef HAVE_INET
		static struct in_addr addr;

		if ((unsigned long)(addr.s_addr = inet_addr(yytext)) ==
							(unsigned long)-1) {
			warning("invalid IP address, assuming 0.0.0.0");
			addr.s_addr = INADDR_ANY;
		}
#else
		static char addr[INADDR_SIZE];

		warning("no IP address support available, assuming 0.0.0.0");
		memset(addr, 0, INADDR_SIZE);
#endif
		yylval.inaddr = (char *)&addr;
		return(INADDR);
	}



	/*
	 ********************************************************************
	 *
	 * Parse an integer number
	 */
{intnum} {
		/* Decimal number */

		char *cp;
		unsigned long result = 0;

		for (cp = yytext; *cp; cp++) {
			result = result * 10 + *cp - '0';
			if (result > MAX_INT) {
				warning("integer number too large, assuming maximum");
				result = MAX_INT;
				break;
			}
		}
		yylval.intarg = result;
		return(NUM);
	}

${hexnum} {
		/* Hex number */

		char *cp;
		unsigned long result = 0;

		for (cp = yytext; *cp; cp++) {
			if (*cp >= '0' && *cp <= '9')
				result = result * 16 + *cp - '0';
			else if (*cp >= 'a' && *cp <= 'f')
				result = result * 16 + *cp + 10 - 'a';
			else if (*cp >= 'A' && *cp <= 'F')
				result = result * 16 + *cp + 10 - 'F';
			if (result > MAX_INT) {
				warning("integer number too large, assuming maximum");
				result = MAX_INT;
				break;
			}
		}
		yylval.intarg = result;
		return(NUM);
	}



	/*
	 ********************************************************************
	 *
	 * Parse a C type string
	 */
\" {
		string_flag = 0;
		string_pos = string_buf;
		BEGIN Cstring;
	}

<Cstring>\" {
		/* All done at closing quote */
		BEGIN INITIAL;
		*string_pos = '\0';
		yylval.string = string_buf;
		return(QSTRING);
	}

<Cstring>\n {
		/* Unterminated string constant */
		BEGIN INITIAL;
		warning("unterminated string constant");
		*string_pos = '\0';
		yylval.string = string_buf;
		return(QSTRING);
	}

<Cstring>\\[0-7]{1,3} {
		/* Octal escape sequence */
		int result;

		if (sscanf(yytext + 1, "%o", &result) != 1) {
			warning("invalid octal number in string constant, "
							"assuming \\377");
			result = '\377';
		}
		if (result > 0xff) {
			warning("octal number in string constant too large, "
							"assuming \\377");
			result = '\377';
		}
		add_string(result);
	}

<Cstring>\\[0-9]+ {
		/* Bad escape sequence */
		warning("bad escape sequence, skipping");
	}

<Cstring>\\n		{ add_string('\n'); }
<Cstring>\\t		{ add_string('\t'); }
<Cstring>\\r		{ add_string('\r'); }
<Cstring>\\b		{ add_string('\b'); }
<Cstring>\\f		{ add_string('\f'); }
<Cstring>\\(.|n)	{ add_string(yytext[1]); }

<Cstring>[^\\\n\"]+ {
		char *yptr = yytext;

		while (*yptr)
			add_string(*yptr++);
	}



	/*
	 ********************************************************************
	 *
	 * Parse a C type character value
	 */
\'\\[0-7]{1,3}\' {
		/* Octal escape sequence */
		int result;

		yytext[strlen(yytext) - 1] = '\0';
		if (sscanf(yytext + 2, "%o", &result) != 1) {
			warning("invalid octal number in character constant, "
							"assuming \\377");
			result = '\377';
		}
		if (result > 0xff) {
			warning("octal number in character constant too large, "
							"assuming \\377");
			result = '\377';
		}
		yylval.chrarg = result & 0xff;
		return(CHR);
	}

\'\\[0-9]+\' {
		/* Bad escape sequence */
		warning("bad escape sequence, assuming \\377");
		yylval.chrarg = 0xff;
		return(CHR);
	}

\'\\n\'		{ yylval.chrarg = '\n'; return(CHR); }
\'\\t\'		{ yylval.chrarg = '\t'; return(CHR); }
\'\\r\'		{ yylval.chrarg = '\r'; return(CHR); }
\'\\b\'		{ yylval.chrarg = '\b'; return(CHR); }
\'\\f\'		{ yylval.chrarg = '\f'; return(CHR); }
\'\\(.|n)\'	{ yylval.chrarg = yytext[2]; return(CHR); }

\'.\'	{
		yylval.chrarg = yytext[1];
		return(CHR);
	}

\'.	{
		warning("missing closing quote in character constant");
		yylval.chrarg = yytext[1];
		return(CHR);
	}



	/*
	 ********************************************************************
	 *
	 * Rules to parse reserved names
	 */
array		{ return(ARRAY); }
begin		{ return(CBEGIN); }
break		{ return(BREAK); }
const		{ return(CONST); }
default		{ return(DEFAULT); }
do		{ return(DO); }
else		{ return(ELSE); }
end		{ return(END); }
function	{ return(FUNCTION); }
if		{ return(IF); }
item		{ return(ITEM); }
of		{ return(OF); }
procedure	{ return(PROCEDURE); }
record		{ return(RECORD); }
repeat		{ return(REPEAT); }
restart		{ return(RESTART); }
return		{ return(RETURN); }
screen		{ return(SCREEN); }
select		{ return(SELECT); }
then		{ return(THEN); }
type		{ return(TYPE); }
until		{ return(UNTIL); }
var		{ return(VAR); }
while		{ return(WHILE); }
with		{ return(WITH); }

":="		{ return(ASSIGN); }
"..."		{ return(DOTS); }



	/*
	 ********************************************************************
	 *
	 * Logical operations
	 */
and	{ yylval.op = CMD_AND; return(ANDOP); }
not	{ yylval.op = CMD_NOT; return(NOTOP); }
or	{ yylval.op = CMD_OR;  return(OROP); }
xor	{ yylval.op = CMD_XOR; return(XOROP); }



	/*
	 ********************************************************************
	 *
	 * Comparison rules
	 */
"="	{ yylval.op = CMD_EQ; return(COMPARISON); }
">"	{ yylval.op = CMD_GT; return(COMPARISON); }
">="	{ yylval.op = CMD_GE; return(COMPARISON); }
"<"	{ yylval.op = CMD_LT; return(COMPARISON); }
"<="	{ yylval.op = CMD_LE; return(COMPARISON); }
"<>"	{ yylval.op = CMD_NE; return(COMPARISON); }



	/*
	 ********************************************************************
	 *
	 * Arithmetic operations
	 */
"-"	{ yylval.op = '-'; return(ADDOP); }
"+"	{ yylval.op = '+'; return(ADDOP); }
"*"	{ yylval.op = '*'; return(MULOP); }
"/"	{ yylval.op = '/'; return(MULOP); }
div	{ yylval.op = '/'; return(MULOP); }
"%"	{ yylval.op = '%'; return(MULOP); }
mod	{ yylval.op = '%'; return(MULOP); }



	/*
	 ********************************************************************
	 *
	 * Symbol names
	 */
{id} {
		struct sym *sp;

		yylval.symbol = NULL;
		if (strlen(yytext) > MAX_ID_LEN) {
			warning("ID too long, truncating to 16 characters");
			yytext[MAX_ID_LEN] = '\0';
		}

		/* Search if symbol already exists */
		for (sp = symtab; sp != NULL; sp = sp->next)
			if (!strcmp(sp->name, yytext))
				break;

		/* If it's a special symbol, return it's token */
		if (isspecialsym(sp))
			return(sp->def.s.token);

		/* If symbol doesn't exist, create it. */
		if (sp == NULL) {
			sp = (struct sym *)nbmalloc(sizeof(struct sym));
			sp->type = nosym;
			sp->level = curlevel;
			copystr(&(sp->name), yytext);
			sp->next = symtab;
			symtab = sp;
		}

		/* Return the symbol to the parser */
		yylval.symbol = sp;
		return(ID);
	}



	/*
	 ********************************************************************
	 *
	 * Misc. rules: count lines and ignore \r, return all other
	 * characters as is to the parser.
	 */
{cr}	;
{nl}	{ lineno++; }
.	{ return(yytext[0]); }

%%



/*
 *****************************************************************************
 *
 * Print current token. This has to be here because the definition of yytext
 * is in the lexer, and it's not consistent among different lex versions.
 */
void print_token()
{
  fprintf(stderr, "'%s'", yytext);
}



/*
 *****************************************************************************
 *
 * Initialize lexer
 */
void yylexinit(infile)
char *infile;
{
  struct sym *sp;
  long size;
  int i;

  /* Open input file */
  if (infile == NULL || !*infile)
	yyin = stdin;
  else if ((yyin = fopen(infile, "r")) == NULL) {
	perror(progname);
	exit(EXIT_MGL_PROGOPEN);
  }

  /* Set name of current input file */
  curfile = infile;

  /* Preset the list of known types with builtin static type list */
  typetab = &bool_type;
  for (i = 0; predeftypes[i].name != NULL; i++) {
	sp = (struct sym *)nbmalloc(sizeof(struct sym));
	sp->type = typesym;
	sp->name = predeftypes[i].name;
	sp->level = -1;
	sp->def.t = predeftypes[i].t;
	sp->next = symtab;
	symtab = sp;
  }

  /* The string type is special */
  string_type.def.a.elementnum = MAX_STR_LEN;
  string_type.def.a.indextype = &strindex_type;
  string_type.def.a.basetype = &char_type;

  /* Preset the list of internal functions */
  for (i = 0; functab[i].name != NULL; i++) {
	sp = (struct sym *)nbmalloc(sizeof(struct sym));
	sp->type = funcsym;
	sp->name = functab[i].name;
	sp->level = -1;
	sp->def.f = functab[i].f;
	sp->next = symtab;
	symtab = sp;
  }

  /* Preset the list of special symbols */
  for (i = 0; specialtab[i].name != NULL; i++) {
	sp = (struct sym *)nbmalloc(sizeof(struct sym));
	sp->type = specialsym;
	sp->name = specialtab[i].name;
	sp->level = -1;
	sp->def.s.token = specialtab[i].token;
	sp->next = symtab;
	symtab = sp;
  }

  /* Preset the list of internal constants */
  for (i = 0; predefconsts[i].name != NULL; i++) {
	sp = (struct sym *)nbmalloc(sizeof(struct sym));
	sp->type = constsym;
	sp->name = predefconsts[i].name;
	sp->level = -1;
	sp->def.c.t = predefconsts[i].t;
	switch (predefconsts[i].t->type) {
		case EXPR_NUM:
			sp->def.c.val.i = predefconsts[i].numval;
			break;
		case EXPR_BOOL:
			sp->def.c.val.b = predefconsts[i].numval;
			break;
		case EXPR_CHAR:
			sp->def.c.val.c = predefconsts[i].numval;
			break;
		default:
			break;
	}
	sp->next = symtab;
	symtab = sp;
  }

  /* Preset the list of internally defined variables */
  for (i = 0; predefvars[i].name != NULL; i++) {
#ifdef PARANOID
	if (dataptr != predefvars[i].addr)
		interror(118, "internal variable address doesn't match default");
#endif
	size = predefvars[i].t->size;
	if (predefvars[i].t->type == EXPR_STRING)
		size++;
	dataptr += (size + 1) & 0xfffe;
	sp = (struct sym *)nbmalloc(sizeof(struct sym));
	sp->type = varsym;
	sp->name = predefvars[i].name;
	sp->addr = predefvars[i].addr;
	sp->level = -1;
	sp->def.v.t = predefvars[i].t;
	sp->def.v.attr = ATTR_CONST;
	sp->next = symtab;
	symtab = sp;
  }
}



/*
 *****************************************************************************
 *
 * Terminate lexer
 */
void yylexterm()
{
  /* Close input file */
  if (curfile != NULL && *curfile)
	fclose(yyin);
}



/*
 *****************************************************************************
 *
 * We don't want to need to use a library, so define yywrap here, if it's
 * not a macro.
 */
#ifndef yywrap
int yywrap()
{
  return(1);
}
#endif



syntax highlighted by Code2HTML, v. 0.9.1