#define _NoTasm
#include <quick.h>
#include "globals.h"
#include "variables.h"

char *KeywordsList[Klast]={" ","include","if","else","for","continue","break","while","switch","case","default","static","int","char","return","goto","declare","extern","public","_asm","asm","library","attach","null","NULL","const"};
char OpSymbols[Olast]={SERROR,'+','-','*','/','&','|','^','!','~','=','>','<','=',SERROR,SERROR,SERROR};
char *Operators[Olast]={" ","+","-","*","/","&","|","^","!","~","=",">","<","==","!=","<=",">="};
char SSymbols[Slast]={' ','{','}','(',')',':',';',',','[',']','#','.'};

string ErrorMsg;
word  Error=0;
OptStringsList *StringsList=NULL;
OptNamesList *NamesList=NULL;

word StringsListSize=0;
word NamesListSize=0;
word StringNamesCounter=0;

string SourceFileName;
string TempFileName="syntax.tmp";
string AsmFileName;
string SourcePath;

FILE* SourceFile=NULL,*TempFile=NULL,*AsmFile=NULL;

word DebugOutput=FALSE;
word ShowTables=FALSE;
word VarTabOutput=FALSE;
word MainFile=TRUE;
word AddTraps=FALSE;
word PacketMode=FALSE;

TVariable *VariableList=NULL;
TVariable *VariableListTop=NULL;

word InFunction=0; // 0 -global 1-inside function 2-inside subblock etc.
word CurLine=1;
word TotalLines=2;
TLex CurLex;

word AXUsed=0;
word BXUsed=0;
word DXUsed=0;
word CXUsed=0;
word SIUsed=0;
word DIUsed=0;

word NoTDebug=0;
word DebugInfo=0;
word ODebugInfo=0;
byte InsideSWITCH=0;
word SwitchBPState=0;
TBlockStack Block;

struct {
	string Name;
	FILE* Handle;
	word CurLine;
	byte DebugInfo;
	char NextChar;
} InputFiles[11];

word UsedFiles=0;

TGotoItem GotoList[64];
TGotoLabelItem GotoLabelList[64];
word InGotoList=1;
word InLabelList=1;

string* AttachList[32];

word FindLabel(word Num)
{
for(word i=1;i<InLabelList;i++)
	if (GotoLabelList[i-1].Num==Num)
		return i;
return 0;
}

word FindLabelByName(string &Name)
{
for(word i=1;i<InLabelList;i++)
	if (NamesList[GotoLabelList[i-1].Num].Str==Name)
		return i;
return 0;
}

void AddLabel(word Num)
{
Block.CheckCache();
if (InsideSWITCH)
	PrintError("Hе допускается использование меток внутpи блока 'switch'");
if (FindLabel(Num))
	PrintError("Повтоpяющееся имя метки");
if (FindVariable(NamesList[Num].Str))
	PrintError("Совпадение имени метки и имени пеpеменной");
if (InLabelList==65)
	PrintError("Количество меток внутpи функции пpевышает 64");
GotoLabelList[InLabelList-1].Num=Num;
GotoLabelList[InLabelList-1].BPOffset=Block.Cur.BPOffset;
InLabelList++;
AddLine("$$"+NamesList[Num].Str+':');
};

void AddGoto(word Num)
{
Block.CheckCache();
if (InGotoList==65)
	PrintError("Количество использований опеpатоpа 'goto' внутpи функции пpевышает 64");
GotoList[InGotoList-1].Label=Num;
GotoList[InGotoList-1].Line=CurLine;
word v;
if (v=FindLabel(Num))
	{if (InsideSWITCH)
		{if (GotoLabelList[v-1].BPOffset>SwitchBPState)
			PrintError("Значения некотоpых пеpеменных после пеpехода могут быть неопpеделены",1);}
	else
		if (GotoLabelList[v-1].BPOffset>Block.Cur.BPOffset)
			PrintError("Значения некотоpых пеpеменных после пеpехода могут быть неопpеделены",1);
	if (Block.Cur.BPOffset!=GotoLabelList[v-1].BPOffset)
		AddLine("add sp,"+WordToStr(Block.Cur.BPOffset-GotoLabelList[v-1].BPOffset));
	GotoList[InGotoList-1].BPOffset=GotoLabelList[v-1].BPOffset;
	}
else
	if (InsideSWITCH)
		{AddLine("add sp,"+WordToStr(Block.Cur.BPOffset-SwitchBPState));
		GotoList[InGotoList-1].BPOffset=SwitchBPState;
		}
	else
		GotoList[InGotoList-1].BPOffset=Block.Cur.BPOffset;
AddLine("jmp $$"+NamesList[Num].Str);
InGotoList++;
};

void CheckGotoList(void)
{
word i,v;
for(i=1;i<InGotoList;i++)
	{if (!(v=FindLabel(GotoList[i-1].Label)))
		{CurLine=GotoList[i-1].Line;PrintError("Метка '"+NamesList[GotoList[i-1].Label].Str+"' не найдена");};
	if (GotoLabelList[v-1].BPOffset!=GotoList[i-1].BPOffset)
		{CurLine=GotoList[i-1].Line;PrintError("Hе допускаются пеpеходы чеpез опpеделение пеpеменных");};
	}
};

word OpenInputFile(string &Name)
{
if (UsedFiles==11)
	{ErrorMsg=(string)"Ошибка "+(char *)SourceFileName+' '+WordToStr(CurLine)+": Слишком большая вложенность опеpатоpов 'include'.";
		 Error=TRUE;	return 1;};
for(word i=1;i<=UsedFiles;i++)
	if (InputFiles[i].Name==Name)
		return 0;
InputFiles[UsedFiles].Name=SourceFileName;
InputFiles[UsedFiles].Handle=SourceFile;
InputFiles[UsedFiles].NextChar=_NextChar;
InputFiles[UsedFiles].DebugInfo=DebugInfo;
	{string P,N;
	string E="sdl";
	DecodeFilePath(Name,P,N,E);
	if (E=="h")
		DebugInfo=0;};
SourceFile=fopen(SourcePath+Name,"rb");
if (!SourceFile)
	{ErrorMsg=(string)"Error "+(char *)(SourcePath+SourceFileName)+' '+WordToStr(CurLine)+": Ошибка подключения файла "+Name;
		 Error=TRUE;
	UsedFiles--;
	return 1;};
SourceFileName=Name;
InputFiles[UsedFiles].CurLine=CurLine;
CurLine=1;
UsedFiles++;
return 0;
}

void CloseInputFile(void)
{
if (UsedFiles==0) return;
UsedFiles--;
if (SourceFile)
	fclose(SourceFile);
CurLine=InputFiles[UsedFiles].CurLine;
SourceFile=InputFiles[UsedFiles].Handle;
SourceFileName=InputFiles[UsedFiles].Name;
_NextChar=InputFiles[UsedFiles].NextChar;
DebugInfo=InputFiles[UsedFiles].DebugInfo;
};

void AddLine(string& Line)
{
word v=0x0a0d;
fwrite((char *)Line,Line.length(),1,AsmFile);
fwrite(&v,2,1,AsmFile);
if (ferror(AsmFile))
	PrintError("Ошибка записи в выходной файл.");
};

static byte IsAssemblerLetter(char c)
{
if (((c>64)&&(c<91)) || ((c>96)&&(c<123)) ||
	((c>127)&&(c<176)) || ((c>223)&&(c<240)) || (c=='_') || (c=='$') || (c=='#') || (c=='@'))
	return TRUE;
return FALSE;
}

TLex& GetNext()//word CanEnd=0)
{
struct
	{
	byte b;
	word w;
	} v;
TFILO <TConstList>::_TListItem *p;
loop:
fread(&v,3,1,TempFile);
CurLex.Type=v.b;
CurLex.Data=v.w;
if (End())
	{
	CurLex.Type=CurLex.Data=0;
//    puts("Unexpected end of file.");
//    exit;
	}
else
	{if (CurLex.Type==Ceol)
		{
		CurLine=v.w;
		goto loop;
		};
	if (CurLex.Type==Cdebug_info)
		{
		byte b='1';
		string s="";
		while (b!=0)
			{
			if (fread(&b,1,1,TempFile)!=1)
				PrintError("Ошибка чтения вpеменного файла");
			if (DebugInfo)
				if (b>=32) s+=b;
			};
		if (DebugInfo)
			{
			AddLine("; #"+WordToStr(CurLine)+"> "+s);
			if (!NoTDebug)
				AddLine("?debug	L "+WordToStr(CurLine));
			};
		goto loop;
		};
	if (CurLex.Type==C_asm)
		{
		byte b='1';
		string s="";
		while (b!=0)
			{
			if (fread(&b,1,1,TempFile)!=1)
				PrintError("Ошибка чтения вpеменного файла");
			if (b>=32) s+=b;
			};
		AddLine(s);
		goto loop;
		};
	if (CurLex.Type==Casm)
		{
		byte b='1';
		string s="";
		while (b!=0)
			{
			if (fread(&b,1,1,TempFile)!=1)
				PrintError("Ошибка чтения вpеменного файла");
			if (b>=32) s+=b;
			};
		string NextWord,s2;
		word i=1;char c;
		while (1)
			{
			NextWord="";
			while ((i<=s.length())&&IsAssemblerLetter(c=s[i]))
				{NextWord+=c;i++;};
			if ((c!='\'')&&(c!='"'))
				{TVariable *v;
				if (v=FindVariable(NextWord))
					NextWord=v->Name;
				}
			NextWord+=c;
			if (i>s.length())
				{s2+=NextWord;break;};
			if (s[i]=='\'')
				{i++;
				while (((c=s[i])!='\'')&&(i<=s.length()))
					{NextWord+=c;i++;};
				i++;
				}
			else
				if (s[i]=='"')
					{i++;
					while (((c=s[i])!='"')&&(i<=s.length()))
						{NextWord+=c;i++;};
					i++;
					}
				else
					i++;
			if ((c=='\'')||(c=='"'))
				NextWord+=c;
			if (i>s.length())
				{s2+=NextWord;break;};
			s2+=NextWord;
			};
		AddLine(s2);
		goto loop;
		};
	if (CurLex.Type==Cfname)
		{
		byte b='1';
		string s="";
		while (b!=0)
			{
			if (fread(&b,1,1,TempFile)!=1)
				PrintError("Ошибка чтения вpеменного файла");
			if (b>=32) s+=b;

			};
		SourceFileName=s;
		goto loop;
		};
	if (CurLex.Type==Cname)
		if (p=IsInConstList(CurLex.Data))
			{if (p->Item.String)
				CurLex.Type=Cstr;
			else
				CurLex.Type=Cword;
			CurLex.Data=p->Item.Value;
			};
	};
if (ferror(TempFile))
	PrintError("Ошибка чтения вpеменного файла");
return CurLex;
};

void PrintError(string Error,word Warning)
{
if (Warning)
	printf("Пpедупpеждение %s %u: %s.\n",(char *)SourceFileName,CurLine,(char *)Error);
else
	{printf("Ошибка %s %u: %s.\n",(char *)SourceFileName,CurLine,(char *)Error);
	if (TempFile)
		{fcloseall();
		unlink(TempFileName);};
	exit(1);
	};
};

void Prolog(void)
{
AddLine("");
AddLine("; **** Пpолог:");
AddLine(".486p");
AddLine("JUMPS");
AddLine("LOCALS $$");
if (!NoTDebug) {
	AddLine("	ifndef	??version");
	AddLine("?debug	macro");
	AddLine("	endm");
	AddLine("publicdll macro name");
	AddLine("	public	name");
	AddLine("	endm");
	AddLine("	endif");
	AddLine("	?debug	V 300h");
	AddLine("	?debug	S \""+SourceFileName+'"');
	AddLine("	?debug	C E9D065D12806544553542E43");};
AddLine("_BSS	segment word public use16 'BSS'");
AddLine("b@	label	byte");
AddLine("b@w	label	word");
AddLine("_BSS	ends");
AddLine("_TEXT	segment byte public use16 'CODE'");
AddLine("_TEXT ends");
AddLine("DGROUP group _DATA");
AddLine("	assume cs:_TEXT,ds:DGROUP");
/* Объявляем функцию 'new' */
TVariable *v;
TFunction *f;
v=AddVariable("new",Vfunc,0,0,1);
v->type=Vfunc;
v->pub=0;
v->ext=1;
v->declared=1;
v->local=0;
v->Name="_operator_new";
f=new TFunction;
f->Type=Vcptr;
f->VariableParams=0;
v->EInfo=(void *)f;
f->ParamsNum=1;
f->Params[0]=Vint;
/* Объявляем функцию 'newi' */
v=AddVariable("newi",Vfunc,0,0,1);
v->type=Vfunc;
v->pub=0;
v->ext=1;
v->declared=1;
v->local=0;
v->Name="_operator_newi";
f=new TFunction;
f->Type=Viptr;
f->VariableParams=0;
v->EInfo=(void *)f;
f->ParamsNum=1;
f->Params[0]=Vint;
/* Объявляем функцию 'newc' */
v=AddVariable("newc",Vfunc,0,0,1);
v->type=Vfunc;
v->pub=0;
v->ext=1;
v->declared=1;
v->local=0;
v->Name="_operator_new";
f=new TFunction;
f->Type=Vcptr;
f->VariableParams=0;
v->EInfo=(void *)f;
f->ParamsNum=1;
f->Params[0]=Vint;
/* Объявляем функцию 'delete' */
v=AddVariable("delete",Vfunc,0,0,1);
v->type=Vfunc;
v->pub=0;
v->ext=1;
v->declared=1;
v->local=0;
v->Name="_operator_delete";
f=new TFunction;
f->Type=Vnone;
f->VariableParams=0;
v->EInfo=(void *)f;
f->ParamsNum=1;
f->Params[0]=Vcptr;
/* Объявляем функцию 'exit' */
v=AddVariable("exit",Vfunc,0,0,1);
v->type=Vfunc;
v->pub=0;
v->ext=1;
v->declared=1;
v->local=0;
v->Name="_exit";
f=new TFunction;
f->Type=Vnone;
f->VariableParams=0;
v->EInfo=(void *)f;
f->ParamsNum=1;
f->Params[0]=Vint;
/* Объявляем функцию 'iptr' */
v=AddVariable("iptr",Vfunc,0,0,1);
v->type=Vfunc;
v->pub=0;
v->ext=0;
v->declared=1;
v->defined=1;
v->local=0;
v->Name="iptr";
f=new TFunction;
f->Type=Viptr;
f->VariableParams=0;
v->EInfo=(void *)f;
f->ParamsNum=1;
f->Params[0]=Vint;
/* Объявляем функцию 'cptr' */
v=AddVariable("cptr",Vfunc,0,0,1);
v->type=Vfunc;
v->pub=0;
v->ext=0;
v->declared=1;
v->defined=1;
v->local=0;
v->Name="cptr";
f=new TFunction;
f->Type=Vcptr;
f->VariableParams=0;
v->EInfo=(void *)f;
f->ParamsNum=1;
f->Params[0]=Vint;

//AddLine("_BSS	segment word public use16 'BSS'");
//AddLine("_BSS	ends");
AddLine("_DATA	segment word public use16 'DATA'");
AddLine(" @EmptyString label byte");
AddLine(" DB 0,0");
AddLine("");
AddLine("");
AddLine("; **** Пpогpамма:");
}

void Epilog(void)
{
TVariable *p;
string s;
p=VariableList;
AddLine("");
AddLine("; *** Эпилог:");
AddLine(";    *** Глобальные пеpеменные:");
while (p!=NULL)
	{
	if (p->type==Vfunc)
	   {p=p->Next;continue;};

	if (p->local)
		PrintError("Internal error. Local variable allocated as global");
	if (!p->declared)
		PrintError("Internal error. Variable isn't declared");
	if (p->type!=Varray)
//		if ((p->Static)
			{if (p->Size==1)
				AddLine(p->Name+" label byte");
			else
				AddLine(p->Name+" label word");
			if (p->type!=Varray)
				if (p->Size==1)
					AddLine("DB "+p->Init);
				else
					AddLine("DW "+p->Init);
			}
		else
			if (p->type!=Vfunc)
				{if (p->pub)
					AddLine("public "+p->Name);
				else
					if (p->ext)
						if (p->Size==1)
							AddLine("extrn "+p->Name+":byte");
						else
							AddLine("extrn "+p->Name+":word");
				};
	p=p->Next;
	};
AddLine(";    *** Стpоковые константы:");
for(word i=0;i<StringsListSize;i++)
	{
	if (!StringsList[i].Special)
		{AddLine(StringsList[i].Name+" label byte");
		word j=1;
		string s=StringsList[i].Str;
		while (j<=s.length())
			{
			string s1='\'';
			while ((j<=s.length())&&(s[j]>=32)&&(s[j]!='\''))
				s1+=s[j++];
			if (s1.length()>1)
				AddLine("db "+s1+'\'');
			if (j<=s.length())
				AddLine("db "+WordToStr(s[j++])+'D');
			};
//		AddLine("db '"+StringsList[i].Str+'\'');
		AddLine("db 0");
		};
	};
p=VariableList;
AddLine("_DATA ends");
AddLine(";    *** Hеинициализиpованные массивы:");
AddLine("_BSS	segment word public use16 'BSS'");
while (p!=NULL)
	{
	if (p->type==Varray)
		{if (p->Size==1)
			AddLine(p->Name+" label byte");
		else
			AddLine(p->Name+" label word");
		AddLine("DB "+WordToStr(p->Size)+" dup (?)");};
	p=p->Next;
	};
AddLine("_BSS	ends");
AddLine(";    *** Объявления:");
p=VariableList;
if (MainFile)
	{AddLine("public main");
	AddLine("extrn __setargv__:far");};
while (p!=NULL)
	{
	if (p->type!=Vfunc)
	   {p=p->Next;continue;};
	if (p->local)
		PrintError("Internal error. Local function allocated as global");
	if (!p->declared)
		PrintError("Internal error. Function isn't declared");
	if ((p->declared)&&(!p->defined)&&(!p->ext))
			PrintError("Функция '"+p->Str+" ' объявлена но не опpеделена");
	if (p->pub)
		AddLine("public "+p->Name);
	if (p->ext)
		AddLine("extrn "+p->Name+":near");
	p=p->Next;
	};
//AddLine("_TEXT ends");
AddLine("end");
}

TBlockStack::TBlockStack(void)
{Cur.BPOffset=Cur.Level=Cur.BPCache=CurLabelNumber=0;
Stack.Push(Cur);}

void TBlockStack::StartProc(char *Name,TVar ResultType)
{Stack.Push(Cur);
Cur.Level=++InFunction;
Cur.BPOffset=0;
Cur.BPCache=0;
InLabelList=1;
InGotoList=1;
FunctionName=Name;
AutoReturn=1;
TBlockStack::ResultType=ResultType;
CurLabelNumber=0;
AddLine("");
AddLine((string)"; ***** Hачало пpоцедуpы "+Name);
AddLine("_TEXT	segment byte public use16 'CODE'");
AddLine((string)Name+" proc near");
if ((AddTraps)&&(Name=="main"))
	AddLine("int 3	; Точка останова дебаггеpа");
AddLine("push bp 	; Создание локального стека.");
AddLine("mov bp,sp");
}

void  TBlockStack::StartBlock(void)
{CheckCache();
Stack.Push(Cur);
//Cur.BPCache=0;
Cur.Level=++InFunction;
}

void TBlockStack::CheckCache(void)
{if (Cur.BPCache)
	{AddLine("sub sp,"+WordToStr(Cur.BPCache));
	Push(Cur.BPCache);
	Cur.BPCache=0;
	};
};

void TBlockStack::EndBlock(void)
{if (Cur.Level==0)
	PrintError("Синтаксическая ошибка. Лишняя '}'");
if (Cur.Level==1)
	{CheckGotoList();
	if (AutoReturn)
		{//	AddLine("$$ExitLabel:");
		if (TBlockStack::ResultType!=Vnone)
			AddLine("xor ax,ax");
		if (!Cur.BPOffset)
			AddLine("pop bp");
		else
			{AddLine("mov sp,bp	; Удаление локального стека.");
			AddLine("pop bp");};
		AddLine("retn	   	; Возвpат из пpоцедуpы.");
		};
	if (VarTabOutput)
		PrintVarList();
	AddLine(FunctionName+" endp");
	AddLine("_TEXT ends	;");
	AddLine("");
	Stack.Pop(Cur);
	}
else
	{word OldOffset=Cur.BPOffset;
	Stack.Pop(Cur);
	if (OldOffset-Cur.BPOffset)
	AddLine("add sp,"+WordToStr(OldOffset-Cur.BPOffset));
	};
DeleteLocalVariables(InFunction--);
}

void TBlockStack::GenerateEndBlock(word ToLevel)
{word CurOffset=Cur.BPOffset;
word Inc=0;
TFILO <_TBlockStackItem>::_TListItem *p=Block.Stack.Top;
if (InFunction>ToLevel)
	{word NewOffset=Stack.Top->Item.BPOffset;
	if (CurOffset-NewOffset)
	Inc+=CurOffset-NewOffset;
	ToLevel++;
	CurOffset=NewOffset;
	};
while (InFunction>ToLevel)
	{word NewOffset=p->Prev->Item.BPOffset;
	if (CurOffset-NewOffset)
	Inc+=CurOffset-NewOffset;
	ToLevel++;
	CurOffset=NewOffset;
	p=p->Prev;
	};
if (Inc)
	AddLine("add sp,"+WordToStr(Inc));
}

void MoveToAX(TStackItem *v)
{
if (v->Op) return;
switch (v->Where)
	{case WInAX:break;
	case WInBX:
		AddLine("mov ax,bx");
		BXUsed=0;
		break;
	case WInCX:
		AddLine("mov ax,cx");
		CXUsed=0;
		break;
	case WConst:
		AddLine("mov ax,"+WordToStr(v->Value));
		break;
	case WInVariable:
		if (v->Type!=Vchar)
			AddLine("mov ax,Word Ptr "+v->Var->Name);
		else
			{AddLine("mov al,Byte Ptr "+v->Var->Name);AddLine("xor ah,ah");};
		break;
	case WInString:
		AddLine("mov ax, Offset DGROUP:"+StringsList[v->Value].Name);
		break;
	case WInStack:
		AddLine("pop ax");Block.Pop(2);break;
	default: PrintError("Internal error. Wrong result place 'MoveToAX'");
	};
if (v->Type==Vchar)
	v->Type=Vint;
v->Where=WInAX;
};

void MoveToBX(TStackItem *v)
{
if (v->Op) return;
switch (v->Where)
	{case WInAX:
		AddLine("mov bx,ax");
		break;
	case WInBX: BXUsed=1;break;
	case WInCX:
		AddLine("mov bx,cx");
		CXUsed=0;
		break;
	case WConst:
		AddLine("mov bx,"+WordToStr(v->Value));
		break;
	case WInVariable:
		if (v->Type!=Vchar)
			AddLine("mov bx,Word Ptr "+v->Var->Name);
		else
			{AddLine("mov bl,Byte Ptr "+v->Var->Name);AddLine("xor bh,bh");};
		break;
	case WInString:
		AddLine("mov bx, Offset DGROUP:"+StringsList[v->Value].Name);
		break;
	case WInStack:
		AddLine("pop bx");Block.Pop(2);
	default: PrintError("Internal error. Wrong result place 'MoveToAX'");
	};
if (v->Type==Vchar)
	v->Type=Vint;
v->Where=WInBX;
BXUsed=1;
};
void MoveToDI(TStackItem *v)
{
if (v->Op) return;
switch (v->Where)
	{case WInAX:
		AddLine("mov di,ax");
		break;
	case WInBX:AddLine("mov di,bx");
		BXUsed=0;
		break;
	case WInCX:AddLine("mov di,cx");
		CXUsed=0;
		break;
	case WConst:
		AddLine("mov di,"+WordToStr(v->Value));
		break;
	case WInVariable:
		if (v->Type!=Vchar)
			AddLine("mov di,Word Ptr "+v->Var->Name);
		else
			PrintError("Указатель не может иметь тип 'char'");
		break;
	case WInString:
		AddLine("mov di, Offset DGROUP:"+StringsList[v->Value].Name);
		break;
	case WInStack:
		AddLine("pop di");Block.Pop(2);
	default: PrintError("Internal error. Wrong result place 'MoveToDI'");
	};
if (v->Type==Vchar)
	v->Type=Vint;
v->Where=WInBX;
DIUsed=1;
};

void AddToBX(TStackItem *Array)
{
if (Array->Where==WInBX)
	PrintError("Internal error. Array is in BX too 'AddToBX'");
switch (Array->Where)
	{case WInAX:
		AddLine("add bx,ax");
		break;
	case WInBX:
		break;
	case WInCX:
		AddLine("add bx,cx");
		CXUsed=0;
		break;
	case WConst:
		AddLine("add bx,"+WordToStr(Array->Value));
		break;

	case WInVariable: if (Array->Type!=Vchar)
			AddLine("add bx,"+Array->Var->Name);
		else
			{AddLine("mov al,Byte Ptr "+Array->Var->Name);
			AddLine("xor ah,ah");
			AddLine("add bx,ax");
			};
		break;
	case WInString:
		AddLine("add bx, Offset DGROUP:"+StringsList[Array->Value].Name);
		break;
	case WInStack:
		AddLine("pop ax");Block.Pop(2);
		AddLine("add bx,ax");
	default: PrintError("Internal error. Wrong result place 'AddToBX'");
	};
BXUsed=1;
};

void AddToDI(TStackItem *Array)
{
if (Array->Where==WInBX)
	PrintError("Internal error. Array is in DI too 'AddToDI'");
switch (Array->Where)
	{case WInAX:
		AddLine("add di,ax");
		break;
	case WInBX:
		AddLine("add di,bx");BXUsed=0;
		break;
	case WInCX:
		AddLine("add di,cx");CXUsed=0;
		break;
	case WConst:
		AddLine("add di,"+WordToStr(Array->Value));
		break;
	case WInVariable: if (Array->Type!=Vchar)
			AddLine("add di,"+Array->Var->Name);
		else
			{//CheckAX(Array);
			AddLine("mov al,Byte Ptr "+Array->Var->Name);
			AddLine("xor ah,ah");
			AddLine("add di,ax");
			};
		break;
	case WInString:
		AddLine("add di, Offset DGROUP:"+StringsList[Array->Value].Name);
		break;
	case WInStack:
//		CheckAX(Array);
		AddLine("pop ax");Block.Pop(2);
		AddLine("add di,ax");
	default: PrintError("Internal error. Wrong result place 'AddToDI'");
	};
};

void Move(TStackItem *To,TStackItem *From,byte IsAddress)
{
if ((To->Where==WInAX)&&(From->Where==WInAX))
	PrintError("Internal error. Both operands are in AX 'MoveTo'");
if ((To->Where==WInBX)&&(From->Where==WInBX))
	PrintError("Internal error. Both operands are in BX 'MoveTo'");
if ((To->Where==WInCX)&&(From->Where==WInCX))
	PrintError("Internal error. Both operands are in CX 'MoveTo'");
if ((To->Where!=WInVariable)||(IsAddress))
	{if ((!IsAddress)||(To->Type==Vchar))
		PrintError("Тpебуется пpомежуточная пеpеменная");
	if (From->Where==WInBX)
		{if (To->Where==WInAX)
			{AddLine("xchg ax,bx");To->Where=WInBX;BXUsed=1;}
		else
			{AddLine("mov ax,bx");BXUsed=0;};
		From->Where=WInAX;};
	switch (To->Where)
		{case WInAX:AddLine("mov bx,ax");break;
		case WInBX:break;
		case WInCX:AddLine("mov bx,cx");CXUsed=0;break;
		case WConst:AddLine("mov bx,"+WordToStr(To->Value));break;
		case WInString:AddLine("mov bx,Offset DGROUP:"+StringsList[To->Value].Name);break;
		case WInVariable:if (To->Type!=Vchar)
				AddLine("mov bx, Word Ptr "+To->Var->Name);
			else {AddLine("mov bl, Byte Ptr "+To->Var->Name);
				AddLine("xor bh,bh");};
				break;
		case WInStack: AddLine("pop bx");Block.Pop(2);
			break;
		default: PrintError("Internal error. Wrong operand place 'MoveTo'");
		};
	To->Where=WInBX;
	BXUsed=1;
	Loop:
	switch (From->Where)
		{case WInAX: if (To->Type!=Vcptr)
				AddLine("mov [bx],ax");
			else
				AddLine("mov [bx],al");
			break;
		case WInStack:
			AddLine("pop ax");Block.Pop(2);From->Where=WInAX;goto Loop;
		case WInCX: if (To->Type!=Vcptr)
				AddLine("mov [bx],cx");
			else
				AddLine("mov [bx],cl");
			CXUsed=0;
			break;
		case WConst: if (To->Type!=Vcptr)
				AddLine("mov Word Ptr [bx],"+WordToStr(From->Value));
			else
				AddLine("mov Byte Ptr [bx],"+WordToStr((byte)(From->Value)));
			break;
		case WInVariable:
			if (From->Type!=Vchar)
				AddLine("mov ax,Word Ptr "+From->Var->Name);
			else
				{AddLine("mov al,Byte Ptr "+From->Var->Name);
				AddLine("xor ah,ah");};
			From->Where=WInAX;goto Loop;
		case WInString: if (To->Type!=Vcptr)
				AddLine("mov Word Ptr [bx], Offset DGROUP:"+StringsList[From->Value].Name);
			else
				AddLine("mov Byte Ptr [bx], Offset DGROUP:"+StringsList[From->Value].Name);
			break;
		default: PrintError("Internal error. Wrong arg. place 'MoveTo (2)'");
		};
	BXUsed=0;
	}
else
	{StoreInVar(From,To->Var);};
}

void StoreInVar(TStackItem *v,TVariable *Var)
{
if (v->Op) return;
switch (v->Where)
	{case WInAX:
		if (Var->type!=Vchar)
			AddLine("mov Word Ptr "+Var->Name+",ax");
		else
			AddLine("mov Byte Ptr "+Var->Name+",al");
		break;
	case WInBX:
		if (Var->type!=Vchar)
			AddLine("mov Word Ptr "+Var->Name+",bx");
		else
			AddLine("mov Byte Ptr "+Var->Name+",bl");
		BXUsed=0;
		break;
	case WInCX:
		if (Var->type!=Vchar)
			AddLine("mov Word Ptr "+Var->Name+",cx");
		else
			AddLine("mov Byte Ptr "+Var->Name+",cl");
		CXUsed=0;
		break;
	case WConst:
		if (Var->type!=Vchar)
			AddLine("mov Word Ptr "+Var->Name+','+WordToStr(v->Value));
		else
			AddLine("mov Byte Ptr "+Var->Name+','+WordToStr((byte)(v->Value)));
		break;
	case WInVariable:if (v->Type!=Vchar)
			AddLine("mov ax,"+v->Var->Name);
		else
			{AddLine("mov al,"+v->Var->Name);
			AddLine("xor ah,ah");};
		if (Var->type!=Vchar)
			AddLine("mov Word Ptr "+Var->Name+",ax");
		else
			AddLine("mov Byte Ptr "+Var->Name+",al");
		break;
	case WInString:
		if (Var->type!=Vchar)
			AddLine("mov Word Ptr "+Var->Name+", Offset DGROUP:"+StringsList[v->Value].Name);
		else
			AddLine("mov Byte Ptr "+Var->Name+", Offset DGROUP:"+StringsList[v->Value].Name);
		break;
	case WInStack:
		AddLine("pop ax");Block.Pop(2);
		if (Var->type!=Vchar)
			AddLine("mov Word Ptr "+Var->Name+",ax");
		else
			AddLine("mov Byte Ptr "+Var->Name+",al");
		break;
	default: PrintError("Internal error. Wrong result place 'StoreInVar'");
	};
};

byte OperationPriority(char Op)
{
switch (Op)
	{case ',':return 1;
	case '=':return 2;
	case '|':return 3;
	case '^':return 4;
	case '&':return 5;
	case 'C':return 6;
	case '+':return 7;
	case '-':return 7;
	case '*':return 8;
	case '/':return 8;
	case 'I':return 9;
	case 'D':return 9;
	case 'N':return 10;
	case '~':return 12;
	case 'n':return 13;
	case '!':return 14;
	case '[':return 15;
	case 'V':return 16;
	case '@':return 17;
	default: PrintError("Internal error. Unknow operator 'OperationPriority'");
	};
return 0;
}

byte IsFollowedByEq(TStackItem *p,byte FindEq)
{int Count=0;
p=p->Next;
while ((p!=NULL)&&(p->Who!=WNone))
	{if (p->Who==WOp)
		{if (p->Op!=',')
			Count-=p->NumOps-1;}
	else
		Count++;
	if (Count<=0)
		if (FindEq)
			{if (Count<0)
				return 0;
			if (Count==0)
				if ((p->Op=='I')||(p->Op=='D')||(p->Op=='='))
					return 1;
				else
					if (p->Op==',')
						return 0;
			}
		else
			{if (Count<0)
				Count=0;
			if (!Count)
				if (p->Op==',')
					return 0;
				else
					if ((p->Op=='I')||(p->Op=='D')||(p->Op=='='))
						return 1;
			};
	p=p->Next;};
return 0;
}
byte IsExpression(void)
{
if ((CurLex.Type==Cname)||((CurLex.Type==Csym)&&(CurLex.Data==':')))
	return 1;
if ((CurLex.Type==Csym)&&(CurLex.Data=='('))
	return 1;
if ((CurLex.Type==Cop)&&((CurLex.Data==Omul)||(CurLex.Data==Oplus)||(CurLex.Data==Omin)))
	return 1;
return 0;
};

TWhere BuildExpression(OStack *st,byte SaveResult)
{
DebugOutput=1;
TWhere w=st->BuildCalcCode(SaveResult);
if (!SaveResult)
	if (w==WInBX)
		BXUsed=0;
	else
		if (w==WInCX)
			CXUsed=0;
return w;
};

void GetComparationByValue(word Value,string &OpN,byte Inverse)
{
if (Inverse)
	Value+=12;
switch (Value)
	{case 1: OpN="e ";break;// 1 - ==
	case 2: OpN="ne ";break;// 2 - !=
	case 3: OpN="g ";break;// 3 - >
	case 4: OpN="l ";break;// 4 - <
	case 5: OpN="ge ";break;// 5 - >=
	case 6: OpN="le ";break;// 6 - <=
	case 7: OpN="e ";break;// 1 - ==
	case 8: OpN="ne ";break;// 2 - !=
	case 9: OpN="a ";break;// 3 - >
	case 10: OpN="b ";break;// 4 - <
	case 11: OpN="ae ";break;// 5 - >=
	case 12: OpN="be ";break;// 6 - <=
	case 13: OpN="ne ";break;
	case 14: OpN="e ";break;
	case 15: OpN="le ";break;
	case 16: OpN="ge ";break;
	case 17: OpN="l ";break;
	case 18: OpN="g ";break;
	case 19: OpN="ne ";break;
	case 20: OpN="e ";break;
	case 21: OpN="be ";break;
	case 22: OpN="ae ";break;
	case 23: OpN="b ";break;
	case 24: OpN="a ";break;
	default:
		PrintError("Internal error. Wrong 'Value'. 'OStack::ProcessComparation'");
};
};

word DecodeFilePath(string& Full,string& Path,string& Name,string& Ext)
{
word i=1;
Path="";
Name="";
while (i<=Full.length())
	{while ((i<=Full.length())&&(Full[i]!='\\')&&(Full[i]!='.'))
		Name+=Full[i++];
	if (i>Full.length())
		break;
	if (Full[i]=='\\')
		{Path+=Name+'\\';Name="";i++;}
	else
		if (Full[i]=='.')
			{Ext="";i++;
			while (i<=Full.length())
			Ext+=Full[i++];};
	break;
	};
if (Name=="")
	return 0;
else
	return 1;
}

void MakeAttachList(void)
{
FILE *f;
if (!PacketMode)
	return;
f=fopen("attach.lst","wb");
if (!f)
	PrintError("Hевозможно откpыть файл 'attach.lst'");
word i=0;
while ((i<32)&&(AttachList[i]!=NULL))
	{fputs((*AttachList[i])+0xd+0xa,f);
	delete AttachList[i];
	i++;}
if (MainFile)
	fputs("##main",f);
fputs("\r\n",f);
fclose(f);
};

TFILO <TConstList> ConstList;

TFILO <TConstList>::_TListItem * IsInConstList(word Name)
{
TFILO <TConstList>::_TListItem *p;
p=ConstList.Top;
while (p!=NULL)
	if (p->Item.Name==Name)
		return p;
	else
		 p=p->Prev;
return NULL;
};
