#include "global.h"
#include "csyntactic.h"
#include "clexical.h"
#include <stdlib.h>
#include "cdatabase.h"
#include "cmemory.h"
#include "cstringtable.h"
#include "resource.h"
#include "cgraph.h"
#include "cdatalog.h"
#include <stddef.h>	//kvoli makru offsetof
#include "cinfowin.h"

CSyntacticAnalyzer::CSyntacticAnalyzer(void *input)
{
	lexicalAnalyzer = _NEW(CLexicalAnalyzer(input));
	table = NULL;
	output = true;
}

void CSyntacticAnalyzer::DestroyTable()
{
	if (table)
	{
		SYNTACTIC_TABLE_RULES *_table = (SYNTACTIC_TABLE_RULES*)table->GetPointer();

		//uvolni datove struktury
		for (DWORD i=_table->nEdb; i<_table->nAtoms; i++)
		{
			SYNTACTIC_ATOM *atom = _table->atoms[i];
			switch (atom->idb.type)
			{
			case SYNTACTIC_ATOM_IDBATOM:
			case SYNTACTIC_ATOM_ANSWER:
			case SYNTACTIC_ATOM_DROPPEDIDBATOM:
				for (DWORD j=0; j<atom->idb.nRules; j++)
				{
					SYNTACTIC_RULE *rule = atom->idb.rules[j];
					_DELETE(rule->varNames);
				}
				break;
			}
		}
		_DELETE(table);
		table = NULL;
	}
}

CSyntacticAnalyzer::~CSyntacticAnalyzer()
{
	_DELETE(lexicalAnalyzer);
	if (table)
		DestroyTable();
}

SYNTACTIC_ATOM_FULLNAME_ARIT* CSyntacticAnalyzer::ReadExpression(bool asArgument)
{
	CMemory *mem = _NEW(CMemory());
	CMemory *str = _NEW(CMemory());
	CMemory *ptr = _NEW(CMemory());

	SYNTACTIC_ATOM_FULLNAME_ARIT arit;
	arit.nVar = 0;

	const char *token;
	DWORD tokenType;

	DWORD i = 0;//pointer v retazci
	int parenthesis = 0;//pocet zatvoriek

	DWORD nextLexema = SYNTACTIC_STRING | SYNTACTIC_LEFTPARENTHESIS | SYNTACTIC_OPERATOR_PLSMIN | SYNTACTIC_OPERATOR_VARNUM;

	bool loop = true;
	bool rollback = false;//vratit spat poslendy token?
	arit.type = 0;

	try
	{
		while (loop)
		{ 
			tokenType = lexicalAnalyzer->ReadToken_NoWhiteSpaces(&token);
			switch (tokenType)
			{
			case LEXICAL_TOKEN_STRING:
				if (nextLexema & SYNTACTIC_STRING)
				{
					arit.type = SYNTACTIC_ARG_STRING;
					nextLexema = SYNTACTIC_EOL;//uz nic nechceme
				}
				else
				{
/*					if (nextLexema & SYNTACTIC_EOL)
					{
						loop = false; rollback = true; continue;
					}*/
					throw "Chybný výraz - neočakávaný string!";
				}
				break;
			case LEXICAL_TOKEN_EOL:
				loop = false;
				continue;
				break;
			case LEXICAL_TOKEN_LEFTPARENTHESIS:
				if (nextLexema & SYNTACTIC_LEFTPARENTHESIS)
				{
					//vyraz uz nemoze byt SYNTACTIC_ARG_VARIABLE
					if (!arit.type)
						arit.type = SYNTACTIC_ARG_NUMERIC;

					parenthesis++;
					nextLexema = SYNTACTIC_EOL | SYNTACTIC_LEFTPARENTHESIS | SYNTACTIC_OPERATOR_PLSMIN | SYNTACTIC_OPERATOR_VARNUM;
				}
				else
				{
					//nevhodny token, ale mozno uz mozme skoncit..
					if (nextLexema & SYNTACTIC_EOL)
					{
						loop = false; rollback = true; continue;
					}
					throw "Chybný výraz - neočakávaný znak ’(’!";
				}
				break;
			case LEXICAL_TOKEN_RIGHTPARENTHESIS:
				if (nextLexema & SYNTACTIC_RIGHTPARENTHESIS)
				{
					if (!parenthesis && asArgument)
					{
						//prava zatvorka navyse a citame argument, skonci
						loop = false; rollback = true; continue;
					}
					parenthesis--;
					nextLexema = SYNTACTIC_RIGHTPARENTHESIS | SYNTACTIC_EOL | SYNTACTIC_OPERATOR_PLSMIN | SYNTACTIC_OPERATOR_MULDIV;
				}
				else
				{
					//nevhodny token, ale mozno uz mozme skoncit..
					if (nextLexema & SYNTACTIC_EOL)
					{
						loop = false; rollback = true; continue;
					}
					throw "Chybný výraz - neočakávaný znak ’)’!";
				}
				break;
			case LEXICAL_TOKEN_DIVIDE:
			case LEXICAL_TOKEN_MULTIPLY:
				if (nextLexema & SYNTACTIC_OPERATOR_MULDIV)
					nextLexema = SYNTACTIC_OPERATOR_VARNUM | SYNTACTIC_LEFTPARENTHESIS;
				else
				{
					//nevhodny token, ale mozno uz mozme skoncit..
					if (nextLexema & SYNTACTIC_EOL)
					{
						loop = false; rollback = true; continue;
					}
					if (tokenType == LEXICAL_TOKEN_DIVIDE)
						throw "Chybný výraz - neočakávaný znak ’/’!";
					else
						throw "Chybný výraz - neočakávaný znak ’*’!";
				}
				break;
			case LEXICAL_TOKEN_PLUS:
			case LEXICAL_TOKEN_MINUS:
				if (nextLexema & SYNTACTIC_OPERATOR_PLSMIN)
					nextLexema = SYNTACTIC_OPERATOR_VARNUM | SYNTACTIC_LEFTPARENTHESIS;
				else
				{
					//nevhodny token, ale mozno uz mozme skoncit..
					if (nextLexema & SYNTACTIC_EOL)
					{
						loop = false; rollback = true; continue;
					}
					if (tokenType == LEXICAL_TOKEN_PLUS)
						throw "Chybný výraz - neočakávaný znak ’+’!";
					else
						throw "Chybný výraz - neočakávaný znak ’-’!";
				}
				break;
			case LEXICAL_TOKEN_IDENTIFIER:
				if (nextLexema & SYNTACTIC_OPERATOR_VARNUM)
				{
					if (!arit.type)
						arit.type = SYNTACTIC_ARG_VARIABLE;
					else
						arit.type = SYNTACTIC_ARG_COMPOSE;

					//zaznac poziciu premennej
					arit.nVar++;
					ptr->AddData(&i, 4);//pridaj pointer na tuto premennu v retazci
					DWORD data = strlen(token);
					ptr->AddData(&data, 4);//pridaj dlzku premennej

					nextLexema = SYNTACTIC_EOL | SYNTACTIC_RIGHTPARENTHESIS | SYNTACTIC_EOL | SYNTACTIC_OPERATOR_PLSMIN | SYNTACTIC_OPERATOR_MULDIV;
				}
				else
				{
					//nevhodny token, ale mozno uz mozme skoncit..
					if (nextLexema & SYNTACTIC_EOL)
					{
						//robime rollback, lebo sme nacitali o jeden token viac

						/*
						if (tokenType == LEXICAL_TOKEN_IDENTIFIER)
							arit.nVar--;//tento identifikator (token) uz nepatri do vystupu
						*/

						loop = false; rollback = true; continue;
					}
					throw "Chybný výraz - neočakávaný identifikátor!";
				}
				break;
			case LEXICAL_TOKEN_NUMBER:
				if (nextLexema & SYNTACTIC_OPERATOR_VARNUM)
				{
					if (!arit.type || (arit.type == SYNTACTIC_ARG_NUMERIC) )
						arit.type = SYNTACTIC_ARG_NUMERIC;
					else
						arit.type = SYNTACTIC_ARG_COMPOSE;

					nextLexema = SYNTACTIC_EOL | SYNTACTIC_RIGHTPARENTHESIS | SYNTACTIC_EOL | SYNTACTIC_OPERATOR_PLSMIN | SYNTACTIC_OPERATOR_MULDIV;
				}
				else
				{
					//nevhodny token, ale mozno uz mozme skoncit..
					if (nextLexema & SYNTACTIC_EOL)
					{
						//robime rollback, lebo sme nacitali o jeden token viac
						if (tokenType == LEXICAL_TOKEN_IDENTIFIER)
						{
							//TODO
							throw "readexpression: arit.nvar--????";
							arit.nVar--;//tento identifikator (token) uz nepatri do vystupu
						}
						loop = false; rollback = true; continue;
					}
					throw "Chybný výraz - neočakávané číslo!";
				}
				break;
			case LEXICAL_TOKEN_KEYWORD:
				if ( (!stricmp(token, "avg")) || (!stricmp(token, "sum")) || (!stricmp(token, "min")) || (!stricmp(token, "max")) ||
					 (!stricmp(token, "count")) )
				{
					if (nextLexema & SYNTACTIC_OPERATOR_VARNUM)
					{
						
						arit.type = SYNTACTIC_ARG_COMPOSE;

						DWORD len;

						//zaznac nazov funkcie
						arit.nVar++; ptr->AddData(&i, 4); len = strlen(token); ptr->AddData(&len, 4);
						str->AddData((void*)token, len); i += len;

						//musi nasledovat zatvorka
						tokenType = lexicalAnalyzer->ReadToken(&token);
						if (tokenType != LEXICAL_TOKEN_LEFTPARENTHESIS)
						{
							switch (tokenType)
							{
							case LEXICAL_TOKEN_WHITESPACE:
							case LEXICAL_TOKEN_EOL:
								throw "Za názvom agregačej funkcie musí nasledovať ’(’!";
							default:
								if (output)
								{
									char *tmp = CMemory::printstr("Za názvom agregačej funkcie musí nasledovať ’(’, nie ’%s’!",
										token);
									app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
								}
								throw "";
							}
						}
						len = strlen(token); str->AddData((void*)token, len); i += len;

						//musi nasledovat premenna
						tokenType = lexicalAnalyzer->ReadToken(&token);
						if (tokenType != LEXICAL_TOKEN_IDENTIFIER)
						{
							switch (tokenType)
							{
							case LEXICAL_TOKEN_WHITESPACE:
							case LEXICAL_TOKEN_EOL:
								throw "V agregačej funkcii musí nasledovať premenná!";
							default:
								if (output)
								{
									char *tmp = CMemory::printstr("V agregačej funkcii musí nasledovať premenná, nie ’%s’!",
										token);
									app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
								}
								throw "";
							}
						}
						//zaznac premennu
						arit.nVar++; ptr->AddData(&i, 4); len = strlen(token); ptr->AddData(&len, 4);
						str->AddData((void*)token, len); i += len;

						//prava zatvorka
						tokenType = lexicalAnalyzer->ReadToken(&token);
						if (tokenType != LEXICAL_TOKEN_RIGHTPARENTHESIS)
						{
							switch (tokenType)
							{
							case LEXICAL_TOKEN_WHITESPACE:
							case LEXICAL_TOKEN_EOL:
								throw "V agregačej funkcii musí nasledovať ’)’!";
							default:
								if (output)
								{
									char *tmp = CMemory::printstr("V agregačej funkcii musí nasledovať ’)’, nie ’%s’!",
										token);
									app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
								}
								throw "";
							}
						}
						//zatvorka sa zapise na konci

						nextLexema = SYNTACTIC_EOL | SYNTACTIC_RIGHTPARENTHESIS | SYNTACTIC_EOL | SYNTACTIC_OPERATOR_PLSMIN | SYNTACTIC_OPERATOR_MULDIV;
					}
					else
						throw "Kľúčové slovo použité v nesprávnom kontexte!";
				}
				else
				{
					if (nextLexema & SYNTACTIC_EOL)
					{
						//nevhodny token, ale mozme skoncit
						loop = false; rollback = true; continue;
					}
					else
						throw "Kľúčové slovo použité v nesprávnom kontexte!";
				}
				break;
			default:
				if (!(nextLexema & SYNTACTIC_EOL))//ak necakame koniec riadku a mame neznamy token..
				{
					if (output)
					{
						char *tmp = CMemory::printstr("Neočakávaný token ’%s’ vo výraze!", token);
						app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
					}
					throw "";
				}
				else
				{
					loop = false;
					rollback = true;
					continue;//neznamy token, ale cakame koniec riadku, takze skonci
				}
			}
			//token sme spracovali; patri do vyrazu; pridaj ho do vystupu
			DWORD len = strlen(token);
			str->AddData((void*)token, len);
			i += len;//uprav pointer
		}
		if (!(nextLexema & SYNTACTIC_EOL))
			throw "Chybný výraz - neočakávaný koniec výrazu!";

		if (parenthesis)
			throw "Chybne ozátvorkovaný výraz!";

	} catch (char *err)
	{
		_DELETE(mem); _DELETE(str); _DELETE(ptr);
		throw err;//koniec
	}

	if (rollback)
		lexicalAnalyzer->RollbackToken();

	BYTE c = 0;
	str->AddData(&c, 1);//ukoncime stringovy retazec

	mem->AddData(&arit, sizeof(SYNTACTIC_ATOM_FULLNAME_ARIT));
	mem->AddData(ptr->GetPointer(), ptr->GetSize());//hned za strukturu vlozime pole pointrov
	DWORD p = mem->AddData(str->GetPointer(), str->GetSize());//a potom za to samotny vyraz

	//priprav navratovu strukturu
	SYNTACTIC_ATOM_FULLNAME_ARIT* ret = (SYNTACTIC_ATOM_FULLNAME_ARIT*)mallocx(mem->GetSize());
	::CopyMemory(ret, mem->GetPointer(), mem->GetSize());

	ret->expression = (char*)(p + DWORD(ret));//uprav pointer

	_DELETE(mem); _DELETE(str); _DELETE(ptr);

	return ret;
}

//nacita atom, ulozi jeho meno a mena jeho argumentov
SYNTACTIC_ATOM_FULLNAME *CSyntacticAnalyzer::ReadAtom()
{
	SYNTACTIC_ATOM_FULLNAME a;
	//SYNTACTIC_ATOM_FULLNAME* _ret = (SYNTACTIC_ATOM_FULLNAME*)ret->GetPointer();
	CMemory *ret = _NEW(CMemory());
	CMemory *tmp = _NEW(CMemory());
	CMemory *tmp2 = _NEW(CMemory());

	CMemory *pargs = NULL;//pointer na parametre
	SYNTACTIC_ATOM_FULLNAME_ARIT *aritL = NULL,*aritR = NULL;
	const char *token;

	try
	{
		DWORD tokenType = lexicalAnalyzer->ReadToken_NoWhiteSpaces(&token);

		//negovany atom?
		if (!stricmp(token,"not") && (lexicalAnalyzer->LookAheadToken() == LEXICAL_TOKEN_WHITESPACE) )
		{
			a.rel.not = true;
			tokenType = lexicalAnalyzer->ReadToken_NoWhiteSpaces(&token);
		} else
			a.rel.not = false;

		if (tokenType == LEXICAL_TOKEN_EOL)
			throw "Neočakávaný koniec riadka - očakávaný atóm!";

		//---------------------------------------------------------------------------------------
		if (!stricmp(token, "groupby"))
		{
			a.groupby.type = SYNTACTIC_ATOM_GROUPBYATOM;
			a.groupby.nVar = 0;

			if (a.groupby.not)
				throw "Predikát groupby nemôže byť negovaný!";

			tokenType = lexicalAnalyzer->ReadToken(&token);
			if (tokenType != LEXICAL_TOKEN_LEFTPARENTHESIS)
			{
				if ( (tokenType != LEXICAL_TOKEN_WHITESPACE) && (tokenType != LEXICAL_TOKEN_EOL))
				{
					if (output)
					{
						char *tmp = CMemory::printstr("Za kľúčovým slovom groupby musí nasledovať ’(’, nie ’%s’!", token);
						app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
					}
					throw "";
				}
				else
					throw "Za kľúčovým slovom groupby musí nasledovať ’(’!";
			}
			//teraz nacitam zoznam premennych oddelenych ciarkov, podla ktorych sa v selecte bude agregovat
			while (true)
			{
				//premenna
				tokenType = lexicalAnalyzer->ReadToken(&token);
				if (tokenType != LEXICAL_TOKEN_IDENTIFIER)
				{
					if (tokenType == LEXICAL_TOKEN_UNDERSCORE)
						throw "V groupby predikáte sa nemôžu nachádazať anonymné premenné!";
					if ( (tokenType != LEXICAL_TOKEN_WHITESPACE) && (tokenType != LEXICAL_TOKEN_EOL))
					{
						if (output)
						{
							char *tmp = CMemory::printstr("V groupby predikáte očakávaná premenná, nie ’%s’!", token);
							app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
						}
						throw "";
					}
					else
						throw "V groupby predikáte očakávaná premenná!";
				}
				//zapis data
				DWORD ofs = tmp->AddData((char*)token, strlen(token)+1);
				tmp2->AddData(&ofs, 4);
				a.groupby.nVar++;
				//alebo ciarka alebo zatvorka
				tokenType = lexicalAnalyzer->ReadToken_NoWhiteSpaces(&token);
				if (tokenType == LEXICAL_TOKEN_RIGHTPARENTHESIS)
					break;
				if (tokenType != LEXICAL_TOKEN_COMMA)
				{
					if ( (tokenType != LEXICAL_TOKEN_WHITESPACE) && (tokenType != LEXICAL_TOKEN_EOL))
					{
						if (output)
						{
							char *tmp = CMemory::printstr("V groupby predikáte očakávaný znak ’,’ alebo ’)’, nie ’%s’!", token);
							app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
						}
						throw "";
					}
					else
						throw "V groupby predikáte očakávaný znak ’,’ alebo ’)’!";
				}
			}

			ret->AddData(&a, sizeof(SYNTACTIC_ATOM_FULLNAME_GROUPBY));
			ret->AddData(tmp2->GetPointer(), tmp2->GetSize());
			ret->AddData(tmp->GetPointer(), tmp->GetSize());
		}
		else
		//relacny atom?
		if ( ((tokenType == LEXICAL_TOKEN_IDENTIFIER) || (!stricmp(token, "answer")) ) &&
			(lexicalAnalyzer->LookAheadToken() == LEXICAL_TOKEN_LEFTPARENTHESIS) )
		{
		
			a.rel.type = SYNTACTIC_ATOM_RELATOM;
			a.rel.nArgs = 0;
			a.rel.name = (char*)tmp->AddData((char*)token, strlen(token)+1);//skopiruj meno atomu
			char* atomName = (char*)(DWORD(a.rel.name) + DWORD(tmp->GetPointer()));

			lexicalAnalyzer->ReadToken_NoWhiteSpaces(&token);//lava zatvorka

			//nacitaj argumenty atomu
			pargs = _NEW(CMemory());
			while (true)
			{
				if (lexicalAnalyzer->LookAheadToken() == LEXICAL_TOKEN_WHITESPACE)
					lexicalAnalyzer->ReadToken(&token);
				if (lexicalAnalyzer->LookAheadToken() == LEXICAL_TOKEN_EOL)
				{
					if (output)
					{
						char *tmp = CMemory::printstr("V relačnom atóme ’%s’ nie sú definované argumenty!", atomName);
						app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
					}
					throw "";
				}
				
				//argumentom relacneho atomu moze byt anonymna premenna _, konstanta alebo vyraz
				if (lexicalAnalyzer->LookAheadToken() == LEXICAL_TOKEN_UNDERSCORE)
				{
					if (a.rel.not)
					{
						if (output)
						{
							//v negovanom relacnom atome nemozu byt anonymne premenne (vsetky premenne musia byt viazane)
							char *tmp = CMemory::printstr("V negovanom relačnom atóme ’%s’ sa nemôžu ako argumenty nachádzať anonymné premenné!", atomName);
							app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
						}
						throw "";
					}
					lexicalAnalyzer->ReadToken(&token);
					char c=SYNTACTIC_ARG_ANONYMOUS; tmp2->AddData(&c,1);//uloz typ
					pargs->AddData(NULL, 4);//uloz prazdny pointer (data nemame)
				}
				else
				{
					//konstanta alebo vyraz
					SYNTACTIC_ATOM_FULLNAME_ARIT *exp = ReadExpression(true);
					char c = exp->type; tmp2->AddData(&c,1);//uloz typ
					switch (c)
					{
					case SYNTACTIC_ARG_COMPOSE:
						pargs->AddData(&exp, 4);//miesto pointra uloz pointer na aritmeticky vyraz
						//..potrebujem nazvy premennych (kvoli StringToExpression)
						break;
					default:
						{
							int p = tmp->AddData(exp->expression, strlen(exp->expression)+1);//uloz data
							pargs->AddData(&p, 4);//uloz pointer na data
							freex(exp);
						}
					}
				}

				a.rel.nArgs++;//dalsi parameter

				//ideme dalej

				tokenType = lexicalAnalyzer->ReadToken_NoWhiteSpaces(&token);
				//bud ciarka, alebo koniec zatvorky
				if (tokenType == LEXICAL_TOKEN_EOL)
				{
					if (output)
					{
						char *tmp = CMemory::printstr("V relačnom atóme ’%s’ očakávaný znak ’)’!", atomName);
						app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
					}
					throw "";
				}
				if (tokenType == LEXICAL_TOKEN_RIGHTPARENTHESIS)
					break;//koniec parametrov
				if (tokenType != LEXICAL_TOKEN_COMMA)
				{
					if (output)
					{
						char *tmp = CMemory::printstr("V relačnom atóme ’%s’ očakávaný znak ’,’!", atomName);
						app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
					}
					throw "";
				}
			}

			//parametre relacneho atomu su nacitane
			//v tmp su vsetky data, pointre na jednotlive argumenty su v pargs

			ret->AddData(&a, sizeof(SYNTACTIC_ATOM_FULLNAME_REL));//hlavna struktura
			//hned za hlavnou strukturou ide pole pointrov na argumenty
			ret->AddData(pargs->GetPointer(), pargs->GetSize());
			//a potom samotne argumenty
			DWORD ofs = ret->AddData(tmp->GetPointer(), tmp->GetSize());
			//SYNTACTIC_ATOM_FULLNAME* safl = (SYNTACTIC_ATOM_FULLNAME*)ret->GetPointer();
			PTR(SYNTACTIC_ATOM_FULLNAME*,ret)->rel.name = (char*)(DWORD(PTR(SYNTACTIC_ATOM_FULLNAME*,ret)->rel.name) + ofs); //uprav ptr, meno atomu
			//a este ich typ
			ofs = ret->AddData(tmp2->GetPointer(), tmp2->GetSize());
			PTR(SYNTACTIC_ATOM_FULLNAME*,ret)->rel.argTypes = (BYTE*)(DWORD(PTR(SYNTACTIC_ATOM_FULLNAME*,ret)) + ofs); //uprav ptr, meno atomu

			//uprav pointre
			DWORD plus = sizeof(SYNTACTIC_ATOM_FULLNAME_REL) + pargs->GetSize();
			for (DWORD i=0; i<a.rel.nArgs; i++)
			{
				switch (PTR(SYNTACTIC_ATOM_FULLNAME*,ret)->rel.argTypes[i])
				{
					case SYNTACTIC_ARG_COMPOSE:
					case SYNTACTIC_ARG_ANONYMOUS:
						break;
					default:
						//uprav pointer
						PTR(SYNTACTIC_ATOM_FULLNAME*,ret)->rel.argValues[i] = 
							(char*)( DWORD(PTR(SYNTACTIC_ATOM_FULLNAME*,ret)->rel.argValues[i]) + plus );
						break;
				}
			}
			//len lokalny pointer (zatial)
			PTR(SYNTACTIC_ATOM_FULLNAME*,ret)->rel.argTypes = 
				(BYTE*)( DWORD(PTR(SYNTACTIC_ATOM_FULLNAME*,ret)->rel.argTypes) - DWORD(PTR(SYNTACTIC_ATOM_FULLNAME*,ret)));
			_DELETE(pargs); pargs = NULL;
		}
		//---------------------------------------------------------------------------------------
		else //aritmeticky atom?
		{
			char op[3]={0,0,0};//operator
			lexicalAnalyzer->RollbackToken();

			//nacitaj lavu stranu vyrazu (over, ci je spravna)
			aritL = ReadExpression(false);
			lexicalAnalyzer->RollbackToken();

			tokenType = lexicalAnalyzer->ReadToken_NoWhiteSpaces(&token);
			switch (tokenType)
			{
				case LEXICAL_TOKEN_EXCLAMATION:
					//!=
					if (lexicalAnalyzer->LookAheadToken() != LEXICAL_TOKEN_EQUAL)
						throw "V aritmetickom výraze neznáma operácia!";
					lexicalAnalyzer->ReadToken(&token);//docitaj =
					op[0] = '!';op[1] = '=';
					a.arit.typeOp = SYNTACTIC_ARIT_OPERAND_NOTEQ;
					break;
				case LEXICAL_TOKEN_LESS:
				{
					//<, <=, <> (tj. !=)
					op[0] = '<';
					DWORD i = lexicalAnalyzer->LookAheadToken();
					if ( (i == LEXICAL_TOKEN_GREATER) || (i == LEXICAL_TOKEN_EQUAL) )
					{
						lexicalAnalyzer->ReadToken(&token);
						op[1] = token[0];

						switch (op[1])
						{
						case '=':
							a.arit.typeOp = SYNTACTIC_ARIT_OPERAND_LEEQ;
							break;
						case '>':
							a.arit.typeOp = SYNTACTIC_ARIT_OPERAND_NOTEQ;
							break;
						}
					}
					else
						a.arit.typeOp = SYNTACTIC_ARIT_OPERAND_LESS;
					break;
				}
				case LEXICAL_TOKEN_EQUAL:
					op[0] = '=';
					a.arit.typeOp = SYNTACTIC_ARIT_OPERAND_EQUAL;
					break;
				case LEXICAL_TOKEN_GREATER:
					{
						op[0] = '>';
						//>, >=
						if (lexicalAnalyzer->LookAheadToken() == LEXICAL_TOKEN_EQUAL)
						{
							lexicalAnalyzer->ReadToken(&token);
							op[1] = '=';
							a.arit.typeOp = SYNTACTIC_ARIT_OPERAND_GREQ;
						}
						else
							a.arit.typeOp = SYNTACTIC_ARIT_OPERAND_GREATER;
					}
					break;
				default:
					throw "V aritmetickom výraze chýba operácia porovnania!";
					break;
			}

			//nacitaj pravu stranu
			aritR = ReadExpression(false);
			lexicalAnalyzer->RollbackToken();

			//ked sme sa dostali az sem, tak mame dva validne vyrazy a medzi nimi operator "op"
			a.arit.type = SYNTACTIC_ATOM_ARITATOM;
			a.arit.nVar = aritL->nVar + aritR->nVar;

			/*
			//ak je na pravej strane string, na lavej musi byt len jedna premenna
			if ( (aritR->type == SYNTACTIC_ARG_STRING) && (aritL->type != SYNTACTIC_ARG_VARIABLE) )
				throw "Ak je na pravej strane aritmetického výrazu string, na ľavej strane musí byť len jedna premenná!";
			*/

			tmp->AddData(&a, sizeof(a));
			tmp->AddData(NULL, 8*a.arit.nVar);//pole pointrov na premenne v expression (zatial len vyhrad pamat)
			//zostroj cely vyraz
			DWORD lenL = strlen(aritL->expression);
			int exp = tmp->AddData(aritL->expression, lenL);//lava strana
			lenL += strlen(op);
			tmp->AddData(op, strlen(op));//operator
			tmp->AddData(aritR->expression, strlen(aritR->expression)+1);//prava strana

			ret->AddData(tmp->GetPointer(), tmp->GetSize());
			PTR(SYNTACTIC_ATOM_FULLNAME*,ret)->arit.expression = (char*)exp;//zatial este lokalny pointer
			//este uprav lokalne pointre
			for (DWORD i=0; i<aritL->nVar; i++)
			{
				PTR(SYNTACTIC_ATOM_FULLNAME*,ret)->arit.var[i].start = aritL->var[i].start;
				PTR(SYNTACTIC_ATOM_FULLNAME*,ret)->arit.var[i].len = aritL->var[i].len;
			}
			for (i=0; i<aritR->nVar; i++)
			{
				PTR(SYNTACTIC_ATOM_FULLNAME*,ret)->arit.var[i + aritL->nVar].start = aritR->var[i].start + lenL;
				PTR(SYNTACTIC_ATOM_FULLNAME*,ret)->arit.var[i + aritL->nVar].len = aritR->var[i].len;
			}
			freex(aritL);
			freex(aritR);
		}
	}
	catch (char *err)
	{
//		app.mainWindow->GetOutputWindow()->AddNumberedLine(err, lexicalAnalyzer->GetLineNumber());
		_DELETE(tmp); _DELETE(tmp2); _DELETE(ret);
		if (pargs) {_DELETE(pargs);}
		if (aritL) freex(aritL);
		if (aritR) freex(aritR);
		throw err;
	}
	_DELETE(tmp); _DELETE(tmp2);

	SYNTACTIC_ATOM_FULLNAME* return_value = (SYNTACTIC_ATOM_FULLNAME*)mallocx(ret->GetSize());
	CopyMemory(return_value, ret->GetPointer(), ret->GetSize());
	_DELETE(ret);

	//uprav pointre
	switch (return_value->rel.type)
	{
	case SYNTACTIC_ATOM_RELATOM:
		{
			return_value->rel.name = (char*)( ((DWORD)return_value->rel.name) + DWORD(return_value) );
			return_value->rel.argTypes = (BYTE*)( ((DWORD)return_value->rel.argTypes) + DWORD(return_value) );

			for (DWORD i=0; i<return_value->rel.nArgs; i++)
			{
				switch (return_value->rel.argTypes[i])
				{
				case SYNTACTIC_ARG_COMPOSE:
				case SYNTACTIC_ARG_ANONYMOUS:
					break;
				default:
					return_value->rel.argValues[i] = (char*)( (DWORD)return_value->rel.argValues[i] + DWORD(return_value) );
					break;
				}
			}
		}
		break;
	case SYNTACTIC_ATOM_ARITATOM:
		{
			return_value->arit.expression = (char*)( (DWORD)(return_value->arit.expression) + (DWORD)return_value);
		}
		break;
	case SYNTACTIC_ATOM_GROUPBYATOM:
		DWORD ofs = sizeof(SYNTACTIC_ATOM_FULLNAME_GROUPBY)+4*return_value->groupby.nVar;
		for (DWORD i=0; i<return_value->groupby.nVar; i++)
		{
			return_value->groupby.varsName[i] = (char*)( ((DWORD)return_value->groupby.varsName[i]) + ofs + DWORD(return_value) );
		}
		break;
	}

	return return_value;
}

//vyzaduje pripojenie k databaze (potrebuje EDB atomy)
void CSyntacticAnalyzer::ReadRules()
{
	CInfoWindow *infoWin = _NEW(CInfoWindow());
	DEBUGPRINT("DEBUG: *** SYNTACTIC ANALYSIS ***");
	infoWin->SetText("Prebieha syntaktická analýza programu");

	if (table) DestroyTable();

	bool stop_err = false;//vyskytla sa chyba?
	CMemory *tableRules = _NEW(CMemory()); tableRules->AddData(NULL, sizeof(SYNTACTIC_TABLE_RULES));
	//SYNTACTIC_TABLE_RULES* _tableRules = (SYNTACTIC_TABLE_RULES*)tableRules->GetPointer();

	CMemory *var_used = _NEW(CMemory());//pamataj si, ktore premenne boli pouzite v tele pravidla (musia medzi nimi byt vsetky premenne z hlavicky)
	CStringTable *edbTable = _NEW(CStringTable());//nazvy edb atomov
	CStringTable *idbTable = _NEW(CStringTable());//nazvy idb atomov
	CMemory *idbTable_index = _NEW(CMemory());//obsahuje id (poradove cislo) idb atomu (pozicia id prislucha pozicii nazvu v idbTable)

	SYNTACTIC_ATOM_FULLNAME *at=NULL, *at2=NULL;//at je atom na lavej strane, at2 su postupne na pravej strane pravidla
	//----------------------------------------------------------------------------------------------------------
	//najskor pridaj EDB atomy

	//nacitaj nanovo strukturu databazy
	if (output)
	{
		app.GetDatabase()->UpdateStructure();
		app.mainWindow->GetAtomsWindow()->Refresh();
	}
	const CDbStructure* dbst = app.GetDatabase()->GetStructure();

	PTR(SYNTACTIC_TABLE_RULES*,tableRules)->nAtoms = dbst->nTables;//pocet EDB atomov


	//pridaj jednotlive atomy
	for (DWORD i=0; i<dbst->nTables; i++)
	{
		CMemory* edb = _NEW(CMemory()); edb->AddData(NULL, sizeof(SYNTACTIC_ATOM_EDB));//alokuj pamat
		PTR(SYNTACTIC_ATOM_EDB*,edb)->type = SYNTACTIC_ATOM_EDBATOM;
		tableRules->AddData(&edb, 4);//pridaj pointer
		edbTable->Insert(dbst->tables[i].name);//zapamataj si nazov do edbTable
	}
	//----------------------------------------------------------------------------------------------------------

	//----------------------------------------------------------------------------------------------------------
	//teraz nacitaj IDB atomy (pravidla)
	while (!lexicalAnalyzer->EndOfInput())
	{
		infoWin->NextStep();
		//nacitaj jedno pravidlo (jeden riadok)

		lexicalAnalyzer->SkipEmptyLines();
		if (lexicalAnalyzer->EndOfInput())
			break;

		//nacitaj hlavicku pravidla
		try
		{
			//uvolni pamat, ak treba
			if (at) { freex(at); at = NULL;}

			at = ReadAtom();//nacitaj (nejaky) atom

			//na lavej strane pravidla (v hlavicke) musi byt IDB atom
			if (at->rel.type != SYNTACTIC_ATOM_RELATOM)//musi byt relacny
				throw "V hlavičke pravidla musí byť IDB atóm!";
			if (edbTable->Index(at->rel.name) != -1)//je to EDB atom?
				//EDB atom ako hlavicka nemoze byt
				throw "V hlavičke pravidla nemôže byť EDB atóm!";
			if (at->rel.not)
				throw "V hlavičke pravidla nemôže byť negácia!";

			//---------------------------------------------------------------
			//ok, je to IDB atom

			DWORD atomId;//jeho id
			DWORD indexInTable;

			CMemory *idb;
			if ( (indexInTable = idbTable->Index(at->rel.name)) == -1)
			{
				//tento IDB atom este nebol definovany

				atomId = PTR(SYNTACTIC_TABLE_RULES*, tableRules)->nAtoms++;//urci nove ID
				idb = _NEW(CMemory()); idb->AddData(NULL, sizeof(SYNTACTIC_ATOM_IDB));
				tableRules->AddData(&idb, 4);//uloz pointer do pola atomov
				//zapis typ..
				if (!stricmp(at->rel.name, "answer"))
					PTR(SYNTACTIC_ATOM_IDB*, idb)->type = SYNTACTIC_ATOM_ANSWER;
				else
					PTR(SYNTACTIC_ATOM_IDB*, idb)->type = SYNTACTIC_ATOM_IDBATOM;
				PTR(SYNTACTIC_ATOM_IDB*, idb)->nArgs = at->rel.nArgs;//pocet argumentov
				PTR(SYNTACTIC_ATOM_IDB*, idb)->nRules = 0;//zatial nemame ziadne pravidla
				CMemory *tmp = _NEW(CMemory()); tmp->AddData(NULL, at->rel.nArgs);//alokuj pamat pre typ vsetkych argumentov
				PTR(SYNTACTIC_ATOM_IDB*, idb)->argTypes = (BYTE*)tmp;
				PTR(SYNTACTIC_ATOM_IDB*, idb)->name = _NEW(char[strlen(at->rel.name)+1]); strcpy(PTR(SYNTACTIC_ATOM_IDB*, idb)->name, at->rel.name);
				//...dalej v idb nasleduju pole pointrov na pravidla...

				idbTable->Insert(at->rel.name);//zapamataj si meno
				idbTable_index->AddData(&atomId, 4);//menu atomu prirad jeho ID
			}
			else
			{
				//atom uz bol definovany predtym
				atomId = PTR(DWORD*, idbTable_index)[indexInTable];//zisti jeho ID
				idb = (CMemory*)PTR(SYNTACTIC_TABLE_RULES*,tableRules)->atoms[atomId];//zisti pointer na jeho strukturu
			}

			//mame dalsie pravidlo
			PTR(SYNTACTIC_ATOM_IDB*, idb)->nRules++;
			CMemory* rule = _NEW(CMemory()); rule->AddData(NULL, sizeof(SYNTACTIC_RULE));//priprav pamat pre nove pravidlo
			idb->AddData(&rule, 4);//pridaj pointer
			PTR(SYNTACTIC_RULE*,rule)->varNames	= _NEW(CStringTable());
			PTR(SYNTACTIC_RULE*,rule)->nAtoms	= 0;
			//..nasleduje pole pointrov na SYNTACTIC_ATOM_RULE



			//spracuj argumenty hlavicky - uloz si premenne a pridel im ID

			if (at->rel.nArgs != PTR(SYNTACTIC_ATOM_IDB*, idb)->nArgs)//sedi pocet argumentov s predtym definovanym poctom?
			{
				//nie
				if (output)
				{
					char *tmp = CMemory::printstr("IDB atóm ’%s’ bol definovaný s iným počtom argumentov!", at->rel.name);
					app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
				}
				throw "";
			}

			var_used->DeleteData();//vynuluj pouzite premenne


			//spracuj argumenty hlavicky

			//najskor pridaj vsetky premenne
			for (DWORD i=0; i<at->rel.nArgs; i++)
			{
				switch (at->rel.argTypes[i])
				{
				case SYNTACTIC_ARG_ANONYMOUS:
					throw "V hlavičke pravidla nemôžu byť anonymné premenné!";
				case SYNTACTIC_ARG_COMPOSE:
					{
						//vyraz sa bude hned substituovat, ale vytvor mu premennu
						SYNTACTIC_ATOM_FULLNAME_ARIT *a = (SYNTACTIC_ATOM_FULLNAME_ARIT*)at->rel.argValues[i];
						char *exp = CDatalog::ExpressionToString(PTR(SYNTACTIC_RULE*,rule), a->expression, false);
						char *tmp = CMemory::printstr("%s", exp);
						PTR(SYNTACTIC_RULE*,rule)->varNames->Add(tmp); _DELETE(tmp);
						char c=1; var_used->AddData(&c, 1);//pouzita v aritm. vyraze
						_DELETE(exp);
						//_rule->varNames->Add("");
					}
					break;
				case SYNTACTIC_ARG_STRING:
				case SYNTACTIC_ARG_NUMERIC:
					{
						//vyraz sa bude hned substituovat, ale vytvor mu premennu
						char *tmp = CMemory::printstr("%s", at->rel.argValues[i]);
						PTR(SYNTACTIC_RULE*,rule)->varNames->Add(tmp); _DELETE(tmp);
						char c=1; var_used->AddData(&c, 1);//pouzita v aritm. vyraze
						//_rule->varNames->Add(""));
					}
					break;
				case SYNTACTIC_ARG_VARIABLE:
					{
						DWORD n = PTR(SYNTACTIC_RULE*,rule)->varNames->GetCount();
						DWORD ix = PTR(SYNTACTIC_RULE*,rule)->varNames->Insert(at->rel.argValues[i]);
						if (ix == n)//novo pridana premenna?
						{
							//ano
							char c=0; var_used->AddData(&c,1);//este nepouzita v tele pravidla
						}
						else
						{
							//premenna uz existuje, musi sa substituovat, pretoze pre kazdy argument v hlavicke musi
							//existovat nejaka premenna! (id premennych je od 0-nArg-1)
							//vytvor novu premennu a prirad do nej uz tuto existujucu premennu
							PTR(SYNTACTIC_RULE*,rule)->varNames->Add(at->rel.argValues[i]);
							char c=1; var_used->AddData(&c,1);//pouzita uz v pravidle vo forme priradenia
							char tmp[5]; *(DWORD*)tmp = (ix<<8) + DATALOG_EXP_VAR; tmp[4]=0;
							AddEqAritAtom(i, tableRules, rule, tmp);
						}
					}
					break;
				}
			}

			//teraz preved substituciu pre ine argumenty ako premenne
			for (i=0; i<at->rel.nArgs; i++)
			{
				switch (at->rel.argTypes[i])
				{
				case SYNTACTIC_ARG_COMPOSE:
					{
						char *exp = CDatalog::StringToExpression(PTR(SYNTACTIC_RULE*,rule), var_used, (SYNTACTIC_ATOM_FULLNAME_ARIT*)at->rel.argValues[i]);
						AddEqAritAtom(i, tableRules, rule, exp); 
						freex(at->rel.argValues[i]);
						_DELETE(exp);
					}
					break;
				case SYNTACTIC_ARG_STRING:
				case SYNTACTIC_ARG_NUMERIC:
					AddEqAritAtom(i, tableRules, rule, at->rel.argValues[i]);
					break;
				}
			}

			//cislo riadku zapis do pravidla
			PTR(SYNTACTIC_RULE*,rule)->inputLine = lexicalAnalyzer->GetLineNumber();

			//nacitame operator <-,  je nepovinny
			bool loop = true;
			const char *token;
			DWORD tokenType = lexicalAnalyzer->ReadToken_NoWhiteSpaces(&token);
			if (tokenType == LEXICAL_TOKEN_EOL)
			{
				lexicalAnalyzer->GoToNextLine();
				loop = false;
			}
			else
			{
				if (tokenType != LEXICAL_TOKEN_LESS)
					throw "Za hlavičkou pravidla očakávaný operátor <-";
				if (lexicalAnalyzer->ReadToken(&token) != LEXICAL_TOKEN_MINUS)
					throw "Za hlavičkou pravidla očakávaný operátor <-";

				if (lexicalAnalyzer->LookAheadToken() == LEXICAL_TOKEN_WHITESPACE)
					lexicalAnalyzer->ReadToken(&token);
				if (lexicalAnalyzer->LookAheadToken() == LEXICAL_TOKEN_EOL)
				{
					lexicalAnalyzer->GoToNextLine();
					loop = false;
				}
			}

			//v pravidle zatial dovolime len jeden groupby atom
			bool groupbyAtomDefined = false;//bol definovany?

			//---------------------------------------------------------------------------------------------------
			//ideme citat pravu stranu pravidla
			while (loop)
			{
				//uvolni pamat, ak treba
				if (at2) { freex(at2); at2 = NULL;}
				at2 = ReadAtom();//nacitaj dalsi atom

				if (at2->rel.type == SYNTACTIC_ATOM_RELATOM)
				{
					if (!stricmp(at2->rel.name, "answer"))
						throw "Relačný atóm ’answer’ sa môže vyskytovať len v hlavičke pravidla a nie v tele!";
				}

				DWORD nArgs;
				PTR(SYNTACTIC_RULE*,rule)->nAtoms++;//mame novy atom v pravidle

				CMemory* atomRule = _NEW(CMemory()); atomRule->AddData(NULL, sizeof(SYNTACTIC_ATOM_RULE));
				rule->AddData(&atomRule, 4);//pridaj pointer
				PTR(SYNTACTIC_ATOM_RULE*,atomRule)->not = at2->arit.not;

				//spracuj atom
				switch (at2->arit.type)
				{
				case SYNTACTIC_ATOM_RELATOM:
					{
						PTR(SYNTACTIC_ATOM_RULE*,atomRule)->nArgs = at2->rel.nArgs;//zapis pocet argumentov
						atomRule->AddData(NULL, 4*at2->rel.nArgs);//pridaj este miesto pre pole id argumentov

						//zisti poradove cislo (identifikuj atom)
						//ak atom este neexistuje, dodefinuj ho
						DWORD index = edbTable->Index(at2->rel.name);//EDB atom?
						if (index == -1)
						{
							//nie je to EDB atom, musi to byt IDB atom
							index = idbTable->Index(at2->rel.name);
							if (index == -1)
							{
								//tento IDB atom este nebol definovany
								//treba ho pridat do tableRules
								//musi sa dodefinovat neskor v programe; inak to bude znamenat chybu

								PTR(SYNTACTIC_ATOM_RULE*,atomRule)->id = PTR(SYNTACTIC_TABLE_RULES*,tableRules)->nAtoms++;//urci ID
								CMemory* idb = _NEW(CMemory()); idb->AddData(NULL, sizeof(SYNTACTIC_ATOM_IDB));
								tableRules->AddData(&idb, 4);//uloz pointer do pola atomov
								//zapis data o atome
								PTR(SYNTACTIC_ATOM_IDB*,idb)->type = SYNTACTIC_ATOM_IDBATOM;//typ
								PTR(SYNTACTIC_ATOM_IDB*,idb)->nArgs = at2->rel.nArgs;//pocet argumentov
								PTR(SYNTACTIC_ATOM_IDB*,idb)->nRules = 0;//zatial nemame ziadne pravidla
								CMemory *tmp = _NEW(CMemory()); tmp->AddData(NULL, at2->rel.nArgs);//alokuj pamat pre typ vsetkych argumentov
								PTR(SYNTACTIC_ATOM_IDB*,idb)->argTypes = (BYTE*)tmp;
								PTR(SYNTACTIC_ATOM_IDB*,idb)->name = _NEW(char[strlen(at2->rel.name)+1]); strcpy(PTR(SYNTACTIC_ATOM_IDB*,idb)->name, at2->rel.name);
								//...dalej v idb nasleduju pole pointrov na pravidla...

								idbTable->Insert(at2->rel.name);//zapamataj si meno
								DWORD atomId = PTR(SYNTACTIC_ATOM_RULE*,atomRule)->id;
								idbTable_index->AddData(&atomId, 4);//menu atomu prirad jeho ID
							}
							else
								PTR(SYNTACTIC_ATOM_RULE*,atomRule)->id = PTR(DWORD*,idbTable_index)[index];//na zaklade mena v idbTable zisti jeho id

							CMemory *tmp = (CMemory*)PTR(SYNTACTIC_TABLE_RULES*,tableRules)->atoms[PTR(SYNTACTIC_ATOM_RULE*,atomRule)->id];
							nArgs = PTR(SYNTACTIC_ATOM_IDB*,tmp)->nArgs;
						}
						else
						{
							//EDB atom
							//jeho id v table je jeho identifikator (prve id vsetkych atomov patria EDB atomom)
							PTR(SYNTACTIC_ATOM_RULE*,atomRule)->id = index;
							nArgs = dbst->tables[index].nAttributes;
						}
						
						//over ci sedi pocet argumentov (z doteraz ulozenych atomov)
						if (at2->rel.nArgs != nArgs)
						{
							if (output)
							{
								char *tmp = CMemory::printstr("Relačný atóm ’%s’ bol definovaný s iným počtom argumentov!", at2->rel.name);
								app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
							}
							throw "";
						}

						//----------------------------------------------------------------------------------------------
						//spracuj argumenty relacneho podciela

						for (i=0; i<nArgs; i++)
						{
							switch (at2->rel.argTypes[i])
							{
							case SYNTACTIC_ARG_ANONYMOUS:
								PTR(SYNTACTIC_ATOM_RULE*,atomRule)->argsId[i] = -1;//anonymna premenna, nezaujimava
								break;
							case SYNTACTIC_ARG_STRING:
							case SYNTACTIC_ARG_NUMERIC:
								{
									//ako argumentu pouzi novu premennu, ktora sa vytvori, a do ktorej sa priradi
									//hodnota povodneho argumentu

									//vytvor nazov premennej
									//char *tmp = CMemory::printstr("%s.%s", at2->rel.name, at2->rel.argValues[i]);
									char *tmp = CMemory::printstr("%s", at2->rel.argValues[i]);
									DWORD varId = PTR(SYNTACTIC_RULE*,rule)->varNames->Add(tmp); _DELETE(tmp);
									char c=1; var_used->AddData(&c,1);//premenna pouzita v aritm. vyraze
									AddEqAritAtom(varId, tableRules, rule, at2->rel.argValues[i]);
									PTR(SYNTACTIC_ATOM_RULE*,atomRule)->argsId[i] = varId;//nastav premennu ako argument do povodneho atomu
								}
								break;
							case SYNTACTIC_ARG_COMPOSE:
								{
									//argumentom je vyraz zlozeny aj z premennych
									SYNTACTIC_ATOM_FULLNAME_ARIT *arg = (SYNTACTIC_ATOM_FULLNAME_ARIT*)at2->rel.argValues[i];

									//char *tmp = CMemory::printstr("%s.%s", at2->rel.name, arg->expression);

									//prepis vyraz na expression
									char *exp = CDatalog::StringToExpression(PTR(SYNTACTIC_RULE*,rule), var_used, arg);

									//v argumente relacneho podciela sa nemozu nachadzat agregacne funkcie
									DWORD ii=0;
									while (exp[ii])
									{
										if (exp[ii] == DATALOG_EXP_VAR)
											ii+=4;
										else
										if (exp[ii] < DATALOG_EXP_SPECIAL)
										{
											//vypis chybu, lebo mame agregacnu funkciu!
											if (output)
											{
												char *tmp = CMemory::printstr("V argumente relačného podcieľa ’%s’ sa nachádza agregačná funkcia, čo nie je dovolené!", at2->rel.name);
												app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
											}
											_DELETE(exp); freex(arg);
											throw "";
										}
										else
											ii++;
									}

									//vytvor novu premennu
									DWORD varId = PTR(SYNTACTIC_RULE*,rule)->varNames->Add(arg->expression);// _DELETE(tmp);
									char c;
									if (!at2->rel.not)
										c=2;//premenna je pouzita v relacnom podcieli
									else
										//v negovanom sa pouzitie premennej ako argument nerata ako v nenegovanom relacnom podcieli
										c=1;
									var_used->AddData(&c,1);
									AddEqAritAtom(varId, tableRules, rule, exp);
									PTR(SYNTACTIC_ATOM_RULE*,atomRule)->argsId[i] = varId;//nastav premennu ako argument do povodneho atomu
									_DELETE(exp);
									freex(arg);
								}
								break;
							case SYNTACTIC_ARG_VARIABLE:
								{
									//argumentom je jedna premenna
									//ak tato premenna uz bola pouzita v relacnom podcieli (var_used=2) ..
									//pridaj novu premennu a rovnost pomocou AddEqAritAtom

									DWORD n = PTR(SYNTACTIC_RULE*,rule)->varNames->GetCount();
									DWORD id = PTR(SYNTACTIC_RULE*,rule)->varNames->Insert(at2->rel.argValues[i]);//urci cislo premennej

									if (id == n)
									{
										//novo pridana premenna
										char c;
										if (!at2->rel.not)
											c=2;//nenegovany relacny podciel
										else
											c=1;//ak je atom negovany, tak premenna neoznac za poucitu v nenegovanom relacnom podcieli
										var_used->AddData(&c,1);
									}
									else
									{
										//premenna uz bola definovana
										BYTE *b = &(((BYTE*)(var_used->GetPointer()))[id]);//zisti ako bola pouzita
										if (*b == 2)
										{
											//uz bola pouzita v relacnom podcieli
											char exp[5]; *(DWORD*)exp = (id<<8) + DATALOG_EXP_VAR; exp[4] = 0;//zostroj vyraz pozostavajuci len z jednej premennej
											//priprav nazov
											id = PTR(SYNTACTIC_RULE*,rule)->varNames->Add(at2->rel.argValues[i]); //pridaj novy nazov a urci nove id
											char c=1; var_used->AddData(&c, 1);//pouzita len v podmienke
											AddEqAritAtom(id, tableRules, rule, exp);
										}
										else
										{
											//premenna este nebola pouzita v relacnom podieli
											//(zatial len v aritmetickom)
											if (!at2->rel.not)
												*b = 2;//prave sa pouzila v nenegovanom relacnom podcieli
										}

									}
									PTR(SYNTACTIC_ATOM_RULE*,atomRule)->argsId[i] = id;//ako argument zapis id premennej
								}
								break;
							}
						}
						//..spracuj argumenty atomu
						//----------------------------------------------------------------------------------------------
					}
					
					break;
				case SYNTACTIC_ATOM_ARITATOM:
					{
						PTR(SYNTACTIC_ATOM_RULE*,atomRule)->id = PTR(SYNTACTIC_TABLE_RULES*,tableRules)->nAtoms++;//urci ID
						CMemory *atom = _NEW(CMemory()); atom->AddData(NULL, sizeof(SYNTACTIC_ATOM_ARIT));//alokuj pamat
						tableRules->AddData(&atom, 4);//uloz do pola pointrov
						//uloz data
						PTR(SYNTACTIC_ATOM_ARIT*,atom)->type = SYNTACTIC_ATOM_ARITATOM;
						PTR(SYNTACTIC_ATOM_ARIT*,atom)->typeOp = at2->arit.typeOp;
						PTR(SYNTACTIC_ATOM_ARIT*,atom)->substitution = false;
						PTR(SYNTACTIC_ATOM_RULE*,atomRule)->nArgs = 0;//aritmeticky atom nema argumenty

						//nasleduje expression (char*)
						char *exp = CDatalog::StringToExpression(PTR(SYNTACTIC_RULE*,rule), var_used, &at2->arit);
						atom->AddData(exp, CDatalog::ExpressionLength(exp)+1);
						_DELETE(exp);
					}
					break;
				case SYNTACTIC_ATOM_GROUPBYATOM:
					{
						if (at2->groupby.not)
							throw "Podcieľ groupby nemôže byť negovaný!";

						if (groupbyAtomDefined)
						{
							throw "V pravidle sa môže nachádzať len jeden groupby podcieľ!";
						}

						groupbyAtomDefined = true;

						PTR(SYNTACTIC_ATOM_RULE*,atomRule)->id = PTR(SYNTACTIC_TABLE_RULES*,tableRules)->nAtoms++;//urci ID
						CMemory *atom = _NEW(CMemory()); atom->AddData(NULL, sizeof(SYNTACTIC_ATOM_GROUPBY));//alokuj pamat
						tableRules->AddData(&atom, 4);//uloz do pola pointrov
						//uloz data
						PTR(SYNTACTIC_ATOM_GROUPBY*,atom)->type = SYNTACTIC_ATOM_GROUPBYATOM;
						//alokuj pamat pre id premennych
						PTR(SYNTACTIC_ATOM_RULE*,atomRule)->nArgs = at2->groupby.nVar;//zapis pocet argumentov
						atomRule->AddData(NULL, at2->groupby.nVar*4);//pridaj este miesto pre pole id argumentov
						for (DWORD i=0; i<at2->groupby.nVar; i++)
						{
							DWORD n = PTR(SYNTACTIC_RULE*,rule)->varNames->GetCount();
							DWORD ix = PTR(SYNTACTIC_RULE*,rule)->varNames->Insert(at2->groupby.varsName[i]);
							if (ix == n)//novo pridana premenna?
							{
								//ano
								char c=1; var_used->AddData(&c,1);//tato premenna bola pouzita v tele, ale nie v relacnom atome (c=1, nie 2)
							}
							else
							{
								//premenna uz existuje
								BYTE *b = &(((BYTE*)(var_used->GetPointer()))[ix]);//zisti ako bola pouzita
								if (!*b)
									*b=1;//pouzita v tele, ale nie v relacnom podcieli
							}
							//zapis do pola groupby premennych
							PTR(SYNTACTIC_ATOM_RULE*,atomRule)->argsId[i] = ix;
						}
					}
					break;
				} //switch - spracuj atom podla typu

				//nacitaj spojku AND alebo zisti koniec...
				tokenType = lexicalAnalyzer->ReadToken_NoWhiteSpaces(&token);
				if (tokenType == LEXICAL_TOKEN_EOL)
					break;
				if (stricmp(token, "and"))
				{
					if (output)
					{
						char *tmp = CMemory::printstr("V tele pravidla očakávané kľúčové slovo ’and’, nie ’%s’!", token);
						app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
					}
					throw "";
				}
			}
			//..docitana prava strana pravidla
			//---------------------------------------------------------------------------------------------------

			//uvolni pamat, ak treba
			if (at) { freex(at); at = NULL;}
			if (at2) { freex(at2); at2 = NULL;}

		} catch (char* err)
		{
			stop_err = true;
			if (err[0] && output)
				app.mainWindow->GetOutputWindow()->AddNumberedLine(err, lexicalAnalyzer->GetLineNumber());
			lexicalAnalyzer->GoToNextLine();

			//uvolni pamat, ak treba
			if (at) { freex(at); at = NULL;}
			if (at2) { freex(at2); at2 = NULL;}
		}
	} //while (!lexicalAnalyzer->EndOfInput())
	//--------------------------------------------------------------------------------------------------------

	app.mainWindow->GetDatalogWindow()->SetIDBAtoms(idbTable);
	app.mainWindow->GetDatalogWindow()->Colorize();

	//najskor spocitaj velkost potrebnej pamate
	DWORD size = tableRules->GetSize();
	for (i=0; i<PTR(SYNTACTIC_TABLE_RULES*,tableRules)->nAtoms; i++)//cyklus atomov
	{
		CMemory* atom = (CMemory*)PTR(SYNTACTIC_TABLE_RULES*,tableRules)->atoms[i];
		size += atom->GetSize();
		switch ( PTR(SYNTACTIC_ATOM_IDB*,atom)->type )
		{
		case SYNTACTIC_ATOM_IDBATOM:
		case SYNTACTIC_ATOM_ANSWER:
			{
				SYNTACTIC_ATOM_IDB* _idb = PTR(SYNTACTIC_ATOM_IDB*,atom);
				for (DWORD j=0; j<_idb->nRules; j++)//cyklus pravidiel
				{
					CMemory* rule = (CMemory*)_idb->rules[j]; SYNTACTIC_RULE* _rule = (SYNTACTIC_RULE*)rule->GetPointer();
					size += rule->GetSize();
					size += 4*_rule->varNames->GetCount();
					for (DWORD k=0; k<_rule->nAtoms; k++)//cyklus podcielov
					{
						CMemory* atomRule = (CMemory*)_rule->atom[k]; SYNTACTIC_ATOM_RULE* _atomRule = (SYNTACTIC_ATOM_RULE*)atomRule->GetPointer();
						size += atomRule->GetSize();
					}
				}
				//este pridaj pole typov argumentov
				size += ((CMemory*)_idb->argTypes)->GetSize();
				//+nazov atomu
				size += strlen(_idb->name)+1;
			}
			break;
		}
	}

	//daj vsetky data do jednej CMemory - ret
	CMemory* ret = _NEW(CMemory());
	ret->AddData(NULL, size);
	SYNTACTIC_TABLE_RULES* _ret = PTR(SYNTACTIC_TABLE_RULES*,ret);

	//-----------------------------------------------------------------------------------------------------------
	//napln (a uvolni niektore) data
	::CopyMemory(_ret, tableRules->GetPointer(), tableRules->GetSize());
	DWORD ptr = tableRules->GetSize() + DWORD(_ret);

	for (i=0; i<PTR(SYNTACTIC_TABLE_RULES*,tableRules)->nAtoms; i++)//cyklus atomov
	{
		CMemory* atom = (CMemory*)PTR(SYNTACTIC_TABLE_RULES*,tableRules)->atoms[i];
		switch ( PTR(SYNTACTIC_ATOM_IDB*,atom)->type)
		{
		case SYNTACTIC_ATOM_IDBATOM:
		case SYNTACTIC_ATOM_ANSWER:
			{
				SYNTACTIC_ATOM_IDB* _idb = (SYNTACTIC_ATOM_IDB*)atom->GetPointer();
				::CopyMemory( ((void*)ptr), atom->GetPointer(), atom->GetSize());
				_ret->atoms[i] = (SYNTACTIC_ATOM*)((void*)ptr);//nastav pointer na atom
				ptr += atom->GetSize();
				for (DWORD j=0; j<_idb->nRules; j++)//cyklus pravidiel
				{
					CMemory* rule = (CMemory*)_idb->rules[j]; SYNTACTIC_RULE* _rule = (SYNTACTIC_RULE*)rule->GetPointer();
					::CopyMemory( ((void*)ptr), rule->GetPointer(), rule->GetSize());
					_ret->atoms[i]->idb.rules[j] = (SYNTACTIC_RULE*)((void*)ptr);//nastav pointer na pravidlo
					ptr += rule->GetSize();
					_ret->atoms[i]->idb.rules[j]->varTypes = (SYNTACTIC_VAR_TYPE*)ptr;
					ptr += sizeof(SYNTACTIC_VAR_TYPE)*_rule->varNames->GetCount();//alokuj miesto pre typ argumentov
					for (DWORD k=0; k<_rule->nAtoms; k++)//cyklus podcielov
					{
						CMemory* atomRule = (CMemory*)_rule->atom[k]; SYNTACTIC_ATOM_RULE* _atomRule = (SYNTACTIC_ATOM_RULE*)atomRule->GetPointer();
						::CopyMemory( ((void*)ptr), atomRule->GetPointer(), atomRule->GetSize());
						_ret->atoms[i]->idb.rules[j]->atom[k] = (SYNTACTIC_ATOM_RULE*)((void*)ptr);
						ptr += atomRule->GetSize();
						_DELETE(atomRule);
					}
					_DELETE(rule);
				}
				//este pridaj pole typov argumentov
				CMemory *arg = (CMemory*)_idb->argTypes;//vymazat argTypes
				::CopyMemory( ((void*)ptr), arg->GetPointer(), arg->GetSize());
				_DELETE(arg);
				_ret->atoms[i]->idb.argTypes = (BYTE*)ptr;
				ptr += _idb->nArgs;
				//este nazov atomu
				DWORD len = strlen(_idb->name)+1;
				::CopyMemory( ((void*)ptr), _idb->name, len);
				_DELETE(_idb->name);
				_ret->atoms[i]->idb.name = (char*)ptr;
				ptr += len;
			}
			break;
		case SYNTACTIC_ATOM_ARITATOM:
		case SYNTACTIC_ATOM_EDBATOM:
		case SYNTACTIC_ATOM_GROUPBYATOM:
			{
				::CopyMemory( ((void*)ptr), atom->GetPointer(), atom->GetSize());
				_ret->atoms[i] = (SYNTACTIC_ATOM*)((void*)ptr);
				ptr += atom->GetSize();
			}
			break;
		}
		_DELETE(atom);
	}

	//::CopyMemory((void*)argTypesPtr, argTypes->GetPointer(), argTypes->GetSize());
	//-----------------------------------------------------------------------------------------------------------

	//---------------------------------------------------------------------------------
	//over, ci atomy, ktore sa pouzili pred tym, ako boli definovane, boli dodefinovane
	//zaroven zisti, ci bol definovany atom answer (nemusi byt, ak #IDB atomov = 1)
	_ret->answerID = -1;
	DWORD count=0;//pocet vsetkych IDB atomov
	DWORD last=-1;
	for (i=0; i<_ret->nAtoms; i++)
	{
		//...definovany answer
		if (_ret->atoms[i]->idb.type == SYNTACTIC_ATOM_ANSWER)
		{
			_ret->answerID = i;
			count++;
		}
		//---------------------
		else
		if (_ret->atoms[i]->idb.type == SYNTACTIC_ATOM_IDBATOM)
		{
			//...definovany answer
			count++;
			last = i;
			//--------------------

			//...dodefinovane atomy?
			const char *name;
			if (!_ret->atoms[i]->idb.nRules) //mame definovane nejake pravidlo?
			{
				//najdi jeho poradie podla ID v idbTable
				DWORD j=0;
				while (j<idbTable->GetCount())
				{
					if (((DWORD*)(idbTable_index->GetPointer()))[j] == i)
					{
						name = idbTable->Get(j); break;
					}
					j++;
				}
				if (output)
				{
					char *tmp = CMemory::printstr("Atom ’%s’ nebol definovaný!", name);
					app.mainWindow->GetOutputWindow()->AddLine(tmp); _DELETE(tmp);
				}
				stop_err = true;
			}
		}
	}
	//---------------------------------------------------------------------------------
	_DELETE(tableRules);
	_DELETE(var_used);
	_DELETE(edbTable);
	_DELETE(idbTable);
	_DELETE(idbTable_index);

	//---------------------------------------------------------------------------------
	//je definovany answer atom?
	if (_ret->answerID == -1)
	{
		if (count!=1)
		{
			if (output)
				app.mainWindow->GetOutputWindow()->AddLine("Nie je definovaný atóm ’answer’!");
			stop_err = true;
		}
		//nemame atom answer, ale je definovany len jeden IDB atom
		_ret->answerID = last;
	}
	//---------------------------------------------------------------------------------

	//zapis posledne data
	_ret->nEdb = dbst->nTables;
	_ret->nIdb = count;

	table = ret;

	if (stop_err)
	{
		DestroyTable();
		table = NULL;
	}

	DEBUGPRINT("DEBUG: *** SYNTACTIC ANALYSIS done ***");

	_DELETE(infoWin);
}

const SYNTACTIC_TABLE_RULES *CSyntacticAnalyzer::GetTable()
{
	if (table)
		return (const SYNTACTIC_TABLE_RULES*)table->GetPointer();
	else
		return NULL;
}

void CSyntacticAnalyzer::AddEqAritAtom(DWORD varId, CMemory *tableRules, CMemory *rule, char *value)
{
	//prida aritmeticky atom [varId] = value

	//SYNTACTIC_RULE *rule = (SYNTACTIC_RULE*)_rule->GetPointer();

	PTR(SYNTACTIC_RULE*, rule)->nAtoms++;//novy aritmeticky atom

	//vytvor substituciu aritmetickeho atomu v pravidle
	CMemory* aritRule = _NEW(CMemory()); aritRule->AddData(NULL, sizeof(SYNTACTIC_ATOM_RULE));
	rule->AddData(&aritRule, 4);//pridaj pointer
	//SYNTACTIC_ATOM_RULE* aritRule = (SYNTACTIC_ATOM_RULE*)_aritRule->GetPointer();
	//napln data
	PTR(SYNTACTIC_ATOM_RULE*,aritRule)->nArgs = 0;//aritmeticky atom nema argumenty
	PTR(SYNTACTIC_ATOM_RULE*,aritRule)->not = false;
	PTR(SYNTACTIC_ATOM_RULE*,aritRule)->id = PTR(SYNTACTIC_TABLE_RULES*,tableRules)->nAtoms++;//prirad nove ID

	//vytvor novy aritmeticky atom do tabulky atomov
	CMemory *atomSub = _NEW(CMemory()); atomSub->AddData(NULL, sizeof(SYNTACTIC_ATOM_ARIT));//alokuj pamat
	tableRules->AddData(&atomSub, 4);//uloz do pola pointrov
	//uloz data
	PTR(SYNTACTIC_ATOM_ARIT*, atomSub)->type = SYNTACTIC_ATOM_ARITATOM;
	PTR(SYNTACTIC_ATOM_ARIT*, atomSub)->typeOp = SYNTACTIC_ARIT_OPERAND_EQUAL;//zatial len podmienka, priradenia sa urcuju pocas sem.an.
	PTR(SYNTACTIC_ATOM_ARIT*, atomSub)->substitution = true;

	//nasleduje expression (char*) tvaru X = value

	//cislo premennej
	DWORD data = (varId<<8) + DATALOG_EXP_VAR; atomSub->AddData(&data, 4);//nova premenna
	atomSub->AddData("=",1);//operacia

	//teraz skopiruj hodnotu argumentu ako pravu stranu rovnice
	atomSub->AddData(value, CDatalog::ExpressionLength(value)+1);

#ifdef _DEBUG
	if (output)
	{
		char *str = CDatalog::ExpressionToString(PTR(SYNTACTIC_RULE*,rule), value, false);
		char *tmp = CMemory::printstr("DEBUG: pridaná podmienka [%i]=%s", varId, str);
		app.mainWindow->GetOutputWindow()->AddNumberedLine(tmp, lexicalAnalyzer->GetLineNumber()); _DELETE(tmp);
		_DELETE(str);
	}
#endif
}