#include "config-parser.h"

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>

#include <stack>

#include "lexeme-parser.h"

// -------------------------------------------------------------------------------------------- //

//
// Implementation: helper classes
//
// ConfigParser::parserState
scm::ConfigParser::parserState::parserState()
{
	idx = 0;

	nalloc = c_alloc_init;
	name_space = new char[c_alloc_init];
	*name_space = '\0';

	name_buf_nalloc = 0;
}
scm::ConfigParser::parserState::parserState(
	const parserState &state)
{
	idx = state.idx;
	nalloc = state.nalloc;

	name_space = new char[nalloc];
	strcpy(name_space, state.name_space);

	name_buf_nalloc = state.name_buf_nalloc;
	if (name_buf_nalloc > 0)
		name_buf = new char[name_buf_nalloc];
}
scm::ConfigParser::parserState::~parserState()
{
	delete[] name_space;

	if (name_buf_nalloc > 0)
		delete[] name_buf;
}

void scm::ConfigParser::parserState::truncate_name_space()
{
	char *ptr = strrchr(name_space, '.');
	if (ptr == NULL)
		*name_space = '\0';
	else
		*ptr = '\0';
}

void scm::ConfigParser::parserState::append_name_space(
	const char *name)
{
	int ptr = (int)strlen(name_space);
	int nsize = ptr + (int)strlen(name) + 2;

	if (nsize > nalloc)
	{
		nalloc = nsize;

		char *buf = new char[nalloc];
		strcpy(buf, name_space);

		delete[] name_space;
		name_space = buf;
	}

	if (ptr > 0)
	{
		name_space[ptr] = '.';
		name_space[ptr + 1] = '\0';
	}
	strcat(name_space, name);
}

const char *scm::ConfigParser::parserState::get_global_name(const char *varname)
{
	int ptr = (int)strlen(name_space);
	int nsize = ptr + (int)strlen(varname) + 2;

	if (nsize > name_buf_nalloc)
	{
		if (name_buf_nalloc > 0)
			delete[] name_buf;
		name_buf_nalloc = nsize;

		name_buf = new char[name_buf_nalloc];
	}

	strcpy(name_buf, name_space);
	if (ptr > 0)
	{
		name_buf[ptr] = '.';
		name_buf[ptr + 1] = '\0';
	}
	strcat(name_buf, varname);

	return name_buf;
}
// -------------------------------------------------------------------------------------------- //

//
// Implementation: ConfigureParser
//
scm::ConfigParser::ConfigParser()
{
	nvars = 0;
	nalloc_vars = c_alloc_vars_init;

	var = new cfgVector[nalloc_vars];
}

scm::ConfigParser::~ConfigParser()
{
	nvars = 0;
	nalloc_vars = 0;

	delete[] var;
}
// -------------------------------------------------------------------------------------------- //

bool scm::ConfigParser::run(const char *filename)
{
	parserCallBack pcb;
	return run(filename, pcb);
}

bool scm::ConfigParser::run(const char *filename,
							parserCallBack &pcb)
{
	// removing elements in config -- but keeping memory
	nvars = 0;

	LexemeList lexeme_list;

	if (!LexemeParser::run(filename, lexeme_list))
		return false;
	if (lexeme_list.get_size() == 0)
		return true; // nothing to parse

	// --- preliminary check
	if (!parse_syntax(lexeme_list, false, pcb))
		return false;
	return parse_syntax(lexeme_list, true, pcb);
}

// * get rpn expression form * //
// -------------------------------------------------------------------------------------------- //
bool scm::ConfigParser::get_rpn(
	mem_buffer<int> &rpn,
	mem_buffer<int> &rpn_key,
	const LexemeList &lexeme_list, const int lex_beg, const int lex_end) const
{
	std::stack<int> narg_stack;

	rpn.reset();
	rpn_key.reset();

	if ((lex_beg < 0) || (lex_end > lexeme_list.get_size() - 1))
	{
		printf(" CONFIG:> incorrect expression range (%i, %i), lexeme list size = %i\n",
			   lex_beg, lex_end, lexeme_list.get_size());
		return false;
	}

	std::stack<int> hstack;
	Lexeme::TYPE lex_type;

	bool status = true;
	enum YARD_STATE
	{
		EXPECT_OPERAND,
		EXPECT_OPERATOR
	} state = EXPECT_OPERAND;

	int idx = lex_beg;
	while (idx <= lex_end)
	{
		lex_type = lexeme_list.get_type(idx);
		// ----------------------------------------------------- //

		if ((Lexeme::is_value(lex_type)) ||
			(lex_type == Lexeme::IS_NAME))
		{
			if (state != EXPECT_OPERAND)
			{
				printf(" CONFIG:> unexpected lexeme '%s' (line, %i)\n",
					   lexeme_list.get_token(idx), lexeme_list.get_tag(idx));
				status = false;
				break;
			}

			// --- adding value | variable reference to output
			// but postpone variable check to evaluation step
			rpn.append(idx);
			// --- adding not a function key to number of arguments
			rpn_key.append(-1);

			state = EXPECT_OPERATOR;
			idx++;

			// --- processing index operator
			if ((lex_type == Lexeme::IS_NAME) && (idx <= lex_end))
			{
				if (lexeme_list.get_type(idx) == Lexeme::IS_BRACKET_OPEN)
				{
					// --- pushing '[' to stack
					hstack.push(idx);

					state = EXPECT_OPERAND;
					idx++;
				}
			}

			continue; // --> next iteration
		}
		// ----------------------------------------------------- //

		if ((Lexeme::is_function(lex_type)) ||
			(Lexeme::is_ctor(lex_type)) ||
			(lex_type == Lexeme::IS_COMMAND))
		{
			if (state != EXPECT_OPERAND)
			{
				printf(" CONFIG:> unexpected lexeme '%s' (line, %i)\n",
					   lexeme_list.get_token(idx), lexeme_list.get_tag(idx));
				status = false;
				break;
			}

			// --- pushing function to stack
			hstack.push(idx);

			// --- processing function opening parenthesis
			idx++;
			if (idx > lex_end)
			{
				printf(" CONFIG:> expecting function '%s' call (line, %i)\n",
					   lexeme_list.get_token(idx - 1), lexeme_list.get_tag(idx - 1));
				status = false;
				break;
			}

			if (lexeme_list.get_type(idx) != Lexeme::IS_PAREN_OPEN)
			{
				printf(" CONFIG:> expecting function '%s' call (line, %i)\n",
					   lexeme_list.get_token(idx - 1), lexeme_list.get_tag(idx - 1));
				printf(" CONFIG:> unexpected lexeme '%s' (line, %i)\n",
					   lexeme_list.get_token(idx), lexeme_list.get_tag(idx));
				status = false;
				break;
			}

			// --- pushing '(' to stack
			hstack.push(idx);

			// --- checking for void function closing parenthesis
			idx++;
			if (idx > lex_end)
			{
				printf(" CONFIG:> expecting function '%s' call (line, %i)\n",
					   lexeme_list.get_token(idx - 2), lexeme_list.get_tag(idx - 2));
				status = false;
				break;
			}

			if (lexeme_list.get_type(idx) == Lexeme::IS_PAREN_CLOSE)
			{
				// --- removing function & '(' from stack
				hstack.pop();
				hstack.pop();

				// --- adding function to output
				rpn.append(idx - 2);

				// --- adding 0 as number of arguments
				rpn_key.append(0);

				state = EXPECT_OPERATOR;
				idx++;
			}
			else
			{
				// --- adding 1 as number of expected arguments
				narg_stack.push(1);

				state = EXPECT_OPERAND;
			}

			continue; // --> next iteration
		}
		// ----------------------------------------------------- //

		if (lex_type == Lexeme::IS_PAREN_OPEN)
		{
			if (state != EXPECT_OPERAND)
			{
				printf(" CONFIG:> unexpected lexeme '%s' (line, %i)\n",
					   lexeme_list.get_token(idx), lexeme_list.get_tag(idx));
				status = false;
				break;
			}

			// --- pushing '(' to stack
			hstack.push(idx);

			state = EXPECT_OPERAND;
			idx++;
			continue; // --> next iteration
		}
		// ----------------------------------------------------- //

		if (lex_type == Lexeme::IS_PAREN_CLOSE)
		{
			if (state != EXPECT_OPERATOR)
			{
				printf(" CONFIG:> unexpected lexeme '%s' (line, %i)\n",
					   lexeme_list.get_token(idx), lexeme_list.get_tag(idx));
				status = false;
				break;
			}

			// --- pop till '(' found & remove '(' from stack
			int stack_idx;
			bool flag = false;
			while (!hstack.empty())
			{
				stack_idx = hstack.top();
				if (lexeme_list.get_type(stack_idx) == Lexeme::IS_BRACKET_OPEN)
					break;

				hstack.pop();
				if (lexeme_list.get_type(stack_idx) == Lexeme::IS_PAREN_OPEN)
				{
					flag = true;
					break;
				}

				rpn.append(stack_idx);
				// --- adding not a function key to number of arguments
				rpn_key.append(-1);
			}
			if (!flag)
			{
				printf(" CONFIG:> missing parenthesis '(' (line, %i)\n",
					   lexeme_list.get_tag(idx));
				status = false;
				break;
			}

			// --- checking if function is at the top of the stack
			if (!hstack.empty())
			{
				stack_idx = hstack.top();
				if ((lexeme_list.is_function(stack_idx)) ||
					(lexeme_list.is_ctor(stack_idx)) ||
					(lexeme_list.get_type(stack_idx) == Lexeme::IS_COMMAND))
				{
					hstack.pop();
					rpn.append(stack_idx);

					// --- adding number of expected arguments from stack
					int narg = narg_stack.top();
					narg_stack.pop();
					rpn_key.append(narg);
				}
			}

			state = EXPECT_OPERATOR;
			idx++;
			continue; // --> next iteration
		}
		// ----------------------------------------------------- //

		if (lex_type == Lexeme::IS_COMMA)
		{
			if (state != EXPECT_OPERATOR)
			{
				printf(" CONFIG:> unexpected lexeme '%s' (line, %i)\n",
					   lexeme_list.get_token(idx), lexeme_list.get_tag(idx));
				status = false;
				break;
			}

			// --- pop till '(' found & keep '(' on stack
			int stack_idx;
			bool flag = false;
			while (!hstack.empty())
			{
				stack_idx = hstack.top();

				if (lexeme_list.get_type(stack_idx) == Lexeme::IS_BRACKET_OPEN)
					break;
				if (lexeme_list.get_type(stack_idx) == Lexeme::IS_PAREN_OPEN)
				{
					flag = true;
					break;
				}

				hstack.pop();
				rpn.append(stack_idx);
				// --- adding not a function key to number of arguments
				rpn_key.append(-1);
			}
			if (!flag)
			{
				printf(" CONFIG:> missing parenthesis '(' or misplaced ',' delimeter (line, %i)\n",
					   lexeme_list.get_tag(idx));
				status = false;
				break;
			}

			// --- checking if function is on top of the stack

			// --- temporary removing '('
			hstack.pop();
			if (hstack.empty())
			{
				printf(" CONFIG:> misplaced ',' delimiter (line, %i)\n",
					   lexeme_list.get_tag(idx));
				status = false;
				break;
			}
			if ((!lexeme_list.is_function(hstack.top())) &&
				(!lexeme_list.is_ctor(hstack.top())) &&
				(lexeme_list.get_type(hstack.top()) != Lexeme::IS_COMMAND))
			{
				printf(" CONFIG:> misplaced ',' delimiter (line, %i)\n",
					   lexeme_list.get_tag(idx));
				status = false;
				break;
			}
			// --- adding '(' back to stack
			hstack.push(stack_idx);

			// --- increase number of expected arguments
			int narg = narg_stack.top();
			narg_stack.pop();
			narg++;
			narg_stack.push(narg);

			state = EXPECT_OPERAND;
			idx++;
			continue; // --> next iteration
		}
		// ----------------------------------------------------- //

		if (Lexeme::is_operator(lex_type))
		{
			// --- handling operators
			if (((is_operator_unary(lex_type)) && (state != EXPECT_OPERAND)) ||
				((is_operator_binary(lex_type)) && (state != EXPECT_OPERATOR)))
			{
				printf(" CONFIG:> unexpected lexeme '%s' (line, %i)\n",
					   lexeme_list.get_token(idx), lexeme_list.get_tag(idx));
				status = false;
				break;
			}

			int stack_idx;
			while (!hstack.empty())
			{
				stack_idx = hstack.top();

				if (lexeme_list.is_operator(stack_idx))
				{
					Lexeme::TYPE stack_type = lexeme_list.get_type(stack_idx);

					if (((is_operator_assoc_left(lex_type)) &&
						 (!operator_priority_lt(stack_type, lex_type))) ||
						((is_operator_assoc_right(lex_type)) &&
						 (operator_priority_lt(lex_type, stack_type))))
					{
						hstack.pop();
						rpn.append(stack_idx);
						// --- adding not a function key to number of arguments
						rpn_key.append(-1);
						continue;
					}
				}

				break;
			}
			hstack.push(idx);

			state = EXPECT_OPERAND;
			idx++;
			continue; // --> next iteration
		}
		// ----------------------------------------------------- //

		if (lex_type == Lexeme::IS_BRACKET_CLOSE)
		{
			if (state != EXPECT_OPERATOR)
			{
				printf(" CONFIG:> unexpected lexeme '%s' (line, %i)\n",
					   lexeme_list.get_token(idx), lexeme_list.get_tag(idx));
				status = false;
				break;
			}

			// --- pop till '[' found & remove '[' from stack & add '[' to output
			int stack_idx;
			bool flag = false;
			while (!hstack.empty())
			{
				stack_idx = hstack.top();
				if (lexeme_list.get_type(stack_idx) == Lexeme::IS_PAREN_OPEN)
					break;

				hstack.pop();
				rpn.append(stack_idx);
				// --- adding not a function key to number of arguments
				rpn_key.append(-1);

				if (lexeme_list.get_type(stack_idx) == Lexeme::IS_BRACKET_OPEN)
				{
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				printf(" CONFIG:> missing bracket '[' (line, %i)\n",
					   lexeme_list.get_tag(idx));
				status = false;
				break;
			}

			state = EXPECT_OPERATOR;
			idx++;
			continue; // --> next iteration
		}
		// ----------------------------------------------------- //

		printf(" CONFIG:> unexpected lexeme in expression: '%s' (line, %i)\n",
			   lexeme_list.get_token(idx), lexeme_list.get_tag(idx));
		status = false;
		break;
	}

	// --- removing elements until stack is empty or getting '('
	int stack_idx;
	while (!hstack.empty())
	{
		stack_idx = hstack.top();
		if (lexeme_list.get_type(stack_idx) == Lexeme::IS_PAREN_OPEN)
		{
			printf(" CONFIG:> missing bracket ')' (line, %i)\n",
				   lexeme_list.get_tag(stack_idx));
			status = false;
			break;
		}
		if (lexeme_list.get_type(stack_idx) == Lexeme::IS_BRACKET_OPEN)
		{
			printf(" CONFIG:> missing bracket ']' (line, %i)\n",
				   lexeme_list.get_tag(stack_idx));
			status = false;
			break;
		}

		hstack.pop();
		rpn.append(stack_idx);
		// --- adding not a function key to number of arguments
		rpn_key.append(-1);
	}
	// ----------------------------------------------------- //

	return status &&
		   (rpn.get_size() != 0) && (state == EXPECT_OPERATOR);
}
// -------------------------------------------------------------------------------------------- //

// * evaluate rpn expression * //
// -------------------------------------------------------------------------------------------- //
bool scm::ConfigParser::evaluate_rpn(
	std::stack<cfgVector> &dyn_expr,
	const mem_buffer<int> &rpn,
	const mem_buffer<int> &rpn_key,
	const int rpn_begin, const int rpn_end,
	const LexemeList &lexeme_list, const char *name_space,
	const bool exe_cntrl) const
{
	Lexeme::TYPE lex_type;
	int eidx;

	mem_buffer<char> arg_name_buf;

	for (int i = max(rpn_begin, 0); i <= min(rpn_end, rpn.get_size() - 1); i++)
	{
		eidx = rpn.get_value(i);
		lex_type = lexeme_list.get_type(eidx);
		// ----------------------------------------------------- //

		if (is_operator_binary(lex_type))
		{
			if (dyn_expr.size() >= 2)
			{
				cfgVector a = dyn_expr.top();
				dyn_expr.pop();
				cfgVector b = dyn_expr.top();
				dyn_expr.pop();

				bool arg_defined = ((a.is_defined()) && (b.is_defined()));

				if (lex_type == Lexeme::IS_OP_ADD)
					b += a;
				else if (lex_type == Lexeme::IS_OP_SUB)
					b -= a;
				else if (lex_type == Lexeme::IS_OP_MUL)
					b *= a;
				else if (lex_type == Lexeme::IS_OP_DIV)
					b /= a;
				else if (lex_type == Lexeme::IS_OP_MOD)
					b %= a;
				else if (lex_type == Lexeme::IS_OP_EXP)
					b ^= a;
				else if (lex_type == Lexeme::IS_OP_EQ)
					b = (b == a);
				else if (lex_type == Lexeme::IS_OP_NEQ)
					b = (b != a);
				else if (lex_type == Lexeme::IS_OP_LT)
					b = (b < a);
				else if (lex_type == Lexeme::IS_OP_GT)
					b = (b > a);
				else if (lex_type == Lexeme::IS_OP_LEQ)
					b = (b <= a);
				else if (lex_type == Lexeme::IS_OP_GEQ)
					b = (b >= a);
				else if (lex_type == Lexeme::IS_OP_LOGICAL_AND)
					b = b.logical_and(a);
				else if (lex_type == Lexeme::IS_OP_LOGICAL_OR)
					b = b.logical_or(a);
				else
				{
					printf(" CONFIG:> unknown binary operation: '%s' (line, %i)\n",
						   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
					return false;
				}

				if ((exe_cntrl || arg_defined) && (!b.is_defined()))
				{
					printf(" CONFIG:> incorrect types of operands: '%s' (line, %i)\n",
						   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
					return false;
				}
				dyn_expr.push(b);
			}
			else
			{
				printf(" CONFIG:> insufficient number of arguments: '%s' (line, %i)\n",
					   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
				return false;
			}

			// --> next iteration
			continue;
		}
		// ----------------------------------------------------- //

		if (lex_type == Lexeme::IS_BRACKET_OPEN)
		{
			// --- index operator
			if (dyn_expr.size() >= 2)
			{
				cfgVector op = dyn_expr.top();
				dyn_expr.pop();
				cfgVector a = dyn_expr.top();
				dyn_expr.pop();

				bool arg_defined = ((a.is_defined()) && (op.is_defined()));

				if (exe_cntrl || arg_defined)
				{
					int index;
					if (!op.get_if_single(&index))
					{
						printf(" CONFIG:> incorrect index value: '[]' (line, %i)\n",
							   lexeme_list.get_tag(eidx));
						return false;
					}

					cfgVector value = cfgVector(a.get(index));
					if (!value.is_defined())
					{
						printf(" CONFIG:> incorrect types of operands: '[]' (line, %i)\n",
							   lexeme_list.get_tag(eidx));
						return false;
					}
					dyn_expr.push(value);
				}
				else
				{
					dyn_expr.push(cfgVector());
				}
			}
			else
			{
				printf(" CONFIG:> insufficient number of arguments: '[]' (line, %i)\n",
					   lexeme_list.get_tag(eidx));
				return false;
			}

			// --> next iteration
			continue;
		}
		// ----------------------------------------------------- //

		if (is_operator_unary(lex_type))
		{
			if (dyn_expr.size() >= 1)
			{
				cfgVector op = dyn_expr.top();
				dyn_expr.pop();
				cfgVector res;

				bool arg_defined = (op.is_defined());

				if (lex_type == Lexeme::IS_OP_PLUS)
					res = +op;
				else if (lex_type == Lexeme::IS_OP_MINUS)
					res = -op;
				else
				{
					printf(" CONFIG:> unknown unary operation: '%s' (line, %i)\n",
						   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
					return false;
				}

				if ((exe_cntrl || arg_defined) && (!res.is_defined()))
				{
					printf(" CONFIG:> incorrect type of operand: '%s' (line, %i)\n",
						   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
					return false;
				}
				dyn_expr.push(res);
			}
			else
			{
				printf(" CONFIG:> insufficient number of arguments: '%s' (line, %i)\n",
					   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
				return false;
			}

			// --> next iteration
			continue;
		}
		// ----------------------------------------------------- //

		if (Lexeme::is_function(lex_type))
		{
			if (rpn_key.get_value(i) != function_nargs(lex_type))
			{
				printf(" CONFIG:> incorrect number (%i) of arguments in function: '%s' (line, %i)\n",
					   rpn_key.get_value(i), lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
				return false;
			}

			if (function_nargs(lex_type) == 2)
			{
				if (dyn_expr.size() >= 2)
				{
					cfgVector op2 = dyn_expr.top();
					dyn_expr.pop();
					cfgVector op1 = dyn_expr.top();
					dyn_expr.pop();
					cfgVector res;

					bool arg_defined = ((op1.is_defined()) && (op2.is_defined()));

					if (lex_type == Lexeme::IS_MIN_FUNCTION)
						res = cfgVector::cfg_min(op1, op2);
					else if (lex_type == Lexeme::IS_MAX_FUNCTION)
						res = cfgVector::cfg_max(op1, op2);
					else if (lex_type == Lexeme::IS_DOT_PRODUCT_FUNCTION)
						res = cfgVector::cfg_dot_product(op1, op2);
					else if (lex_type == Lexeme::IS_UNIRAND_FUNCTION)
					{
						if (exe_cntrl || arg_defined)
						{
							double a, b;
							if (!op1.get_if_single(&a))
							{
								printf(" CONFIG:> incorrect type of operand: '%s' (line, %i)\n",
									   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
								return false;
							}
							if (!op2.get_if_single(&b))
							{
								printf(" CONFIG:> incorrect type of operand: '%s' (line, %i)\n",
									   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
								return false;
							}

							double value = a + (rand() / (RAND_MAX / (b - a)));
							res = cfgVector(cfgValue(value));
						}
						else
						{
							res = cfgVector();
						}
					}
					else
					{
						printf(" CONFIG:> unknown function: '%s' (line, %i)\n",
							   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
						return false;
					}

					if ((exe_cntrl || arg_defined) && (!res.is_defined()))
					{
						printf(" CONFIG:> incorrect type of operand: '%s' (line, %i)\n",
							   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
						return false;
					}
					dyn_expr.push(res);
				}
				else
				{
					printf(" CONFIG:> insufficient number of arguments: '%s' (line, %i)\n",
						   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
					return false;
				}

				// --> next iteration
				continue;
			}

			if (function_nargs(lex_type) == 1)
			{
				if (dyn_expr.size() >= 1)
				{
					cfgVector op = dyn_expr.top();
					dyn_expr.pop();
					cfgVector res;

					bool arg_defined = (op.is_defined());

					if (lex_type == Lexeme::IS_VECMIN_FUNCTION)
						res = cfgVector::cfg_vecmin(op);
					else if (lex_type == Lexeme::IS_VECMAX_FUNCTION)
						res = cfgVector::cfg_vecmax(op);
					else if (lex_type == Lexeme::IS_SIN_FUNCTION)
						res = cfgVector::cfg_sin(op);
					else if (lex_type == Lexeme::IS_COS_FUNCTION)
						res = cfgVector::cfg_cos(op);
					else if (lex_type == Lexeme::IS_TAN_FUNCTION)
						res = cfgVector::cfg_tan(op);
					else if (lex_type == Lexeme::IS_LOG_FUNCTION)
						res = cfgVector::cfg_log(op);
					else if (lex_type == Lexeme::IS_SQRT_FUNCTION)
						res = cfgVector::cfg_sqrt(op);
					else if (lex_type == Lexeme::IS_ABS_FUNCTION)
						res = cfgVector::cfg_abs(op);
					else if (lex_type == Lexeme::IS_L2NORM_FUNCTION)
						res = cfgVector::cfg_l2norm(op);
					else if (lex_type == Lexeme::IS_CNORM_FUNCTION)
						res = cfgVector::cfg_cnorm(op);
					else if (lex_type == Lexeme::IS_TO_STRING_FUNCTION)
						res = cfgVector::cfg_to_string(op);
					else if (lex_type == Lexeme::IS_SIZE_FUNCTION)
						res = cfgVector(cfgValue(op.get_size()));
					else if (lex_type == Lexeme::IS_DEFINED_FUNCTION)
					{
						if (exe_cntrl || arg_defined)
						{
							std::string name;
							if (!op.get_if_single(name))
							{
								printf(" CONFIG:> incorrect type of operand: '%s' (line, %i)\n",
									   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
								return false;
							}
							res = (is_varname(name.c_str())) ? cfgVector(cfgValue(true)) : cfgVector(cfgValue(false));
						}
						else
						{
							res = cfgVector();
						}
					}
					else
					{
						printf(" CONFIG:> unknown function: '%s' (line, %i)\n",
							   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
						return false;
					}

					if ((exe_cntrl || arg_defined) && (!res.is_defined()))
					{
						printf(" CONFIG:> incorrect type of operand: '%s' (line, %i)\n",
							   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
						return false;
					}
					dyn_expr.push(res);
				}
				else
				{
					printf(" CONFIG:> insufficient number of arguments: '%s' (line, %i)\n",
						   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
					return false;
				}

				// --> next iteration
				continue;
			}

			printf(" CONFIG:> unknown function: '%s' (line, %i)\n",
				   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
			return false;
		}
		// ----------------------------------------------------- //

		if (Lexeme::is_ctor(lex_type))
		{
			if (lex_type == Lexeme::IS_VECTOR_CTOR)
			{
				int narg = rpn_key.get_value(i);
				if (narg < 0)
				{
					printf(" CONFIG:> incorrect number of arguments: '%s' (line, %i)\n",
						   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
					return false;
				}

				cfgVector vec;
				if (dyn_expr.size() >= (unsigned int)narg)
				{

					std::stack<cfgValue> vec_arg;
					for (int k = 0; k < narg; k++)
					{

						cfgVector arg = dyn_expr.top();
						dyn_expr.pop();

						if ((exe_cntrl) && (!arg.is_defined()))
						{
							printf(" CONFIG:> undefined argument: '%s' (line, %i)\n",
								   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
							return false;
						}
						if ((exe_cntrl || (arg.is_defined())) && (!arg.is_single()))
						{
							printf(" CONFIG:> incorrect argument size > 1: '%s' (line, %i)\n",
								   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
							return false;
						}
						vec_arg.push(arg.get(0));
					}

					cfgVector vec;
					// --- using stack to append values in correct order
					while (!vec_arg.empty())
					{
						vec.append(vec_arg.top());
						vec_arg.pop();
					}

					dyn_expr.push(vec);
				}
				else
				{
					printf(" CONFIG:> insufficient number of arguments: '%s' (line, %i)\n",
						   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
					return false;
				}

				// --> next iteration
				continue;
			}

			if (lex_type == Lexeme::IS_VECTOR_CONST_CTOR)
			{
				if (rpn_key.get_value(i) != 2)
				{
					printf(" CONFIG:> incorrect number (%i) of arguments in constructor: '%s' (line, %i)\n",
						   rpn_key.get_value(i), lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
					return false;
				}

				if (dyn_expr.size() >= 2)
				{
					cfgVector vec_value = dyn_expr.top();
					dyn_expr.pop();
					cfgVector vec_size = dyn_expr.top();
					dyn_expr.pop();

					bool arg_defined = ((vec_value.is_defined()) &&
										(vec_size.is_defined()));

					if (exe_cntrl || arg_defined)
					{
						int size;
						if (!vec_size.get_if_single(&size))
						{
							printf(" CONFIG:> incorrect size argument: '%s' (line, %i)\n",
								   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
							return false;
						}
						if (size < 0)
						{
							printf(" CONFIG:> invalid size (%i) argument: '%s' (line, %i)\n",
								   size, lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
							return false;
						}

						if (!vec_value.is_defined())
						{
							printf(" CONFIG:> undefined argument: '%s' (line, %i)\n",
								   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
							return false;
						}
						if (!vec_value.is_single())
						{
							printf(" CONFIG:> incorrect argument size > 1: '%s' (line, %i)\n",
								   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
							return false;
						}

						cfgVector vec;
						vec.set(vec_value.get(0), size);
						dyn_expr.push(vec);
					}
					else
					{
						dyn_expr.push(cfgVector());
					}
				}
				else
				{
					printf(" CONFIG:> insufficient number of arguments: '%s' (line, %i)\n",
						   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
					return false;
				}

				// --> next iteration
				continue;
			}

			if (lex_type == Lexeme::IS_VECTOR_UNIRAND_CTOR)
			{
				if (rpn_key.get_value(i) != 3)
				{
					printf(" CONFIG:> incorrect number (%i) of arguments in constructor: '%s' (line, %i)\n",
						   rpn_key.get_value(i), lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
					return false;
				}

				if (dyn_expr.size() >= 3)
				{
					cfgVector vec_b = dyn_expr.top();
					dyn_expr.pop();
					cfgVector vec_a = dyn_expr.top();
					dyn_expr.pop();
					cfgVector vec_size = dyn_expr.top();
					dyn_expr.pop();

					bool arg_defined = ((vec_b.is_defined()) &&
										(vec_a.is_defined()) &&
										(vec_size.is_defined()));

					if (exe_cntrl || arg_defined)
					{
						int size;
						if (!vec_size.get_if_single(&size))
						{
							printf(" CONFIG:> incorrect size argument: '%s' (line, %i)\n",
								   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
							return false;
						}
						if (size < 0)
						{
							printf(" CONFIG:> invalid size (%i) argument: '%s' (line, %i)\n",
								   size, lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
							return false;
						}

						double a, b;
						if (!vec_a.get_if_single(&a))
						{
							printf(" CONFIG:> incorrect type of operand: '%s' (line, %i)\n",
								   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
							return false;
						}
						if (!vec_b.get_if_single(&b))
						{
							printf(" CONFIG:> incorrect type of operand: '%s' (line, %i)\n",
								   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
							return false;
						}

						double value = (a == b) ? a : a + (rand() / (RAND_MAX / (b - a)));
						cfgVector vec;
						vec.set(cfgValue(value), size);
						for (int k = 1; k < size; k++)
						{
							value = a + (rand() / (RAND_MAX / (b - a)));
							vec.change(k, cfgValue(value));
						}
						dyn_expr.push(vec);
					}
					else
					{
						dyn_expr.push(cfgVector());
					}
				}
				else
				{
					printf(" CONFIG:> insufficient number of arguments: '%s' (line, %i)\n",
						   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
					return false;
				}

				// --> next iteration
				continue;
			}

			printf(" CONFIG:> unknown constructor: '%s' (line, %i)\n",
				   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
			return false;
		}
		// ----------------------------------------------------- //

		if (Lexeme::is_value(lex_type))
		{
			cfgVector vec(cfgValue::make_implicit(lexeme_list.get_token(eidx)));

			if (!vec.is_defined())
			{
				printf(" CONFIG:> incorrect constant value: '%s' (line, %i)\n",
					   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
				return false;
			}

			dyn_expr.push(vec);

			// --> next iteration
			continue;
		}
		// ----------------------------------------------------- //

		if (lex_type == Lexeme::IS_NAME)
		{
			if (exe_cntrl)
			{
				cfgVector arg;

				arg_name_buf.reset();
				arg_name_buf.append(name_space, strlen(name_space));
				arg_name_buf.append('.');
				arg_name_buf.append(lexeme_list.get_token(eidx), strlen(lexeme_list.get_token(eidx)));
				arg_name_buf.append('\0');

				if (is_varname(arg_name_buf.get_ptr()))
					arg = get_variable(arg_name_buf.get_ptr());
				else
				{
					if (is_varname(lexeme_list.get_token(eidx)))
					{
						arg = get_variable(lexeme_list.get_token(eidx));
					}
					else
					{
						printf(" CONFIG:> reference to undefined variable: '%s' (line, %i)\n",
							   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
						return false;
					}
				}

				dyn_expr.push(arg);
			}
			else
			{
				dyn_expr.push(cfgVector());
			}

			// --> next iteration
			continue;
		}
		// ----------------------------------------------------- //

		printf(" CONFIG:> unexpected lexeme in expression: '%s' (line, %i)\n",
			   lexeme_list.get_token(eidx), lexeme_list.get_tag(eidx));
		return false;
	}

	return true;
}

bool scm::ConfigParser::evaluate_rpn(
	cfgVector &res,
	const mem_buffer<int> &rpn,
	const mem_buffer<int> &rpn_key,
	const LexemeList &lexeme_list, const char *name_space,
	const bool exe_cntrl) const
{
	std::stack<cfgVector> dyn_expr;

	if (!evaluate_rpn(dyn_expr, rpn, rpn_key,
					  0, rpn.get_size() - 1, lexeme_list, name_space, exe_cntrl))
	{
		res = cfgVector();
		return false;
	}

	if (dyn_expr.size() != 1)
	{
		res = cfgVector();
		printf(" CONFIG:> undefined expression (line, %i)\n",
			   lexeme_list.get_tag(rpn.get_value(0)));
		return false;
	}

	res = cfgVector(dyn_expr.top()); // delay res type checking [in add]
	return true;
}
// -------------------------------------------------------------------------------------------- //

bool scm::ConfigParser::set_command(
	cfgCommand &command,
	const mem_buffer<int> &rpn,
	const mem_buffer<int> &rpn_key,
	const LexemeList &lexeme_list,
	const bool exe_cntrl) const
{
	const int rpn_size = rpn.get_size();
	std::stack<cfgVector> dyn_expr;

	command.reset();
	command.set_name(lexeme_list.get_token(rpn.get_value(rpn_size - 1)));

	if (!evaluate_rpn(dyn_expr, rpn, rpn_key,
					  0, rpn_size - 2, lexeme_list, "", exe_cntrl))
	{
		printf(" CONFIG:> undefined command arguments '%s' (line, %i)\n",
			   lexeme_list.get_token(rpn.get_value(rpn_size - 1)),
			   lexeme_list.get_tag(rpn.get_value(rpn_size - 1)));
		return false;
	}

	if (dyn_expr.size() != rpn_key.get_value(rpn_size - 1))
	{
		printf(" CONFIG:> incorrect number (%li) of command '%s' arguments (line, %i)\n",
			   dyn_expr.size(),
			   lexeme_list.get_token(rpn.get_value(rpn_size - 1)),
			   lexeme_list.get_tag(rpn.get_value(rpn_size - 1)));
		return false;
	}

	// --- copy arguments in correct order
	std::stack<cfgVector> dyn_expr_back;

	while (!dyn_expr.empty())
	{
		dyn_expr_back.push(dyn_expr.top());
		dyn_expr.pop();
	}

	cfgVector arg;
	while (!dyn_expr_back.empty())
	{
		arg = dyn_expr_back.top();
		if (exe_cntrl || (arg.is_defined()))
			if (!command.add_arg(arg))
			{
				printf(" CONFIG:> undefined command arguments '%s' (line, %i)\n",
					   lexeme_list.get_token(rpn.get_value(rpn_size - 1)),
					   lexeme_list.get_tag(rpn.get_value(rpn_size - 1)));
				return false;
			}
		dyn_expr_back.pop();
	}
	// ----------------------------------------------------- //

	return true; // delay arg type checking [in add]
}
// -------------------------------------------------------------------------------------------- //

// * syntax analysis * //
// -------------------------------------------------------------------------------------------- //
bool scm::ConfigParser::parse_syntax(
	const LexemeList &lexeme_list,
	const bool exe_cntrl, parserCallBack &pcb)
{
	const int nlexeme = lexeme_list.get_size();
	if (nlexeme == 0)
		return true;

	parserState state;
	if (!parse_block(state, lexeme_list, exe_cntrl, pcb))
		return false;

	if (state.idx < nlexeme)
	{
		printf(" CONFIG:> unexpected lexeme: '%s' (line, %i)\n",
			   lexeme_list.get_token(state.idx), lexeme_list.get_tag(state.idx));
		return false;
	}

	return true;
}

bool scm::ConfigParser::parse_block(
	parserState &state,
	const LexemeList &lexeme_list,
	const bool exe_cntrl, parserCallBack &pcb)
{
	const int nlexeme = lexeme_list.get_size();

	while (state.idx < nlexeme)
	{

		if (lexeme_list.get_type(state.idx) == Lexeme::IS_COMMAND)
		{
			if (!parse_command(state, lexeme_list, exe_cntrl, pcb))
				return false;
			continue;
		}
		if (lexeme_list.get_type(state.idx) == Lexeme::IS_KEY_WHILE)
		{
			if (!parse_while_statement(state, lexeme_list, exe_cntrl, pcb))
				return false;
			continue;
		}
		if (lexeme_list.get_type(state.idx) == Lexeme::IS_KEY_IF)
		{
			if (!parse_if_statement(state, lexeme_list, exe_cntrl, pcb))
				return false;
			continue;
		}
		if (lexeme_list.get_type(state.idx) == Lexeme::IS_NAME)
		{

			if ((state.idx + 1 < nlexeme) &&
				(lexeme_list.get_type(state.idx + 1) == Lexeme::IS_BRACE_OPEN))
			{
				if (!parse_name_space(state, lexeme_list, exe_cntrl, pcb))
					return false;
				continue;
			}

			if ((state.idx + 1 < nlexeme) &&
				(lexeme_list.get_type(state.idx + 1) == Lexeme::IS_ASSIGNMENT))
			{
				if (!parse_assignment(state, lexeme_list, exe_cntrl, pcb))
					return false;
				continue;
			}

			if ((state.idx + 1 < nlexeme) &&
				(lexeme_list.get_type(state.idx + 1) == Lexeme::IS_BRACKET_OPEN))
			{
				if (!parse_address_assignment(state, lexeme_list, exe_cntrl, pcb))
					return false;
				continue;
			}

			printf(" CONFIG:> expecting '=' or '{' for name: '%s' (line, %i)\n",
				   lexeme_list.get_token(state.idx), lexeme_list.get_tag(state.idx));
			return false;
		}

		break; // unknown lexeme found
	}

	return true;
}

bool scm::ConfigParser::parse_command(
	parserState &state,
	const LexemeList &lexeme_list,
	const bool exe_cntrl, parserCallBack &pcb)
{
	mem_buffer<int> rpn;
	mem_buffer<int> rpn_key;
	const int nlexeme = lexeme_list.get_size();
	int beg_idx = state.idx;

	if (strcmp(state.name_space, ""))
	{
		// --- nesting commands in namespaces is not allowed!
		printf(" CONFIG:> nested command '%s' found (line, %i)\n",
			   lexeme_list.get_token(beg_idx), lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- setting pointer to first lexeme in expression
	const int expr_begin = state.idx;
	int expr_end;

	// --- matching end of expression
	state.idx++;
	bool is_closed = false;
	while ((state.idx < nlexeme) && (!is_closed))
	{
		if (lexeme_list.get_type(state.idx) == Lexeme::IS_SEMICOLON)
		{
			is_closed = true;
			expr_end = state.idx - 1;
		}
		state.idx++;
	}
	if (!is_closed)
	{
		printf(" CONFIG:> unclosed command '%s' statement, missing ';' (line, %i)\n",
			   lexeme_list.get_token(beg_idx), lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- converting to postfix notation
	if (!get_rpn(rpn, rpn_key, lexeme_list, expr_begin, expr_end))
	{
		printf(" CONFIG:> failed to process command '%s' statement (line, %i)\n",
			   lexeme_list.get_token(beg_idx), lexeme_list.get_tag(beg_idx));
		return false;
	}
	if (rpn.get_value(rpn.get_size() - 1) != beg_idx)
	{
		printf(" CONFIG:> incorrect command '%s' expression (line, %i)\n",
			   lexeme_list.get_token(beg_idx), lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- evaluating command arguments
	cfgCommand command;
	if (!set_command(command, rpn, rpn_key, lexeme_list, exe_cntrl))
	{
		printf(" CONFIG:> failed to set command '%s' (line, %i)\n",
			   lexeme_list.get_token(beg_idx), lexeme_list.get_tag(beg_idx));
		return false;
	}

	if (!exe_cntrl)
		return true;

	// --- calling command
	bool status = pcb.call_back(command);
	if (!status)
	{
		printf(" CONFIG:> command '%s' failed -- stop (line, %i)\n",
			   lexeme_list.get_token(beg_idx), lexeme_list.get_tag(beg_idx));
	}
	return status;
}

bool scm::ConfigParser::parse_name_space(
	parserState &state,
	const LexemeList &lexeme_list,
	const bool exe_cntrl, parserCallBack &pcb)
{
	const int nlexeme = lexeme_list.get_size();
	int beg_idx = state.idx;

	// --- no dots allowed in namespace name at least for now
	if (strchr(lexeme_list.get_token(state.idx), '.') != NULL)
	{
		printf(" CONFIG:> '.' not allowed in namespaces: '%s' (line, %i)\n",
			   lexeme_list.get_token(beg_idx), lexeme_list.get_tag(beg_idx));
		return false;
	}

	state.append_name_space(lexeme_list.get_token(state.idx));
	state.idx += 2;

	if (!parse_block(state, lexeme_list, exe_cntrl, pcb))
		return false;

	// --- closing namespace [}]
	if ((state.idx < nlexeme) && (lexeme_list.get_type(state.idx) == Lexeme::IS_BRACE_CLOSE))
	{
		state.truncate_name_space();
		state.idx++;
		return true;
	}

	// --- unclosed namespace [}]
	printf(" CONFIG:> unclosed namespace '%s', missing '}' (line, %i)\n",
		   lexeme_list.get_token(beg_idx), lexeme_list.get_tag(beg_idx));
	return false;
}

bool scm::ConfigParser::parse_assignment(
	parserState &state,
	const LexemeList &lexeme_list,
	const bool exe_cntrl, parserCallBack &pcb)
{
	mem_buffer<int> rpn;
	mem_buffer<int> rpn_key;
	const int nlexeme = lexeme_list.get_size();
	int beg_idx = state.idx;
	const char *global_varname = state.get_global_name(lexeme_list.get_token(beg_idx));

	// --- setting state at expression: NAME = EXPRESSION;
	state.idx += 2;

	// --- setting pointer to first lexeme in expression
	const int expr_begin = state.idx;
	int expr_end;

	// --- matching end of expression
	bool is_closed = false;
	while ((state.idx < nlexeme) && (!is_closed))
	{
		if (lexeme_list.get_type(state.idx) == Lexeme::IS_SEMICOLON)
		{
			is_closed = true;
			expr_end = state.idx - 1;
		}

		state.idx++;
	}
	if (!is_closed)
	{
		printf(" CONFIG:> unclosed assignment statement '%s = ', missing ';' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- converting to postfix notation
	if (!get_rpn(rpn, rpn_key, lexeme_list, expr_begin, expr_end))
	{
		printf(" CONFIG:> failed to process expression for variable: '%s' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- evaluating postfix expression (need namespace - not full variable name)
	cfgVector value;
	if (!evaluate_rpn(value, rpn, rpn_key, lexeme_list, state.name_space, exe_cntrl))
	{
		printf(" CONFIG:> failed to evaluate expression for variable: '%s' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}
	if ((exe_cntrl) && (!value.is_defined()))
	{
		printf(" CONFIG:> failed to evaluate expression for variable: '%s' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- setting variable name
	value.set_name(global_varname);
	if (value.is_name_empty())
	{
		printf(" CONFIG:> invalid variable name: '%s' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- adding variable
	if (exe_cntrl)
		add_unsafe(value);
	return true;
}

bool scm::ConfigParser::parse_address_assignment(
	parserState &state,
	const LexemeList &lexeme_list,
	const bool exe_cntrl, parserCallBack &pcb)
{
	mem_buffer<int> rpn;
	mem_buffer<int> rpn_key;
	const int nlexeme = lexeme_list.get_size();
	int beg_idx = state.idx;
	const char *global_varname = state.get_global_name(lexeme_list.get_token(beg_idx));

	// --- setting state at index: NAME[INDEX] = EXPRESSION;
	state.idx += 2;

	// --- setting pointer to last lexeme in index
	const int index_begin = state.idx;
	int index_end;

	// --- matching end of index
	bool is_closed = false;
	while ((state.idx < nlexeme) && (!is_closed))
	{
		if ((lexeme_list.get_type(state.idx) == Lexeme::IS_ASSIGNMENT) &&
			(lexeme_list.get_type(state.idx - 1) == Lexeme::IS_BRACKET_CLOSE))
		{
			is_closed = true;
			index_end = state.idx - 2;
		}

		state.idx++;
	}
	if (!is_closed)
	{
		printf(" CONFIG:> unclosed index operator '%s[] = ', missing ']' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}
	if (index_end < index_begin)
	{
		printf(" CONFIG:> no arguments in index operator '%s[] = ' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- converting to postfix notation
	if (!get_rpn(rpn, rpn_key, lexeme_list, index_begin, index_end))
	{
		printf(" CONFIG:> failed to process expression for index of variable: '%s' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- evaluating postfix expression (need namespace - not full variable name)
	cfgVector vec_index;
	if (!evaluate_rpn(vec_index, rpn, rpn_key, lexeme_list, state.name_space, exe_cntrl))
	{
		printf(" CONFIG:> failed to evaluate index of variable: '%s' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}

	int index = 0;
	if ((exe_cntrl | (vec_index.is_defined())) && (!vec_index.get_if_single(&index)))
	{
		printf(" CONFIG:> incorrect argument for index of variable: '%s' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- setting pointer to first lexeme in expression
	const int expr_begin = state.idx;
	int expr_end;

	// --- matching end of expression
	is_closed = false;
	while ((state.idx < nlexeme) && (!is_closed))
	{
		if (lexeme_list.get_type(state.idx) == Lexeme::IS_SEMICOLON)
		{
			is_closed = true;
			expr_end = state.idx - 1;
		}

		state.idx++;
	}
	if (!is_closed)
	{
		printf(" CONFIG:> unclosed assignment statement '%s = ', missing ';' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- converting to postfix notation
	if (!get_rpn(rpn, rpn_key, lexeme_list, expr_begin, expr_end))
	{
		printf(" CONFIG:> failed to process expression for variable: '%s' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- evaluating postfix expression (need namespace - not full variable name)
	cfgVector value;
	if (!evaluate_rpn(value, rpn, rpn_key, lexeme_list, state.name_space, exe_cntrl))
	{
		printf(" CONFIG:> failed to evaluate expression for variable: '%s' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}
	if ((exe_cntrl) && (!value.is_defined()))
	{
		printf(" CONFIG:> failed to evaluate expression for variable: '%s' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}
	if ((exe_cntrl || (value.is_defined())) && (!value.is_single()))
	{
		printf(" CONFIG:> incorrect expression for variable: '%s' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- setting variable name
	value.set_name(global_varname);
	if (value.is_name_empty())
	{
		printf(" CONFIG:> invalid variable name: '%s' (line, %i)\n",
			   global_varname, lexeme_list.get_tag(beg_idx));
		return false;
	}

	if (!exe_cntrl)
		return true;

	// --- adding value only if vector is already allocated
	for (int k = 0; k < nvars; k++)
	{
		if (var[k].is_name_eq(value))
		{
			if ((index < 0) || (index >= var[k].get_size()))
			{
				printf(" CONFIG:> out of range index (%i) for vector(%i): '%s' (line, %i)\n",
					   index, var[k].get_size(), global_varname, lexeme_list.get_tag(beg_idx));
				return false;
			}

			var[k].change(index, value.get(0));
			return true;
		}
	}

	printf(" CONFIG:> variable '%s' doesn't exist (line, %i)\n",
		   global_varname, lexeme_list.get_tag(beg_idx));
	return false;
}

bool scm::ConfigParser::parse_if_statement(
	parserState &state,
	const LexemeList &lexeme_list,
	const bool exe_cntrl,
	parserCallBack &pcb)
{
	mem_buffer<int> rpn;
	mem_buffer<int> rpn_key;
	const int nlexeme = lexeme_list.get_size();
	int beg_idx = state.idx;

	// --- setting state at expression: IF EXPRESSION THEN ...
	state.idx++;

	// --- setting pointer to first lexeme in expression
	const int expr_begin = state.idx;
	int expr_end;

	// --- matching end of expression
	bool is_closed = false;
	while ((state.idx < nlexeme) && (!is_closed))
	{
		if (lexeme_list.get_type(state.idx) == Lexeme::IS_KEY_THEN)
		{
			is_closed = true;
			expr_end = state.idx - 1;
		}
		state.idx++;
	}
	if (!is_closed)
	{
		printf(" CONFIG:> unclosed 'if' statement, missing 'then' (line, %i)\n",
			   lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- converting to postfix notation
	if (!get_rpn(rpn, rpn_key, lexeme_list, expr_begin, expr_end))
	{
		printf(" CONFIG:> failed to process 'if' statement expression (line, %i)\n",
			   lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- evaluating postfix expression
	cfgVector value;
	if (!evaluate_rpn(value, rpn, rpn_key, lexeme_list, state.name_space, exe_cntrl))
	{
		printf(" CONFIG:> failed to evaluate 'if' statement expression, expecting bool value (line, %i)\n",
			   lexeme_list.get_tag(beg_idx));
		return false;
	}

	bool if_cntrl = false;
	if (exe_cntrl || (value.is_defined()))
	{
		if (!value.get_if_single(&if_cntrl))
		{
			printf(" CONFIG:> failed to evaluate 'if' statement expression, expecting bool value (line, %i)\n",
				   lexeme_list.get_tag(beg_idx));
			return false;
		}
	}

	if (!parse_block(state, lexeme_list, (exe_cntrl && if_cntrl), pcb))
		return false;

	// --- processing 'else' case
	if ((state.idx < nlexeme) &&
		(lexeme_list.get_type(state.idx) == Lexeme::IS_KEY_ELSE))
	{
		state.idx++;
		if (!parse_block(state, lexeme_list, (exe_cntrl && (!if_cntrl)), pcb))
			return false;
	}

	if ((state.idx < nlexeme) &&
		(lexeme_list.get_type(state.idx) == Lexeme::IS_KEY_ENDIF))
	{
		state.idx++;
		return true;
	}

	printf(" CONFIG:> unclosed 'if' statement, missing 'endif' (line, %i)\n",
		   lexeme_list.get_tag(beg_idx));
	return false;
}

bool scm::ConfigParser::parse_while_statement(
	parserState &state,
	const LexemeList &lexeme_list,
	const bool exe_cntrl, parserCallBack &pcb)
{
	const int c_max_iters = INT_MAX; // max iters to handle infinite cycles

	mem_buffer<int> rpn;
	mem_buffer<int> rpn_key;
	const int nlexeme = lexeme_list.get_size();
	int beg_idx = state.idx;

	// --- setting state at expression: WHILE EXPRESSION DO ...
	state.idx++;

	// --- setting pointer to first lexeme in expression
	const int expr_begin = state.idx;
	int expr_end;

	// --- matching end of expression
	bool is_closed = false;
	while ((state.idx < nlexeme) && (!is_closed))
	{
		if (lexeme_list.get_type(state.idx) == Lexeme::IS_KEY_DO)
		{
			is_closed = true;
			expr_end = state.idx - 1;
		}
		state.idx++;
	}
	if (!is_closed)
	{
		printf(" CONFIG:> unclosed 'while' statement, missing 'do' (line, %i)\n",
			   lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- evaluating while condition
	bool while_cntrl;

	// converting to postfix notation
	if (!get_rpn(rpn, rpn_key, lexeme_list, expr_begin, expr_end))
	{
		printf(" CONFIG:> failed to process 'while' statement expression (line, %i)\n",
			   lexeme_list.get_tag(beg_idx));
		return false;
	}

	// --- evaluating postfix expression
	cfgVector value;
	if (!evaluate_rpn(value, rpn, rpn_key, lexeme_list, state.name_space, exe_cntrl))
	{
		printf(" CONFIG:> failed to evaluate 'while' statement expression, expecting bool value (line, %i)\n",
			   lexeme_list.get_tag(beg_idx));
		return false;
	}

	while_cntrl = false;
	if (exe_cntrl || (value.is_defined()))
	{
		if (!value.get_if_single(&while_cntrl))
		{
			printf(" CONFIG:> failed to evaluate 'while' statement expression, expecting bool value (line, %i)\n",
				   lexeme_list.get_tag(beg_idx));
			return false;
		}
	}

	// --- pointer to do code beginning
	const int do_beg_idx = state.idx;
	// --- iterations counter
	int iters = 0;

	while (
		(exe_cntrl && while_cntrl) ||
		(iters == 0)) // --- parsing at least iteration to check syntax and move state
	{
		// --- resetting state
		state.idx = do_beg_idx;

		if (!parse_block(state, lexeme_list, (exe_cntrl && while_cntrl), pcb))
			return false;

		// --- checking 'while' statement closure
		if ((state.idx < nlexeme) &&
			(lexeme_list.get_type(state.idx) == Lexeme::IS_KEY_ENDDO))
		{
			state.idx++;

			// --- in case just checking syntax
			if ((!exe_cntrl) || (!while_cntrl))
				break;

			// --- checking if infinite loop
			iters++;
			if (iters == c_max_iters)
			{
				printf(" CONFIG:> max allowed iterations ( = %i) reached for 'while' statement, aborting (line, %i)\n",
					   c_max_iters, lexeme_list.get_tag(beg_idx));
				return false;
			}

			// --- evaluating while condition

			// --- evaluating postfix expression
			cfgVector value;
			if (!evaluate_rpn(value, rpn, rpn_key, lexeme_list, state.name_space, exe_cntrl))
			{
				printf(" CONFIG:> failed to evaluate 'while' statement expression, expecting bool value (line, %i)\n",
					   lexeme_list.get_tag(beg_idx));
				return false;
			}

			if (!value.get_if_single(&while_cntrl))
			{
				printf(" CONFIG:> failed to evaluate 'while' statement expression, expecting bool value (line, %i)\n",
					   lexeme_list.get_tag(beg_idx));
				return false;
			}
		}
		else
		{
			printf(" CONFIG:> unclosed 'while' statement, missing 'enddo' (line, %i)\n",
				   lexeme_list.get_tag(beg_idx));
			return false;
		}
	}

	return true;
}
// -------------------------------------------------------------------------------------------- //

bool scm::ConfigParser::add(const cfgVector &ex)
{
	if (!ex.is_defined())
		return false;
	if (!is_valid_name(ex.get_name()))
		return false;

	return add_unsafe(ex);
}

bool scm::ConfigParser::is_valid_name(const char *name) const
{
	int token_length = (int)strlen(name);

	if (token_length == 0)
		return false;
	if ((!isalpha(name[0])) && (name[0] != '_'))
		return false;

	for (int i = 1; i < token_length; i++)
	{
		if ((!isalnum(name[i])) &&
			(name[i] != '_') && (name[i] != '.'))
			return false;
	}
	if (name[token_length - 1] == '.')
		return false;

	return true;
}

bool scm::ConfigParser::add_unsafe(const cfgVector &ex)
{
	// --- just overwriting variable if already exists
	for (int k = 0; k < nvars; k++)
	{
		if (var[k].is_name_eq(ex))
		{
			var[k] = ex;
			return true;
		}
	}

	if (nvars >= nalloc_vars)
	{
		cfgVector *hvar = new cfgVector[nalloc_vars + c_alloc_vars_init];
		for (int i = 0; i < nalloc_vars; i++)
		{
			hvar[i] = var[i];
		}

		delete[] var;
		var = hvar;

		nalloc_vars += c_alloc_vars_init;
	}

	var[nvars] = ex;
	nvars++;
	return true;
}
// -------------------------------------------------------------------------------------------- //

int scm::ConfigParser::operator_priority(
	const Lexeme::TYPE type)
{
	if ((type == Lexeme::IS_OP_LOGICAL_AND) || (type == Lexeme::IS_OP_LOGICAL_OR))
		return 1;
	if ((type == Lexeme::IS_OP_EQ) || (type == Lexeme::IS_OP_NEQ))
		return 2;
	if ((type == Lexeme::IS_OP_LT) || (type == Lexeme::IS_OP_GT) ||
		(type == Lexeme::IS_OP_LEQ) || (type == Lexeme::IS_OP_GEQ))
		return 3;
	if ((type == Lexeme::IS_OP_ADD) || (type == Lexeme::IS_OP_SUB))
		return 4;
	if ((type == Lexeme::IS_OP_MUL) || (type == Lexeme::IS_OP_DIV) ||
		(type == Lexeme::IS_OP_MOD))
		return 5;
	if ((type == Lexeme::IS_OP_PLUS) || (type == Lexeme::IS_OP_MINUS))
		return 6;
	if ((type == Lexeme::IS_OP_EXP))
		return 7;

	return 0;
}

bool scm::ConfigParser::operator_priority_lt(
	const Lexeme::TYPE typeA, const Lexeme::TYPE typeB)
{
	return (operator_priority(typeA) < operator_priority(typeB));
}

bool scm::ConfigParser::is_operator_binary(const Lexeme::TYPE type)
{
	return (
		(type == Lexeme::IS_OP_ADD) || (type == Lexeme::IS_OP_SUB) ||
		(type == Lexeme::IS_OP_MUL) || (type == Lexeme::IS_OP_DIV) ||
		(type == Lexeme::IS_OP_MOD) ||
		(type == Lexeme::IS_OP_EXP) ||
		(type == Lexeme::IS_OP_EQ) || (type == Lexeme::IS_OP_NEQ) ||
		(type == Lexeme::IS_OP_LOGICAL_AND) || (type == Lexeme::IS_OP_LOGICAL_OR) ||
		(type == Lexeme::IS_OP_LT) || (type == Lexeme::IS_OP_GT) ||
		(type == Lexeme::IS_OP_LEQ) || (type == Lexeme::IS_OP_GEQ));
}

bool scm::ConfigParser::is_operator_unary(const Lexeme::TYPE type)
{
	return ((type == Lexeme::IS_OP_PLUS) || (type == Lexeme::IS_OP_MINUS));
}

bool scm::ConfigParser::is_operator_assoc_right(const Lexeme::TYPE type)
{
	return ((type == Lexeme::IS_OP_EXP) ||
			(type == Lexeme::IS_OP_PLUS) || (type == Lexeme::IS_OP_MINUS));
}

bool scm::ConfigParser::is_operator_assoc_left(const Lexeme::TYPE type)
{
	return (
		(type == Lexeme::IS_OP_ADD) || (type == Lexeme::IS_OP_SUB) ||
		(type == Lexeme::IS_OP_MUL) || (type == Lexeme::IS_OP_DIV) ||
		(type == Lexeme::IS_OP_MOD) ||
		(type == Lexeme::IS_OP_EQ) || (type == Lexeme::IS_OP_NEQ) ||
		(type == Lexeme::IS_OP_LOGICAL_AND) || (type == Lexeme::IS_OP_LOGICAL_OR) ||
		(type == Lexeme::IS_OP_LT) || (type == Lexeme::IS_OP_GT) ||
		(type == Lexeme::IS_OP_LEQ) || (type == Lexeme::IS_OP_GEQ));
}

int scm::ConfigParser::function_nargs(const Lexeme::TYPE type)
{
	if ((type == Lexeme::IS_MIN_FUNCTION) || (type == Lexeme::IS_MAX_FUNCTION) ||
		(type == Lexeme::IS_DOT_PRODUCT_FUNCTION) ||
		(type == Lexeme::IS_UNIRAND_FUNCTION))
		return 2;
	if ((type == Lexeme::IS_VECMIN_FUNCTION) || (type == Lexeme::IS_VECMAX_FUNCTION) ||
		(type == Lexeme::IS_SIN_FUNCTION) || (type == Lexeme::IS_COS_FUNCTION) ||
		(type == Lexeme::IS_TAN_FUNCTION) ||
		(type == Lexeme::IS_LOG_FUNCTION) ||
		(type == Lexeme::IS_SQRT_FUNCTION) ||
		(type == Lexeme::IS_ABS_FUNCTION) ||
		(type == Lexeme::IS_L2NORM_FUNCTION) || (type == Lexeme::IS_CNORM_FUNCTION) ||
		(type == Lexeme::IS_TO_STRING_FUNCTION) ||
		(type == Lexeme::IS_SIZE_FUNCTION) ||
		(type == Lexeme::IS_DEFINED_FUNCTION))
		return 1;

	return 0;
}
// -------------------------------------------------------------------------------------------- //

// * get & check calls * //
// -------------------------------------------------------------------------------------------- //
bool scm::ConfigParser::is_varname(const char *name) const
{
	for (int i = 0; i < nvars; i++)
		if (var[i].is_name_eq(name))
			return true;

	return false;
}

const scm::cfgVector
scm::ConfigParser::get_variable(const int idx) const
{
	if ((idx < 0) || (idx >= nvars))
		return cfgVector();

	return var[idx];
}

const scm::cfgVector
scm::ConfigParser::get_variable(const char *name) const
{
	for (int k = 0; k < nvars; k++)
	{
		if (var[k].is_name_eq(name))
			return var[k];
	}

	return cfgVector();
}

void scm::ConfigParser::print() const
{
	for (int i = 0; i < nvars; i++)
	{
		var[i].print();
		getc(stdin);
	}
}

bool scm::ConfigParser::get_value(const char *name, int *value) const
{
	cfgVector vec = get_variable(name);
	if (vec.get_size() > 1)
	{
		printf(" CONFIG:> failed to set (int) variable: '%s' -- vector (size > 1)\n", name);
		return false;
	}

	cfgValue var = vec.get(0);
	cfgValue::VAR_TYPE type = var.get_type();

	if (type == cfgValue::IS_INT)
		return var.get(value);
	else if (type == cfgValue::IS_DOUBLE)
	{
		printf(" CONFIG:> failed to set (int) variable: '%s' - declared as double\n",
			   name);
		return false;
	}
	else if (type == cfgValue::IS_STRING)
	{
		printf(" CONFIG:> failed to set (int) variable: '%s' - declared as string\n",
			   name);
		return false;
	}
	else if (type == cfgValue::IS_BOOLEAN)
	{
		printf(" CONFIG:> failed to set (int) variable: '%s' - declared as boolean\n",
			   name);
		return false;
	}

	printf(" CONFIG:> failed to set (int) variable: '%s'\n", name);
	return false;
}

bool scm::ConfigParser::get_value(const char *name, float *value) const
{
	cfgVector vec = get_variable(name);
	if (vec.get_size() > 1)
	{
		printf(" CONFIG:> failed to set (float) variable: '%s' -- vector (size > 1)\n", name);
		return false;
	}

	cfgValue var = vec.get(0);
	cfgValue::VAR_TYPE type = var.get_type();

	if (type == cfgValue::IS_INT)
		return var.get(value);
	else if (type == cfgValue::IS_DOUBLE)
		return var.get(value);
	else if (type == cfgValue::IS_STRING)
	{
		printf(" CONFIG:> failed to set (float) variable: '%s' - declared as string\n",
			   name);
		return false;
	}
	else if (type == cfgValue::IS_BOOLEAN)
	{
		printf(" CONFIG:> failed to set (float) variable: '%s' - declared as boolean\n",
			   name);
		return false;
	}

	printf(" CONFIG:> failed to set (float) variable: '%s' - undefined\n",
		   name);
	return false;
}

bool scm::ConfigParser::get_value(const char *name, double *value) const
{
	cfgVector vec = get_variable(name);
	if (vec.get_size() > 1)
	{
		printf(" CONFIG:> failed to set (double) variable: '%s' -- vector (size > 1)\n", name);
		return false;
	}

	cfgValue var = vec.get(0);
	cfgValue::VAR_TYPE type = var.get_type();

	if (type == cfgValue::IS_INT)
		return var.get(value);
	else if (type == cfgValue::IS_DOUBLE)
		return var.get(value);
	else if (type == cfgValue::IS_STRING)
	{
		printf(" CONFIG:> failed to set (double) variable: '%s' - declared as string\n",
			   name);
		return false;
	}
	else if (type == cfgValue::IS_BOOLEAN)
	{
		printf(" CONFIG:> failed to set (double) variable: '%s' - declared as boolean\n",
			   name);
		return false;
	}

	printf(" CONFIG:> failed to set (double) variable: '%s' - undefined\n",
		   name);
	return false;
}

bool scm::ConfigParser::get_value(const char *name, long double *value) const
{
	cfgVector vec = get_variable(name);
	if (vec.get_size() > 1)
	{
		printf(" CONFIG:> failed to set (double) variable: '%s' -- vector (size > 1)\n", name);
		return false;
	}

	cfgValue var = vec.get(0);
	cfgValue::VAR_TYPE type = var.get_type();

	if (type == cfgValue::IS_INT)
		var.get(value);
	else if (type == cfgValue::IS_DOUBLE)
		var.get(value);
	else if (type == cfgValue::IS_STRING)
	{
		printf(" CONFIG:> failed to set (double) variable: '%s' - declared as string\n",
			   name);
		return false;
	}
	else if (type == cfgValue::IS_BOOLEAN)
	{
		printf(" CONFIG:> failed to set (double) variable: '%s' - declared as boolean\n",
			   name);
		return false;
	}

	printf(" CONFIG:> failed to set (double) variable: '%s' - undefined\n",
		   name);
	return false;
}

bool scm::ConfigParser::get_value(const char *name, char **value) const
{
	cfgVector vec = get_variable(name);
	if (vec.get_size() > 1)
	{
		printf(" CONFIG:> failed to set (string) variable: '%s' -- vector (size > 1)\n", name);
		return false;
	}

	cfgValue var = vec.get(0);
	cfgValue::VAR_TYPE type = var.get_type();

	if (type == cfgValue::IS_INT)
	{
		printf(" CONFIG:> failed to set (string) variable: '%s' - declared as int\n",
			   name);
		return false;
	}
	else if (type == cfgValue::IS_DOUBLE)
	{
		printf(" CONFIG:> failed to set (string) variable: '%s' - declared as double\n",
			   name);
		return false;
	}
	else if (type == cfgValue::IS_STRING)
		return var.get(value);
	else if (type == cfgValue::IS_BOOLEAN)
	{
		printf(" CONFIG:> failed to set (string) variable: '%s' - declared as boolean\n",
			   name);
		return false;
	}

	printf(" CONFIG:> failed to set (string) variable: '%s' - undefined\n",
		   name);
	return false;
}

bool scm::ConfigParser::get_value(const char *name, std::string &value) const
{
	cfgVector vec = get_variable(name);
	if (vec.get_size() > 1)
	{
		printf(" CONFIG:> failed to set (string) variable: '%s' -- vector (size > 1)\n", name);
		return false;
	}

	cfgValue var = vec.get(0);
	cfgValue::VAR_TYPE type = var.get_type();

	if (type == cfgValue::IS_INT)
	{
		printf(" CONFIG:> failed to set (string) variable: '%s' - declared as int\n",
			   name);
		return false;
	}
	else if (type == cfgValue::IS_DOUBLE)
	{
		printf(" CONFIG:> failed to set (string) variable: '%s' - declared as double\n",
			   name);
		return false;
	}
	else if (type == cfgValue::IS_STRING)
		return var.get(value);
	else if (type == cfgValue::IS_BOOLEAN)
	{
		printf(" CONFIG:> failed to set (string) variable: '%s' - declared as boolean\n",
			   name);
		return false;
	}

	printf(" CONFIG:> failed to set (string) variable: '%s' - undefined\n",
		   name);
	return false;
}

bool scm::ConfigParser::get_value(const char *name, bool *value) const
{
	cfgVector vec = get_variable(name);
	if (vec.get_size() > 1)
	{
		printf(" CONFIG:> failed to set (bool) variable: '%s' -- vector (size > 1)\n", name);
		return false;
	}

	cfgValue var = vec.get(0);
	cfgValue::VAR_TYPE type = var.get_type();

	if (type == cfgValue::IS_INT)
	{
		printf(" CONFIG:> failed to set (bool) variable: '%s' - declared as int\n",
			   name);
		return false;
	}
	else if (type == cfgValue::IS_DOUBLE)
	{
		printf(" CONFIG:> failed to set (bool) variable: '%s' - declared as double\n",
			   name);
		return false;
	}
	else if (type == cfgValue::IS_STRING)
	{
		printf(" CONFIG:> failed to set (bool) variable: '%s' - declared as string\n",
			   name);
		return false;
	}
	else if (type == cfgValue::IS_BOOLEAN)
		return var.get(value);

	printf(" CONFIG:> failed to set (bool) variable: '%s'\n", name);
	return false;
}
// -------------------------------------------------------------------------------------------- //