// utf-8

/*	Copyright  (c)	Günter Woigk   2001-2003
  					mailto:kio@little-bat.de

	This file is free software

 	This program is distributed in the hope that it will be useful,
 	but WITHOUT ANY WARRANTY; without even the implied warranty of
 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 	Permission to use, copy, modify, distribute, and sell this software and
 	its documentation for any purpose is hereby granted without fee, provided
 	that the above copyright notice appear in all copies and that both that
 	copyright notice and this permission notice appear in supporting
 	documentation, and that the name of the copyright holder not be used
 	in advertising or publicity pertaining to distribution of the software
 	without specific, written prior permission. The copyright holder makes no
 	representations about the suitability of this software for any purpose.
 	It is provided "as is" without express or implied warranty.

 	THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 	INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 	EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 	CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 	DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 	TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 	PERFORMANCE OF THIS SOFTWARE.

	2003-07... kio	changes to use UCS4String library
	2003-08-21 kio	changes for eliminated Var type 'isVoid'
	2003-08-23 kio	trap calling non-procs, trap procs returning no value
*/


#define	SAFE 2
#define LOG	 1


#include	"config.h"
#include	<math.h>
#include	"VScript.h"
#include	"token.h"
#include	"SystemIO.h"
#include	"unix/os_utilities.h"



extern char **environ;


/* ----	misc. constants --------------------------------
*/

const double	d_pi	=	3.1415926535897932386;		// constant pi
const double	d_ec	=	exp(1.0);					// constant e
const double	d_ln2	=	log(2.0);					// ln(2)
const double	d_ln16	=	log(16.0);					// ln(16)
const double	d_ln10	=	log(10.0);					// ln(10)




/* ----	expect and skip token -------------------------------
*/
uchar* VScript::Expect ( uchar*ip, uchar token )
{
	if (Error()) return ip;

	if (*ip==token) return ip+1;
	SetError( String("Expected '") + TokenName(token) + "' found '" + TokenName(*ip) + "'" );
	return ip;
}


/* ----	evaluate expression --------------------------------
		require number
		tries to convert text to number
		target variable is always set to isNumber
*/
uchar* VScript::NumValue ( uchar*ip, Var*&v, int prio )
{	XXXTRAP(!v);
	Var* v0 = v;

	ip = Value(ip,v,prio);
	if (v->IsNumber()) return ip;
	*v0 = Error() ? 0.0 : v->ToNumber();
	v = v0;
	return ip;
}


/* ----	evaluate expression --------------------------------
		require string
		if uninitialized var is referenced, it is silently set to string
*/
uchar* VScript::StrValue ( uchar*ip, Var*&v, int prio )
{
	ip = Value(ip,v,prio);
	if (!v->IsText()) { SetError(expectedstring); *v=String(); }
	return ip;
}


/* ----	get single argument for function --------------------
		may be enclosed by round brackets
		suppress index evaluation of (text)[i] or (list)[i]
		••• das sollte besser über op.prio machbar sein? •••
			nein, ganz praktisch hier!
			=> 	<value>()  und <value>[] und <value>.<idf>
				können () [] . auswerten ohne prio zu checken!
*/
uchar* VScript::SingleArg ( uchar*ip, Var*&v )
{
	if (*ip!=tRKauf) return Value ( ip, v, pUna );
	else return Expect ( Value(ip+1,v,pAny), tRKzu );
}

uchar* VScript::SingleStrArg ( uchar*ip, Var*&v )
{
	if (*ip!=tRKauf) return StrValue ( ip, v, pUna );
	else return Expect ( StrValue(ip+1,v,pAny), tRKzu );
}

uchar* VScript::SingleNumArg ( uchar*ip, Var*&v )
{
	if (*ip!=tRKauf) return NumValue ( ip, v, pUna );
	else return Expect ( NumValue(ip+1,v,pAny), tRKzu );
}


/* ----	evaluate expression --------------------------------
		require reference to variable
*/
uchar* VScript::RefValue ( uchar*ip, Var*&v, int prio )
{
	Var* v0 = v;			// merken
	ip = Value(ip,v,prio);
	if (v==v0) { SetError(expectedvarref); }
	return ip;
}


/* ----	evaluate expression --------------------------------
		require reference to number variable
		target variable is always set to number
*/
uchar* VScript::NumRefValue ( uchar*ip, Var*&v, int prio )
{
	Var* v0 = v;			// merken
	ip = Value(ip,v,prio);
	if (v==v0 || v->IsNoNumber()) { SetError(expectednumvarref); v=v0; *v=0.0; }
	return ip;
}


/* ----	evaluate expression --------------------------------
		require reference to string variable
		if uninitialized var is referenced, it is silently set to string
*/
uchar* VScript::StrRefValue ( uchar*ip, Var*&v, int prio )
{
	Var* v0 = v;			// merken
	ip = Value(ip,v,prio);
	if (v==v0 || !v->IsText()) { SetError(expectedstrvarref); v=v0; *v=""; }
	return ip;
}


/* ----	find local or global variable --------------------------
		searchs in:	local variables
					this_item ... globals
*/
Var* VScript::FindVar ( cString& name )
{	Var*v,*w;
	bool g=0;

	XXXLogIn("FindVar()");
	XXXTRAP(globals->Parent());

	for ( v=locals; v; v=v->Parent() )
	{
		if(XXXXSAFE)v->Validate();
		XXXLog("(%s)",v->Name().CString());
		w = v->FindItem(name);
		if (w) { XXXLogOut(""); return w; }
		g = v==globals;
	}

	if (name=="locals") { XXXLogOut(""); return locals; }		// ••• token for 'locals' ? •••

	XXXTRAP(!proc);
	if ( (v=proc->Parent()) )
	{
		w = v->FindItem(name);
		if (w) { XXXLogOut(""); return w; }
		if (name=="self") { XXXLogOut(""); return proc; }					// ••• token for 'self' ? •••
		if (name=="this") { XXXLogOut(""); return proc->Parent(); }		// ••• token for 'this' ? •••
	}

	if (!g)
	{
		w = globals->FindItem(name);
		if (w) { XXXLogOut(""); return w; }
	}
	if (name=="globals") { XXXLogOut(""); return globals; }

	XXXLogOut("[!%s]",CString(name));
	return NULL;
}


/* ----	evaluate identifier -------------------------------------------
		parse token stream *ip++
		return identifier name in string idf
		possible identifiers:
			anton
			@stringvalue
*/
uchar* VScript::GetIdentifier ( uchar* ip, String& idf )
{
	XXXLogIn("GetIdentifier()");
	if (*ip==tIDF)
	{	int csz = ip[1];
		int len = ip[2]; ip+=3;
		idf = String(proc->Proc(),ip,len,CharSize(csz));
		ip += csz*len;
	}
	else if (*ip==tAT)		// @
	{
		Var z; Var* v = &z;
		ip = SingleStrArg(ip+1,v);
		idf = v->Text();
	}
	else
	{
		SetError(expectedidentifier);
		idf.Clear();
	}

	XXXLogOut("");
	return ip;
}



/* ======================================================================
					V A L U E
====================================================================== */		


/* ----	evaluate expression -----------------------------------------------------
		in:  v -> temp cell
		out: if result=value			 : v-> temp cell && value in temp cell
			 if result=reference		 : v-> variable
			 if pAcceptVoid && operator= : v=NULL ((don't print result in shell))
*/

uchar* VScript::Value ( uchar*ip, Var*&v, int prio )
{	XXXTRAP(!v);
	XXLogIn("Value()");

	if (Error()) {XXLogOut("");return ip;}

// ---- expect term ----
	if (XXXLOG) Disass(stderrStream,ip,1);
	XXXTRAP(globals->Contains(v));
	XXXTRAP(locals->Contains(v));
	if(XXXXSAFE) globals->Validate();
	if(XXXXSAFE && !globals->Contains(v)) v->Validate();


	Var* v0 = v;		// remember temp cell of caller
	Var temp;			// my own temp cell for right side term of operator
	Var* w = &temp;
	String idf;
	Var& (Var::*oper)(cVar&);

	switch(*ip++)
	{

// ----	literals, variables ------------------------

	case tIDF:		// user-defined variable: void, number, text, list, proc
	{	int csz = *ip++;
		int len = *ip++;
		idf = String(proc->Proc(),ip,len,CharSize(csz));
		ip += csz*len;
		v = FindVar(idf);
		if (!v) { SetError(variablenotfound,String("Variable ")+ToQuoted(idf)+" not found"); v=v0; }
	}	break;

	case tGLOBALS:
		v = globals;
		break;

	case tNUM0:		// number literal 0
		*v = 0.0;
		break;

	case tNUM1:		// number literal 1
		*v = 1.0;
		break;

	case tBYTE:		// small number literal
		*v = (char)*ip;
		ip += sizeof(char);
		break;

	case tSHORT:		// small number literal
		*v = *(short*)(ip);
		ip += sizeof(short);
		break;

	case tNUM:		// number literal
		*v = read_double(ip);
		ip += sizeof(double);
		break;

	case tSTR:		// 1-byte length prefixed text literal
	{	int csz = *ip++;
		int len = *ip++;
		*v = String( proc->Proc(), ip, len, CharSize(csz) );
		ip += csz*len;
	}	break;

	case tSTR2:		// 2-byte length prefixed text literal
	{	int csz = *ip++;
		int len = *(ushort*)(ip); ip += sizeof(ushort);
		*v = String( proc->Proc(), ip, len, CharSize(csz) );
		ip += csz*len;
	}	break;

	case tGKauf:	// list literal:	{ … }
	{
		v0->SetList();
		if (*ip==tGKzu) { ip++; break; }

		uint n = 0;
		do
		{
		// 1. test for item name:
		//		idf  =  ...
		//		@  text  [ . idf|@text]  [ [..] ]  [ (..) ]  =  ...
			bool f = false;		// named item?
			if( *ip==tIDF || *ip==tAT )
			{
				uptr ip2 = SkipTerm(ip,pAssign);
				if( *ip2==tGL && NoError() )
				{
					ip = GetIdentifier(ip,idf) +1;
					XXXTRAP(ip!=ip2+1);
					f  = true;
				}
			}

			Var *a,*b;
			a = b = &(*v0)[n++];
			ip = Value(ip,b,pAny);
			if (a!=b) *a = *b;
			if (f) a->SetName(idf);
		}
		while (*ip++==tKOMMA && NoError());

		ip=Expect(ip-1,tGKzu);
		break;
	}

// ----	monadic operators -----------------------------

	case tADD:		// number sign +
		ip=NumValue(ip,v,pUna);
		break;

	case tSUB:		// number sign -
		ip=NumValue(ip,w,pUna); *v = -w->Value();
		break;
		
	case tNOT:		// Unary operator !		((boolean not))
		ip=NumValue(ip,w,pUna); *v = !w->Value();
		break;
		
	case tTILDE:	// Unary operator ~		((bitwise not))
		ip=NumValue(ip,w,pUna); *v = ~(ulong)w->Value();
		break;
		
	case tMIMI:		// unary operator --	((pre-decrement))
		ip = RefValue(ip,v,pUna);
		if (!TrapVarInUse(v)) *v -= 1.0;
		break;

	case tPLPL:		// unary operator ++	((pre-increment))
		ip = RefValue(ip,v,pUna);
		if (!TrapVarInUse(v)) *v += 1.0;
		break;

	case tAT:		// unary operator @		((@"name" = name))
		ip = StrValue(ip,v,pUna);
		v  = FindVar(v->Text());
		if (!v) { SetError(variablenotfound); v=v0; }
		break;
	
	case tRKauf:	// ( … )
		ip = Value(ip,v,pAny);
		ip = Expect(ip,tRKzu);			
		break;
		
// ----	constants, functions with no arguments --------------

	case tEC:
		*v = d_ec;
		break;

	case tPI:
		*v = d_pi;
		break;

	case tSTDIN:  
		*v = 0.0;
		break;
		
	case tSTDOUT: 
		*v = 1.0;
		break;
		
	case tSTDERR: 
		*v = 2.0;
		break;

	case tNEWLINE:
		*v = CharString('\n');
		break;
	
	case tTAB:
		*v = CharString('\t');
		break;
	
// ----	functions with 1 argument --------------------------

	case tABS:		// abs
		ip=SingleNumArg(ip,w); *v = Abs(w->Value());
		break;

	case tSIGN:		// sign
		ip=SingleNumArg(ip,w); *v = Sign(w->Value());
		break;
		
	case tROUND:	// round
		ip=SingleNumArg(ip,w); *v = floor(0.5+w->Value());
		break;
		
	case tFRACT:	// fract
		ip=SingleNumArg(ip,w); { double i; *v = modf(w->Value(),&i); }
		break;
		
	case tINT:		// int
		ip=SingleNumArg(ip,w); { double i; modf(w->Value(),&i); *v=i; }
		break;
			
	case tSIN:
		ip=SingleNumArg(ip,w); *v = sin(w->Value());
		break;

	case tCOS:	
		ip=SingleNumArg(ip,w); *v = cos(w->Value());
		break;

	case tTAN:	
		ip=SingleNumArg(ip,w); *v = tan(w->Value());
		break;

	case tASIN:	
		ip=SingleNumArg(ip,w); *v = asin(w->Value());
		break;

	case tACOS:	
		ip=SingleNumArg(ip,w); *v = acos(w->Value());
		break;

	case tATAN:	
		ip=SingleNumArg(ip,w); *v = atan(w->Value());
		break;

	case tSINH:	
		ip=SingleNumArg(ip,w); *v = sinh(w->Value());
		break;

	case tCOSH:	
		ip=SingleNumArg(ip,w); *v = cosh(w->Value());
		break;

	case tTANH:	
		ip=SingleNumArg(ip,w); *v = tanh(w->Value());
		break;

	case tSQUARE:
		ip=SingleNumArg(ip,w); *v = w->Value()*w->Value();
		break;

	case tSQRT:	
		ip=SingleNumArg(ip,w); *v = sqrt(w->Value());
		break;

	case tRANDOM:					// Random(upper_limit)		returns non-integer result!!
		ip=SingleNumArg(ip,w);
		*v = random() * w->Value() / 0x80000000u;
		break;
		
	case tINDEX:
		ip = RefValue(ip,w,pUna);		// non_ref valuea make no sense here, because index information get's lost!
		*v = w->Index()+1;				// no check for parent: all variables except 'globals' do have a parent
		break;							// Var's without a parent silently work on this call
		
	case tCOUNT:						// Get number of elements in vektor or characters in text or bytes in proc
	{	ip=SingleArg(ip,w);
		uint n=0;
		if (w->IsList())		n = w->ListSize();
		else if (w->IsText())	n = w->Text().Len();
		else if (w->IsProc())	n = w->Proc().Len();
		*v = n;
	}	break;

	case tCHAR:
		ip = SingleStrArg(ip,w);
		*v = w->Text().Len() ? (double)w->Text()[0] : 0.0; break;	// already ucs-4 string method

	case tISTEXT:
		ip = SingleArg(ip,w);
		*v = w->IsText();
		break;
	
	case tISNUMBER:
		ip = SingleArg(ip,w);
		*v = w->IsNumber(); 
		break;
	
	case tISLIST:
		ip = SingleArg(ip,w);
		*v = w->IsList();
		break;
	
	case tISPROC:
		ip = SingleArg(ip,w);
		*v = w->IsProc();
		break;
	
	case tISFILE:
		ip=SingleStrArg(ip,w);
		*v = IsFile(FullPath(w->Text().CString()));
		break;

	case tISDIR:
		ip=SingleStrArg(ip,w);
		*v = IsFolder(FullPath(w->Text().CString()));
		break;
	
//	log_B(X)  = ln(X) / ln(B)
//	log_2(X)  = ln(X) / ln(2)
//	log_10(X)= ln(X) / ln(10)
//	B^X  = e ^ ( X * ln(B) )
//	2^X  = e ^ ( X * ln(2) )
//	10^X = e ^ ( X * ln(10) )

	case tLOGE:	
		ip=SingleNumArg(ip,w); *v = log(w->Value());
		break;
		
	case tLOG10:	
		ip=SingleNumArg(ip,w); *v = log10(w->Value());
		break;
		
	case tLOG2:	
		ip=SingleNumArg(ip,w); *v = log(w->Value())/d_ln2;
		break;
		
	case tEXPE:	
		ip=SingleNumArg(ip,w); *v = exp(w->Value());
		break;

	case tEXP2:	
		ip=SingleNumArg(ip,w); *v = exp(w->Value()*d_ln2);
		break;
		
	case tEXP10:	
		ip=SingleNumArg(ip,w); *v = exp(w->Value()*d_ln10);
		break;
		
	case tLOGN:	// exp(base,value)
		ip=Expect(ip,tRKauf);
		ip=NumValue(ip,w,pUna); 	// w=base
		ip=Expect(ip,tKOMMA);
		ip=NumValue(ip,v,pUna); 	// v=vale
		ip=Expect(ip,tRKzu); 
		*v0 = log(v->Value())/log(w->Value()); 
		v=v0; break;

	case tEXPN:	
		ip=Expect(ip,tRKauf);
		ip=NumValue(ip,v,pAny); 	// v=base
		ip=Expect(ip,tKOMMA);
		ip=NumValue(ip,w,pAny); 	// w=vale
		ip=Expect(ip,tRKzu); 
		*v0 = exp( w->Value() *log(v->Value()) );
		v=v0; break;

	case tMIN:			// min ( a[,b[,c...]] )
		ip=Expect(ip,tRKauf);
		ip=Value(ip,v,pAny);
		if (*ip==tRKzu)
		{
			if (v==v0) { v = &v->MinVar(); idf = v->Name(); *v0=*v; v=v0; v->SetName(idf); }
			else v = &v->MinVar();
		}
		else
		{
			while(*ip==tKOMMA&&NoError())
			{
				w = &temp;
				ip=Value(ip+1,w,pAny);
				if (*w<*v)
				{
					if (w==&temp) { v0->MoveData(*w); v=v0; } 	// w=val
					else 		  { v=w; }						// w=ref
				}
			}
		}
		ip=Expect(ip,tRKzu);
		break;

	case tMAX:			// max ( a[,b[,c...]] )
		ip=Expect(ip,tRKauf);
		ip=Value(ip,v,pAny);
		if (*ip==tRKzu)
		{
			if (v==v0) { v = &v->MaxVar(); idf = v->Name(); *v0=*v; v=v0; v->SetName(idf); }
			else v = &v->MaxVar();
		}
		else
		{
			while(*ip==tKOMMA&&NoError())
			{
				w = &temp;
				ip=Value(ip+1,w,pAny);
				if (*w>*v)
				{
					if (w==&temp) { v0->MoveData(*w); v=v0; } 	// w=val
					else 		  { v=w; }						// w=ref
				}
			}
		}
		ip=Expect(ip,tRKzu);
		break;


	case tCHARSTR:
		ip = SingleNumArg(ip,w);
		*v = String((UCS4Char)w->LongValue()); break;

	case tLEFTSTR:
		ip=Expect(ip,tRKauf);
		ip=StrValue(ip,v,pAny);
		ip=Expect(ip,tKOMMA);
		ip=NumValue(ip,w,pAny);
		ip=Expect(ip,tRKzu);
		*v0 = LeftString(v->Text(),w->LongValue());
		v=v0; break;

	case tRIGHTSTR:
		ip=Expect(ip,tRKauf);
		ip=StrValue(ip,v,pAny);
		ip=Expect(ip,tKOMMA);
		ip=NumValue(ip,w,pAny);
		ip=Expect(ip,tRKzu);
		*v0 = RightString(v->Text(),w->LongValue());
		v=v0; break;

	case tMIDSTR:	// MidStr(str,p1,cnt)
	{	Var temp2; Var* u=&temp2;
		ip=Expect(ip,tRKauf);
		ip=StrValue(ip,v,pAny);
		ip=Expect(ip,tKOMMA);
		ip=NumValue(ip,w,pAny);
		if (*ip==tRKzu)
		{
			ip++;
			*v0 = MidString( v->Text(), (long)w->Value()-1 );
		}
		else
		{
			ip=Expect(ip,tKOMMA);
			ip=NumValue(ip,u,pAny);
			ip=Expect(ip,tRKzu);
			*v0 = MidString( v->Text(), (long)w->Value()-1, (long)u->Value() );
		}
	}	v=v0; break;

	case tSUBSTR:	// SubStr(str,p1,p2) = str[p1,p2]
	{	Var temp2; Var* u=&temp2;
		ip=Expect(ip,tRKauf);
		ip=StrValue(ip,v,pAny);
		ip=Expect(ip,tKOMMA);
		ip=NumValue(ip,w,pAny);
		ip=Expect(ip,tKOMMA);
		ip=NumValue(ip,u,pAny);
		ip=Expect(ip,tRKzu);
		*v0 = SubString( v->Text(), w->LongValue()-1, u->LongValue() );
	}	v=v0; break;

	case tFIND:
	{	Var temp2=1.0,*u=&temp2;
		ip=Expect(ip,tRKauf);
		ip=StrValue(ip,v,pAny);
		ip=Expect(ip,tKOMMA);
		ip=StrValue(ip,w,pAny);
		if(*ip==tKOMMA) ip=NumValue(ip+1,u,pAny);
		ip=Expect(ip,tRKzu);
		// FindString: not found => -1; else index [0...[len   -->   FIND: not found => 0; else [1...len]
		*v0 = 1+ v->Text().Find(w->Text(),u->LongValue()-1);
	}	v=v0; break;

	case tRFIND:
	{	Var temp2=0x80000000u,*u=&temp2;
		ip=Expect(ip,tRKauf);
		ip=StrValue(ip,v,pAny);
		ip=Expect(ip,tKOMMA);
		ip=StrValue(ip,w,pAny);
		if(*ip==tKOMMA) ip=NumValue(ip+1,u,pAny);
		ip=Expect(ip,tRKzu);
		// RFindString: not found => -1; else index [0...[len   -->   RFIND: not found => 0; else [1...len]
		*v0 = 1+ v->Text().RFind(w->Text(),u->LongValue()-1);
	}	v=v0; break;

	case tREPLACE:	// text = replace ( targettext, searchtext, replacementtext )
	{	Var temp2,*u=&temp2;
		ip=Expect(ip,tRKauf);
		ip=StrValue(ip,v,pAny);		// v = target text
		ip=Expect(ip,tKOMMA);
		ip=StrValue(ip,w,pAny);		// w = search text
		ip=Expect(ip,tKOMMA);
		ip=StrValue(ip,u,pAny);		// u = replacement text
		ip=Expect(ip,tRKzu);
		if (v!=v0) { *v0 = *v; v=v0; }
		v->Text().Replace( w->Text(), u->Text() );
		break;
	}

	case tSPLIT:
		ip=Expect(ip,tRKauf);
		ip=Value(ip,v,pAny); if (v!=v0) { *v0=*v; v=v0; }
		if(*ip==tKOMMA)
		{
			ip=Value(ip+1,w,pAny);
			if (w->IsNumber()) { temp = String(w->LongValue()); w=&temp; } else
			if (w->IsNoText()) SetError(expectedstring);
			ip=Expect(ip,tRKzu);
			if(NoError()) v->SplitLines(w->Text());
		}
		else
		{
			ip=Expect(ip,tRKzu);
			if(NoError()) v->SplitLines();
		}
		break;

	case tCONVERT:	//	text = convert(text from|to idf)
		{	ip = Expect(ip,tRKauf);
			ip = Value(ip,v,pAny); if (Error()) goto x;
			bool f = *ip==tFROM;
			if (!f&&*ip!=tTO) { SetError(syntaxerror, "'from' or 'to' expected"); goto x; }
			ip = GetIdentifier(ip+1,idf);
			ip = Expect(ip,tRKzu);
			CharEncoding what = GetEncodingFromName(idf);
			if (v!=v0) { *v0=*v;v=v0; }
			if (NoError()) { if (f) v->ConvertFrom(what); else v->ConvertTo(what); }
			break;
		}

	case tENV:
	{	v->SetList();
		cstr s;
		uint i;
		for( i=0; (s=environ[i]); i++ )
		{
			char* t = strchr(s,'=');
			if (!t) { Log(Using("bummer: %s\n",s)); continue; }
			(*v)[i] = t+1;
			(*v)[i].SetName(SubStr(s,t));
		}
		v->ResizeList(i);
	}	break;

	case tMATCH:		// match ( pathstr, patternstr [,flag] )
	{	ip = Expect(ip,tRKauf);
		ip = StrValue(ip,v,pAny);		// v = path
		ip = Expect(ip,tKOMMA);
		ip = StrValue(ip,w,pAny);		// w = pattern

		MatchType flags = fnm_basic;
				//  | fnm_escape		// handle '\' as escape character
				//  | fnm_casefold		// case-insensitive
				//	| fnm_pathname		// '/' must match
				//	| fnm_pweriod		// leading '.'s must match
		if (*ip==tKOMMA) { Var temp_z; Var*z=&temp_z; ip=NumValue(ip+1,z,pAny); flags = (MatchType)z->Value(); }
		ip = Expect(ip,tRKzu);
		*v0 = FNMatch(CString(v->Text()),CString(w->Text()),flags);
		v = v0;
	}	break;
	
			
	case tHEXSTR:
	{	ip = Expect(ip,tRKauf);
		ip = NumValue(ip,w,pAny);
		int digits;
		double d,f; modf(w->Value(),&f); if(f<0) f=0;
		if (*ip==tKOMMA) { w=&temp; ip = NumValue(ip+1,w,pAny); digits=(uchar)w->Value(); }
		else digits = f==0 ? 1 : int(1.0+log(f)/d_ln16);
		ip = Expect(ip,tRKzu);
		if (Error()) goto x;
		*v = String(digits,csz1);
		f = modf( f / exp(digits*d_ln16),&d);
		for (int i=0;i<digits;i++)
		{
			f = modf(f*16,&d);
			*(v->Text().Text()+i) = '0' + (uchar)d + 7*(d>=10);
		}
	}	break;

	case tBINSTR:
	{	ip = Expect(ip,tRKauf);
		ip = NumValue(ip,w,pAny); 
		int digits;
		double d,f; modf(w->Value(),&f); if(f<0)f=0;
		if (*ip==tKOMMA) { w=&temp; ip = NumValue(ip+1,w,pAny); digits=(uchar)w->Value(); }
		else digits = Max(1,int(1.0+log(f)/d_ln2));
		ip = Expect(ip,tRKzu);
		if (Error()) goto x;
		*v = SpaceStr(digits);
		f = modf( f / exp(digits*d_ln2),&d);
		for (int i=0;i<digits;i++)
		{
			f = modf(f*2,&d);
			*(v->Text().Text()+i) = '0' + (uchar)d;
		}
	}	break;		
	
	case tTOUPPER:
		ip = SingleStrArg(ip,w); if (Error()) goto x;
		*v = w->Text().ToUpper();
		break;
	
	case tTOLOWER:
		ip = SingleStrArg(ip,w); if (Error()) goto x;
		*v = w->Text().ToLower();
		break;
	
	case tGETNAME:
		ip = SingleArg(ip,w);
		*v = w->Name();
		break;
		
	case tTOTEXT:
		ip = SingleArg(ip,w); if (Error()) goto x;
		*v = w->ToString(no,DisassString); 			// 2003-08-07 kio: quotestring=no, disass procs
		break;
	
	case tFULLPATH:
		ip = SingleStrArg(ip,w);
		*v = FullPath(w->Text().CString());
		break;
	
	case tMDATE:	
		ip = SingleStrArg(ip,w);
		if (NoError()) *v = FileMTime(CString(w->Text()));
		break;
		
	case tDIR:
		ip = SingleStrArg(ip,w);
		if (NoError()) ReadDirectory(w->Text(),*v);
		break;

	case tFILE:
		ip = SingleStrArg(ip,w);
		if (NoError()) LoadFile(w->Text(),*v);
		break;

	case tDATESTR:
		ip=SingleNumArg(ip,w);
		*v = DateStr((time_t)w->Value());
		break;

	case tDATEVAL:
		ip=SingleStrArg(ip,w);
		*v = DateVal(CString(w->Text()));
		break;

	case tNOW:
		*v = FloatCurrentTime();
		break;

	case tEXISTS:	// if exists [ refval | file "url" ]
	{
		bool kf = *ip==tRKauf;
		ip += kf;

		if (*ip!=tIDF)
		{
			if (*ip==tDIR)
			{
				ip = StrValue(ip+1,w,kf?pAny:pUna);
				*v = IsFolder(FullPath(w->Text().CString()));
				if (Error()) { *v = no; ClearError(); }
				goto ex;
			}
			if (*ip==tFILE)
			{
				ip = StrValue(ip+1,w,kf?pAny:pUna);
				*v = IsFile(FullPath(w->Text().CString()));
				if (Error()) { *v = no; ClearError(); }
				goto ex;
			}
		}

		// tIDF

		ip = Value(ip,w,pUna); 
		*v = NoError();
		if (Error())
		{
			if (Error()==variablenotfound||Error()==componentnotfound)
			{
				ClearError();
				while(*ip==tDOT) { String p; ip=GetIdentifier(ip+1,p); }
			}
		}	
		else
		{
			if (*ip==tDOT)
			{
				*v = (double)no;
				while(*ip==tDOT) { String p; ip=GetIdentifier(ip+1,p); }
			}
		}
	ex:	if (kf) ip=Expect(ip,tRKzu);
		break;
	}

	case tEVAL:		// any_type = eval text
	{
		ip = SingleStrArg(ip,w);
		if (Error()) break;
		Var myproc("eval",isProc);							// note: we must set VScript.proc properly before executing arbitrary code!
		long idx_io=0;
		myproc.Proc() = Tokenize( w->Text(), idx_io );
		w = proc; proc = &myproc;
		Value((uptr)myproc.Proc().Text(),v,pAny);
		proc = w;
		break;
	}

	case tINCLUDE:	// any_type = include filename
		ip = SingleStrArg(ip,w);
		if (NoError()) Run(w->Text(),v);
		if (v) break;
		v=v0; SetError(procnovalue);
		break;

	case tCALL:		// any_type = call ( scriptpath, arguments, ... )
	{
		ip = Expect(ip,tRKauf);
		ip = StrValue(ip,w,pAny);
		idf = w->Text();

		Var* mylocals = new Var("locals",isList);

		while (NoError() && *ip==tKOMMA)
		{
			Var* a = new Var;	// next arg
			mylocals->AppendItem(a);
			w = a;
			ip = Value(ip+1,w,pAny);
			if(w!=a) *a = *w;
		}
		ip = Expect(ip,tRKzu);

		if(NoError())
		{
			Var*z=globals;w=locals;globals=locals=mylocals;
			Run(idf,v);
			if(!v) { SetError(procnovalue); v=v0; }
			else if(mylocals->Contains(v)) { *v0=*v; v=v0; }
			globals=z;locals=w;
		}

		delete mylocals;
		break;
	}

	case tSYS:		// text = sys ( cmd_path, text_arguments, ... )
	{
		ip = Expect(ip,tRKauf);

		int n = 0;
		Var args(isList), *a;
		w = a = &args[n++];	// cmd
		ip = StrValue(ip,w,pAny);
		if(w!=a) *a = *w;

		if ( NoError() && *ip==tKOMMA )
		{
			w = &temp;
			ip = Value(ip+1,w,pAny);
			if(w->IsList())
			{
				for (uint i=0;i<w->ListSize();i++)
				{
					a = &(*w)[i];
					args[n++] = a->IsText() ? *a : a->ToString(no,DisassString);
				}
			}
			else
			{
				a = &args[n++];
				for(;;)
				{
					if (w->IsNoText()) *a = w->ToString(no,DisassString);
					else if (w!=a) *a = *w;
					if (Error() || *ip!=tKOMMA) break;
					w = a = &args[n++];
					ip = Value(ip+1,w,pAny);
				}
			}
		}

		ip = Expect(ip,tRKzu);

		if(NoError())
		{
			str* argv = new str[n+1];
			for (int i=0;i<n;i++) argv[i] = NewCopy(args[i].Text().CString());
			argv[n] = NULL;
			str result = ExecCmd(argv);
			*v = result ? result : "";
			delete[] result;
			for (int i=0;i<n;i++) delete[] argv[i];
			delete[] argv;
		}
		break;
	}

	default:
		ip--;
		SetError(expectedvalue);
		break;
	}


// now: v0 still points to temp cell of caller
//		v points to result of 1st term, which may be in temp cell of caller or a variable
//		w is void

// ---- expect operator ----
	while(Error()==ok)
	{
		if (XXXXSAFE)
		{
			globals->Validate();
			if (!globals->Contains(v)) v->Validate();
		}
		if (XXXLOG) Disass(stderrStream,ip,1);

		uchar instr = *ip;
		if (tokenprio[instr]<=prio) break;
		w = &temp;		// again: provide own temp cell
		ip++;

		switch ( instr )
		{
		case tADD:		ip=Value(ip,w,pAdd); *v0 = *v +  *w;	v=v0; continue;	// add
		case tSUB:		ip=Value(ip,w,pAdd); *v0 = *v -  *w;	v=v0; continue;	// sub

		case tMUL:		ip=Value(ip,w,pMul); *v0 = *v *  *w;	v=v0; continue;	// multiply
		case tDIV:		ip=Value(ip,w,pMul); *v0 = *v /  *w;	v=v0; continue;	// divide
		case tMOD:		ip=Value(ip,w,pMul); *v0 = *v %  *w;	v=v0; continue;	// remainder

		case tOR:		ip=Value(ip,w,pBit); *v0 = *v |  *w;	v=v0; continue;	// ulong, bitwise or
		case tXOR:		ip=Value(ip,w,pBit); *v0 = *v ^  *w;	v=v0; continue;	// ulong, bitwise xor
		case tAND:		ip=Value(ip,w,pBit); *v0 = *v &  *w;	v=v0; continue;	// ulong, bitwise and

		case tLTLT:		ip=Value(ip,w,pRot); *v0 = *v << *w;	v=v0; continue;	// ulong, bitshift left
		case tGTGT:		ip=Value(ip,w,pRot); *v0 = *v >> *w;	v=v0; continue;	// ulong, bitshift left

		case tEQ:		ip=Value(ip,w,pCmp); *v0 = *v == *w;	v=v0; continue;
		case tNE:		ip=Value(ip,w,pCmp); *v0 = *v != *w;	v=v0; continue;
		case tGT:		ip=Value(ip,w,pCmp); *v0 = *v >  *w;	v=v0; continue;
		case tGE:		ip=Value(ip,w,pCmp); *v0 = *v >= *w;	v=v0; continue;
		case tLT:		ip=Value(ip,w,pCmp); *v0 = *v <  *w;	v=v0; continue;
		case tLE:		ip=Value(ip,w,pCmp); *v0 = *v <= *w;	v=v0; continue;

		case tANDAND:	if( v->ToNumber() )
							 { ip = NumValue(ip,w,pBoo); *v0 = w->Value()!=0.0; }
						else { ip = SkipTerm(ip,pBoo);   *v0 = 0.0; }
						v=v0; continue;

		case tOROR:		if( v->ToNumber() )
						     { ip = SkipTerm(ip,pBoo);   *v0 = 1.0; }
						else { ip = NumValue(ip,w,pBoo); *v0 = w->Value()!=0.0; }
						v=v0; continue;

		case tPLPL:		if (v==v0) SetError(expectedvarref); else
						if (!TrapVarInUse(v)) { *v0=*v; *v += 1.0; v=v0; } continue;

		case tMIMI:		if (v==v0) SetError(expectedvarref); else
						if (!TrapVarInUse(v)) { *v0=*v; *v -= 1.0; v=v0; } continue;

		case tGL:		if (v==v0) SetError(expectedvarref);
						TrapVarInUse(v); ip = Value(ip,w,pAny);
						if (NoError()) if (w!=&temp) *v = *w; else v->MoveData(*w);
						if (prio!=pAcceptVoid) continue;
						else { v=NULL; XXLogOut(""); return ip; }

		case tHASHGL:	// text_var #= text
						if (v==v0) SetError(expectedvarref);
						TrapVarInUse(v); ip = Value(ip,w,pAny); if(Error()) break;
						if (v->IsText()&&w->IsText()) v->Text() += w->Text();
						else						  v->AppendString(*w,DisassString);
						continue;

		case tHASHHASHGL:	// list_var ##= list
						if (v==v0) SetError(expectedvarref);
						TrapVarInUse(v);
						ip = Value(ip,w,pAny);
						if(Error()) break;
						if ( w!=&temp ) temp = *w;		// => no intersection; need copy anyway
						v->AppendList(temp);
						continue;

		case tANDANDGL:	// bool_var &&= bool		((pruning nimp))
					{	if (v==v0) SetError(expectedvarref);
						TrapVarInUse(v);
						double f = v->ToNumber();
						ip = NumValue(ip,w,pAny);
						if(NoError()) *v = f && w->Value();
					}	continue;

		case tORORGL:		// bool_var ||= bool		((pruning nimp))
					{	if (v==v0) SetError(expectedvarref);
						TrapVarInUse(v);
						double f = v->ToNumber();
						ip = NumValue(ip,w,pAny);
						if(NoError()) *v = f || w->Value();
					}	continue;

		case tGTGTGL:	oper = &Var::operator>>=;	goto ogl;		// num_var >>= num
		case tLTLTGL:	oper = &Var::operator<<=;	goto ogl;		// num_var <<= num
		case tANDGL:	oper = &Var::operator&=;	goto ogl;		// &=
		case tXORGL:	oper = &Var::operator^=;	goto ogl;		// ^=
		case tORGL:		oper = &Var::operator|=;	goto ogl;		// |=
		case tDVGL:		oper = &Var::operator/=;	goto ogl;		// /=
		case tMODGL:	oper = &Var::operator%=;	goto ogl;		// %=
		case tMUGL:		oper = &Var::operator*=;	goto ogl;		// *=
		case tMIGL:		oper = &Var::operator-=;	goto ogl;		// -=
		case tPLGL:		oper = &Var::operator+=;	goto ogl;		// +=

			ogl:		if (v==v0) SetError(expectedvarref);
						TrapVarInUse(v);
						ip = Value(ip,w,pAny);
						if(NoError()) (v->*oper)(*w);				// krätze die syntax!!
						continue;


		case tQMARK:	//	0 ? a : 0 ? b : 0 ? c : 1 ? d : x ? e : x ? f ...  =  d
					do
					{
						bool f = v->ToNumber();
						v = v0;

						if( f )
						{
							ip = Value(ip,v,pSel);
							ip = Expect(ip,tCOLON);
							ip = SkipTerm(ip,pSel-1);
							break;
						}
						else
						{
							ip = SkipTerm(ip,pSel);
							ip = Expect(ip,tCOLON);
							ip = Value(ip,v,pSel);
							if (*ip!=tQMARK) break; else ip++;
						}
					}
					while (NoError());
					continue;

		case tDOT:	if (!v->IsList()) { ip--; break; }			// don't set error!   ***WHY?***
					ip = GetIdentifier(ip,idf); if(Error()) break;
					w = v->FindItem(idf);
					if (w)
					{
						if (v!=v0)
						{
							v = w;						// ref_var := ref_var.idf
						}
						else							// temp := temp.idf
						{
							*v0 = *w; 					// need real copy, no pointer to nirvana
							v0->SetName(idf); 			// set name for temp cell  =>  name(env.USER) works  (höherer Blödsinn?)
						}
						continue;
					}
					SetError(
						componentnotfound,
						CatStr(
								"named component not found: ",
								(v->Name()+"."+idf).ToQuoted().CString() )
					);
					break;

		case tHASH:	ip = Value(ip,w,pHash); if (Error()) break;
					if(v->IsText()&&w->IsText()) *v0 = v->Text() + w->Text();
					else 						 *v0 = v->ConcatString(*w,DisassString);
					v=v0;
					continue;

		case tHASHHASH:
					ip = Value(ip,w,pHash);  if (Error()) break;
					if (v!=v0) { *v0=*v; v=v0; }
					if (w!=&temp) temp = *w;
					v->AppendList(temp);
					continue;

		case tEKauf:
					ip--;
					if (v->IsText())
					{
				s:  	ip++;
						if (*ip==tTO)	// string [ to num_value ]
						{
							ip  = NumValue(ip+1,w,pAny);
							*v0 = LeftString(v->Text(),w->LongValue());
						}
						else
						{
							ip = NumValue(ip,w,pAny); if (Error()) break;
							if (*ip==tTO)
							{
								ip++;
								if (*ip==tEKzu)	// string [ num_value to ]
								{
									*v0 = MidString(v->Text(),w->LongValue()-1);
								}
								else	// string [ num_value to num_value ]
								{
									Var temp2;
									Var* e=&temp2; ip = NumValue(ip,e,pAny);
									*v0 = SubString(v->Text(),w->LongValue()-1,e->LongValue());
								}
							}
							else	// string [ num_value ]
							{
								*v0 = SubString(v->Text(),w->LongValue()-1,w->LongValue());
							}
						}
						v  = v0;
						if (XXXXSAFE) v->Text()._check(__FILE__,__LINE__);
					}
					else if (v->IsList())
					{
				v:		long i=0,j;
						ip++;
						if (*ip==tTO) goto v2;

						ip = NumValue(ip,w,pAny);
						i = w->LongValue();
						if (Error()) goto x;

						if (*ip==tKOMMA||*ip==tEKzu)
						{
							i -= 1;
							if(i>>26) { SetError(indexoutofrange); goto x; }

							if (v==v0)
							{	// temp := temp[i]
								idf = (*v0)[i].Name();
								*v0 = (*v0)[i];				// need real copy, no pointer to nirvana
								v0->SetName(idf); 			// set name for temp cell  =>  name(env[1]) works
								if(Error()) goto x;
							}
							else { w = &(*v)[i]; if (Error()) goto x; v = w; }

							if (*ip==tKOMMA)
							{
								w=&temp;	// restore
								if (v->IsList()) goto v;
								if (v->IsText()) goto s;
							}
						}
						else if (*ip==tTO)
						{
							if (i>0) i--; else i=0;

				v2:			ip++;
							j = v->ListSize();

							if(*ip!=tEKzu)
							{
								ip = NumValue(ip,w,pAny);
								j = w->LongValue();
								if (Error()) goto x;
								if (j>(long)v->ListSize()) j=v->ListSize();
							}

							if (i>=j) { v=v0; v->SetList(); }
							else if (v==v0) { v->ResizeList(j); v->DeleteItems(0,i); }
							else { v0->SetList(); while (i<j) { v0->AppendItem(new Var((*v)[i++])); } v=v0; }
						}
					}
					else
					{
						SetError(wrongdatatype,"list or text required");
					}
					ip=Expect(ip,tEKzu);
					continue;

		case tRKauf:
			if (v->IsNoProc()) { SetError(notaprocedure); break; }
			{	// create local variables
					Var* myproc   = v;
					Var* mylocals = new Var("locals",isList);

				// parse argument list:		procedure data starts with the opening bracket
					uchar* pip = myproc->ProcAddr();					// pip = procedure's ip
					if (XXXSAFE) pip=Expect(pip,tRKauf); else pip++;	// skip "(" in argument list
					Var*a,*b;

//					ip++;		// skip "("
					if (*pip!=tRKzu) while (NoError())
					{
						if (*pip==tMUL)
						{
							pip++; XXXTRAP(*pip!=tRKzu);
							if (*ip!=tRKzu) while (NoError())
							{
								a = b = new Var; OMEM(a);
								ip = Value(ip,b,pAny); if (Error()) goto xp;
								if (a!=b) *a=*b;
								mylocals->AppendItem(a);
								if (*ip!=tKOMMA) break;
								ip++;
							}
							break;
						}

						if (XXSAFE) if(*pip!=tIDF) { SetError(expectedidentifier); break; }

					// manually extract idf name:
					// GetIdentifier() would a) accept @text ((should be rejected by proc creator)) and
					// b) would pass VScript.proc->Proc() as surrounding string and crash
						idf = String ( myproc->Proc(), pip+3, pip[2], CharSize(pip[1]) );
						pip += 3 + pip[2]*pip[1];

						a = b = new Var(idf,isNumber); OMEM(a);
						ip = Value(ip,b,pAny);
						if (a!=b) *a=*b;
						mylocals->AppendItem(a);

						if (*pip==tKOMMA)
						{
							if (*ip==tKOMMA) { ip++; pip++; continue; }
							if (*(pip+1)==tMUL) pip+=2;
							else Expect(ip,tKOMMA);
						}
						break;
					}
					ip=Expect(ip,tRKzu);

					if (XXXSAFE) { pip=Expect(pip,tRKzu);pip=Expect(pip,tGKauf); } else pip += 2;	// skip "){"

				// execute proc
					if (NoError())
					{
						Var* oldlocals=locals;	locals = mylocals;

						v = v0;
						pip = Execute(*myproc,v,pip);	// -> error, errGKzu if "}", errEOF if phys. eof, noErr if END or RETURN
						if (Error()==errGKzu&&pip==myproc->ProcAddr()+myproc->ProcSize()) ClearError();

						if ( v==NULL )					// proc returned no value
						{
							if ( prio==pAcceptVoid && NoError() )	// acceptable ?
							{
								locals=oldlocals;
								delete mylocals;
								XXLogOut("");
								return ip;				// return v = NULL
							}
							SetError(procnovalue);
							v=v0;
						}

						if (Error())
						{
							str n = CString(myproc->Name()); if (strchr(n,'/')==NULL) n = CatStr(n,"()");
							ForceError( Error(), CatStr(ErrorStr(),"\n       in: ",n) );
							uptr p = (uptr)proc->Proc().Text();
							if( pip>=p && pip<p+proc->Proc().Len() )
							{
							// error position can be found with Tokenize():
							//	ip = pip;
							}
							else
							{
							// outside body of current proc!
							// not shure whether error position can be found with Tokenize():
							}
							v=v0;
						}

					// deref. ref_to_local_var
						if ( v!=v0  &&					// it's a ref_var?
							mylocals->Contains(v) )		// it's a ref_to_local_var ?
						{
							v0->MoveData(*v);
							v = v0;
						}

						locals = oldlocals;
					}

				// purge local variables
				xp:	delete mylocals;
					continue;
			}

		default: ip--; break;		// break from infinite loop: no error!
		}
		break;
	}

// no operator followed  =>  return value; caller will check the reason of returning anyway
x:	XXXTRAP(!v);
	XXLogOut("");
	return ip;
}





