#include <Sic/Sic.h>

void LambdaArgs(CParser& p, SLambda& l)
{
	p.PassChar('(');
	if(!p.Char(')'))
		for(;;) {
			l.ref.Add(p.Char('&'));
			l.arg.Add(p.ReadId());
			if(p.Char(')'))
				break;
			p.PassChar(',');
		}
	l.ref.Shrink();
	l.arg.Shrink();
}

String Sic::ReadName()
{
	if(Id("operator")) {
		if(Char2('+', '='))
			return "operator+=";
		else
		if(Char2('-', '='))
			return "operator-=";
		else
		if(Char2('*', '='))
			return "operator*=";
		else
		if(Char2('/', '='))
			return "operator/=";
		else
		if(Char2('%', '='))
			return "operator%=";
		else
		if(Char2('>', '<'))
			return "operator><";
		else
		if(Char2('=', '='))
			return "operator==";
		else
		if(Char2('<', '<'))
			return "operator<<";
		else
			ThrowError("invalid operator");
	}
	return ReadId();
}

SVal Sic::ReadLambda()
{
	SVal lambda;
	SLambda& l = lambda.CreateLambda();
	LambdaArgs(*this, l);
	if(!IsChar('{'))
		ThrowError("missing '{'");
	const char *t = GetPtr();
	SkipStatement();
	l.code = String(t, GetPtr());
	return lambda;
}

SVal Sic::ExecuteLambda(const String& id, SVal lambda, SVal *self, Vector<SRVal>& arg)
{
	if(!lambda.IsLambda())
		ThrowError("lambda required");
	const SLambda& l = lambda.GetLambda();
	if(l.arg.GetCount() != arg.GetCount())
		ThrowError("invalid number of arguments in call to '" + id + "'");
	if(l.escape) {
		SVal ret_val = (*l.escape)(*this, arg);
		return ret_val;
	}
	if(stack_level > 40)
		ThrowError("Stack overflow");
	Sic sub(global, l.code);
	sub.self = self;
	sub.stack_level = stack_level + 1;
	for(int i = 0; i < l.arg.GetCount(); i++) {
		Var& v = sub.var.GetAdd(l.arg[i]);
		if(l.ref[i]) {
			if(!arg[i].lvalue)
				ThrowError("l-value required for " + l.arg[i] +
				           " argument in call to  '" + id + "'");
			v.ref = arg[i].lvalue;
		}
		else
			v.var = arg[i];
	}
	sub.Run();
	return sub.return_value;
}

Sic::SRVal Sic::Term()
{
	if(IsInt())
		return ReadInt();
	if(IsString())
		return ReadString();
	if(Char('@'))
		return ReadLambda();
	SRVal v;
	if(Id("void"))
		return v;
	if(Char('{')) {
		SVal map;
		map.SetMap();
		if(!Char('}'))
			for(;;) {
				SVal v = Exp();
				if(Char(':'))
					map.Set(v) = Exp();
				else
					map.Add() = v;
				if(Char('}'))
					break;
				PassChar(',');
			}
		return map;
	}
	if(Char('(')) {
		SRVal v = Exp();
		PassChar(')');
		return v;
	}
	SVal *_self = NULL;
	bool  _global = false;
	if(Char('.')) {
		if(!self)
			ThrowError("member-access in non-member lambda");
		_self = self;
	}
	else
	if(Char(':'))
		_global = true;
	if(IsId()) {
		String id = ReadName();
		int ii;
		SVal *self = _self;
		if(!_self && !_global && var.Find(id) < 0 &&
		   IsChar('(') && (ii = global.Find(id)) >= 0 &&
		   global[ii].IsLambda())
			v = global[ii];
		else {
			if(_self)
				v.lvalue = &_self->Set(id);
			else
			if(_global)
				v.lvalue = &global.GetAdd(id);
			else {
				Var& q = var.GetAdd(id);
				if(q.ref)
					v.lvalue = q.ref;
				else
					v.lvalue = &q.var;
			}
			for(;;)
				if(Char('[')) {
					self = v.lvalue;
					if(Char(']'))
						v.lvalue = &v.lvalue->Add();
					else {
						SVal x = Exp();
						v.lvalue = &v.lvalue->Set(x);
						PassChar(']');
					}
				}
				else
				if(Char('.')) {
					self = v.lvalue;
					id = ReadId();
					v.lvalue = &v.lvalue->Set(id);
				}
				else
					break;
		}
		if(Char('(')) {
			Vector<SRVal> arg;
			if(!Char(')'))
				for(;;) {
					arg.Add(Exp());
					if(Char(')')) break;
					PassChar(',');
				}
			v = ExecuteLambda(id, v, self, arg);
		}
	}
	else
		ThrowError("invalid expression");
	return v;
}

void Escape(ArrayMap<String, SVal>& global,
            const char *function,
            SVal (*fn)(Sic& sic, Vector<Sic::SRVal>& var))
{
	CParser p(function);
	SVal& v = global.GetAdd(p.ReadId());
	SLambda& l = v.CreateLambda();
	l.escape = fn;
	LambdaArgs(p, l);
}

Sic::SRVal Sic::Unary()
{
	SRVal a;
	if(Char2('+', '+')) {
		a = Unary();
		if(!a->IsNumber())
			ThrowError("invalid type for prefix '++' operator");
		if(!a.lvalue)
			ThrowError("l-value required for prefix '++' operator");
		*a.lvalue = a->GetNumber() + 1;
	}
	else
	if(Char2('-', '-')) {
		a = Unary();
		if(!a->IsNumber())
			ThrowError("invalid type for prefix '--' operator");
		if(!a.lvalue)
			ThrowError("l-value required for prefix '--' operator");
		*a.lvalue = a->GetNumber() - 1;
	}
	else
	if(Char('-')) {
		a = Unary();
		if(a->IsNumber())
			a = -a->GetNumber();
		else
			ThrowError("incompatible types for unary '-' operator");
	}
	else
	if(Char('+')) {
		a = Unary();
		if(a->IsNumber())
			a = -a->GetNumber();
		else
			ThrowError("incompatible types for unary '+' operator");
	}
	else
	if(Char('!'))
		a = !IsTrue(Unary());
	else
	if(Char('~')) {
		a = Unary();
		if(a->IsNumber())
			a = ~(int)a->GetNumber();
		else
			ThrowError("incompatible types for unary '~' operator");
	}
	else
		a = Term();

	if(Char2('+', '+')) {
		if(!a->IsNumber())
			ThrowError("invalid type for postfix '++' operator");
		if(!a.lvalue)
			ThrowError("l-value required for postfix '++' operator");
		SRVal x = a->GetNumber();
		*a.lvalue = a->GetNumber() + 1;
		return x;
	}
	if(Char2('-', '-')) {
		if(!a->IsNumber())
			ThrowError("invalid type for postfix '--' operator");
		if(!a.lvalue)
			ThrowError("l-value required for postfix '--' operator");
		SRVal x = a->GetNumber();
		*a.lvalue = a->GetNumber() - 1;
		return x;
	}
	return a;
}

Sic::SRVal Sic::Operator(Sic::SRVal a, Sic::SRVal b, const char *oper, const char *op, bool sym)
{
	if(a->IsMap()) {
		const ArrayMap<SVal, SVal>& m = a->GetMap();
		int q = m.Find(String(oper));
		if(q >= 0 && m[q].IsLambda()) {
			Vector<SRVal> arg;
			arg.SetCount(1);
			arg[0] = b;
			ExecuteLambda(oper, m[q], a.lvalue ? a.lvalue : &a.rvalue, arg);
			return a;
		}
	}
	if(sym)
		return Operator(b, a, oper, op);
	ThrowError(String() << "incompatible types for '" << op << "' operator");
	return a;
}

Sic::SRVal Sic::Mul()
{
	SRVal a = Unary();
	for(;;)
		if(!IsChar2('*', '=') && Char('*')) {
			SRVal b = Unary();
			if(a->IsNumber() && b->IsNumber())
				a = a->GetNumber() * b->GetNumber();
			else
				a = Operator(~a, ~b, "operator*=", "*", true);
		}
		else
		if(!IsChar2('/', '=') && Char('/')) {
			SRVal b = Unary();
			if(a->IsNumber() && b->IsNumber())
				a = a->GetNumber() / b->GetNumber();
			else
				a = Operator(~a, b, "operator/=", "/");
		}
		else
		if(!IsChar2('%', '=') && Char('%')) {
			SRVal b = Unary();
			if(a->IsNumber() && b->IsNumber())
				a = (int)a->GetNumber() % (int)b->GetNumber();
			else
				a = Operator(~a, b, "operator%=", "%");
		}
		else
			return a;
}

Sic::SRVal Sic::Add()
{
	SRVal a = Sic::Mul();
	for(;;)
		if(!IsChar2('+', '=') && Char('+')) {
			SRVal b = Mul();
			if(a->IsNumber() && b->IsNumber())
				a = a->GetNumber() + b->GetNumber();
			else
			if(a->IsString() && b->IsString())
				a = a->GetString() + b->GetString();
			else
				a = Operator(~a, ~b, "operator+=", "+", true);
		}
		else
		if(!IsChar2('-', '=') && Char('-')) {
			SRVal b = Mul();
			if(a->IsNumber() && b->IsNumber())
				a = a->GetNumber() - b->GetNumber();
			else
				a = Operator(~a, b, "operator-=", "-");
		}
		else
			return a;
}

Sic::SRVal Sic::Shift()
{
	SRVal a = Add();
	for(;;)
		if(Char2('<', '<')) {
			SRVal b = Add();
			if(a->IsNumber() && b->IsNumber())
				a = (int)a->GetNumber() << (int)b->GetNumber();
			else {
				if(a->IsVoid())
					if(a.lvalue)
						*a.lvalue = String();
					else
						a.rvalue = String();
				if(a->IsString()) {
					if(b->IsString())
						a.lvalue->GetString() << b->GetString();
					else
					if(b->IsNumber())
						a.lvalue->GetString() << b->GetNumber();
					else
					if(!b->IsVoid())
						ThrowError("Not implemented");
				}
				else
					a = Operator(a, b, "operator<<", "<<", true);
			}
		}
		else
		if(Char2('>', '>')) {
			SRVal b = Add();
			if(a->IsNumber() && b->IsNumber())
				a = (int)a->GetNumber() >> (int)b->GetNumber();
			else
				ThrowError("incompatible types for '>>' operator");
		}
		else
			return a;

}

int Sic::DoCompare(const SVal& a)
{
	SVal b = Shift();
	if(a.IsNumber() && b.IsNumber())
		return a.GetNumber() - b.GetNumber();
	if(a.IsString() && b.IsString())
		return a.GetString().Compare(b.GetString());
	ThrowError("incompatible types for comparison operator");
	return 0;
}

Sic::SRVal Sic::Compare()
{
	SRVal a = Shift();
	for(;;)
		if(Char2('>', '='))
			a = DoCompare(a) >= 0;
		else
		if(Char2('<', '='))
			a = DoCompare(a) <= 0;
		else
		if(Char('>'))
			a = DoCompare(a) > 0;
		else
		if(Char('<'))
			a = DoCompare(a) < 0;
		else
			return a;
}

Sic::SRVal Sic::Equal()
{
	SRVal a = Compare();
	for(;;)
		if(Char2('=', '=')) {
			SRVal b = Compare();
			a = int(~a == ~b);
		}
		else
		if(Char2('!', '=')) {
			SRVal b = Compare();
			a = int(~a != ~b);
		}
		else
			return a;
}

Sic::SRVal Sic::BinAnd()
{
	SRVal a = Equal();
	while(!IsChar2('&', '&') && Char('&')) {
		SRVal b = Equal();
		if(a->IsNumber() && b->IsNumber())
			a = (int)a->GetNumber() & (int)b->GetNumber();
		else
			ThrowError("incompatible types for '&' operator");
	}
	return a;
}

Sic::SRVal Sic::BinXor()
{
	SRVal a = BinAnd();
	while(Char('^')) {
		SRVal b = BinAnd();
		if(a->IsNumber() && b->IsNumber())
			a = (int)a->GetNumber() ^ (int)b->GetNumber();
		else
			ThrowError("incompatible types for '^' operator");
	}
	return a;
}

Sic::SRVal Sic::BinOr()
{
	SRVal a = BinXor();
	while(!IsChar2('|', '|') && Char('|')) {
		SRVal b = BinXor();
		if(a->IsNumber() && b->IsNumber())
			a = (int)a->GetNumber() | (int)b->GetNumber();
		else
			ThrowError("incompatible types for '|' operator");
	}
	return a;
}

Sic::SRVal Sic::And()
{
	SRVal a = BinOr();
	while(Char2('&', '&')) {
		SRVal b = BinOr();
		a = IsTrue(a) && IsTrue(b);
	}
	return a;
}

Sic::SRVal Sic::Or()
{
	SRVal a = And();
	while(Char2('|', '|')) {
		SRVal b = And();
		a = IsTrue(a) || IsTrue(b);
	}
	return a;
}

Sic::SRVal Sic::Cond()
{
	SRVal a = Or();
	if(Char('?')) {
		SRVal b = Or();
		PassChar(':');
		SRVal c = Or();
		return IsTrue(a) ? b : c;
	}
	return a;
}

Sic::SRVal Sic::Assign()
{
	SRVal a = Cond();
	if(Char('=')) {
		SRVal b = Assign();
		if(!a.lvalue)
			ThrowError("l-value required on the left side of '=' operator");
		*a.lvalue = b;
	}
	else
	if(Char2('+', '=')) {
		SRVal b = Assign();
		if(!a.lvalue)
			ThrowError("l-value required on the left side of '+=' operator");
		if(a->IsNumber() && b->IsNumber())
			*a.lvalue = a->GetNumber() + b->GetNumber();
		else
		if(a->IsString() && b->IsString())
			a.lvalue->GetString() += b->GetString();
		else
			Operator(a, b, "operator+=", "+=");
	}
	else
	if(Char2('-', '=')) {
		SRVal b = Assign();
		if(!a.lvalue)
			ThrowError("l-value required on the left side of '-=' operator");
		if(a->IsNumber() && b->IsNumber())
			*a.lvalue = a->GetNumber() - b->GetNumber();
		else
			Operator(a, b, "operator-=", "-=");
	}
	else
	if(Char2('*', '=')) {
		SRVal b = Assign();
		if(!a.lvalue)
			ThrowError("l-value required on the left side of '*=' operator");
		if(a->IsNumber() && b->IsNumber())
			*a.lvalue = a->GetNumber() * b->GetNumber();
		else
			Operator(a, b, "operator*=", "*=");
	}
	else
	if(Char2('/', '=')) {
		SRVal b = Assign();
		if(!a.lvalue)
			ThrowError("l-value required on the left side of '/=' operator");
		if(a->IsNumber() && b->IsNumber())
			*a.lvalue = a->GetNumber() / b->GetNumber();
		else
			Operator(a, b, "operator/=", "/=");
	}
	else
	if(Char2('%', '=')) {
		SRVal b = Assign();
		if(!a.lvalue)
			ThrowError("l-value required on the left side of '%=' operator");
		if(a->IsNumber() && b->IsNumber())
			*a.lvalue = (int)a->GetNumber() % (int)b->GetNumber();
		else
			Operator(a, b, "operator%=", "%=");
	}
	else
	if(Char2(':', '=')) {
		if(!a.lvalue)
			ThrowError("l-value required on the left side of ':='");
		while(Char('.')) {
			String id = ReadName();
			a.lvalue->Set(id) = ReadLambda();
		}
	}
	return a;
}

Sic::SRVal Sic::Exp()
{
	return Assign();
}

void Sic::SkipTerm()
{
	if(IsEof())
		throw CParser::Error("unexpected end of file");
	CParser::SkipTerm();
}

void Sic::SkipExp()
{
	int level = 0;
	for(;;) {
		if(IsChar(';'))
			return;
		if(IsChar(')') && level == 0)
			return;
		if(Char(')'))
			level--;
		else
		if(Char('('))
			level++;
		else
			SkipTerm();
	}
}

void Sic::SkipStatement()
{
	for(;;) {
		if(Char(';')) return;
		if(Char('{'))
			break;
		SkipTerm();
	}
	int level = 1;
	while(level > 0) {
		if(Char('{')) level++;
		else
		if(Char('}')) level--;
		else
			SkipTerm();
	}
}

bool  Sic::PCond()
{
	PassChar('(');
	bool c = IsTrue(Exp());
	PassChar(')');
	return c;
}

void Sic::FinishSwitch()
{
	while(!Char('}') && no_break && no_return) {
		if(Id("case")) {
			Exp();
			PassChar(':');
		}
		else
		if(Id("default"))
			PassChar(':');
		DoStatement();
	}
}

void  Sic::DoStatement()
{
	if(Id("if"))
		if(PCond()) {
			DoStatement();
			if(Id("else"))
				SkipStatement();
		}
		else {
			SkipStatement();
			if(Id("else"))
				DoStatement();
		}
	else
	if(Id("do")) {
		loop++;
		const char *term = GetPtr();
		do {
			SetPtr(term);
			DoStatement();
			PassId("while");
		}
		while(PCond() && no_break && no_return);
		PassChar(';');
		no_break = true;
		loop--;
	}
	else
	if(Id("while")) {
		loop++;
		const char *term = GetPtr();
		for(;;) {
			SetPtr(term);
			if(!PCond() || !no_break || !no_return) {
				SkipStatement();
				break;
			}
			DoStatement();
		}
		no_break = true;
		loop--;
	}
	else
	if(Id("for")) {
		loop++;
		PassChar('(');
		if(!Char(';')) {
			Exp();
			PassChar(';');
		}
		const char *cond = NULL;
		if(!Char(';')) {
			cond = GetPtr();
			SkipExp();
			PassChar(';');
		}
		const char *after = NULL;
		if(!Char(')')) {
			after = GetPtr();
			SkipExp();
			PassChar(')');
		}
		const char *stmt = GetPtr();
		for(;;) {
			bool c = true;
			if(cond) {
				SetPtr(cond);
				c = IsTrue(Exp());
			}
			SetPtr(stmt);
			if(!c || !no_break || !no_return) {
				SkipStatement();
				break;
			}
			DoStatement();
			if(after) {
				SetPtr(after);
				Exp()->Dump();
			}
		}
		no_break = true;
		loop--;
	}
	else
	if(Id("foreach")) {
		loop++;
		PassChar('(');
		SVal *value = Exp().lvalue;
		SVal *key = NULL;
		if(Char(':')) {
			key = value;
			value = Exp().lvalue;
		}
		if(!value || !key)
			ThrowError("l-value expected in 'foreach' statement");
		PassId("in");
		SVal c = Exp();
		if(!c.IsMap())
			ThrowError("container expected in 'foreach' statement");
		PassChar(')');
		const char *stmt = GetPtr();
		const ArrayMap<SVal, SVal>& map = c.GetMap();
		for(int i = 0; i < map.GetCount() && no_break && no_return; i++) {
			if(!map.IsUnlinked(i) && !map[i].IsVoid()) {
				if(key)
					*key = map.GetKey(i);
				*value = map[i];
				SetPtr(stmt);
				DoStatement();
			}
		}
		no_break = true;
		loop--;
	}
	else
	if(Id("break")) {
		if(!loop)
			ThrowError("misplaced 'break'");
		no_break = false;
		PassChar(';');
	}
	else
	if(Id("case"))
		ThrowError("misplaced 'case'");
	else
	if(Id("default"))
		ThrowError("misplaced 'default'");
	else
	if(Id("else"))
		ThrowError("misplaced 'else'");
	else
	if(Id("return")) {
		no_return = false;
		if(!Char(';')) {
			return_value = Exp();
			PassChar(';');
		}
		else
			return_value = Null;
	}
	else
	if(Id("switch")) {
		loop++;
		PassChar('(');
		SVal a = Exp();
		PassChar(')');
		PassChar('{');
		while(!Char('{')) {
			if(Id("case")) {
				SVal b = Exp();
				PassChar(':');
				if(a == b) {
					FinishSwitch();
					break;
				}
			}
			else
			if(Id("default")) {
				PassChar(':');
				FinishSwitch();
				break;
			}
			else
				SkipStatement();
		}
		loop--;
		no_break = true;
	}
	else
	if(Char('{')) {
		while(!Char('}') && no_break && no_return)
			DoStatement();
	}
	else
	if(!Char(';')) {
		SVal v = Exp();
		Char(';');
	}
}

void  Sic::Run()
{
	no_return = no_break = true;
	loop = 0;
	while(!IsEof() && no_return && no_break)
		DoStatement();
}

void Scan(ArrayMap<String, SVal>& global, const char *file)
{
	Sic p(global, file);
	while(!p.IsEof()) {
		SVal& v = global.GetAdd(p.ReadId());
		SLambda& l = v.CreateLambda();
		LambdaArgs(p, l);
		const char *t = p.GetPtr();
		if(!p.IsChar('{'))
			p.ThrowError("missing function body");
		p.SkipStatement();
		l.code = String(t, p.GetPtr());
	}
}

SVal Execute(ArrayMap<String, SVal>& global, const char *name, const Vector<SVal>& arg)
{
	int ii = global.Find(name);
	if(ii < 0 || !global[ii].IsLambda())
		throw CParser::Error("invalid function name");
	const SLambda& l = global[ii].GetLambda();
	if(arg.GetCount() != l.arg.GetCount())
		throw CParser::Error("invalid number of arguments");
	Sic sub(global, l.code);
	for(int i = 0; i < l.arg.GetCount(); i++) {
		Sic::Var& v = sub.var.GetAdd(l.arg[i]);
		v.var = arg[i];
		if(l.ref[i])
			v.ref = &v.var;
	}
	sub.Run();
	return sub.return_value;
}

SVal Execute(ArrayMap<String, SVal>& global, const char *name)
{
	return Execute(global, name, Vector<SVal>());
}
