/*
 * mgl.h  -  Definitions for the Menu Generation Language
 *
 * Copyright (C) 1997-2007 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: mgl.h,v 1.21 2007/01/06 18:31:27 gkminix Exp $
 */


/*
 ***************************************************************************
 *
 * Miscellaneous defines
 */
#define MAX_INT		0x7fff		/* max value for integer */
#define MAX_CHAR	0xff		/* max value for character */
#define MAX_STR_LEN	255		/* max length for strings */
#define MAX_EXPRS	16		/* max number of expressions */
#define MAX_LEVELS	8		/* max number of nesting levels */
#define MAX_ARRAY_SIZE	16384		/* max size of an array in bytes */
#define MAX_REC_SIZE	8096		/* max size of a record in bytes */
#define MAX_CMT_ARG	256		/* max size of argument in comment */



/*
 ***************************************************************************
 *
 * Type for addresses. This has to be signed in order to allow for negative
 * address offsets. For the same reason we need at least 32 bits even though
 * all addresses are always 16 bits.
 */
#if SIZEOF_UNSIGNED_SHORT >= 4
typedef short addr_t;
#else
# if SIZEOF_UNSIGNED_INT >= 4
typedef int addr_t;
# else
typedef long addr_t;
# endif
#endif



/*
 ***************************************************************************
 *
 * Type used for single-byte values. We use "unsigned short" here in order
 * to avoid overflows with byte calculations
 */
typedef unsigned short byte_t;

#define BYTE_NULL	((byte_t) 0)



/*
 ***************************************************************************
 *
 * Type used for labels
 */
typedef unsigned int slabel_t;

#define LABEL_NULL	((slabel_t) 0)



/*
 ***************************************************************************
 *
 * Command codes in expressions
 */
#define CMD_NONE	0x00		/* no command */
#define CMD_CONST	0x01		/* expression is a constant */
#define CMD_VAR		0x02		/* expression is a variable */
#define CMD_TMPVAR	0x03		/* expression is a temp variable */
#define CMD_EQ		0x04		/* equal */
#define CMD_GT		0x05		/* greater than */
#define CMD_GE		0x06		/* greater or equal */
#define CMD_LT		0x07		/* lower than */
#define CMD_LE		0x08		/* lower or equal */
#define CMD_NE		0x09		/* not equal */
#define CMD_CMP		0x0a		/* comparison - not used by parser */
#define CMD_OR		0x0b
#define CMD_XOR		0x0c
#define CMD_AND		0x0d
#define CMD_NOT		0x0e
#define CMD_ORD		0x0f		/* get ordinal number of scalar */
#define CMD_ODD		0x10		/* odd function */
#define CMD_CHR		0x11		/* convert a number into a character */
#define CMD_SUCC	0x12		/* increase scalar to next ordinal no */
#define CMD_PRED	0x13		/* decrease scalar to next ordinal no */
#define CMD_ABS		0x14		/* abs function */
#define CMD_SQR		0x15		/* square function */

#define CMD_USERFUNC	0x60		/* user defined function */
#define CMD_RTFUNC	0x61		/* global ID for runtime functions */


/* Check for conditional command */
#define iscmdcond(a)	((a)->opcode >= CMD_EQ && (a)->opcode <= CMD_NE)

/* Check for general scalar commands */
#define iscmdscalar(a)	((a)->opcode == CMD_ORD || (a)->opcode == CMD_PRED || \
			 (a)->opcode == CMD_SUCC)

/* Check for logical command */
#define iscmdlogical(a)	((a)->opcode >= CMD_OR && (a)->opcode <= CMD_NOT)

/* Check for associative command, e.g.  A^(B^C) == (A^B)^C  */
#define iscmdassoc(a)	((a)->opcode == '+' || (a)->opcode == '*' || \
			 (a)->opcode == CMD_OR || (a)->opcode == CMD_XOR || \
			 (a)->opcode == CMD_AND)

/* Check for commutative command, e.g.  A^B == B^A  */
#define iscmdcommut(a)	((a)->opcode == '+' || (a)->opcode == '*' || \
			 (a)->opcode == CMD_OR || (a)->opcode == CMD_XOR || \
			 (a)->opcode == CMD_AND || (a)->opcode == CMD_EQ || \
			 (a)->opcode == CMD_NE)

/* Check for user defined function command */
#define iscmdfunc(a)	((a)->opcode >= CMD_USERFUNC)

/* Check for runtime function command */
#define iscmdrtfunc(a)	((a)->opcode == CMD_RTFUNC)



/*
 ***************************************************************************
 *
 * Encoding of types.
 */
/* Basic types */
typedef enum {
	EXPR_NONE,			/* no assigned type */
	EXPR_ANY,			/* any type (used for arguments) */
	EXPR_STRING,			/* string type */
	EXPR_NUM,			/* numerical type */
	EXPR_BOOL,			/* boolean type */
	EXPR_CHAR,			/* character type */
	EXPR_ENUM,			/* enumeration type */
	EXPR_ARRAY,			/* array type */
	EXPR_RECORD,			/* record type */
	EXPR_IPADDR,			/* IP address type */
	EXPR_POINTER			/* pointer type */
} basictypes;

/* Definitions for scalar types */
struct scalar_type {
	int             min;		/* Minimum value for scalar type */
	int             max;		/* Maximum value for scalar type */
	addr_t          boundaddr;	/* Addr of bounds in const seg */
};

/* Definitions for array type (also for strings) */
struct array_type {
	struct typesdef *indextype;	/* Type of index */
	struct typesdef *basetype;	/* Type of each element */
};

/* Definitions for record type */
struct record_type {
	int             elementnum;	/* Number of elements in record */
	struct symlist *elements;	/* Symbols for all elements */
};

/* Definitions for pointer type */
struct pointer_type {
	struct typesdef *basetype;	/* Type of element pointed to */
	struct sym      *unknownsym;	/* Symbol if pointer type unknown */
};

/* Global structure holding all type information */
struct typesdef {
	basictypes       type;		/* Identifier for type */
	addr_t           size;		/* Total size of type */
	union {				/* The scalar type has to come first! */
		struct scalar_type  s;	/* Definitions for scalar types */
		struct array_type   a;	/* Definitions for an array */
		struct record_type  r;	/* Definitions for a record */
		struct pointer_type p;	/* Definitions for a pointer */
	} def;
	struct typesdef *next;		/* Pointer to next type */
};

/* Predefined type definitions */
extern struct typesdef none_type;	/* type not defined */
extern struct typesdef any_type;	/* catch all types */
extern struct typesdef int_type;	/* integer type */
extern struct typesdef string_type;	/* string type */
extern struct typesdef strindex_type;	/* default string index type */
extern struct typesdef strlength_type;	/* default string length type */
extern struct typesdef char_type;	/* character type */
extern struct typesdef bool_type;	/* boolean type */
extern struct typesdef ipaddr_type;	/* IP address type */
extern struct typesdef pointer_type;	/* pointer type */

/* Check if a type is scalar */
#define isscalar(a)	((a) != NULL && \
			 ((a)->type == EXPR_NUM || (a)->type == EXPR_CHAR || \
			  (a)->type == EXPR_BOOL || (a)->type == EXPR_ENUM))

/* Check if a type is a non-scalar type */
#define isnonscalar(a)	((a) != NULL && \
			 ((a)->type == EXPR_STRING || \
			  (a)->type == EXPR_ARRAY || \
			  (a)->type == EXPR_RECORD || \
			  (a)->type == EXPR_IPADDR))

/* Check if a type is an array */
#define isarray(a)	((a) != NULL && (a)->type == EXPR_ARRAY)

/* Check if a type is a string type */
#define isstring(a)	((a) != NULL && (a)->type == EXPR_STRING)

/* Check if a type is a record */
#define isrecord(a)	((a) != NULL && (a)->type == EXPR_RECORD)

/* Check if a type is an enumeration */
#define isenum(a)	((a) != NULL && (a)->type == EXPR_ENUM)

/* Check if a type is a pointer type */
#define ispointer(a)	((a) != NULL && (a)->type == EXPR_POINTER)

/* Check if a type is a signed scalar */
#define issigned(a)	((a) != NULL && (a)->type == EXPR_NUM)

/* Check if a type is any type */
#define isanytype(a)	((a) != NULL && (a)->type == EXPR_ANY)



/*
 ***************************************************************************
 *
 * Storage classes for variables and function arguments
 */
typedef enum {
	CLASS_STATIC,		/* static variable */
	CLASS_STATICRO,		/* static read-only variale */
	CLASS_LOCAL,		/* variable is relative to current frame */
	CLASS_REF,		/* variable is an arg passed by reference */
	CLASS_CONST		/* passed by value but never changed */
} varclass;



/*
 ***************************************************************************
 *
 * Encoding of symbols. The level value is the current nesting level. If
 * it's greater than zero, the address is relative to the level's frame
 * pointer.
 */
struct vardef {
	struct typesdef *t;		/* type definition of variable */
	varclass         class;		/* storage class of variable */
};

struct constdef {
	struct typesdef *t;		/* type of constant */
	union {
		int       i;		/* constant integer value */
		byte_t   *m;		/* constant generic memory block */
		byte_t   *a;		/* constant IP address value */
		byte_t   *s;		/* constant string value */
		byte_t    c;		/* constant character value */
		int       b;		/* constant boolean value */
		int       e;		/* constant enumeration value */
		addr_t    p;		/* constant pointer value */
	} val;
};

struct funcdef {
	addr_t           retaddr;	/* BP relative address of return value */
	addr_t           varsize;	/* Total size of local variables */
	addr_t           argsize;	/* Total size of arguments on stack */
	int              opcode;	/* command value (see below) */
	int              argnum;	/* Number of arguments */
	struct symlist  *labels;	/* list of labels */
	struct typesdef *ret;		/* return type of function */
	struct vardef   *args;		/* expression types of args */
};


/* Special symbols are only used within the lexer, never passed to the parser */
struct specialdef {
	int  token;			/* token to return to parser */
};



/*
 ***************************************************************************
 *
 * Symbol reference ID. If a symbol is valid in a certain context (for
 * example within a record definition or as an enumeration value), the
 * reference ID is the value of the base type pointer.
 * Some special reference values are :
 *	SYMBOL_NOREF	symbol hasn't been assigned a reference ID
 *	SYMBOL_ANYREF	used for calling findsym() to not use the reference ID
 *	SYMBOL_RTREF	symbol is defined in runtime library
 *      SYMBOL_SYSREF	symbol is defined by the compiler
 */
typedef voidstar ref_t;

#define SYMBOL_NOREF	(ref_t)( 0)
#define SYMBOL_ANYREF	(ref_t)(-1)
#define SYMBOL_RTREF	(ref_t)(-2)
#define SYMBOL_SYSREF	(ref_t)(-3)



/*
 ***************************************************************************
 *
 * Symbol table
 */
typedef enum symtype {
	nosym,
	specialsym,			/* special lexer symbol */
	programsym,			/* symbol is the program name */
	labelsym,			/* symbol is a label */
	varsym,				/* symbol is a variable */
	constsym,			/* symbol is a constant */
	typesym,			/* symbol is a type definition */
	funcsym,			/* symbol is a function or procedure */
	recsym				/* symbol is a record element */
} symtype;

struct sym {
	symtype    type;		/* symbol type */
	char      *name;		/* name of symbol */
	union {
		slabel_t label;		/* label used for symbol (for code) */
		addr_t  addr;		/* address of symbol (for variables) */
	} loc;
	int        level;		/* nesting level of symbol */
	ref_t      ref;			/* symbol reference ID */
	union {
		struct funcdef     f;	/* function declaration */
		struct specialdef  s;	/* special symbol for lexer */
		struct vardef      v;	/* variable declaration */
		struct constdef    c;	/* constant declaration */
		struct typesdef   *t;	/* type declaration */
	} def;
	struct sym *next;		/* pointer to next symbol */
};

/* Check if symbol is not declared yet */
#define isnosym(a)	((a) != NULL && (a)->type == nosym)

/* Check for special lexer symbol */
#define isspecialsym(a)	((a) != NULL && (a)->type == specialsym)

/* Check for special lexer symbol */
#define isprogramsym(a)	((a) != NULL && (a)->type == programsym)

/* Check for label symbol */
#define islabelsym(a)	((a) != NULL && (a)->type == labelsym)

/* Check for variable symbol */
#define isvarsym(a)	((a) != NULL && (a)->type == varsym)

/* Check for constant symbol */
#define isconstsym(a)	((a) != NULL && (a)->type == constsym)

/* Check for function symbol */
#define isfuncsym(a)	((a) != NULL && (a)->type == funcsym)

/* Check for function symbol */
#define istypesym(a)	((a) != NULL && (a)->type == typesym)

/* Check for record element symbol */
#define isrecsym(a)	((a) != NULL && (a)->type == recsym)

/* Check for enumeration symbol */
#define isenumsym(a)	((a) != NULL && (a)->type == enumsym)

/* Check for symbol name */
#define issymname(a, n)	((a) != NULL && !strcmp((a)->name, (n)))



/*
 *****************************************************************************
 *
 * In order to create a symbol list like <symbol>,<symbol>,... for enume-
 * rations or variable declarations, we use a temporary list, which contains
 * the symbol links. Analogous we use a type list.
 */
struct symlist {
	int             num;		/* Current symbol number */
	struct sym     *sym;		/* Pointer to symbol */
	struct symlist *next;		/* Pointer to next list entry */
};

struct typelist {
	struct typesdef *t;		/* Pointer to type */
	struct typelist *next;		/* Pointer to next list entry */
};



/*
 ***************************************************************************
 *
 * Structure to hold a variable reference. For example, the expression
 *
 *		a[2].b
 *
 * consists of three such structure elements:
 *
 *  1.)  The base variable: (a)
 *  2.)  An array index:    (2)
 *  3.)  A record element:  (b)
 *
 * All elements are stored in a linear list, with the base element being
 * the first element in the list.
 *
 * Note: the vartype in the following structure specifies the type of the
 * expression _after_ applying the element command. For the example above,
 * vartype contains:
 *
 *  1.)  The type of the base variable (a)
 *  2.)  The type of one element of (a)
 *  3.)  The type of the record element (b)
 */
typedef enum varinfocmd {
	basevar,				/* variable base element */
	arrayindex,				/* array index */
	recordelement,				/* record element */
	ptrderef				/* pointer dereferentiation */
} varinfocmd;

struct varinfo {
	varinfocmd       cmd;			/* cmd to apply to element */
	struct typesdef *vartype;		/* type of element */
	union {
		struct sym    *basesym;		/* variable base symbol */
		struct sym    *recsym;		/* record element symbol */
		struct expr   *index;		/* array index */
	} spec;
	struct varinfo  *next;			/* pointer to next element */
};



/*
 ***************************************************************************
 *
 * Definitions to build expression trees
 */

/* Structure of one expression node */
struct expr {
	struct typesdef *type;			/* type of expression */
	int              opcode;		/* type of operation */
	int              exprnum;		/* number of subexpressions */
	struct expr     *exprlist[MAX_EXPRS];	/* list of subexpressions */
	union {
		struct constdef  cval;		/* constant value */
		struct varinfo  *var;		/* variable definition */
		struct sym      *func;		/* function pointer */
	} spec;
};

#define left	exprlist[0]			/* left (or only) expr tree */
#define right	exprlist[1]			/* right expression tree */

/* Get expression type - this is used so often, that it's better in a macro */
#define exprtype(a)	((a)->type == NULL ? EXPR_NONE : (a)->type->type)

/* Get expression size */
#define exprsize(a)	((a)->type == NULL ? 0 : (a)->type->size)

/* Check if an expression is constant */
#define isconst(a)	((a)->opcode == CMD_CONST && (a)->exprnum == 0)

/* Check if an expression is a temporary variable */
#define istempvar(a)	((a)->exprnum == 0 && (a)->opcode == CMD_TMPVAR)

/* Check if an expression is a variable */
#define isvariable(a)	((a)->exprnum == 0 && \
			 ((a)->opcode == CMD_VAR || \
			  (a)->opcode == CMD_TMPVAR))

/* Check if an expression is a leaf node */
#define isleaf(a)	((a)->exprnum == 0 && \
			 ((a)->opcode == CMD_VAR || \
			  (a)->opcode == CMD_TMPVAR || \
			  (a)->opcode == CMD_CONST))

/* Check if an expression is a procedure call */
#define isproc(a)	((a)->type == NULL && iscmdfunc(a))

/* Check if an expression is a function call */
#define isfunc(a)	((a)->type != NULL && iscmdfunc(a))


/* Check constant subclass range */
#define isconstrange(a,t)	(getord(a) >= (t)->def.s.min && \
				 getord(a) <= (t)->def.s.max)



/*
 ***************************************************************************
 *
 * Special codes for function which have to be implemented by the runtime
 * module. These codes can be used to lookup the corresponding symbol.
 */
#define RTCMD_STRCMP	0
#define RTCMD_STRSUB	1
#define RTCMD_STRCATCC	2
#define RTCMD_STRCATCS	3
#define RTCMD_STRCATSC	4
#define RTCMD_STRCAT	5
#define RTCMD_STRSET	6
#define RTCMD_MEMCMP	7
#define RTCMD_PUTBOOTP	8
#define RTCMD_GETBOOTP	9
#define RTCMD_PRINTINT	10
#define RTCMD_PRINTSTR	11
#define RTCMD_PRINTCHR	12
#define RTCMD_GETINT	13
#define RTCMD_GETSTR	14
#define RTCMD_GETCHR	15
#define RTCMD_SELECT	16
#define RTCMD_STR2IP	17
#define RTCMD_IP2STR	18
#define RTCMD_LOAD	19
#define RTCMD_GOTOXY	20
#define RTCMD_HANDLETRY	21
#define RTCMD_ENDEXCEPT	22
#define RTCMD_RAISE	23
#define RTCMD_CHKSTK	24
#define RTCMD_MALLOC	25
#define RTCMD_FREE	26
#define RTVAR_EXCEPTION	27
#define RTVAR_TFTP	28
#define RTVAR_GATEWAY	29

#define RTSYM_NUM	30

extern struct sym *rtsymbols[RTSYM_NUM];



/*
 ***************************************************************************
 *
 * Definition of operation codes to be used in the interface from the
 * parser to the code generator.
 */
typedef enum {
	CODE_PROC_START,
	CODE_PROC_END,
	CODE_CALL_PROC,
	CODE_RESTART,
	CODE_RETURN,
	CODE_LABEL,
	CODE_GOTO,
	CODE_ASSIGN,
	CODE_IF,
	CODE_ELSE,
	CODE_WHILE,
	CODE_REPEAT,
	CODE_SELECT,
	CODE_ITEM,
	CODE_ENDNEST,
	CODE_BREAK
} codes;



/*
 ***************************************************************************
 *
 * External functions
 */

/* compile.c */
extern void interror __P((int num, const char *msg));
extern void warning __P((const char *msg));
extern void error __P((const char *msg, int printtok));
extern int compile __P((char *infile, char *rtfile, char *outfile,
							char *incpath));


/* mgllex.l */
extern int yylex __P((void));
extern void yylexinit __P((char *infile, char *incpath));


/* mglparse.y */
extern int yyparse __P((void));


/* gencode.c */
extern void codeinit __P((const char *rtfile));
extern void codewrite __P((int fd));


/* expr.c */
extern int getord __P((const struct expr *ep));
extern void delexpr __P((struct expr *ep));
extern struct expr *reorg __P((struct expr *ep));


/* gencmd.c */
extern void docmd __P((codes cmd, struct sym *sp,
					struct expr *ep1, struct expr *ep2));

/* symbols.c */
extern void syminit __P((void));
extern struct sym *findsym __P((const char *name, ref_t ref));
extern struct sym *addsym __P((const char *name, ref_t ref));
extern struct sym *newsym __P((struct sym *sp, ref_t ref));
extern void delsymbols __P((ref_t ref));
extern void addsymlist __P((struct symlist **slp, struct sym *sp));
extern struct symlist *createsymlist __P((ref_t ref));
extern void delsymlist __P((struct symlist *slist, int force));


/* utils.c */
extern byte_t *getinet __P((const char *name, int noerror));
extern byte_t *make_string __P((const char *str));
extern byte_t *copy_string __P((const byte_t *mglstr));
extern char *get_string __P((const byte_t *mglstr));
extern void releasetmpvar __P((struct expr *ep));
extern addr_t freetmpvars __P((void));
extern struct typesdef *newstrtype __P((int size));
extern struct expr *createtmpvar __P((struct typesdef *tp,
					struct sym *funcsym));


/*
 ***************************************************************************
 *
 * External variables
 */
extern struct typesdef *typetab;	/* types table */
extern char *curfile;			/* name of current input file */
extern int lineno;			/* current line number */
extern int warnings;			/* number of warnings */
extern int errors;			/* number of errors */
extern int curlevel;			/* current nesting level */

#ifdef YYTEXT_POINTER
extern char *yytext;			/* current token */
#else
extern char yytext[];			/* current token */
#endif

