/*
 *  ADP (Another Data Processor) www.adp.la
 *  Copyright (C) 2010 Katsuhisa Ohfuji <katsuhisa@ohfuji.name>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  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.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA 02110-1301, USA.
 */
#include "adp.h"

// 必ずPObject &dstの引数の実体を作成すること（これはサボれない）
PObject *PString::add(const PObject &dst, PObjectArray *objs) const	{ return dst.add(*this, objs); }
PObject *PString::add(const PString &src, PObjectArray *objs) const	{ 
	if ( src.value.empty()) {
		return this->clone(objs);
	}
	if ( value.empty() ) {
		return src.clone(objs);
	}
	string result_ = src.value + this->value;
	return pmm.newPString(objs,result_);	
}


PObject *PDouble::add(const PObject &dst, PObjectArray *objs) const	{ return dst.add(*this, objs); }
PObject *PDouble::add(const PDouble &src, PObjectArray *objs) const		{ return pmm.newPDouble(objs, src.value + value); }
PObject *PDouble::add(const PInteger &src, PObjectArray *objs) const	{ return pmm.newPDouble(objs, src.value + value); }
PObject *PInteger::add(const PObject &dst, PObjectArray *objs) const	{ return dst.add(*this, objs); }
PObject *PInteger::add(const PDouble &src, PObjectArray *objs) const	{ return pmm.newPDouble(objs, src.value + value); }
PObject *PInteger::add(const PInteger &src, PObjectArray *objs) const	{ return pmm.newPInteger(objs, src.value + value); }
PObject *PList::add(const PObject &dst, PObjectArray *objs) const		{ return dst.add(*this, objs); }
PObject *PList::add(const PList &src, PObjectArray *objs) const	{ 
		// srcが[]の場合、dst(this)を返す
		if ( src.lvalue == &pnil && src.rvalue == &pnil ) {
			return this->clone(objs);
		}
		
		// dst(this)が[]の場合、srcを返す
		if ( lvalue == &pnil && rvalue == &pnil ) {
			return src.clone(objs);
		}

		// Listの結合 src.rvalueをサーチし最後にdst(this)を加える
		PList *result = src.clone(objs);

		// rvalueをサーチ
		PList *list = result;
		while ( list && list->rvalue != &pnil ) {
			list = dynamic_cast<PList *>(list->rvalue);
		}

		// 最後にdstを追加
		if ( list ) list->rvalue = this->clone(objs);
		
		return result;
		
	}
PObject *PList::add(const PArray &src, PObjectArray *objs) const	{ 
		PArray *result = pmm.newPArray(objs, src);
		
		// [A|B] の対応（ハッシュの要素の対応）
		if ( typeid(*lvalue) != typeid(const PList) && typeid(*rvalue) != typeid(const PList) ) {
			result->value.push_back(rvalue);
			string key;
			if ( lvalue->cnv_string(key) ) {
				result->hamap[key] = result->value.size() - 1;
			}
			return result;
		}

		// [a,b,c,d,e...]ないし、[[A|B],[C|D],[E|F]....]のみ対応
		const PList *list = this;

		while ( list ) {
			const PObject *value_ = list->lvalue;
			const PList	*vl = dynamic_cast<const PList *>(value_);
			if ( vl == 0 ) {	// [a,b,c,d,e...]
				result->value.push_back(value_);
			} else {			// [[A|B],[C|D],[E|F]....]
				result->value.push_back(vl->rvalue);
				string key;
				if ( vl->lvalue->cnv_string(key) ) {
					result->hamap[key] = result->value.size() - 1;
				}
			}
			list = dynamic_cast<const PList *>(list->rvalue);
		}
	
		return result;
	}
PObject *PArray::add(const PObject &dst, PObjectArray *objs) const	{ return dst.add(*this, objs); }
PObject *PArray::add(const PArray &src, PObjectArray *objs) const	{ 
		PArray *result = pmm.newPArray(objs, src);
		size_t	siz = result->value.size();
		// Valueの追加
		for ( PObjectArray::const_iterator i = value.begin(); i < value.end(); i++ ) {
			result->value.push_back(*i);
		}
		// Hashの追加
		for ( PHashArrayMap::const_iterator i = hamap.begin(); i != hamap.end(); i++ ) {
			 result->hamap[i->first] = i->second + siz; 
		}
		result->constant &= constant;	// constantはAND
		result->args = false ;	// 引数でない
		result->brace;			// braceは左辺（src）のものを継承
		return result;
	}

PObject *PString::sub(const PObject &dst, PObjectArray *objs) const	{ return 0; }
PObject *PDouble::sub(const PObject &dst, PObjectArray *objs) const	{ return dst.sub(*this,objs); }
PObject *PDouble::sub(const PDouble &src, PObjectArray *objs) const		{ return pmm.newPDouble(objs, src.value - value); }
PObject *PDouble::sub(const PInteger &src, PObjectArray *objs) const	{ return pmm.newPDouble(objs, src.value - value); }
PObject *PInteger::sub(const PObject &dst, PObjectArray *objs) const	{ return dst.sub(*this,objs); }
PObject *PInteger::sub(const PDouble &src, PObjectArray *objs) const	{ return pmm.newPDouble(objs, src.value - value); }
PObject *PInteger::sub(const PInteger &src, PObjectArray *objs) const	{ return pmm.newPInteger(objs, src.value - value);}
PObject *PList::sub(const PObject &dst, PObjectArray *objs) const		{ return 0; }
PObject *PArray::sub(const PObject &dst, PObjectArray *objs) const	{ return 0; }

PObject *PString::mul(const PObject &dst, PObjectArray *objs) const	{ return 0; }
PObject *PDouble::mul(const PObject &dst, PObjectArray *objs) const	{ return dst.mul(*this,objs); }
PObject *PDouble::mul(const PDouble &src, PObjectArray *objs) const		{ return pmm.newPDouble(objs, src.value * value); }
PObject *PDouble::mul(const PInteger &src, PObjectArray *objs) const	{ return pmm.newPDouble(objs, src.value * value); }
PObject *PInteger::mul(const PObject &dst, PObjectArray *objs) const	{ return dst.mul(*this,objs); }
PObject *PInteger::mul(const PDouble &src, PObjectArray *objs) const	{ return pmm.newPDouble(objs, src.value * value); }
PObject *PInteger::mul(const PInteger &src, PObjectArray *objs) const	{ return pmm.newPInteger(objs, src.value * value);}
PObject *PList::mul(const PObject &dst, PObjectArray *objs) const		{ return 0; }
PObject *PArray::mul(const PObject &dst, PObjectArray *objs) const	{ return 0; }

PObject *PString::div(const PObject &dst, PObjectArray *objs) const	{ return 0; }
PObject *PDouble::div(const PObject &dst, PObjectArray *objs) const	{ return dst.div(*this,objs); }
PObject *PDouble::div(const PDouble &src, PObjectArray *objs) const		{ return pmm.newPDouble(objs, src.value / value); }
PObject *PDouble::div(const PInteger &src, PObjectArray *objs) const	{ return pmm.newPDouble(objs, src.value / value); }
PObject *PInteger::div(const PObject &dst, PObjectArray *objs) const	{ return dst.div(*this,objs); }
PObject *PInteger::div(const PDouble &src, PObjectArray *objs) const	{ return pmm.newPDouble(objs,src.value / value); }
PObject *PInteger::div(const PInteger &src, PObjectArray *objs) const	{ return pmm.newPInteger(objs,src.value / value);}
PObject *PList::div(const PObject &dst, PObjectArray *objs) const		{ return 0; }
PObject *PArray::div(const PObject &dst, PObjectArray *objs) const	{ return 0; }

PObject *PString::mod(const PObject &dst, PObjectArray *objs) const	{ return 0; }
PObject *PDouble::mod(const PObject &dst, PObjectArray *objs) const	{ return 0; }
PObject *PInteger::mod(const PObject &dst, PObjectArray *objs) const	{ return dst.mod(*this,objs); }
PObject *PInteger::mod(const PInteger &src, PObjectArray *objs) const	{ return pmm.newPInteger(objs,src.value % value);}
PObject *PList::mod(const PObject &dst, PObjectArray *objs) const		{ return 0; }
PObject *PArray::mod(const PObject &dst, PObjectArray *objs) const	{ return 0; }

bool PString::cmp(const PObject &dst, int &result) const	{ return dst.cmp(*this, result); }
bool PString::cmp(const PString &src, int &result) const	{ result = sign<int>(strcmp(src.c_str(), c_str())); return true; }
bool PDouble::cmp(const PObject &dst, int &result) const	{ return dst.cmp(*this, result); }
bool PDouble::cmp(const PDouble &src, int &result) const	{ result = sign<double>(static_cast<int>(src.value - value)); return true; }
bool PDouble::cmp(const PInteger &src, int &result) const	{ result = sign<double>(static_cast<int>(src.value - value)); return true; }
bool PInteger::cmp(const PObject &dst, int &result) const	{ return dst.cmp(*this, result); }
bool PInteger::cmp(const PDouble &src, int &result) const	{ result = sign<double>(static_cast<int>(src.value - value)); return true; }
bool PInteger::cmp(const PInteger &src, int &result) const	{ result = sign<int>(static_cast<int>(src.value - value)); return true; }
bool PList::cmp(const PObject &dst, int &result) const	{ return 0; }
bool PArray::cmp(const PObject &dst, int &result) const	{ return 0; }


// 式のコンパイル
ExpressionTree *ExpressionCompileContext::makeExpressionTree_Term(CompileContext &c, bool &funcflg)
{
	if ( find( termchar.begin(), termchar.end(), *c) != termchar.end() ) {
		return 0;
	}
	if ( c.eof() ) {
		c.err( "C0027: unexpected EOF ");
		return 0;
	}
	// '('が出てきたときは括弧の処理を行う。
	if ( *c == '(' ) {
		funcflg = true;
		string	back = termchar;
		termchar.clear();
		termchar.push_back(')');
		c.next();
		ExpressionTree *ret = makeExpressionTree(c, funcflg);
		termchar = back;
		c.next();
		return ret;
	}

	// '='が出てきたときは式モードに変更する
	if ( *c == '=' ) {
		funcflg = true;
		c.next();
	}

	PObject *o = c.createObject(poFactory, funcflg);	// ここではエラーチェックをしない（呼び出し元に任せる）
	if ( !o ) return 0;
	PObject *p = o->compile(c, funcflg);
	
	// 配列の参照
	// 配列、コレクション型の値参照
	// $prd[...] → item( $prd, ...)に変換する
	if ( *c == '[' && (typeid(*p) == typeid(PVeriable) || typeid(*p) == typeid(PArray) || typeid(*p) == typeid(PList)) ) {
		PString *s = poFactory.createObject<PString>();
		s->value = string("item");
		PPredicate *pp = poFactory.createObject<PPredicate>();
		pp->setName(s);
		pp->arglist.push_back(p);
		pp->opt.result_return = true;	// $pred['item']の場合、値が無くてバックトラックをするというのは記述者に混乱を招く
		c.next();
		pp->compileArglist(c, ']');

		// 関数形式の処理（戻り値を引数にする）
		PVeriable *v = poFactory.createObject<PVeriable>();
		string vname;
		vname = cformat("item~%u", c.goalcontext->size());
		v->makeVeriable(c, vname, p->isargs() );
		pp->arglist.push_back(v);
		pp->constant = false;
		c.goalcontext->addBody(pp);
		
		p = v;
	}	

	// 述語名が変更の場合の処理
	//   （配列変更ではない場合）
	PVeriable *vp = dynamic_cast<PVeriable*>(p);
	if ( *c == '(' && (vp && !vp->isargs()) ) {
		PPredicate *pp = poFactory.createObject<PPredicate>();
		pp->setName(p);
		c.next();
		pp->compileArglist(c);

		if ( funcflg ) {
			// 関数形式の処理（戻り値を引数にする）
			PVeriable *v = poFactory.createObject<PVeriable>();
			string vname;
			vname = cformat("_verb~%u", c.goalcontext->size());
			v->makeVeriable(c, vname, p->isargs() );
			pp->arglist.push_back(v);
			pp->constant = false;
			c.goalcontext->addBody(pp);
			p = v;
		} else {
			p = pp;
		}
	}

	// 配列の参照
	// 配列、コレクション型の値参照
	// @prd:key → item( @prd, 'key')に変換する
	// @prdは、変項かつ配列形式のみ対応（@の時はコレクションの参照とみなす)
	vp = dynamic_cast<PVeriable*>(p);
	if ( (/**c == '.' ||*/ *c == ':' ) && (vp && vp->isargs()) ) {
		PString *s = poFactory.createObject<PString>();
		s->value = string("item");
		PPredicate *pp = poFactory.createObject<PPredicate>();
		pp->setName(s);
		pp->opt.result_return = true;	// $pred['item']の場合、値が無くてバックトラックをするというのは記述者に混乱を招く
		pp->arglist.push_back(p);
		
		// 名前の部分のコンパイル
		PString *key = poFactory.createObject<PString>();
		key->compile( c, false);
		pp->arglist.push_back(key);

		// 関数形式の処理（戻り値を引数にする）
		PVeriable *v = poFactory.createObject<PVeriable>();
		string vname;
		vname = cformat("item~%u", c.goalcontext->size());
		v->makeVeriable(c, vname, false );
		pp->arglist.push_back(v);
		pp->constant = false;
		c.goalcontext->addBody(pp);
		
		p = v;
	}	

	// メソッド形式の処理 
	// オブジェクトの後に.が来た場合（メソッド形式）は、継続するものはpredicateのcompileで処理する。
	// 上のo->compileで、pがPridicateの場合は、メソッド形式はpredicateのコンパイルで処理される。
	if ( (*c == '.' /*|| *c == ':'*/ ) && (vp == 0 || !vp->isargs()) ) {
		c.next();
		PPredicate *pred = poFactory.createObject<PPredicate>();
		const PObject *farg = p;
		const PObject *parg = 0;
		#if 0
		if ( *c == ':' ) {
			farg = 0;
			parg = p;
		}
		#endif
		p = pred->compile( c, funcflg, farg, parg);
	}

	return newExpressionTree(p);
}

ExpressionTree *ExpressionCompileContext::makeExpressionTree_unary(CompileContext &c, bool &funcflg)
{
	ExpressionTree *left = 0;
	string op;
	if ( getOperatorUnary(c, op) && !c.eof() ) { // 単項演算子があると式とみなす 
		funcflg = true;
		ExpressionTree *right = makeExpressionTree_Term(c, funcflg);
		left = newExpressionTree( op, 0, right);
	} else {
		left = makeExpressionTree_Term(c, funcflg);
	}
	return left;
}

ExpressionTree *ExpressionCompileContext::makeExpressionTree_mul(CompileContext &c, bool &funcflg)
{
	ExpressionTree *left = makeExpressionTree_unary(c, funcflg);
	string op;
	while ( getOperatorMul(c, op) && !c.eof() ) { // 乗除算演算子があると式とみなす 
		funcflg = true;
		ExpressionTree *right = makeExpressionTree_unary(c, funcflg);	// 式の中（必ずfuncflg==trueとなる）
		left = newExpressionTree( op, left, right);
	}
	return left;
}

ExpressionTree *ExpressionCompileContext::makeExpressionTree_add(CompileContext &c, bool &funcflg)
{
	ExpressionTree *left = makeExpressionTree_mul(c, funcflg);
	string op;
	while ( getOperatorAdd(c, op) && !c.eof() ) { // 加減演算子があると式とみなす 
		funcflg = true;
		ExpressionTree *right = makeExpressionTree_mul(c, funcflg);	// 式の中（必ずfuncflg==trueとなる）
		left = newExpressionTree( op, left, right);
	}
	return left;
}

ExpressionTree *ExpressionCompileContext::makeExpressionTree_cmp(CompileContext &c, bool &funcflg)
{
	ExpressionTree *left = makeExpressionTree_add(c, funcflg);	// 左辺値
	string op;
	while ( getOperatorCMP(c, op) && !c.eof() ) { // 比較演算子があると式とみなす 
		funcflg = true;
		ExpressionTree *right = makeExpressionTree_add(c, funcflg);	// 右辺値(こちらは必ずfuncflg==trueとなる 
		left = newExpressionTree( op, left, right);
	}
	return left;
}

ExpressionTree *ExpressionCompileContext::makeExpressionTree_and(CompileContext &c, bool &funcflg)
{
	ExpressionTree *left = makeExpressionTree_cmp(c, funcflg);	// 左辺値 
	string op;
	while ( funcflg && getOperatorAND(c, op) && !c.eof() ) {
		ExpressionTree *right = makeExpressionTree_cmp(c, funcflg);	// 右辺値(こちらは必ずfuncflg==trueとなる 
		left = newExpressionTree( op, left, right);
	}
	return left;
}

ExpressionTree *ExpressionCompileContext::makeExpressionTree_xor(CompileContext &c, bool &funcflg)
{
	ExpressionTree *left = makeExpressionTree_and(c, funcflg);	// 左辺値 
	string op;
	while ( funcflg &&  getOperatorXOR(c, op) && !c.eof() ) {
		ExpressionTree *right = makeExpressionTree_and(c, funcflg);	// 右辺値(こちらは必ずfuncflg==trueとなる 
		left = newExpressionTree( op, left, right);
	}
	return left;
}

ExpressionTree *ExpressionCompileContext::makeExpressionTree_or(CompileContext &c, bool &funcflg)
{
	ExpressionTree *left = makeExpressionTree_xor(c, funcflg);	// 左辺値
	string op;
	while ( funcflg && getOperatorOR(c, op) && !c.eof() ) {
		funcflg = true;
		ExpressionTree *right = makeExpressionTree_xor(c, funcflg);	// 右辺値(こちらは必ずfuncflg==trueとなる
		left = newExpressionTree( op, left, right);
	}
	return left;
}

ExpressionTree *ExpressionCompileContext::makeExpressionTree(CompileContext &c, bool funcflg)
{
	ExpressionTree *left = makeExpressionTree_or(c, funcflg);	// 左辺値
	string op;
	while ( getOperatorLET(c, op) && !c.eof() ) {	// 代入演算子があると式とみなす 
		funcflg = true;
		ExpressionTree *right = makeExpressionTree_or(c, funcflg);	// 右辺値(こちらは必ずfuncflg==trueとなる 
		left = newExpressionTree( op, left, right);
	}
	return left;
}

PObject *ExpressionCompileContext::makeCode(CompileContext &c, ExpressionTree *exp, PObject *dst)
{	
	if ( !exp ) return 0;
	if ( exp->obj ) return exp->obj;
	if ( !exp->op.empty() ) {
		// make code normal. 
		PObject *left = makeCode(c, exp->left, 0);

		// Optimaize when using equal operator (==) and numeric operator(+-*/%&|^) . 
		if ( strcmp( exp->op.c_str(), "==") == 0 ) {
			if ( exp->right != 0 && !exp->right->op.empty() ) {
				return makeCode( c, exp->right, left);
			}
		}

		PObject *right = makeCode(c, exp->right, 0);
		if ( !left || !right ) {
			if ( strcmp(exp->op.c_str(), "-") != 0 || right == 0 ) {
				c.err("C0028: expression syntax error ");
				return 0;
			}
		}
		PPredicate *p = poFactory.createObject<PPredicate>();
		PObject *ret;
		if ( strcmp(exp->op.c_str(), "+") == 0 ) {
			if ( dst ) {
				ret = p->make3Operator( c, "_add", left, right, dst, false);
			} else {
				ret = p->make3Operator( c, "_add", left, right, true);
			}
		} else if ( strcmp(exp->op.c_str(), "-") == 0 ) {
			if ( left ) {
				if ( dst ) {
					ret = p->make3Operator( c, "_sub", left, right, dst, false);
				} else {
					ret = p->make3Operator( c, "_sub", left, right, true);
				}
			} else {
				if ( dst ) {
					ret = p->make2Operator( c, "_neg", right, dst, false);
				} else {
					ret = p->make1Operator( c, "_neg", right, true);
				}
			}
		} else if ( strcmp(exp->op.c_str(), "~") == 0 ) {
				if ( dst ) {
					ret = p->make2Operator( c, "_not", right, dst, false);
				} else {
					ret = p->make1Operator( c, "_not", right, true);
				}
		} else if ( strcmp(exp->op.c_str(), "*") == 0 ) {
			if ( dst ) {
				ret = p->make3Operator( c, "_mul", left, right, dst, false);
			} else {
				ret = p->make3Operator( c, "_mul", left, right, true);
			}
		} else if ( strcmp(exp->op.c_str(), "/") == 0 ) {
			if ( dst ) {
				ret = p->make3Operator( c, "_div", left, right, dst, false);
			} else {
				ret = p->make3Operator( c, "_div", left, right, true);
			}
		} else if ( strcmp(exp->op.c_str(), "%") == 0 ) {
			if ( dst ) {
				ret = p->make3Operator( c, "_mod", left, right, dst, false);
			} else {
				ret = p->make3Operator( c, "_mod", left, right, true);
			}
		} else if ( strcmp(exp->op.c_str(), "<") == 0 ) {
			if ( dst ) c.err("C0031: unexpected expression ");
			ret = p->make2Operator( c, "_lt", left, right, false);
		} else if ( strcmp(exp->op.c_str(), "<=") == 0 ) {
			if ( dst ) c.err("C0032: unexpected expression ");
			ret = p->make2Operator( c, "_le", left, right, false);
		} else if ( strcmp(exp->op.c_str(), ">") == 0 ) {
			if ( dst ) c.err("C0033: unexpected expression ");
			ret = p->make2Operator( c, "_gt", left, right, false);
		} else if ( strcmp(exp->op.c_str(), ">=") == 0 ) {
			if ( dst ) c.err("C0034: unexpected expression ");
			ret = p->make2Operator( c, "_ge", left, right, false);
		} else if ( strcmp(exp->op.c_str(), "==") == 0 ) {
			if ( dst ) c.err("C0035: unexpected expression ");
			ret = p->make2Operator( c, "_equ", left, right, false);
		} else if ( strcmp(exp->op.c_str(), "!=") == 0 ) {
			if ( dst ) c.err("C0036: unexpected expression ");
			ret = p->make2Operator( c, "_neq", left, right, false);
		} else if ( strcmp(exp->op.c_str(), "<>") == 0 ) {
			if ( dst ) c.err("C0037: unexpected expression ");
			ret = p->make2Operator( c, "_neq", left, right, false);
		} else if ( strcmp(exp->op.c_str(), "&") == 0 ) {
			if ( dst ) {
				ret = p->make3Operator( c, "_and", left, right, dst, false);
			} else {
				ret = p->make3Operator( c, "_and", left, right, true);
			}
		} else if ( strcmp(exp->op.c_str(), "^") == 0 ) {
			if ( dst ) {
				ret = p->make3Operator( c, "_xor", left, right, dst, false);
			} else {
				ret = p->make3Operator( c, "_xor", left, right, true);
			}
		} else if ( strcmp(exp->op.c_str(), "|") == 0 ) {
			if ( dst ) {
				ret = p->make3Operator( c, "_or", left, right, dst, false);
			} else {
				ret = p->make3Operator( c, "_or", left, right, true);
			}
		} else if ( strcmp(exp->op.c_str(), "=") == 0 ) {
			ret = p->make2Operator( c, "_let", right, left, false);
		} else if ( strcmp(exp->op.c_str(), "+=") == 0 ) {
			ret = p->make2Operator( c, "_letadd", right, left, false);
		} else if ( strcmp(exp->op.c_str(), "-=") == 0 ) {
			ret = p->make2Operator( c, "_letsub", right, left, false);
		} else if ( strcmp(exp->op.c_str(), "*=") == 0 ) {
			ret = p->make2Operator( c, "_letmul", right, left, false);
		} else if ( strcmp(exp->op.c_str(), "/=") == 0 ) {
			ret = p->make2Operator( c, "_letdiv", right, left, false);
		} else if ( strcmp(exp->op.c_str(), "%=") == 0 ) {
			ret = p->make2Operator( c, "_letmod", right, left, false);
		} else if ( strcmp(exp->op.c_str(), "&=") == 0 ) {
			ret = p->make2Operator( c, "_letand", right, left, false);
		} else if ( strcmp(exp->op.c_str(), "|=") == 0 ) {
			ret = p->make2Operator( c, "_letor", right, left, false);
		} else if ( strcmp(exp->op.c_str(), "^=") == 0 ) {
			ret = p->make2Operator( c, "_letxor", right, left, false);
		}
		return ret;
	}
	c.err("C0029: unexpected expression ");
	return 0;
}

PObject *ExpressionCompileContext::compileExpression(CompileContext &c, bool funcflg)
{
	ExpressionTree *exp = makeExpressionTree(c, funcflg);
	if ( !exp ) return 0;
	if ( !exp->obj && !c.goalcontext ) {
		c.err("C0030: unexpected expression ");
		return 0;
	}
	return makeCode(c,exp,0);
}
