/***************************************************************************
  
  gbc_trans_expr.c
  
  (c) Benoît Minisini <benoit.minisini@gambas-basic.org>

  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, or (at your option)
  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., 51 Franklin Street, Fifth Floor, Boston,
  MA 02110-1301, USA.
  
***************************************************************************/

#define __GBC_TRANS_EXPR_C

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>

#include "gb_common.h"
#include "gb_error.h"
#include "gbc_compile.h"
#include "gbc_info.h"
#include "gbc_trans.h"
#include "gb_code.h"

#define DEBUG 0

//static bool _accept_statement = FALSE;

static bool _must_drop_vargs = FALSE;

static TYPE _type[MAX_EXPR_LEVEL];
static int _type_level = 0;
static TYPE _last_type;

static CLASS_SYMBOL *_last_symbol_used = NULL;
static bool _last_symbol_global = FALSE;

static void trans_expression(bool check_statement);

static TYPE _type_array[18] = { 0 };
static TYPE _type_collection;
static TYPE _type_complex;

#if DEBUG

static void _push_type(TYPE type)
{
	if (_type_level >= MAX_EXPR_LEVEL) // should have been detected by TRANS_tree()
		THROW("Expression too complex");
	
	fprintf(stderr, " [% 2d] push (%d %d)\n", _type_level + 1, type.t.id, type.t.value);
	_type[_type_level++] = type;
}

static TYPE _pop_type(void)
{
	if (_type_level <= 0) // should have been detected by TRANS_tree()
		THROW("Expression too complex");
	
	TYPE type = _type[--_type_level];
	fprintf(stderr, " [% 2d] pop (%d %d)\n", _type_level, type.t.id, type.t.value);
	return type;
}

static void _drop_type(int n)
{
	_type_level -= n;
	fprintf(stderr, " [% 2d] drop %d\n", _type_level, n);
	if (_type_level < 0)
		ERROR_panic("Incorrect type analyze");
}

#define push_type(_type) (fprintf(stderr, "<%s.%d>", __func__, __LINE__), _push_type(_type))
#define push_type_id(_id) (fprintf(stderr, "<%s.%d>", __func__, __LINE__), _push_type(TYPE_make_simple(_id)))
#define pop_type() (fprintf(stderr, "<%s.%d>", __func__, __LINE__), _pop_type())
#define drop_type(_n) (fprintf(stderr, "<%s.%d>", __func__, __LINE__), _drop_type(_n))
#define get_type(_i, _nparam) (fprintf(stderr, "<%s.%d> get_type(%d, %d)\n", __func__, __LINE__, (_i), (_nparam)), (_type[_type_level + (_i) - (_nparam)]))
#define dup_type() (fprintf(stderr, "<%s.%d> dup_type ", __func__, __LINE__), push_type(_type[_type_level - 1]))

#else

static void push_type(TYPE type)
{
	if (_type_level >= MAX_EXPR_LEVEL) // should have been detected by TRANS_tree()
		THROW("Expression too complex");
	
	_type[_type_level++] = type;
}

static void drop_type(int n)
{
	_type_level -= n;
	if (_type_level < 0)
		ERROR_panic("Incorrect type analyze");
}

#define push_type_id(_id) push_type(TYPE_make_simple(_id))
#define pop_type() (_type[--_type_level])
#define get_type(_i, _nparam) (_type[_type_level + (_i) - (_nparam)])
#define dup_type() push_type(_type[_type_level - 1])

#endif

#define get_type_id(_i, _nparam) (get_type(_i, _nparam).t.id)
#define push_type_object(_value) push_type(TYPE_make(T_OBJECT, _value, 0))
#define push_type_class(_value) push_type(TYPE_make(T_CLASS, _value, 0))


static TYPE get_array_type(TYPE_ID type_id)
{
	if (TYPE_is_void(_type_array[type_id]))
		_type_array[type_id] = INFO_get_object(TYPE_array_name[type_id]);
	
	return _type_array[type_id];
}


static short get_nparam(PATTERN *tree, int count, int *pindex)
{
	PATTERN pattern;
	int nparam = 0;
	int index = *pindex;

	if (index < count)
	{
		pattern = tree[index + 1];
		if (PATTERN_is_param(pattern))
		{
			index++;
			nparam = PATTERN_index(pattern);
		}

		while (index < count)
		{
			pattern = tree[index + 1];
			if (!PATTERN_is_param(pattern))
				break;
			index++;
		}

		*pindex = index;
	}

	// Handle the case of a subroutine without parenthesis
	return (short)nparam;
}

static short get_nparam_byref(PATTERN *tree, int count, int *pindex, uint64_t *byref)
{
	PATTERN pattern;
	int nparam = 0;
	int index = *pindex;
	int shift;

	if (index < count)
	{
		pattern = tree[index + 1];
		if (PATTERN_is_param(pattern))
		{
			index++;
			nparam = PATTERN_index(pattern);
		}

		shift = 0;
		*byref = 0;
		while (index < count)
		{
			pattern = tree[index + 1];
			if (!PATTERN_is_param(pattern))
				break;
			index++;
			*byref |= (uint64_t)PATTERN_index(pattern) << shift;
			shift += 16;
		}

		*pindex = index;
	}

	return (short)nparam;
}


static void push_integer(int index)
{
	CODE_push_number(index);
	push_type_id(T_INTEGER);
}


static void push_number(int index)
{
	TRANS_NUMBER number;
	TRANS_DECL decl;

	if (TRANS_get_number(index, &number))
		THROW(E_SYNTAX);

	if (number.type == T_INTEGER)
	{
		CODE_push_number(number.ival);
	}
	else if (number.type == T_FLOAT && COMP_version >= 0x03180000 && number.dval == (double)(int)number.dval && number.dval >= -128 && number.dval <= 127)
	{
		CODE_push_float(number.dval);
	}
	else
	{
		CLEAR(&decl);
		decl.type = TYPE_make_simple(number.type);
		decl.index = NO_SYMBOL;
		decl.value = index;
		if (number.type == T_LONG)
			decl.lvalue = number.lval;
		CODE_push_const(CLASS_add_constant(JOB->class, &decl));
	}

	if (number.complex)
	{
		CODE_push_complex();
		push_type(_type_complex);
	}
	else
		push_type_id(number.type);
}


static void push_string(int index, bool trans)
{
	TRANS_DECL decl;
	SYMBOL *sym;
	int len;

	if (index == VOID_STRING_INDEX)
		len = 0;
	else
	{
		sym = TABLE_get_symbol(JOB->class->string, index);
		len = sym->len;
	}

	if (len == 0)
	{
		CODE_push_void_string();
	}
	else if (len == 1 && !trans)
	{
		CODE_push_char(*(sym->name));
	}
	else
	{
		//CLEAR(&decl);

		if (trans)
			decl.type = TYPE_make_simple(T_CSTRING);
		else
			decl.type = TYPE_make_simple(T_STRING);
		decl.index = NO_SYMBOL;
		decl.value = index;

		CODE_push_const(CLASS_add_constant(JOB->class, &decl));
	}
	
	push_type_id(T_STRING);
}


bool TRANS_string(PATTERN pattern)
{
	if (!PATTERN_is_string(pattern))
		return TRUE;
	push_string(PATTERN_index(pattern), FALSE);
	return FALSE;
}


static void trans_class(int index)
{
	index = TRANS_get_class(index);
	CODE_push_class(index);
	push_type_class(index);
}


static void trans_identifier(int index, bool point, PATTERN next)
{
	CLASS_SYMBOL *sym = CLASS_get_symbol(JOB->class, index);
	bool is_static;
	bool is_func;
	int type;
	CONSTANT *constant;

#if DEBUG
	fprintf(stderr, "trans_identifier: %.*s\n", sym->symbol.len, sym->symbol.name);
#endif
	
	_last_symbol_used = NULL;
	//fprintf(stderr, "_last_symbol_used = NULL\n");
	

	if (!TYPE_is_null(sym->local.type) && !point)
	{
		if (TRANS_in_do_next)
			THROW("Local symbols are not allowed in AFTER or EVERY expressions");
		
		if (TYPE_get_kind(sym->local.type) == TK_LABEL)
		{
			TRANS_LABEL *info = TRANS_get_label_info(sym->local.value);
			if (info->ctrl_id)
				THROW("Label must be top-level");

			CODE_push_number(sym->local.value + 1);
			push_type_id(T_INTEGER);
		}
		else
		{
			if (TYPE_is_static(sym->local.type))
				CODE_push_global(sym->local.value, TRUE, FALSE);
			else
				CODE_push_local_ref(sym->local.value, TYPE_must_ref(sym->local.type));

			push_type(sym->local.type);
		}
		
		sym->local_used = TRUE;
		_last_symbol_used = sym;
		_last_symbol_global = FALSE;
		//fprintf(stderr, "_last_symbol_used = %.*s / local\n", sym->symbol.len, sym->symbol.name);
	}
	else if (!TYPE_is_null(sym->global.type) && !point)
	{
		type = TYPE_get_kind(sym->global.type);

		if (!TYPE_is_public(sym->global.type))
		{
			sym->global_used = TRUE;
			_last_symbol_used = sym;
			_last_symbol_global = TRUE;
			//fprintf(stderr, "_last_symbol_used = %.*s / global\n", sym->symbol.len, sym->symbol.name);
		}
		
		if (type == TK_CONST)
		{
			if (PATTERN_is_point(next))
				goto __CLASS;

			constant = &JOB->class->constant[sym->global.value];
			type = TYPE_get_id(constant->type);
			if (type == T_BOOLEAN)
				CODE_push_boolean(constant->value);
			else if (type == T_INTEGER)
				CODE_push_number(constant->value);
			else if (type == T_FLOAT && constant->is_integer)
			{
				if (COMP_version >= 0x03180000 && constant->value >= -128 && constant->value <= 127)
					CODE_push_float(constant->value);
				else
				{
					CODE_push_number(constant->value);
					type = T_INTEGER;
				}
			}
			else
				CODE_push_const(sym->global.value);
			
			push_type_id(type);
		}
		else if (type == TK_EXTERN)
		{
			if (PATTERN_is_point(next))
				goto __CLASS;

			CODE_push_extern(sym->global.value);
			push_type(TYPE_make(T_EXTFUNC, sym->global.value, 0));
		}
		/* That breaks some code if the property has the same name as a class!
		else if (type == TK_PROPERTY)
		{
			CODE_push_me(FALSE);
			CODE_push_unknown(CLASS_add_unknown(JOB->class, index));
		}*/
		else if (type == TK_EVENT || type == TK_LABEL || type == TK_PROPERTY)
		{
			goto __CLASS;
		}
		else /* TK_FUNCTION || TK_VARIABLE */
		{
			is_static = TYPE_is_static(sym->global.type);
			is_func = type == TK_FUNCTION;

			if (is_func && PATTERN_is_point(next))
				goto __CLASS;

			if (!is_static && TYPE_is_static(JOB->func->type))
				THROW("Dynamic symbols cannot be used in static function");

			CODE_push_global(sym->global.value, is_static, is_func);
				
			if (is_func)
				push_type(TYPE_make(T_FUNCTION, sym->global.value, 0));
			else if (is_static)
				push_type(JOB->class->stat[sym->global.value].type);
			else
				push_type(JOB->class->dyn[sym->global.value].type);
		}
		
		CLASS_add_call(JOB->class, TYPE_make(T_OBJECT, TV_ME, 0), index);
	}
	else
	{
		if (point)
		{
			TYPE ob_type = get_type(0, 1);
			int ind;
			INFO_DESC *idesc;
			
			ind = INFO_check_exist_symbol(ob_type, index);
			idesc = INFO_get_desc(ind);
			
			CLASS_add_call(JOB->class, ob_type, index);
			CODE_push_unknown(CLASS_add_unknown(JOB->class, index));
			
			if (ind < 0)
				push_type_id(T_VARIANT);
			else
			{
				switch (TYPE_get_kind(idesc->type))
				{
					case TK_VARIABLE:
					case TK_PROPERTY:
					case TK_CONST:
						push_type(idesc->type);
						break;
					case TK_FUNCTION:
						push_type(TYPE_make(T_IFUNC, ind, 0));
						break;
					default:
						push_type_id(T_VOID);
				}
			}
		}
		else
			goto __CLASS;
	}

	return;

__CLASS:

	trans_class(index);
}


static void trans_subr(int subr, short nparam)
{
	SUBR_INFO *info = TRANS_find_subr(subr);
	int type;

	if (nparam < info->min_param)
	{
		if (subr == SUBR_Pi)
		{
			CODE_push_float(1);
			push_type_id(T_FLOAT);
			nparam = 1;
		}
		else
			THROW("Not enough arguments to &1()", info->name);
	}
	else if (nparam > info->max_param)
		THROW("Too many arguments to &1()", info->name);

	if (subr == SUBR_VarPtr)
	{
		if (CODE_check_varptr())
			THROW("VarPtr() argument must be a dynamic, a static or a local variable");
	}
	else if (subr == SUBR_IsMissing)
	{
		JOB->func->use_is_missing = TRUE;
		if (CODE_check_ismissing())
			THROW("IsMissing() requires a function argument");
	}

	CODE_subr(info->opcode, nparam, info->optype, info->max_param == info->min_param);
	
	type = info->type;
	switch(type)
	{
		case RST_SAME:
			
			type = get_type_id(0, nparam);
			break;
			
		case RST_ABS:
			
			type = get_type_id(0, nparam);
			if (type >= T_OBJECT)
				type = T_FLOAT;
			break;
			
		case RST_BCLR:
			
			type = get_type_id(0, nparam);
			break;
		
		case RST_MIN:
			
			type = Max(get_type_id(0, nparam), get_type_id(1, nparam));
			if (type > T_DATE && type != T_VARIANT)
				THROW("Number or Date expected");
			break;
		
		case RST_CLAMP:
			
			type = Max(get_type_id(0, nparam), get_type_id(1, nparam));
			type = Max(type, get_type_id(2, nparam));
			if (type > T_DATE && type != T_VARIANT)
				THROW("Number or Date expected");
			break;
			
		case RST_STRINGARRAY:
		case RST_COLLECTION:
			
			break;
			
		default:
			
			if (type > T_OBJECT)
				ERROR_panic("Subroutine type analysis not implemented.");
	}
	
	if (nparam)
		drop_type(nparam);
	
	if (type == RST_STRINGARRAY)
		push_type(get_array_type(T_STRING));
	else if (type == RST_COLLECTION)
		push_type(_type_collection);
	else
		push_type_id(type);
}


static void trans_operation(short op, short nparam, PATTERN previous)
{
	COMP_INFO *info = &COMP_res_info[op];
	int type = info->type;
	int type1, type2;
	TYPE ftype;
	int i;
	int ind;
	INFO_DESC *idesc;

	switch(type)
	{
		case RST_SAME:
			
			ftype = get_type(0, nparam);
			break;

		case RST_ADD:
			
			if (nparam == 1) // negation operator
				ftype = get_type(0, nparam);
			else
			{
				type = Max(get_type_id(0, nparam), get_type_id(1, nparam));
				if (type == T_DATE)
					ftype = TYPE_make_simple(T_FLOAT);
				else
					ftype = TYPE_make_simple(type);
			}
			break;

		case RST_AND:
			
			type1 = get_type_id(0, nparam);
			type2 = get_type_id(1, nparam);

			if (type1 != T_VARIANT && type2 != T_VARIANT)
			{
				if ((type1 > T_BOOLEAN && type1 <= T_LONG) ^ (type2 > T_BOOLEAN && type2 <= T_LONG))
					COMPILE_print(MSG_WARNING, JOB->line, "integer and boolean mixed with `&1' operator", info->name);
			}

			type = Max(type1, type2);

			if (type > T_LONG)
			{
				if (type != T_VARIANT)
					type = T_BOOLEAN;
			}

			ftype = TYPE_make_simple(type);
			break;

		case RST_NOT:
			
			type = get_type_id(0, nparam);
			if (type == T_STRING || type == T_OBJECT || type == T_DATE)
				type = T_BOOLEAN;
			ftype = TYPE_make_simple(type);
			break;

		case RST_MOD:
			
			type1 = get_type_id(0, nparam);
			type2 = get_type_id(1, nparam);

			if (type1 != T_VARIANT && type2 != T_VARIANT)
			{
				if (type1 <= T_BOOLEAN || type1 > T_FLOAT || type2 <= T_BOOLEAN || type2 > T_FLOAT)
					THROW("Type mismatch");
			}

			ftype = TYPE_make_simple(Min(T_LONG, Max(type1, type2)));
			break;

		case RST_DIV:
			
			type = Max(get_type_id(0, nparam), get_type_id(1, nparam));
			if (type != T_SINGLE)
			{
				if (type <= T_FLOAT)
					type = T_FLOAT;
				else
					type = T_VARIANT;
			}
			ftype = TYPE_make_simple(type);
			break;

		case RST_FLEX:
			
			type = Max(get_type_id(0, nparam), get_type_id(1, nparam));
			if (type <= T_FLOAT)
				type = T_FLOAT;
			else
				type = T_VARIANT;
			ftype = TYPE_make_simple(type);
			break;

		case RST_GET:
		
			ftype = get_type(0, nparam);
			ind = INFO_check_exist_symbol(ftype, INFO_GET);
			idesc = INFO_get_desc(ind);

			if (idesc)
				ftype = idesc->type;
			else
				ftype = TYPE_make_simple(T_VARIANT);
		
			break;

		case RST_BCLR:
			
			ftype = get_type(0, nparam);
			break;

		case RST_LIKE:
			
			type1 = get_type_id(0, nparam);
			type2 = get_type_id(1, nparam);
			
			if (type1 == T_BOOLEAN)
				COMPILE_print(MSG_WARNING, JOB->line, "the left expression of `&1' should not be a boolean", info->name);
		
			if (type2 == T_BOOLEAN)
				COMPILE_print(MSG_WARNING, JOB->line, "the right expression of `&1' should not be a boolean", info->name);
			
			ftype = TYPE_make_simple(T_BOOLEAN);
			break;
		
		default:
			
			if (type > T_OBJECT)
				ERROR_panic("Operator type analysis not implemented.");
		
			ftype = TYPE_make_simple(type);
	}

	switch (info->value)
	{
		case OP_PT:
			if (nparam == 0)
			{
				ftype = TRANS_use_with();
			}
			else if (!PATTERN_is_identifier(previous))
				THROW(E_SYNTAX);
			else
			{
				switch(get_type_id(0, nparam))
				{
					case T_OBJECT:
					case T_CLASS:
					case T_VARIANT:
					case T_STRUCT:
					case T_STRING:
					case T_ARRAY:
					case T_IOBJECT:
						break;
						
					default:
						THROW("Not an object");
				}
				ftype = get_type(1, nparam);
			}

			break;

		case OP_EXCL:
			if (!PATTERN_is_identifier(previous))
				THROW(E_SYNTAX);
			break;

		case OP_LSQR:
			CODE_push_array(nparam);
			CLASS_add_call(JOB->class, get_type(0, nparam), CC_GET);
			break;

		case OP_RSQR:
			TRANS_subr(TS_SUBR_ARRAY, nparam);
			if (nparam > MAX_PARAM_OP)
			{
				nparam--;
				ftype = get_array_type(T_VARIANT);
			}
			else if (nparam > 0)
			{
				TYPE_ID type_id = get_type_id(0, nparam);
				
				for (i = 1; i < nparam; i++)
				{
					if (get_type_id(i, nparam) != type_id)
					{
						type_id = T_VARIANT;
						break;
					}
				}
				
				ftype = get_array_type(type_id);
			}
			else
				ftype = get_array_type(T_VARIANT);
			 
			break;

		case OP_COLON:
			TRANS_subr(TS_SUBR_COLLECTION, nparam);
			if (nparam > MAX_PARAM_OP)
				nparam -= 2;
			ftype = _type_collection;
			break;

		case OP_MINUS:
			if (nparam == 1)
				CODE_op(C_NEG, 0, nparam, TRUE);
			else
				CODE_add_sub(info->code, info->subcode, nparam, type);
			break;
			
		case OP_PLUS:
			CODE_add_sub(info->code, info->subcode, nparam, type);
			break;

		case OP_AMP:
			if (nparam == 1)
				CODE_op(info->code, 1, 2, TRUE);
			else
				CODE_op(info->code, info->subcode, nparam, FALSE);
			break;

		default:
			CODE_op(info->code, info->subcode, nparam, (info->flag != RSF_OPN));
	}
	
	if (nparam)
		drop_type(nparam);
	
	push_type(ftype);
}


static void trans_call(short nparam, uint64_t byref)
{
	TYPE type;
	const char *name;
	int ind;
	
	if (!byref)
	{
		CODE_call(nparam);
		if (_must_drop_vargs)
		{
			CODE_end_vargs();
			_must_drop_vargs = FALSE;
		}
	}
	else
	{
		CODE_call_byref(nparam, byref);
		if (_must_drop_vargs)
		{
			CODE_drop_vargs();
			_must_drop_vargs = FALSE;
		}
	}
	
	if (nparam)
		drop_type(nparam);
	
	type = pop_type();
	
	if (TYPE_get_id(type) == T_OBJECT || TYPE_get_id(type) == T_IOBJECT || TYPE_get_id(type) == T_CLASS)
	{
		ind = INFO_check_exist_symbol(type, INFO_CALL);
		
		if (ind == NO_SYMBOL)
		{
			push_type_id(T_VARIANT);
			return;
		}
		else if (ind == INFO_CONVERT)
		{
			if (nparam < 1)
			{
				name = TABLE_get_symbol_name(JOB->class->table, JOB->class->class[TYPE_get_value(type)].index);
				goto NOT_ENOUGH;
			}
			else if (nparam > 1)
			{
				name = TABLE_get_symbol_name(JOB->class->table, JOB->class->class[TYPE_get_value(type)].index);
				goto TOO_MANY;
			}
			push_type(type);
			return;
		}
		
		type = TYPE_make(T_IFUNC, ind, 0);
	}
	
	switch (TYPE_get_id(type))
	{
		case T_IFUNC:
		{
			INFO_DESC *idesc = INFO_get_desc(TYPE_get_value(type));
			
			if (idesc)
			{
				if (nparam < idesc->npmin)
				{
					fprintf(stderr, "%d / %d\n", idesc->npmin, idesc->npmax);
					name = INFO_get_name_from_desc(idesc);
					goto NOT_ENOUGH;
				}
				else if (nparam > idesc->npmax)
				{
					fprintf(stderr, "%d / %d\n", idesc->npmin, idesc->npmax);
					name = INFO_get_name_from_desc(idesc);
					goto TOO_MANY;
				}
					
				push_type(idesc->type);
			}
			else
				push_type_id(T_VARIANT);
			
			break;
		}
	
		case T_FUNCTION:
		{
			FUNCTION *func = &JOB->class->function[TYPE_get_value(type)];
			
			if (nparam < func->npmin)
			{
				name = TABLE_get_symbol_name(JOB->class->table, func->name);
				goto NOT_ENOUGH;
			}
			else if (nparam > func->nparam && !func->vararg)
			{
				name = TABLE_get_symbol_name(JOB->class->table, func->name);
				goto TOO_MANY;
			}
			
			push_type(func->type);
			break;
		}
		
		case T_EXTFUNC:
		{
			EXTFUNC *func = &JOB->class->ext_func[TYPE_get_value(type)];
			
			if (nparam < func->nparam)
			{
				name = TABLE_get_symbol_name(JOB->class->table, func->name);
				goto NOT_ENOUGH;
			}
			else if (nparam > func->nparam && !func->vararg)
			{
				name = TABLE_get_symbol_name(JOB->class->table, func->name);
				goto TOO_MANY;
			}
			
			push_type(func->type);
			break;
		}
		
		case T_VARIANT:
			
			push_type_id(T_VARIANT);
			break;
		
		default:
			
			if (COMP_verbose)
				fprintf(stderr, "**** Unhandled function call: {%d %d}\n", TYPE_get_id(type), TYPE_get_value(type));
			push_type_id(T_VARIANT);
	}
	
	return;
	
NOT_ENOUGH:
	THROW("Not enough arguments to &1()", name);
	
TOO_MANY:
	THROW("Too many arguments to &1()", name);
}

static void trans_expr_from_tree(TRANS_TREE *tree, int count)
{
	static void *jump[] = {
		&&__CONTINUE, &&__CONTINUE, &&__RESERVED, &&__IDENTIFIER, &&__INTEGER, &&__NUMBER, &&__STRING, &&__TSTRING, &&__CONTINUE, &&__SUBR, &&__CLASS, &&__CONTINUE, &&__CONTINUE
	};
	
	int i, op;
	short nparam;
	PATTERN pattern, next_pattern, prev_pattern;
	uint64_t byref = 0;

	pattern = NULL_PATTERN;

	#if DEBUG
	fprintf(stderr, "-------------------------------------------- %d\n", _type_level);
	#endif

	i = 0;
	
	for (i = 0; i < count; i++)
	{
		TRANS_tree_index = i;
		prev_pattern = pattern;
		pattern = tree[i];
		next_pattern = tree[i + 1];

		goto *jump[PATTERN_type(pattern)];
		
	__INTEGER:
		
		push_integer(PATTERN_signed_index(pattern));
		continue;
		
	__NUMBER:
		
		push_number(PATTERN_index(pattern));
		continue;
		
	__STRING:

		push_string(PATTERN_index(pattern), FALSE);
		continue;

	__TSTRING:

		push_string(PATTERN_index(pattern), TRUE);
		continue;

	__IDENTIFIER:
	
		trans_identifier(PATTERN_index(pattern), PATTERN_is_point(pattern), next_pattern);
		continue;

	__CLASS:
	
		trans_class(PATTERN_index(pattern));
		continue;

	__SUBR:
	
		nparam = get_nparam(tree, count, &i);
		trans_subr(PATTERN_index(pattern), nparam);
		continue;
		
	__RESERVED:

		if (PATTERN_is(pattern, RS_TRUE))
		{
			CODE_push_boolean(TRUE);
			push_type_id(T_BOOLEAN);
		}
		else if (PATTERN_is(pattern, RS_FALSE))
		{
			CODE_push_boolean(FALSE);
			push_type_id(T_BOOLEAN);
		}
		else if (PATTERN_is(pattern, RS_NULL))
		{
			CODE_push_null();
			push_type_id(T_NULL);
		}
		else if (PATTERN_is(pattern, RS_ME))
		{
			/*if (FUNCTION_is_static(JOB->func))
				THROW("ME cannot be used in a static function");*/

			CODE_push_me(FALSE);
			push_type_object(TV_ME);
		}
		else if (PATTERN_is(pattern, RS_SUPER))
		{
			/*if (FUNCTION_is_static(JOB->func))
				THROW("ME cannot be used in a static function");*/

			CODE_push_super(FALSE);
			push_type_object(TV_SUPER);
		}
		else if (PATTERN_is(pattern, RS_LAST))
		{
			CODE_push_last();
			push_type_id(T_OBJECT);
		}
		else if (PATTERN_is(pattern, RS_AT))
		{
			if (TRANS_popify_last(FALSE))
				THROW("This expression cannot be passed by reference");
		}
		else if (PATTERN_is(pattern, RS_COMMA))
		{
			CODE_drop();
			drop_type(1);
		}
		else if (PATTERN_is(pattern, RS_ERROR))
		{
			TRANS_subr(TS_SUBR_ERROR, 0);
			push_type_id(T_BOOLEAN);
		}
		else if (PATTERN_is(pattern, RS_OPTIONAL))
		{
			CODE_push_void();
			push_type_id(T_VOID);
		}
		else if (PATTERN_is(pattern, RS_PINF))
		{
			CODE_push_inf(FALSE);
			push_type_id(T_FLOAT);
		}
		else if (PATTERN_is(pattern, RS_MINF))
		{
			CODE_push_inf(TRUE);
			push_type_id(T_FLOAT);
		}
		else if (PATTERN_is(pattern, RS_3PTS))
		{
			_must_drop_vargs = TRUE;
			CODE_push_vargs();
			// push_type_id(T_FLOAT); we push no type
		}
		else
		{
			op = PATTERN_index(pattern);
			if (op == RS_LBRA)
			{
				nparam = get_nparam_byref(tree, count, &i, &byref);
				trans_call(nparam, byref);
			}
			else
			{
				nparam = get_nparam(tree, count, &i);
				trans_operation((short)op, nparam, prev_pattern);
			}
		}
		
	__CONTINUE:
		;
	}
	
	TRANS_tree_index = -1;
	_last_type = pop_type();
}


void TRANS_new(void)
{
	int index;
	int i, nparam;
	bool array = FALSE;
	bool event = FALSE;
	//bool collection = FALSE;
	bool check_param = FALSE;

	nparam = 0;

	if (PATTERN_is_class(*JOB->current))
	{
		index = TRANS_get_class(PATTERN_index(*JOB->current));
		//CLASS_add_class(JOB->class, PATTERN_index(*JOB->current));
		if (PATTERN_is(JOB->current[1], RS_LSQR))
			index = CLASS_get_array_class(JOB->class, T_OBJECT, index);

		CODE_push_class(index);
		nparam = 1;
		
		CLASS_add_call(JOB->class, TYPE_make(T_OBJECT, index, 0), CC_NEW);
	}
	else if (PATTERN_is_type(*JOB->current))
	{
		if (!PATTERN_is(JOB->current[1], RS_LSQR))
			THROW("Cannot instantiate native types");

		//CODE_push_number(RES_get_type(PATTERN_index(*JOB->current)));
		index = CLASS_get_array_class(JOB->class, RES_get_type(PATTERN_index(*JOB->current)), -1);
		CODE_push_class(index);
		nparam = 1;

		CLASS_add_call(JOB->class, TYPE_make(T_OBJECT, index, 0), CC_NEW);
	}
	else if (PATTERN_is(*JOB->current, RS_LBRA))
	{
		/* NEW ("Class", ...) */
		nparam = 0;
		JOB->current--;
		check_param = TRUE;
	}
	else
		THROW(E_SYNTAX);

	JOB->current++;

	if (TRANS_is(RS_LSQR))
	{
		//if (collection)
		//	THROW("Array declaration is forbidden with typed collection");

		if (!PATTERN_is(*JOB->current, RS_RSQR))
		{
			for (i = 0;; i++)
			{
				if (i > MAX_ARRAY_DIM)
					THROW("Too many dimensions");

				TRANS_expression(FALSE);
				nparam++;

				if (PATTERN_is(*JOB->current, RS_RSQR))
					break;

				if (!PATTERN_is(*JOB->current, RS_COMMA))
					THROW(E_MISSING, "']'");

				JOB->current++;
			}
		}

		JOB->current++;
		array = TRUE;
	}
	else
	{
		if (TRANS_is(RS_LBRA))
		{
			for(;;)
			{
				if (nparam > MAX_PARAM_FUNC)
					THROW("Too many arguments");

				if (PATTERN_is(*JOB->current, RS_AT) || PATTERN_is(*JOB->current, RS_BYREF))
					THROW("NEW cannot have arguments passed by reference");

				TRANS_expression(FALSE);
				nparam++;

				if (PATTERN_is(*JOB->current, RS_RBRA))
					break;

				if (!PATTERN_is(*JOB->current, RS_COMMA))
					THROW(E_MISSING, "')'");

				JOB->current++;
			}

			JOB->current++;

			if (check_param && nparam == 0)
				THROW("Not enough argument to New()");
		}

		if (TRANS_is(RS_AS))
		{
			TRANS_expression(FALSE);
			nparam++;
			event = TRUE;
		}

		/*
		CODE_call(nparam, FALSE);
		CODE_drop();
		*/
	}

	//if (collection)
	//	CODE_new(nparam, TRUE, event);
	//else
	CODE_new(nparam, array, event);
}


static void trans_expression(bool check_statement)
{
	TRANS_TREE *tree;
	int tree_length;

	if (!check_statement)
	{
		if (TRANS_is(RS_NEW))
		{
			TRANS_new();
			return;
		}
		else if (TRANS_is(RS_READ))
		{
			TRANS_read();
			return;
		}
		else if (TRANS_is(RS_PEEK))
		{
			TRANS_peek();
			return;
		}
	}

	TRANS_tree(check_statement, &tree, &tree_length);

	JOB->step = JOB_STEP_TREE;
	trans_expr_from_tree(tree, tree_length);
	JOB->step = JOB_STEP_CODE;

	//FREE(&tree);
	
	if (check_statement)
	{
		/*
		if (!CODE_check_statement_last())
			THROW("This expression cannot be a statement");
		*/

		CODE_drop();
	}
}


void TRANS_ignore_expression()
{
	TRANS_tree(FALSE, NULL, NULL);
}


TYPE TRANS_variable_get_type()
{
	TYPE type = TYPE_make_simple(T_VOID);
	TRANS_TREE *tree;
	int count;
	int index;
	CLASS_SYMBOL *sym;

	TRANS_tree(FALSE, &tree, &count);

	if (count == 1 && PATTERN_is_identifier(*tree))
	{
		index = PATTERN_index(*tree);
		sym = CLASS_get_symbol(JOB->class, index);

		if (!TYPE_is_null(sym->local.type))
		{
			return sym->local.type;
		}
		else if (!TYPE_is_null(sym->global.type))
		{
			int type = TYPE_get_kind(sym->global.type);
			if (type == TK_VARIABLE)
				return sym->global.type;
		}
	}

	//FREE(&tree);

	return type;
}


bool TRANS_popify_last(bool no_conv)
{
	if (!CODE_popify_last(no_conv))
		return TRUE;
	
	if (_last_symbol_used)
	{
		if (_last_symbol_global)
			_last_symbol_used->global_assigned = TRUE;
		else
			_last_symbol_used->local_assigned = TRUE;
	}
	
	return FALSE;
}


void TRANS_reference(void)
{
	TRANS_expression(FALSE);
	if (TRANS_popify_last(FALSE))
		THROW("Invalid assignment");
}


void TRANS_reference_type(TYPE type)
{
	TRANS_expression(FALSE);
	if (TRANS_popify_last(TYPE_get_id(_last_type) == TYPE_get_id(type)))
		THROW("Invalid assignment");
}


bool TRANS_affectation(bool dup)
{
	static TRANS_STATEMENT statement[] = {
		//{ RS_NEW, TRANS_new },
		{ RS_OPEN, TRANS_open },
		{ RS_CLOSE, TRANS_close },
		{ RS_SHELL, TRANS_shell },
		{ RS_EXEC, TRANS_exec },
		{ RS_RAISE, TRANS_raise },
		{ RS_PIPE, TRANS_pipe },
		{ RS_LOCK, TRANS_lock },
		{ RS_MEMORY, TRANS_memory },
		//{ RS_READ, TRANS_read },
		{ RS_NONE, NULL }
	};

	TRANS_STATEMENT *st;
	PATTERN *look = JOB->current;
	PATTERN *left, *expr, *after;
	int niv = 0;
	bool equal = FALSE;
	bool stat = FALSE;
	int op = RS_NONE;
	int id = RS_NONE;
	
	for(;;)
	{
		if (PATTERN_is_newline(*look)) // || PATTERN_is_end(*look))
			break;

		if (PATTERN_is(*look, RS_LBRA) || PATTERN_is(*look, RS_LSQR))
		{
			niv++;
		}
		else if (PATTERN_is(*look, RS_RBRA) || PATTERN_is(*look, RS_RSQR))
		{
			if (niv > 0)
				niv--;
		}
		else if (niv == 0)
		{
			if (PATTERN_is(*look, RS_EQUAL))
			{
				equal = TRUE;
				op = RS_NONE;
				break;
			}
			else if (PATTERN_is_reserved(*look) && RES_is_assignment(PATTERN_index(*look)))
			{
				equal = TRUE;
				op = RES_get_assignment_operator(PATTERN_index(*look));
				break;
			}
		}

		look++;
	}

	if (!equal || look == JOB->current)
		return FALSE;

	left = JOB->current;
	*look++ = PATTERN_make(RT_NEWLINE, 0);
	expr = look;

	JOB->current = expr;

	if (op == RS_NONE && (PATTERN_is_reserved(*JOB->current)))
	{
		if (TRANS_is(RS_NEW))
		{
			TRANS_in_assignment++;
			TRANS_new();
			TRANS_in_assignment--;
			id = RS_NEW;
			stat = TRUE;
		}
		else
		{
			for (st = statement; st->id; st++)
			{
				if (TRANS_is(st->id))
				{
					id = st->id;
					TRANS_in_assignment++;
					(*st->func)();
					TRANS_in_assignment--;
					stat = TRUE;
					break;
				}
			}
		}
	}

	if (!stat)
	{
		TYPE type;
		
		if (op != RS_NONE)
		{
			JOB->current = left;
			trans_expression(FALSE);
			type = _last_type;
		}

		JOB->current = expr;
		trans_expression(FALSE);
		after = JOB->current;

		if (op != RS_NONE)
		{
			push_type(_last_type);
			push_type(type);
			/*if (op == RS_AMP && COMP_version >= 0x03150000)
				trans_operation(op, 1, NULL_PATTERN);
			else*/
				trans_operation(op, 2, NULL_PATTERN);
			_last_type = pop_type();
		}
	}

	after = JOB->current;

	if (dup)
		CODE_dup();

	if (COMP_version >= 0x03070000)
	{
		if (id == RS_EXEC || id == RS_SHELL)
			CODE_dup();
	}

	JOB->current = left;
	TRANS_reference_type(_last_type);

	if (!PATTERN_is_newline(*JOB->current))
		THROW(E_SYNTAX);

	if (COMP_version >= 0x03150000 && op == RS_AMP)
		CODE_check_fast_cat();
	
	JOB->current = after;

	if (COMP_version >= 0x03070000)
	{
		if (id == RS_EXEC || id == RS_SHELL)
		{
			TRANS_subr(TS_SUBR_CHECK_EXEC, 1);
			CODE_drop();
		}
	}

	return TRUE;
}


void TRANS_expression(bool check_statement)
{
	_type_level = 0;
	trans_expression(check_statement);
}


TYPE TRANS_get_last_type(void)
{
	return _last_type;
}

void TRANS_init(void)
{
	_last_symbol_used = NULL;
	_last_symbol_global = FALSE;
	
	_type_collection = INFO_get_object("Collection");
	_type_complex = INFO_get_object("Complex");
}
