#include <../source/TAL/objbase.h>

/*
 * Implementation of NSL ver. 0.01.
 * NSL stands for Nano Scripting Language. And - surprisingly - it
 * works (used in "ipl").
 *
 * BUGBUG: got some problems compiling with ICL 8.
 *
 * This code proved to be usefull (though not to compare with lexer)
 * and, thus, are subject for later development.
 */

#ifndef	STR_COMP
#define STR_COMP		strcasecmp
#endif

#ifndef	STR_EQ
#define STR_EQ(s1,s2)   (!STR_COMP(s1,s2))
#endif

/*
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *							NSL Internal definitions
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#define EXIT_CODE_NAME	  "%" /* mimic Maple syntax */

STATIC_STRING(nsl_open_bracket,"(")
STATIC_STRING(nsl_close_bracket,")")

STATIC_STRING(nsl_open_brace,"{")
STATIC_STRING(nsl_close_brace,"}")

STATIC_STRING(nsl_comma,",")
STATIC_STRING(nsl_semicolon,";")
STATIC_STRING(nsl_equal,"=")
STATIC_STRING(nsl_key_var,"var")
STATIC_STRING(nsl_key_return,"return")
STATIC_STRING(nsl_key_if,"if")
STATIC_STRING(nsl_key_else,"else")

DEBUG_MESSAGE(nsl_end_of_expression,"end of expression")
DEBUG_MESSAGE(nsl_unordered_variable,"unordered variable")
DEBUG_MESSAGE(nsl_unused_operands,"unused operand(s)")
DEBUG_MESSAGE(nsl_empty_expression,"empty_expression")
DEBUG_MESSAGE(nsl_unknown_statement,"unknown statement")
DEBUG_MESSAGE(nsl_identifier,"identifier")
DEBUG_MESSAGE(nsl_duplicate_definition,"duplicate definition")
DEBUG_MESSAGE(nsl_variable_delimiter,"comma or semicolon")
DEBUG_MESSAGE(nsl_not_parsing,"parsing already finished")
DEBUG_MESSAGE(nsl_no_function,"no such function")
DEBUG_MESSAGE(nsl_still_parsing,"parsing still in progress")
DEBUG_MESSAGE(nsl_expression,"(RPN expression)|constant|variable")

/*
 * Node of expression tree
 */
struct nsl_node_s;

#define	NSL_NODE_CONSTANT	1
#define	NSL_NODE_VARIABLE	2
#define	NSL_NODE_OPERATION	3

typedef vec_t (V_CALL *nsl_op_t)(struct nsl_node_s *self,vec_t *vars);

typedef struct nsl_node_s
{
	u32_t		node_type; /* used at parsing and optimization stages */
	union
	{
		vec_t		constant;
		u32_t		offset;   /* offset in var_pool */
	} data;
	nsl_op_t	operation;
	struct	  nsl_node_s *left, *right;
}   nsl_node_t, *nsl_node_p;

#define NSL_EVAL(root,var_pool)	((root)->operation(root,var_pool))

typedef struct nsl_op_entry_s
{
	char		*name;
	nsl_op_t	 operation;
	char		*desc;
}   nsl_op_entry_t, *nsl_op_entry_p;

#define	NSL_ADD_IDENTIFIER(ids,name)	vpsPush((ids),strDup(name))

typedef struct nsl_st_s
{
	/*
	 * Execution of the compound statement continues while
	 * st->operate() routines return 'true'. Currently,
	 * only the 'return' operator returns 'false'.
	 */
	vboolean	(*operate)(struct nsl_st_s *self,vec_p variables);
	void		(*destroy)(struct nsl_st_s *self);
}   nsl_st_t, *nsl_st_p;

#define NSL_KEYWORD		 1   /* any operator except assignment */
#define NSL_VARIABLE		2   /* assignment */

typedef struct nsl_st_start_id_s
{
	u32_t   token_type;
	char_p	*token_value_ptr;
}   nsl_st_start_id_t, *nsl_st_start_id_p;

struct nsl_st_parse_entry_s;

typedef struct nsl_st_parse_context_s
{
	vhandle_t lexer;
	struct nsl_st_parse_entry_s *statements;
	vp_stack_p ids;
	nsl_const_entry_p constants;
	nsl_st_p st_ret;
	nsl_unresolved_cb unresolved;
	void_p unresolved_arg;
}	nsl_st_parse_context_t, *nsl_st_parse_context_p;

typedef struct nsl_st_parse_entry_s
{
	vboolean (*parse)(nsl_st_parse_context_p ctx);
	nsl_st_start_id_t start_id;
	char *desc;
}   nsl_st_parse_entry_t, *nsl_st_parse_entry_p;

/* -------------------------------------------------------------------- */
/*							  Identifiers							 */
/* -------------------------------------------------------------------- */

V_LOCAL vboolean
nslFindConstant(nsl_const_entry_p consts,
	char *name,vec_t *const_ret)
{
	while(consts->name)
	{
		if(STR_EQ(name,consts->name))
		{
			if(const_ret)
				*const_ret = consts->value;
			return true;
		}
		consts++;
	}
	return false;
}

V_LOCAL	vboolean
nslAppendIdentifiers(nsl_var_entry_p vars,vp_stack_p ids)
{
	CALLER_NAME(nslAppendIdentifiers)
	void *id;
	u32_t idx, start_len = vpsCount(ids);
	idx = start_len;
	while(vars->name)
	{
		/* all variables must be ordered */
		if(idx != vars->offset)
		{
			ERROR_GENERIC(nsl_unordered_variable);
			while(vpsCount(ids) > start_len)
			{
				vpsPop(ids,&id);
				memFree(id);
			}
			return false;
		}
		NSL_ADD_IDENTIFIER(ids,vars->name);
		vars++;
		idx++;
	}
	return true;
}

V_LOCAL vboolean
nslFindIdentifier(char *name,vp_stack_p ids,u32_t *idx_ret)
{
	char *ptr;
	u32_t i, len;
	len = vpsCount(ids);
	for(i = 0;i < len;i++)
	{
		ptr = (char*)vpsGet(ids,i);
		if(STR_EQ(name,ptr))
		{
			if(idx_ret)
				*idx_ret = i;
			return true;
		}
	}
	return false;
}

V_LOCAL void
nslFreeIdentifiers(vp_stack_p ids)
{
	void *ptr;
	while(vpsPop(ids,&ptr))
		if(ptr)
			memFree(ptr);
	vpsClear(ids);
}

/*
 * Access to constants and variables
 */
#include <../source/PAL/argsused.h>
V_LOCAL vec_t V_CALL
nsl_op_const(nsl_node_p root,vec_p vars)
{
	return root->data.constant;
}

V_LOCAL vec_t V_CALL
nsl_op_var_access(nsl_node_p root,vec_p vars)
{
	return vars[root->data.offset];
}

/* -------------------------------------------------------------------- */
/*							  Operations							  */
/* -------------------------------------------------------------------- */

/*
 * Unary operations
 */
V_LOCAL vec_t V_CALL
nsl_op_neg(nsl_node_p root,vec_p vars)
{
	return -NSL_EVAL(root->left,vars);
}

V_LOCAL vec_t V_CALL
nsl_op_abs(nsl_node_p root,vec_p vars)
{
	return v_abs(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_ceil(nsl_node_p root,vec_p vars)
{
	return (vec_t)ceil(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_floor(nsl_node_p root,vec_p vars)
{
	return (vec_t)floor(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_sqr(nsl_node_p root,vec_p vars)
{
	vec_t square_root = (NSL_EVAL(root->left,vars));
	return square_root * square_root;
}

V_LOCAL vec_t V_CALL
nsl_op_sqrt(nsl_node_p root,vec_p vars)
{
	vec_t square = (NSL_EVAL(root->left,vars));
	if(square < 0)
		return 0;
	return (vec_t)sqrt(square);
}

/*
 * Trigonometric functions
 */

V_LOCAL vec_t V_CALL
nsl_op_sin(nsl_node_p root,vec_p vars)
{
	return (vec_t)sin(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_csc(nsl_node_p root,vec_p vars)
{
	return (vec_t)1.0 / (vec_t)sin(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_cos(nsl_node_p root,vec_p vars)
{
	return (vec_t)cos(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_sec(nsl_node_p root,vec_p vars)
{
	return (vec_t)1.0 / (vec_t)cos(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_tg(nsl_node_p root,vec_p vars)
{
	return (vec_t)tan(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_ctg(nsl_node_p root,vec_p vars)
{
	return (vec_t)1.0 / (vec_t)tan(NSL_EVAL(root->left,vars));
}

/*
 * Inverse trigonometric functions
 */
V_LOCAL vec_t V_CALL
nsl_op_arcsin(nsl_node_p root,vec_p vars)
{
	return (vec_t)asin(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_arccsc(nsl_node_p root,vec_p vars)
{
	vec_t cosecant = NSL_EVAL(root->left,vars);
	if(cosecant >= 0 && cosecant <= 1) {
		return M_PI2;
	} else if(cosecant < 0 && cosecant >= -1) {
		return -M_PI2;
	}
	return (vec_t)asin((vec_t)1.0 / cosecant);
}

V_LOCAL vec_t V_CALL
nsl_op_arccos(nsl_node_p root,vec_p vars)
{
	return (vec_t)acos(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_arcsec(nsl_node_p root,vec_p vars)
{
	vec_t secant = NSL_EVAL(root->left,vars);
	if(secant >= 0 && secant <= 1) {
		return 0;
	} else if(secant < 0 && secant >= -1) {
		return M_PI;
	}
	return (vec_t)acos((vec_t)1.0 / secant);
}

V_LOCAL vec_t V_CALL
nsl_op_arctg(nsl_node_p root,vec_p vars)
{
	return (vec_t)atan(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_arcctg(nsl_node_p root,vec_p vars)
{
	vec_t cotangent = NSL_EVAL(root->left,vars);
	if(IsZero(cotangent))
		return M_PI2; 
	return (vec_t)atan((vec_t)1.0 / cotangent);
}

/*
 * Hyperbolic functions
 */

int count = 0;

V_LOCAL vec_t V_CALL
nsl_op_exp(nsl_node_p root,vec_p vars)
{
	vec_t res;
	res = (vec_t)exp(NSL_EVAL(root->left,vars));
	return res;
}

V_LOCAL vec_t V_CALL
nsl_op_sh(nsl_node_p root,vec_p vars)
{
	return (vec_t)sinh(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_ch(nsl_node_p root,vec_p vars)
{
	return (vec_t)cosh(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_th(nsl_node_p root,vec_p vars)
{
	/*
	vec_t expx = (vec_t)exp(NSL_EVAL(root->left,vars));
	vec_t rexpx = (vec_t)1.0 / exp_x;
	return (expx - rexpx) / (expx + rexpx);
	*/
	return (vec_t)tanh(NSL_EVAL(root->left,vars));
}

/*
 * Inverse hyperbolic functions
 */

V_LOCAL vec_t V_CALL
nsl_op_ln(nsl_node_p root,vec_p vars)
{
	return (vec_t)log(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_lg(nsl_node_p root,vec_p vars)
{
	/*
#define	RLN_10	(vec_t)(0.43429448190325182765112891891661)
	return RLN_10 * (vec_t)log(NSL_EVAL(root->left,vars));
	*/
	return (vec_t)log10(NSL_EVAL(root->left,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_log2(nsl_node_p root,vec_p vars)
{
#ifdef	__WATCOMC__
	return (vec_t)log2(NSL_EVAL(root->left,vars));
#else
#define	RLN_2	(vec_t)(1.4426950408889634073599246810019)
	return RLN_2 * (vec_t)log(NSL_EVAL(root->left,vars));
#undef	RLN_2
#endif
}

V_LOCAL vec_t V_CALL
nsl_op_arsh(nsl_node_p root,vec_p vars)
{
#ifdef	__WATCOMC__
	return (vec_t)asinh(NSL_EVAL(root->left,vars));
#else
	double hsin = (double)NSL_EVAL(root->left,vars);
	return (vec_t)log(hsin + sqrt(hsin * hsin + 1));
#endif
}

V_LOCAL vec_t V_CALL
nsl_op_arch(nsl_node_p root,vec_p vars)
{
#ifdef	__WATCOMC__
	return (vec_t)acosh(NSL_EVAL(root->left,vars));
#else
	double hcos = (double)NSL_EVAL(root->left,vars);
	if(hcos <= 1)
		return (vec_t)0;
	return (vec_t)log(hcos + sqrt(hcos * hcos - 1));
#endif
}

V_LOCAL vec_t V_CALL
nsl_op_arth(nsl_node_p root,vec_p vars)
{
#ifdef	__WATCOMC__
	return (vec_t)atanh(NSL_EVAL(root->left,vars));
#else
	double htan = (double)NSL_EVAL(root->left,vars);
	if(fabs(htan) >= (vec_t)1)
		return (htan > 0) ? MAXVEC : -MAXVEC;
	return (vec_t)0.5 * (vec_t)log((1 + htan) / (1 - htan));
#endif
}

/*
 * Binary operations
 */
V_LOCAL vec_t V_CALL
nsl_op_add(nsl_node_p root,vec_p vars)
{
	return NSL_EVAL(root->left,vars) + NSL_EVAL(root->right,vars);
}

V_LOCAL vec_t V_CALL
nsl_op_sub(nsl_node_p root,vec_p vars)
{
	return NSL_EVAL(root->left,vars) - NSL_EVAL(root->right,vars);
}

V_LOCAL vec_t V_CALL
nsl_op_mul(nsl_node_p root,vec_p vars)
{
	return NSL_EVAL(root->left,vars) * NSL_EVAL(root->right,vars);
}

V_LOCAL vec_t V_CALL
nsl_op_div(nsl_node_p root,vec_p vars)
{
	vec_t num = NSL_EVAL(root->left,vars);
	vec_t den = NSL_EVAL(root->right,vars);
	if(!IsZero(den))
		return num / den;
	if(!IsZero(num))
		return (num > 0) ? MAXVEC : -MAXVEC;
	return 1; /* better than NAN */
}

V_LOCAL vec_t V_CALL
nsl_op_mod(nsl_node_p root,vec_p vars)
{
	return (vec_t)fmod(NSL_EVAL(root->left,vars),NSL_EVAL(root->right,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_pow(nsl_node_p root,vec_p vars)
{
	return (vec_t)pow(NSL_EVAL(root->left,vars),NSL_EVAL(root->right,vars));
}

V_LOCAL vec_t V_CALL
nsl_op_log(nsl_node_p root,vec_p vars)
{
	double num_log = log(NSL_EVAL(root->left,vars));
	double den_log = log(NSL_EVAL(root->right,vars));
	return (vec_t)(num_log / den_log);
}

V_LOCAL vec_t V_CALL
nsl_op_arctg2(nsl_node_p root,vec_p vars)
{
	return (vec_t)atan2(NSL_EVAL(root->left,vars),NSL_EVAL(root->right,vars));
}

/*
 * Pseudo-boolean operations.
 */
V_LOCAL vec_t V_CALL
nsl_op_less(nsl_node_p root,vec_t *vars)
{
	if(NSL_EVAL(root->left,vars) < NSL_EVAL(root->right,vars))
		return 1;
	else
		return 0;
}

V_LOCAL vec_t V_CALL
nsl_op_greater(nsl_node_p root,vec_t *vars)
{
	if(NSL_EVAL(root->left,vars) > NSL_EVAL(root->right,vars))
		return 1;
	else
		return 0;
}

V_LOCAL vec_t V_CALL
nsl_op_equal(nsl_node_p root,vec_t *vars)
{
	if(IsZero(NSL_EVAL(root->left,vars) - NSL_EVAL(root->right,vars)))
		return 1;
	else
		return 0;
}

V_LOCAL vec_t V_CALL
nsl_op_not_equal(nsl_node_p root,vec_t *vars)
{
	if(IsZero(NSL_EVAL(root->left,vars) - NSL_EVAL(root->right,vars)))
		return 0;
	else
		return 1;
}

/*
 * List of known operations
 */

V_LOCAL nsl_op_entry_t nsl_builtin_unary_ops[] =
{
	{"neg",nsl_op_neg,"negates tos[0] (unary minus)"},
	{"abs",nsl_op_abs,"absolute value of tos[0]"},
	{"ceil",nsl_op_ceil,"smallest integer not less than tos[0]"},
	{"floor",nsl_op_floor,"largest integer not greater than tos[0]"},
	{"sqr",nsl_op_sqr,"square of tos[0]"},
	{"sqrt",nsl_op_sqrt,"square root of tos[0]"},
	{"sin",nsl_op_sin,"sine of tos[0]"},
	{"csc",nsl_op_csc,"cosecant of tos[0]"},
	{"cos",nsl_op_cos,"cosine of tos[0]"},
	{"sec",nsl_op_sec,"secant of tos[0]"},
	{"tg",nsl_op_tg,"tangent of tos[0]"},
	{"ctg",nsl_op_ctg,"cotangent of tos[0]"},
	{"arcsin",nsl_op_arcsin,"arcsine of tos[0]"},
	{"arccsc",nsl_op_arccsc,"arccosecant of tos[0]"},
	{"arccos",nsl_op_arccos,"arccosine of tos[0]"},
	{"arcsec",nsl_op_arcsec,"arcsecant of tos[0]"},
	{"arctg",nsl_op_arctg,"arctangent of tos[0]"},
	{"arcctg",nsl_op_arcctg,"arccotangent of tos[0]"},
	{"exp",nsl_op_exp,"exponent of tos[0]"},
	{"sh",nsl_op_sh,"hyperbolic sine of tos[0]"},
	{"ch",nsl_op_ch,"hyperbolic cosine of tos[0]"},
	{"th",nsl_op_th,"hyperbolic tangent of tos[0]"},
	{"ln",nsl_op_ln,"natural logarithm of tos[0]"},
	{"lg",nsl_op_lg,"base 10 logarithm of tos[0]"},
	{"log2",nsl_op_log2,"base 2 logarithm of tos[0]"},
	{"arsh",nsl_op_arsh,"inverse hyperbolic sine of tos[0]"},
	{"arch",nsl_op_arch,"inverse hyperbolic cosine of tos[0]"},
	{"arth",nsl_op_arth,"inverse hyperbolic tangent of tos[0]"},
	{NULL,NULL,NULL}
};

V_LOCAL nsl_op_entry_t nsl_builtin_binary_ops[] =
{
	{"+",nsl_op_add,"adds tos[1] to tos[0]"},
	{"-",nsl_op_sub,"subtracts tos[0] from tos[1]"},
	{"*",nsl_op_mul,"multiplies tos[1] by tos[0]"},
	{"/",nsl_op_div,"divides tos[1] by tos[0]"},
	{"mod",nsl_op_mod,"remainder of tos[1] / tos[0]"},
	{"^",nsl_op_pow,"raises tos[1] to the power of tos[0]"},
	{"log",nsl_op_log,"base tos[0] logarithm of tos[1]"},
	{"arctg2",nsl_op_arctg2,"arctangent of fraction tos[1] / tos[0]"},
	{"<",nsl_op_less,"returns (tos[1] < tos[0]) ? 1 : 0"},
	{">",nsl_op_greater,"returns (tos[1] > tos[0]) ? 1 : 0"},
	{"==",nsl_op_equal,
		"returns (abs(tos[1] - tos[0]) < FLOAT_ZERO_EPSILON) ? 1 : 0"},
	{"!=",nsl_op_not_equal,
		"returns (abs(tos[1] - tos[0]) > FLOAT_ZERO_EPSILON) ? 1 : 0"},
	{NULL,NULL,NULL}
};

V_LOCAL nsl_op_t
nslFindUnaryOp(char *name)
{
	nsl_op_entry_p ptr = nsl_builtin_unary_ops;
	while(ptr->name)
	{
		if(STR_EQ(name,ptr->name))
			break;
		ptr++;
	}
	return ptr->operation; /* NULL if end reached */
}

void	V_GLOBAL
nslListUnaryOperations(void)
{
	nsl_op_entry_p ptr = nsl_builtin_unary_ops;
	while(ptr->name)
	{
		conPrintf("%s:\t%s\n",ptr->name,ptr->desc);
		ptr++;
	}
}

V_LOCAL nsl_op_t
nslFindBinaryOp(char *name)
{
	nsl_op_entry_p ptr = nsl_builtin_binary_ops;
	while(ptr->name)
	{
		if(STR_EQ(name,ptr->name))
			break;
		ptr++;
	}
	return ptr->operation; /* NULL if end reached */
}

void	V_GLOBAL
nslListBinaryOperations(void)
{
	nsl_op_entry_p ptr = nsl_builtin_binary_ops;
	while(ptr->name)
	{
		conPrintf("%s:\t%s\n",ptr->name,ptr->desc);
		ptr++;
	}
}

/* -------------------------------------------------------------------- */
/*							  Expressions							 */
/* -------------------------------------------------------------------- */

/*
 * ET construction
 */
V_LOCAL nsl_node_p
nslConstantLeaf(vec_t constant)
{
	nsl_node_p self = (nsl_node_p)memAllocZ(sizeof(nsl_node_t));
	self->node_type = NSL_NODE_CONSTANT;
	self->data.constant = constant;
	self->operation = nsl_op_const;
	return self;
}

V_LOCAL nsl_node_p
nslVariableLeaf(u32_t offset)
{
	nsl_node_p self = (nsl_node_p)memAllocZ(sizeof(nsl_node_t));
	self->node_type = NSL_NODE_VARIABLE;
	self->data.offset = offset;
	self->operation = nsl_op_var_access;
	return self;
}

V_LOCAL nsl_node_p
nslOperationNode(nsl_op_t operation)
{
	nsl_node_p self = (nsl_node_p)memAllocZ(sizeof(nsl_node_t));
	self->node_type = NSL_NODE_OPERATION;
	self->operation = operation;
	return self;
}

void	V_GLOBAL
nslDestroyTree(nsl_node_p root)
{
	if(root->left)
		nslDestroyTree(root->left);
	if(root->right)
		nslDestroyTree(root->right);
	memFree(root);
}

/*
 * nslConstructTreeFromRPN()
 *
 * Using 'lexer' as a lexer, constructs expression tree
 * from its reverse polish notation. All known variables (but
 * _not_ built-in _constants_) had to be stored in 'ids'
 * _prior_ to this call.
 */

V_LOCAL nsl_node_p
nslConstructTreeFromRPN(vhandle_t lexer,vp_stack_p ids,
	nsl_const_entry_p constants,
	nsl_unresolved_cb unresolved,
	void_p unresolved_arg)
{
	CALLER_NAME(nslConstructTreeFromRPN)
	u32_t offset;
	vec_t constant;
	nsl_op_t operation;
	char *token;
	nsl_node_p self, left, right;
	vp_stack_t nodes = TYPED_STACK_EMPTY_INITIALIZER;
	vboolean expect_token = true;
	vboolean parse_error = false;
	for(;;)
	{
		token = lexNext(lexer);
		if(!token || STR_EQ(token,nsl_close_bracket))
		{
			if(expect_token)
			{
				SYN_UNEXPECTED(lexer,nsl_end_of_expression);
				parse_error = true;
			}
			break;
		}
		if(STR_EQ(token,nsl_comma)) /* delimiter */
		{
			if(expect_token)
			{
				SYN_UNEXPECTED(lexer,token);
				parse_error = true;
				break;
			}
			expect_token = true;
			continue;
		}
		expect_token = false;
		if(strToFloat(token,&constant)) /* direct constant */
		{
			vpsPush(&nodes,nslConstantLeaf(constant));
			continue;
		}
		if(nslFindIdentifier(token,ids,&offset)) /* variable */
		{
			vpsPush(&nodes,nslVariableLeaf(offset));
			continue;
		}
		if(vpsCount(&nodes) >= 1)
			operation = nslFindUnaryOp(token);
		else
			operation = NULL;
		if(operation)
		{
			/* get operand */
			vpsPop(&nodes,(void_p*)&left);
			/* if it is a constant, pre-evaluate the result */
			if(left->node_type == NSL_NODE_CONSTANT)
			{
				nsl_node_t temp;
				temp.left = left;
				temp.operation = operation;
				constant = NSL_EVAL(&temp,NULL); /* don't need var_pool */
				self = nslConstantLeaf(constant);
				nslDestroyTree(left);
			}
			else
			{
				self = nslOperationNode(operation);
				/* connect operand to _left_ node */
				self->left = left;
			}
			vpsPush(&nodes,self);
			continue;
		}
		if(vpsCount(&nodes) >= 2)
			operation = nslFindBinaryOp(token);
		else
			operation = NULL;
		if(operation)
		{
			/* get operands */
			vpsPop(&nodes,(void_p*)&right);
			vpsPop(&nodes,(void_p*)&left);
			/* if both of them are constants, pre-evaluate the result */
			if((left->node_type == NSL_NODE_CONSTANT) &&
				(right->node_type == NSL_NODE_CONSTANT))
			{
				nsl_node_t temp;
				temp.left = left;
				temp.right = right;
				temp.operation = operation;
				constant = NSL_EVAL(&temp,NULL);
				self = nslConstantLeaf(constant);
				nslDestroyTree(left);
				nslDestroyTree(right);
			}
			else
			{
				self = nslOperationNode(operation);
				/* connect operands */
				self->left = left;
				self->right = right;
			}
			vpsPush(&nodes,self);
			continue;
		}
		if(nslFindConstant(constants,token,&constant))
		{
			/* built-in constant */
			vpsPush(&nodes,nslConstantLeaf(constant));
			continue;
		}
		if(unresolved)
		{
			if((*unresolved)(unresolved_arg,lexer,&constant))
			{
				/* extra constant */
				vpsPush(&nodes,nslConstantLeaf(constant));
				continue;
			}
			/* assume that 'unresolved()' already reported an error */
			parse_error = true;
			break;
		}
		SYN_UNEXPECTED(lexer,token);
		parse_error = true;
		break;
	}
	if(parse_error)
	{
		while(vpsPop(&nodes,(void_p*)&self))
			nslDestroyTree(self);
		return NULL;
	}
	if(vpsCount(&nodes) > 1)
	{
		WARNING_GENERIC(nsl_unused_operands);
	} else if(vpsCount(&nodes) == 0)
	{
		PARSE_ERROR(lexer,nsl_empty_expression);
		return NULL;
	}
	vpsPop(&nodes,(void_p*)&self);
	while(vpsPop(&nodes,(void_p*)&left))
		nslDestroyTree(left);
	vpsClear(&nodes);
	return self;
}

V_LOCAL nsl_node_p
nslParseExpression(nsl_st_parse_context_p ctx)
{
	CALLER_NAME(nslParseExpression)
	vec_t constant;
	u32_t offset;
	nsl_node_p expr;
	if(lexNextIs(ctx->lexer,nsl_open_bracket))
	{
		expr = nslConstructTreeFromRPN(ctx->lexer,ctx->ids,ctx->constants,
			ctx->unresolved,ctx->unresolved_arg);
		if(expr && !lexLastWas(ctx->lexer,nsl_close_bracket))
		{
			SYN_EXPECTED(ctx->lexer,nsl_close_bracket);
			nslDestroyTree(expr);
			expr = NULL;
		}
	}
	else
	{
		char *token = lexLast(ctx->lexer);
		if(strToFloat(token,&constant))
			expr = nslConstantLeaf(constant); /* constant expression */
		else if(nslFindIdentifier(token,ctx->ids,&offset))
			expr = nslVariableLeaf(offset); /* variable */
		else if(nslFindConstant(ctx->constants,token,&constant))
			expr = nslConstantLeaf(constant); /* built-in constant */
		else if(ctx->unresolved)
		{
			if((*ctx->unresolved)(ctx->unresolved_arg,
					ctx->lexer,&constant))
				expr = nslConstantLeaf(constant); /* extra constant */
			else
				/* assume that 'unresolved()' already reported an error */
				return NULL;
		}
		else
		{
			SYN_EXPECTED(ctx->lexer,nsl_expression);
			return NULL;
		}
	}
	return expr;
}

/* -------------------------------------------------------------------- */
/*							  Statements							  */
/* -------------------------------------------------------------------- */

vboolean V_GLOBAL
nslExecuteCompound(vp_stack_p body,vec_p var_pool)
{
	nsl_st_p ptr;
	u32_t i, nops;
	nops = vpsCount(body);
	for(i = 0;i < nops;i++)
	{
		ptr = (nsl_st_p)vpsGet(body,i);
		if(!(*ptr->operate)(ptr,var_pool))
			return false;
	}
	return true;
}

V_LOCAL	void
nslDestroyCompound(vp_stack_p body)
{
	nsl_st_p ptr;
	while(vpsPop(body,(void_p*)&ptr))
		(*ptr->destroy)(ptr);
	vpsClear(body);
}

V_LOCAL vboolean
nslParseStatement(nsl_st_parse_context_p ctx)
{
	CALLER_NAME(nslParseStatement)
	u32_t offset;
	vboolean res = false;
	char *token;
	nsl_st_parse_entry_p ptr;
	ctx->st_ret = NULL;
	/* assume first token of statement is already read in */
	token = lexLast(ctx->lexer);
	if(token[0] == ';' && !token[1]) /* empty */
		return true;
	for(ptr = ctx->statements;ptr->parse;ptr++)
	{
		switch(ptr->start_id.token_type)
		{
			case NSL_KEYWORD:
				if(STR_EQ(token,*ptr->start_id.token_value_ptr))
				{
					res = (*ptr->parse)(ctx);
					goto outer_break;
				}
				continue;
			case NSL_VARIABLE:
				if(nslFindIdentifier(token,ctx->ids,&offset))
				{
					res = (*ptr->parse)(ctx);
					goto outer_break;
				}
				continue;
			default:
				V_ASSERT(0); /* shouldn't happen */
				return false;
		}
	}
	PARSE_ERROR(ctx->lexer,nsl_unknown_statement);
	return false;
outer_break:;
	if(res)
		return true;
	if(ctx->st_ret)
		(ctx->st_ret->destroy)(ctx->st_ret);
	ctx->st_ret = NULL;
	return false;
}

V_LOCAL vboolean
nslParseCompound(nsl_st_parse_context_p ctx,vp_stack_p body)
{
	if(!lexNext(ctx->lexer))
		return false;
	if(lexLastWas(ctx->lexer,nsl_open_brace)) /* compound statement */
	{
		for(;;)
		{
			ctx->st_ret = NULL;
			if(lexNextIs(ctx->lexer,nsl_close_brace))
				break;
			if(nslParseStatement(ctx))
			{
				if(ctx->st_ret)
					vpsPush(body,ctx->st_ret);
				continue;
			}
			nslDestroyCompound(body);
			return false;
		}
	}
	else /* single statement */
	{
		ctx->st_ret = NULL;
		if(!nslParseStatement(ctx))
			return false;
		if(ctx->st_ret)
			vpsPush(body,ctx->st_ret);
	}
	return true;
}

/*
 * Declaration of variable
 */

V_LOCAL vboolean
nsl_var_parse(nsl_st_parse_context_p ctx)
{
	CALLER_NAME(nsl_var_parse)
	u32_t temp;
	char *token;
	ctx->st_ret = NULL;
	/* assume keyword 'var' already read in */
	for(;;)
	{
		token = lexNext(ctx->lexer);
		if(!token)
		{
			SYN_EXPECTED(ctx->lexer,nsl_identifier);
			return false;
		}
		if(nslFindIdentifier(token,ctx->ids,&temp))
		{
			PARSE_ERROR(ctx->lexer,nsl_duplicate_definition);
			return false;
		}
		NSL_ADD_IDENTIFIER(ctx->ids,token);
		if(lexNextIs(ctx->lexer,nsl_comma))
			continue;
		if(lexLastWas(ctx->lexer,nsl_semicolon))
			return true;
		break;
	}
	SYN_EXPECTED(ctx->lexer,nsl_variable_delimiter);
	return false;
}

#define VAR_PARSE_ENTRY \
	{nsl_var_parse,{NSL_KEYWORD,&nsl_key_var}, \
	"var <identifier>[,<identifier>,...,<identifier>];"}

/*
 * Assignment operator
 */
typedef struct nsl_st_assign_s
{
	nsl_st_t	head;
	u32_t		left_offset; /* index of left side variable */
	nsl_node_p	right_side; /* tree of right-side expression */
}   nsl_st_assign_t, *nsl_st_assign_p;

V_LOCAL vboolean
nsl_assign_operate(struct nsl_st_s *self,vec_p variables)
{
	nsl_st_assign_p st = (nsl_st_assign_p)self;
	variables[st->left_offset] = NSL_EVAL(st->right_side,variables);
	return true;
}

V_LOCAL void
nsl_assign_destroy(struct nsl_st_s *self)
{
	nsl_st_assign_p st = (nsl_st_assign_p)self;
	nslDestroyTree(st->right_side);
	memFree(st);
}

V_LOCAL vboolean
nsl_assign_parse(nsl_st_parse_context_p ctx)
{
	CALLER_NAME(nsl_assign_parse)
	u32_t offset;
	nsl_node_p right_side;
	nsl_st_assign_p st;
	ctx->st_ret = NULL;
	/* assume the variable name already read in */
	if(!nslFindIdentifier(lexLast(ctx->lexer),ctx->ids,&offset))
	{
		SYN_UNDECLARED(ctx->lexer,lexLast(ctx->lexer));
		return false;
	}
	if(!lexNextIs(ctx->lexer,nsl_equal))
	{
		SYN_EXPECTED(ctx->lexer,nsl_equal);
		return false;
	}
	right_side = nslParseExpression(ctx);
	if(!right_side)
		return false;
	if(!lexNextIs(ctx->lexer,nsl_semicolon))
	{
		SYN_EXPECTED(ctx->lexer,nsl_semicolon);
		nslDestroyTree(right_side);
		return false;
	}
	st = (nsl_st_assign_p)memAllocZ(sizeof(nsl_st_assign_t));
	st->head.operate = nsl_assign_operate;
	st->head.destroy = nsl_assign_destroy;
	st->left_offset = offset;
	st->right_side = right_side;
	ctx->st_ret = (nsl_st_p)st;
	return true;
}

#define ASSIGN_PARSE_ENTRY \
	{nsl_assign_parse,{NSL_VARIABLE,NULL},"<variable> = <expression>;"}

/*
 * Return operator
 */
typedef struct nsl_st_return_s
{
	nsl_st_t	head;
	nsl_node_p	expression;
}   nsl_st_return_t, *nsl_st_return_p;

V_LOCAL vboolean
nsl_return_operate(struct nsl_st_s *self,vec_p variables)
{
	nsl_st_return_p st = (nsl_st_return_p)self;
	variables[EXIT_CODE_OFFSET] = NSL_EVAL(st->expression,variables);
	return false;
}

V_LOCAL void
nsl_return_destroy(struct nsl_st_s *self)
{
	nsl_st_return_p st = (nsl_st_return_p)self;
	nslDestroyTree(st->expression);
	memFree(st);
}

V_LOCAL vboolean
nsl_return_parse(nsl_st_parse_context_p ctx)
{
	CALLER_NAME(nsl_return_parse)
	nsl_node_p expression;
	nsl_st_return_p st;
	ctx->st_ret = NULL;
	/* assume the 'return' keyword already read in */
	expression = nslParseExpression(ctx);
	if(!expression)
		return false;
	if(!lexNextIs(ctx->lexer,nsl_semicolon))
	{
		SYN_EXPECTED(ctx->lexer,nsl_semicolon);
		nslDestroyTree(expression);
		return false;
	}
	st = (nsl_st_return_p)memAllocZ(sizeof(nsl_st_return_t));
	st->head.operate = nsl_return_operate;
	st->head.destroy = nsl_return_destroy;
	st->expression = expression;
	ctx->st_ret = (nsl_st_p)st;
	return true;
}

#define RETURN_PARSE_ENTRY \
	{nsl_return_parse,{NSL_KEYWORD,&nsl_key_return},"return <expression>;"}

/*
 * Conditional operator
 */

typedef struct nsl_st_if_s
{
	nsl_st_t	head;
	nsl_node_p	control;		/* tree of control expression */
	vp_stack_t  true_clause;	/* each of nsl_st_p */
	vp_stack_t  false_clause;	/* each of nsl_st_p */
}   nsl_st_if_t, *nsl_st_if_p;

V_LOCAL vboolean
nsl_if_operate(struct nsl_st_s *self,vec_p variables)
{
	nsl_st_if_p st = (nsl_st_if_p)self;
	if(NSL_EVAL(st->control,variables) > 0.5)
		return nslExecuteCompound(&st->true_clause,variables);
	return nslExecuteCompound(&st->false_clause,variables);
}

V_LOCAL void
nsl_if_destroy(struct nsl_st_s *self)
{
	nsl_st_if_p st = (nsl_st_if_p)self;
	nslDestroyTree(st->control);
	nslDestroyCompound(&st->true_clause);
	nslDestroyCompound(&st->false_clause);
	memFree(st);
}

V_LOCAL vboolean
nsl_if_parse(nsl_st_parse_context_p ctx)
{
	vboolean has_else;
	nsl_st_if_p st = (nsl_st_if_p)memAllocZ(sizeof(nsl_st_if_t));
	st->head.operate = nsl_if_operate;
	st->head.destroy = nsl_if_destroy;
	ctx->st_ret = NULL;
	/* assume the keyword 'if' is already read in */
	st->control = nslParseExpression(ctx);
	if(!st->control)
	{
		memFree(st);
		return false;
	}
	if(!nslParseCompound(ctx,&st->true_clause))
	{
		nslDestroyTree(st->control);
		memFree(st);
		return false;
	}
	lexPush(ctx->lexer,LEX_POSITION_BIT);
	has_else = lexNextIsEx(ctx->lexer,nsl_key_else,STR_COMP);
	lexPop(ctx->lexer,LEX_POSITION_BIT);
	if(has_else)
	{
		lexNext(ctx->lexer);
		if(!nslParseCompound(ctx,&st->false_clause))
		{
			nslDestroyTree(st->control);
			nslDestroyCompound(&st->true_clause);
			memFree(st);
			return false;
		}
	}
	else
		vpsClear(&st->false_clause);
	ctx->st_ret = (nsl_st_p)st;
	return true;
}

#define IF_PARSE_ENTRY \
	{nsl_if_parse,{NSL_KEYWORD,&nsl_key_if},"if <expression> <statement> [else <statement>]"}

/* -------------------------------------------------------------------- */

V_LOCAL nsl_st_parse_entry_t nsl_builtin_statements[] = 
{
	ASSIGN_PARSE_ENTRY,
	IF_PARSE_ENTRY,
	VAR_PARSE_ENTRY,
	RETURN_PARSE_ENTRY,
	{NULL,{0,NULL},NULL}
};

void	V_GLOBAL
nslListStatements(void)
{
	nsl_st_parse_entry_p ptr = nsl_builtin_statements;
	while(ptr->parse)
	{
		switch(ptr->start_id.token_type)
		{
			case NSL_KEYWORD:
				conPrintf("\"%s\":  %s\n",
					*ptr->start_id.token_value_ptr,ptr->desc);
				break;
			case NSL_VARIABLE:
				conPrintf("assignment:  %s\n",ptr->desc);
				break;
			default:
				V_ASSERT(0); /* shouldn't happen */
				break;
		}
		ptr++;
	}
}

/*
 * -----------------------------------------------------------------------
 * Wrapper object.
 *
 * The variables are shared between all functions. Moreover, if the
 * variable 'some_id' declared in one function, declaration of the
 * variable with the same name in another function considered as an error.
 * -----------------------------------------------------------------------
 */

typedef struct nslfg_s
{
	libv_object_t	base;
	nsl_st_parse_context_p parse_context;
	vp_stack_t  	identifiers;
	vec_p			var_pool; /* memory for variables */
	vp_stack_t		functions; /* each of vp_stack_p of nsl_st_p */
}   nslfg_t, *nslfg_p;

#ifdef	CHECKED_BUILD
V_LOCAL char *nslfg_name	= "NSL function group";
#define	NSLFG_MAGIC			0x4E4C4647 /* NLFG */
#endif	/* CHECKED_BUILD */

#define	GET_NSLFG(handle) \
	(nslfg_p)libvIsHandle(handle,caller_name,nslfg_name,NSLFG_MAGIC)

V_LOCAL	u32_t	V_CALL
nslfg_destroy(vhandle_t handle)
{
	vp_stack_p fn_body;
	nslfg_p self = (nslfg_p)handle;
	UNREF_EXIT(self);
	nslFreeIdentifiers(&self->identifiers);
	if(self->parse_context)
		memFree(self->parse_context);
	if(self->var_pool)
		memFree(self->var_pool);
	while(vpsPop(&self->functions,(void_p*)&fn_body))
	{
		nslDestroyCompound(fn_body);
		memFree(fn_body);
	}
	vpsClear(&self->functions);
	memFreeObject(self);
	return 0;
}

V_LOCAL nsl_var_entry_t nsl_built_in_vars[] =
{
	{EXIT_CODE_NAME,EXIT_CODE_OFFSET,"exit code of last operator"},
	{NULL,0,NULL}
};

V_LOCAL nsl_const_entry_t nsl_no_constants[] =
{
	{NULL,0,NULL}
};

vhandle_t	V_GLOBAL
nslCreateFunctionGroup(nsl_var_entry_p extra_variables,
	nsl_const_entry_p constants,
	nsl_unresolved_cb unresolved,
	void_p unresolved_arg)
{
	nslfg_p self;
	ALLOC_OBJECT(self,nslfg_t,0,NSLFG_MAGIC);
	self->base.destroy = nslfg_destroy;
	vpsInit(&self->identifiers);
	vpsInit(&self->functions);
	V_ASSERT(nslAppendIdentifiers(nsl_built_in_vars,&self->identifiers));
	if(extra_variables &&
		!nslAppendIdentifiers(extra_variables,&self->identifiers))
	{
		nslfg_destroy(self);
		return LIBV_INVALID_HANDLE;
	}
	ALLOCZ_VAR(self->parse_context,nsl_st_parse_context_t);
	self->parse_context->unresolved = unresolved;
	self->parse_context->unresolved_arg = unresolved_arg;
	self->parse_context->lexer = LIBV_INVALID_HANDLE;
	self->parse_context->statements = nsl_builtin_statements;
	self->parse_context->ids = &self->identifiers;
	self->parse_context->constants = 
		(constants) ? constants : nsl_no_constants;
	return (vhandle_t)self;
}

vboolean	V_GLOBAL
nslParseFunction(vhandle_t nslfg,vhandle_t lexer)
{
	CALLER_NAME(nslParseFunction)
	vp_stack_p fn_body;
	nslfg_p self = GET_NSLFG(nslfg);
	if(!self)
		return false;
	if(!self->parse_context)
	{
		ERROR_GENERIC(nsl_not_parsing);
		return false;
	}
	ALLOC_VAR(fn_body,vp_stack_t);
	vpsInit(fn_body);
	self->parse_context->lexer = lexer;
	if(!nslParseCompound(self->parse_context,fn_body))
	{
		nslDestroyCompound(fn_body);
		memFree(fn_body);
		return false;
	}
	vpsPush(&self->functions,fn_body);
	return true;
}

vboolean	V_GLOBAL
nslCreateExecutables(vhandle_t nslfg)
{
	CALLER_NAME(nslCreateExecutables)
	nslfg_p self = GET_NSLFG(nslfg);
	if(!self)
		return false;
	if(!self->parse_context)
	{
		ERROR_GENERIC(nsl_not_parsing);
		return false;
	}
	self->var_pool =
		(vec_p)memAllocZ(sizeof(vec_t) * vpsCount(&self->identifiers));
	nslFreeIdentifiers(&self->identifiers);
	if(self->parse_context)
	{
		memFree(self->parse_context);
		self->parse_context = NULL;
	}
	return true;
}

vboolean	V_GLOBAL
nslGetExecutable(vhandle_t nslfg,u32_t index,
	vp_stack_p *fn_ret,vec_p *var_pool_ret)
{
	CALLER_NAME(nslGetExecutable)
	nslfg_p self = GET_NSLFG(nslfg);
	if(!self)
		return false;
	if(self->parse_context)
	{
		ERROR_GENERIC(nsl_still_parsing);
		return false;
	}
	if(index >= vpsCount(&self->functions))
	{
		ERROR_GENERIC(nsl_no_function);
		return false;
	}
	if(fn_ret)
		*fn_ret = (vp_stack_p)vpsGet(&self->functions,index);
	if(var_pool_ret)
		*var_pool_ret = self->var_pool;
	return true;
}

void	V_GLOBAL
nslDestroyFunctionGroup(vhandle_t nslfg)
{
	CALLER_NAME(nslDestroy)
	libvCloseHandle(nslfg,caller_name,nslfg_name,NSLFG_MAGIC);
}
