/*
**      parser.y     -
**
**
** Copyright (c) 1995  Hughes Technologies Pty Ltd
**
** Permission to use, copy, and distribute for non-commercial purposes,
** is hereby granted without fee, providing that the above copyright
** notice appear in all copies and that both the copyright notice and this
** permission notice appear in supporting documentation.
**
** The software may be modified for your own purposes, but modified versions
** may not be distributed.
**
** This software is provided "as is" without any expressed or implied warranty.
**
*/


%{
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <msql/msql.h>
#include <common/config.h>

#include "lite.h"


#define YYDEBUG	1

int	doingSet = 0;
int	curLabel = 1,
	contLabel = 0,
	breakLabel = 0;
char	errBuf[160];

extern	int	contLabel,
		breakLabel;
extern  code_t  *codeHead, *codeTail;
extern  sym_t   *paramHead, *paramTail;
extern	funct_t	*curFunct, mainFunct;
extern  sym_t   *curArrayLiteral;
extern	int	curArrayElement;

%}

%token	HTML
%token  END_OF_INPUT
%token  TAG_CLOSE

%token  GE
%token  LE
%token  NE
%token  EQ
%token  GT
%token  LT
%token  RE

%token	ADD
%token	SUB
%token	MUL
%token	DIV

%token	LOGICAL_OR
%token	LOGICAL_AND

%token	IF
%token	ELSE
%token	WHILE
%token	CONTINUE
%token	BREAK

%token IDENT
%token TEXT
%token NUM
%token SIGNED_NUM
%token REAL
%token SIGNED_REAL
%token VAR

%token	FUNCT
%token	LOAD
%token	MOD_LOAD
%token	RETURN

%left	'+' '-' '|' '&'
%left	'*' '/'
%nonassoc	'[' ']'
%nonassoc	'='

%%

page
	: script END_OF_INPUT


script
	: /* NULL */
	| HTML 
	{
		sym_t *sym;

		if ($1)
		{
			sym = symCreateLiteral((char*)$1,TEXT);
			codeEcho(sym->name);
			free($1);
		}
	}
	script

	| opt_functs statements TAG_CLOSE script

opt_functs
	: /* NULL */
	| FUNCT IDENT '(' funct_args ')' '{' 
		{
			if (addFunction($2) < 0)
				exit(1);
		}
		statements 
		{
			endOfFunction();
		}
		'}' opt_functs
	| MOD_LOAD TEXT ';' 
		{
#if defined(HAVE_DYNAMIC) && defined(HAVE_DLFCN_H)
			code_t	*tmpCodeTail;

			if (codeHead)
			{
				mainFunct.code = codeHead;
				codeHead = NULL;
				tmpCodeTail = codeTail;
				codeTail = NULL;
			}
			if (modLoadModule($2) < 0)
			{
				sprintf(errBuf, "Couldn't load module '%s'",
					$2+1);
				parseError(errBuf);
				return(-1);
			}
			paramHead = paramTail = NULL;
			codeHead = mainFunct.code;
			codeTail = tmpCodeTail;
			curFunct = &mainFunct;
#else
			parseError("Dynamic module loading is disabled");
			return(-1);
#endif
		}
	  opt_functs
	| LOAD TEXT ';' 
		{
			code_t	*tmpCodeTail;
			int	res;

			if (codeHead)
			{
				mainFunct.code = codeHead;
				codeHead = NULL;
				tmpCodeTail = codeTail;
				codeTail = NULL;
			}
			res=libLoadLibrary($2);
			if (res == -1)
			{
				sprintf(errBuf, "Couldn't load library '%s'",
					$2+1);
				parseError(errBuf);
			}
			if (res < 0)
				return(-1);
			paramHead = paramTail = NULL;
			codeHead = mainFunct.code;
			codeTail = tmpCodeTail;
			curFunct = &mainFunct;
		}
	opt_functs


funct_args
	:  /* NULL */
	| IDENT VAR opt_funct_args
		{
			if (addParam($1, $2) < 0)
				exit(1);
		}

opt_funct_args
	: /* NULL */
	| ',' IDENT  VAR opt_funct_args
		{
			if (addParam($2, $3) < 0)
				exit(1);
		}

statements
	: /* NULL */
	| function ';' statements
	| assign ';' statements
	| RETURN '(' return_arg ')' ';'
		{
			codeReturn();
		}
	| if statements
	| 
		{
			curLabel++;
			codeLabel(curLabel);
			codePushLabel(curLabel);
			codePushContinue(curLabel);
			curLabel++;
			codePushLabel(curLabel);
			codePushBreak(curLabel);
		}
	  while statements
	| CONTINUE ';' 
		{
			contLabel = codePopContinue();
			if (contLabel < 0)
			{
				runError("Illegal continue position");
				exit(1);
			}
			codeJmpBack(contLabel);
			codePushContinue(contLabel);
		}
	  statements;
	| BREAK ';'
		{
			breakLabel = codePopBreak();
			if (breakLabel < 0)
			{
				runError("Illegal break position");
				exit(1);
			}
			codeJmp(breakLabel);
			codePushBreak(breakLabel);
		}


function
	: IDENT '(' arg_list ')'
		{
			codeCall((char *)$1);
			free($1);
		}


arg_list
	: /* NULL */
	| 
		{
			codeBlockIn();
		}
	expr  args
		{
			codeBlockOut();
		}

args
	: /* NULL */
	| ',' expr args


assign
	: VAR '=' expr
		{
			codeStore((char *)$1);
			free($1);
		}
	| VAR '[' expr ']' '=' expr
		{
			codeArrayStore((char *)$1);
			free($1);
		}
	| VAR '+' '+'
		{
                        sym_t *sym;

                        codePush((char *)$1);
                        sym = symCreateLiteral("1",NUM);
                        codePush(sym->name);
			codeALU((int)OP_ADD);
			codeStore((char *)$1);
                        free($1);
		}
	| VAR '-' '-'
		{
                        sym_t *sym;

                        codePush((char *)$1);
                        sym = symCreateLiteral("1",NUM);
                        codePush(sym->name);
			codeALU((int)OP_SUB);
			codeStore((char *)$1);
                        free($1);
		}

if 
	: IF '(' condition ')'
		{
			curLabel++;
			codePushLabel(curLabel);
			curLabel++;
			codePushLabel(curLabel);
			codeJmpFalse(curLabel);
		}
	'{' statements '}' opt_else
		{
			int	label;

			label = codePopLabel();
			if (label < 0)
			{
				runError("Bad label!");
				exit(1);
			}
			codeLabel(label);
		}


opt_else
	: /* NULL */
		{
			int	label;

			label = codePopLabel();
			if (label < 0)
			{
				runError("Bad label!");
				exit(1);
			}
			codeLabel(label);
		}
	|
		{
			int	elseLabel,
				outerLabel;

			elseLabel = codePopLabel();
			outerLabel = codePopLabel();
			if (elseLabel < 0 || outerLabel < 0)
			{
				runError("Bad label!");
				exit(1);
			}
			codeJmp(outerLabel);
			codeLabel(elseLabel);
			codePushLabel(outerLabel);
		}
	  ELSE '{' statements '}'



condition
	: condition_expr opt_logical
	| '(' condition ')' opt_logical

condition_expr
	: expr cond_op expr
		{
			codeCmp((int)$2);
		}

opt_logical
        : /* NULL */
        | LOGICAL_OR condition
          {
                codeALU(OP_OR);
          }

        | LOGICAL_AND condition
          {
                codeALU(OP_AND);
          }



while
	: WHILE '(' condition ')'
		{
			int	label;

			label = codePopLabel();
			if (label < 0)
			{
				runError("Bad label");
				exit(1);
			}
			codeJmpFalse(label);
			breakLabel = label;
		}
	 '{' statements '}'
		{
			int	label;

			label = codePopLabel();
			if (label < 0)
			{
				runError("Bad label");
				exit(1);
			}
			codeJmpBack(label);
			codeLabel(label + 1);
			codePopBreak();
			codePopContinue();
	}

expr
	: value
	| '(' expr ')'
	| expr alu_op expr
		{
			codeALU((int)$2);
		}
	| expr signed_value
		{
			codeALU((int)OP_ADD);
		}
	| '(' IDENT ')' '(' expr ')'
		{
			codeCast($2);
			free($2);
		}


alu_op
	: '+'
		{ $$ = (YYSTYPE)OP_ADD;}
	| '-'
		{ $$ = (YYSTYPE)OP_SUB;}
	| '*'
		{ $$ = (YYSTYPE)OP_MUL;}
	| '/'
		{ $$ = (YYSTYPE)OP_DIV;}
	| '%'
		{ $$ = (YYSTYPE)OP_MOD;}

return_arg
	: /* NULL */
		{
			codePush(NULL);
		}
	| value
	


value
	: '#' value
		{
			codeCount();
		}
	| '(' IDENT ')' value
		{
			codeCast($2);
			free($2);
		}
	| TEXT
		{
			sym_t *sym;
			char	*tmp;

			tmp = (char *)malloc(strlen((char *)$1));
			bzero(tmp,strlen((char *)$1));
			strncpy(tmp,(char *)$1+1,strlen((char *)$1)-2);
			sym = symCreateLiteral(tmp,TEXT);
			codePush(sym->name);
			free(tmp);
			free($1);
		}
	| NUM
		{
			sym_t *sym;

			sym = symCreateLiteral((char *)$1,NUM);
			codePush(sym->name);
			free($1);
		}
	| SIGNED_NUM
		{
			sym_t *sym;

			sym = symCreateLiteral((char *)$1,NUM);
			codePush(sym->name);
			free($1);
		}
	| REAL
		{
			sym_t *sym;

			sym = symCreateLiteral((char  *)$1,NUM);
			codePush(sym->name);
			free($1);
		}
	| SIGNED_REAL
		{
			sym_t *sym;

			sym = symCreateLiteral((char  *)$1,NUM);
			codePush(sym->name);
			free($1);
		}
	| VAR
		{
			codePush((char *)$1);
			free($1);
		}
	| VAR '[' expr ']'
		{
			codeDeref((char *)$1);
			free($1);
		}
	| '[' 
		{
			curArrayLiteral = (sym_t *)createArrayLiteral();
			curArrayElement = 0;
		}
	  array_elements ']'
		{
			codePush(curArrayLiteral->name);
		}
	| IDENT
		{
			sym_t	*sym;

			sym = symCreateMacroLiteral((char *)$1);
			codePush(sym->name);
			free($1);
		}
	| funct_val



signed_value
	: SIGNED_NUM
		{
			sym_t *sym;

			sym = symCreateLiteral((char *)$1,NUM);
			codePush(sym->name);
			free($1);
		}
	| SIGNED_REAL
		{
			sym_t *sym;

			sym = symCreateLiteral((char  *)$1,NUM);
			codePush(sym->name);
			free($1);
		}



array_elements
	: /* NULL */
	| TEXT 
		{
			symAddArrayElement((char *)$1);
		}
	  array_element_list


array_element_list
	: /* NULL */
	| ',' TEXT
		{
			symAddArrayElement((char *)$2);
		}
	  array_element_list


funct_val
	: IDENT '(' arg_list ')'
		{
			codeCall((char *)$1);
			free($1);
			codePushRet();
		}


cond_op
        : EQ
          {
                $$ = (u_char *) EQ;
          }
        | LT
          {
                $$ = (u_char *) LT;
          }
        | GT
          {
                $$ = (u_char *) GT;
          }
        | GE
          {
                $$ = (u_char *) GE;
          }
        | LE
          {
                $$ = (u_char *) LE;
          }
        | NE
          {
                $$ = (u_char *) NE;
          }
        | RE
          {
                $$ = (u_char *) RE;
          }

