view dmdscript_tango/expression.d @ 3:8363a4bf6a8f

rename package: dmdscript to dmdscript_tango
author saaadel
date Sun, 24 Jan 2010 18:33:05 +0200
parents 55c2951c07be
children
line wrap: on
line source


/* Digital Mars DMDScript source code.
 * Copyright (c) 2000-2002 by Chromium Communications
 * D version Copyright (c) 2004-2005 by Digital Mars
 * All Rights Reserved
 * written by Walter Bright
 * www.digitalmars.com
 * Use at your own risk. There is no warranty, express or implied.
 * License for redistribution is by the GNU General Public License in gpl.txt.
 *
 * A binary, non-exclusive license for commercial use can be
 * purchased from www.digitalmars.com/dscript/buy.html.
 *
 * DMDScript is implemented in the D Programming Language,
 * www.digitalmars.com/d/
 *
 * For a C++ implementation of DMDScript, including COM support,
 * see www.digitalmars.com/dscript/cppscript.html.
 */


module dmdscript_tango.expression;

import std.string;

import dmdscript_tango.script;
import dmdscript_tango.lexer;
import dmdscript_tango.scopex;
import dmdscript_tango.text;
import dmdscript_tango.textgen.errmsgs;
import dmdscript_tango.functiondefinition;
import dmdscript_tango.irstate;
import dmdscript_tango.ir;
import dmdscript_tango.opcodes;
import dmdscript_tango.identifier;

/******************************** Expression **************************/

class Expression
{
    const uint EXPRESSION_SIGNATURE = 0x3AF31E3F;
    uint signature = EXPRESSION_SIGNATURE;

    Loc loc;			// file location
    TOK op;

    this(Loc loc, TOK op)
    {
	this.loc = loc;
	this.op = op;
	signature = EXPRESSION_SIGNATURE;
    }

    invariant
    {
	assert(signature == EXPRESSION_SIGNATURE);
	assert(op != TOKreserved && op < TOKmax);
    }

    /**************************
     * Semantically analyze Expression.
     * Determine types, fold constants, etc.
     */

    Expression semantic(Scope *sc)
    {
	return this;
    }

    tchar[] toString()
    {   tchar[] buf;

	toBuffer(buf);
	return buf;
    }

    void toBuffer(inout char[] buf)
    {
	buf ~= toString();
    }

    void checkLvalue(Scope *sc)
    {
	tchar[] buf;

	//writefln("checkLvalue(), op = %d", op);
	if (sc.funcdef)
	{   if (sc.funcdef.isanonymous)
		buf = "anonymous";
	    else if (sc.funcdef.name)
		buf = sc.funcdef.name.toString();
	}
	buf ~= std.string.format("(%d) : Error: ", loc);
	buf ~= std.string.format(errmsgtbl[ERR_CANNOT_ASSIGN_TO], toString());

	if (!sc.errinfo.message)
	{   sc.errinfo.message = buf;
	    sc.errinfo.linnum = loc;
	    sc.errinfo.srcline = Lexer.locToSrcline(sc.getSource().ptr, loc);
	}
    }

    // Do we match for purposes of optimization?

    int match(Expression e)
    {
	return false;
    }

    // Is the result of the expression guaranteed to be a boolean?

    int isBooleanResult()
    {
	return false;
    }

    void toIR(IRstate *irs, uint ret)
    {
	//writef("Expression::toIR('%s')\n", toChars());
    }

    void toLvalue(IRstate *irs, out uint base, IR *property, out int opoff)
    {
	base = irs.alloc(1);
	toIR(irs, base);
	property.index = 0;
	opoff = 3;
    }
}

/******************************** RealExpression **************************/

class RealExpression : Expression
{
    real_t value;

    this(Loc loc, real_t value)
    {
	super(loc, TOKreal);
	this.value = value;
    }

    tchar[] toString()
    {   tchar[] buf;
	long i;

	i = cast(long) value;
	if (i == value)
	    buf = std.string.format("%d", i);
	else
	    buf = std.string.format("%g", value);
	return buf;
    }

    void toBuffer(inout tchar[] buf)
    {
	buf ~= std.string.format("%g", value);
    }

    void toIR(IRstate *irs, uint ret)
    {
	//writef("RealExpression::toIR(%g)\n", value);

	static assert(value.sizeof == 2 * uint.sizeof);
	if (ret)
	    irs.gen(loc, IRnumber, 3, ret, value);
    }
}

/******************************** IdentifierExpression **************************/

class IdentifierExpression : Expression
{
    Identifier *ident;

    this(Loc loc, Identifier *ident)
    {
	super(loc, TOKidentifier);
	this.ident = ident;
    }

    Expression semantic(Scope *sc)
    {
	return this;
    }

    tchar[] toString()
    {
	return ident.toString();
    }

    void checkLvalue(Scope *sc)
    {
    }

    int match(Expression e)
    {
	if (e.op != TOKidentifier)
	    return 0;

	IdentifierExpression ie = cast(IdentifierExpression)(e);

	return ident == ie.ident;
    }

    void toIR(IRstate *irs, uint ret)
    {
	Identifier* id = ident;

	assert(id.sizeof == uint.sizeof);
	if (ret)
	    irs.gen2(loc, IRgetscope, ret, cast(uint)id);
    }

    void toLvalue(IRstate *irs, out uint base, IR *property, out int opoff)
    {
	//irs.gen1(loc, IRthis, base);
	property.id = ident;
	opoff = 2;
	base = ~0u;
    }
}

/******************************** ThisExpression **************************/

class ThisExpression : Expression
{
    this(Loc loc)
    {
	super(loc, TOKthis);
    }

    tchar[] toString()
    {
	return TEXT_this;
    }

    Expression semantic(Scope *sc)
    {
	return this;
    }

    void toIR(IRstate *irs, uint ret)
    {
	if (ret)
	    irs.gen1(loc, IRthis, ret);
    }
}

/******************************** NullExpression **************************/

class NullExpression : Expression
{
    this(Loc loc)
    {
	super(loc, TOKnull);
    }

    tchar[] toString()
    {
	return TEXT_null;
    }

    void toIR(IRstate *irs, uint ret)
    {
	if (ret)
	    irs.gen1(loc, IRnull, ret);
    }
}

/******************************** StringExpression **************************/

class StringExpression : Expression
{
    tchar[] string;

    this(Loc loc, tchar[] string)
    {
	//writefln("StringExpression('%s')", string);
	super(loc, TOKstring);
	this.string = string;
    }

    void toBuffer(inout tchar[] buf)
    {
	buf ~= '"';
	foreach (dchar c; string)
	{
	    switch (c)
	    {
		case '"':
		    buf ~= '\\';
		    goto Ldefault;

		default:
		Ldefault:
		    if (c & ~0xFF)
			buf ~= std.string.format("\\u%04x", c);
		    else if (std.ctype.isprint(c))
			buf ~= cast(tchar)c;
		    else
			buf ~= std.string.format("\\x%02x", c);
		    break;
	    }
	}
	buf ~= '"';
    }

    void toIR(IRstate *irs, uint ret)
    {
	static assert((Identifier*).sizeof == uint.sizeof);
	if (ret)
	{   uint u =  cast(uint)Identifier.build(string);
	    irs.gen2(loc, IRstring, ret, u);
	}
    }
}

/******************************** RegExpLiteral **************************/

class RegExpLiteral : Expression
{
    tchar[] string;

    this(Loc loc, tchar[] string)
    {
	//writefln("RegExpLiteral('%s')", string);
	super(loc, TOKregexp);
	this.string = string;
    }

    void toBuffer(inout tchar[] buf)
    {
	buf ~= string;
    }

    void toIR(IRstate *irs, uint ret)
    {   d_string pattern;
	d_string attribute = null;
	int e;

	uint argc;
	uint argv;
	uint b;

	// Regular expression is of the form:
	//	/pattern/attribute

	// Parse out pattern and attribute strings
	assert(string[0] == '/');
	e = std.string.rfind(string, '/');
	assert(e != -1);
	pattern = string[1 .. e];
	argc = 1;
	if (e + 1 < string.length)
	{   attribute = string[e + 1 .. length];
	    argc++;
	}

	// Generate new Regexp(pattern [, attribute])

	b = irs.alloc(1);
	Identifier* re = Identifier.build(TEXT_RegExp);
	irs.gen2(loc, IRgetscope, b, cast(uint)re);
	argv = irs.alloc(argc);
	irs.gen2(loc, IRstring, argv, cast(uint)Identifier.build(pattern));
	if (argc == 2)
	    irs.gen2(loc, IRstring, argv + 1 * INDEX_FACTOR, cast(uint)Identifier.build(attribute));
	irs.gen4(loc, IRnew, ret,b,argc,argv);
	irs.release(b, argc + 1);
    }
}

/******************************** BooleanExpression **************************/

class BooleanExpression : Expression
{
    int boolean;

    this(Loc loc, int boolean)
    {
	super(loc, TOKboolean);
	this.boolean = boolean;
    }

    tchar[] toString()
    {
	return boolean ? "true" : "false";
    }

    void toBuffer(inout tchar[] buf)
    {
	buf ~= toString();
    }

    int isBooleanResult()
    {
	return true;
    }

    void toIR(IRstate *irs, uint ret)
    {
	if (ret)
	    irs.gen2(loc, IRboolean, ret, boolean);
    }
}

/******************************** ArrayLiteral **************************/

class ArrayLiteral : Expression
{
    Expression[] elements;

    this(Loc loc, Expression[] elements)
    {
	super(loc, TOKarraylit);
	this.elements = elements;
    }

    Expression semantic(Scope *sc)
    {
	foreach (inout Expression e; elements)
	{
	    if (e)
		e = e.semantic(sc);
	}
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {   uint i;

	buf ~= '[';
	foreach (Expression e; elements)
	{
	    if (i)
		buf ~= ',';
	    i = 1;
	    if (e)
		e.toBuffer(buf);
	}
	buf ~= ']';
    }

    void toIR(IRstate *irs, uint ret)
    {
	uint argc;
	uint argv;
	uint b;
	uint v;

	b = irs.alloc(1);
	static Identifier* ar;
	if (!ar)
	    ar = Identifier.build(TEXT_Array);
	irs.gen2(loc, IRgetscope, b, cast(uint)ar);
	if (elements.length)
	{   Expression e;

	    argc = elements.length;
	    argv = irs.alloc(argc);
	    if (argc > 1)
	    {   uint i;

		// array literal [a, b, c] is equivalent to:
		//	new Array(a,b,c)
		for (i = 0; i < argc; i++)
		{
		    e = elements[i];
		    if (e)
		    {
			e.toIR(irs, argv + i * INDEX_FACTOR);
		    }
		    else
			irs.gen1(loc, IRundefined, argv + i * INDEX_FACTOR);
		}
		irs.gen4(loc, IRnew, ret,b,argc,argv);
	    }
	    else
	    {   //	[a] translates to:
		//	ret = new Array(1);
		//  ret[0] = a
		irs.gen(loc, IRnumber, 3, argv, 1.0);
		irs.gen4(loc, IRnew, ret,b,argc,argv);

		e = elements[0];
		v = irs.alloc(1);
		if (e)
		    e.toIR(irs, v);
		else
		    irs.gen1(loc, IRundefined, v);
		irs.gen3(loc, IRputs, v, ret, cast(uint)Identifier.build(TEXT_0));
		irs.release(v, 1);
	    }
	    irs.release(argv, argc);
	}
	else
	{
	    // Generate new Array()
	    irs.gen4(loc, IRnew, ret,b,0,0);
	}
	irs.release(b, 1);
    }
}

/******************************** FieldLiteral **************************/

class Field
{
    Identifier* ident;
    Expression exp;

    this(Identifier *ident, Expression exp)
    {
	this.ident = ident;
	this.exp = exp;
    }
}

/******************************** ObjectLiteral **************************/

class ObjectLiteral : Expression
{
    Field[] fields;

    this(Loc loc, Field[] fields)
    {
	super(loc, TOKobjectlit);
	this.fields = fields;
    }

    Expression semantic(Scope *sc)
    {
	foreach (Field f; fields)
	{
	    f.exp = f.exp.semantic(sc);
	}
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {   uint i;

	buf ~= '{';
	foreach (Field f; fields)
	{
	    if (i)
		buf ~= ',';
	    i = 1;
	    buf ~= f.ident.toString();
	    buf ~= ':';
	    f.exp.toBuffer(buf);
	}
	buf ~= '}';
    }

    void toIR(IRstate *irs, uint ret)
    {
	uint b;

	b = irs.alloc(1);
	//irs.gen2(loc, IRstring, b, TEXT_Object);
	Identifier* ob = Identifier.build(TEXT_Object);
	irs.gen2(loc, IRgetscope, b, cast(uint)ob);
	// Generate new Object()
	irs.gen4(loc, IRnew, ret,b,0,0);
	if (fields.length)
	{
	    uint x;

	    x = irs.alloc(1);
	    foreach (Field f; fields)
	    {
		f.exp.toIR(irs, x);
		irs.gen3(loc, IRputs, x, ret, cast(uint)(f.ident));
	    }
	}
    }
}

/******************************** FunctionLiteral **************************/

class FunctionLiteral : Expression
{   FunctionDefinition func;

    this(Loc loc, FunctionDefinition func)
    {
	super(loc, TOKobjectlit);
	this.func = func;
    }

    Expression semantic(Scope *sc)
    {
	func = cast(FunctionDefinition)(func.semantic(sc));
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {
	func.toBuffer(buf);
    }

    void toIR(IRstate *irs, uint ret)
    {
	func.toIR(null);
	irs.gen2(loc, IRobject, ret, cast(uint)cast(void*)func);
    }
}

/***************************** UnaExp *************************************/

class UnaExp : Expression
{
    Expression e1;

    this(Loc loc, TOK op, Expression e1)
    {
	super(loc, op);
	this.e1 = e1;
    }

    Expression semantic(Scope *sc)
    {
	e1 = e1.semantic(sc);
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {
	buf ~= Token.toString(op);
	buf ~= ' ';
	e1.toBuffer(buf);
    }
}

/***************************** BinExp *************************************/

class BinExp : Expression
{
    Expression e1;
    Expression e2;

    this(Loc loc, TOK op, Expression e1, Expression e2)
    {
	super(loc, op);
	this.e1 = e1;
	this.e2 = e2;
    }

    Expression semantic(Scope *sc)
    {
	e1 = e1.semantic(sc);
	e2 = e2.semantic(sc);
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {
	e1.toBuffer(buf);
	buf ~= ' ';
	buf ~= Token.toString(op);
	buf ~= ' ';
	e2.toBuffer(buf);
    }

    void binIR(IRstate *irs, uint ret, uint ircode)
    {   uint b;
	uint c;

	if (ret)
	{
	    b = irs.alloc(1);
	    e1.toIR(irs, b);
	    if (e1.match(e2))
	    {
		irs.gen3(loc, ircode, ret, b, b);
	    }
	    else
	    {
		c = irs.alloc(1);
		e2.toIR(irs, c);
		irs.gen3(loc, ircode, ret, b, c);
		irs.release(c, 1);
	    }
	    irs.release(b, 1);
	}
	else
	{
	    e1.toIR(irs, 0);
	    e2.toIR(irs, 0);
	}
    }
}

/************************************************************/

/* Handle ++e and --e
 */

class PreExp : UnaExp
{
    uint ircode;

    this(Loc loc, uint ircode, Expression e)
    {
	super(loc, TOKplusplus, e);
	this.ircode = ircode;
    }

    Expression semantic(Scope *sc)
    {
	super.semantic(sc);
	e1.checkLvalue(sc);
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {
	e1.toBuffer(buf);
	buf ~= Token.toString(op);
    }

    void toIR(IRstate *irs, uint ret)
    {
	uint base;
	IR property;
	int opoff;

	//writef("PreExp::toIR('%s')\n", toChars());
	e1.toLvalue(irs, base, &property, opoff);
	assert(opoff != 3);
	if (opoff == 2)
	{
	    //irs.gen2(loc, ircode + 2, ret, property.index);
	    irs.gen3(loc, ircode + 2, ret, property.index, property.id.toHash());
	}
	else
	    irs.gen3(loc, ircode + opoff, ret, base, property.index);
    }
}

/************************************************************/

class PostIncExp : UnaExp
{
    this(Loc loc, Expression e)
    {
	super(loc, TOKplusplus, e);
    }

    Expression semantic(Scope *sc)
    {
	super.semantic(sc);
	e1.checkLvalue(sc);
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {
	e1.toBuffer(buf);
	buf ~= Token.toString(op);
    }

    void toIR(IRstate *irs, uint ret)
    {
	uint base;
	IR property;
	int opoff;

	//writef("PostIncExp::toIR('%s')\n", toChars());
	e1.toLvalue(irs, base, &property, opoff);
	assert(opoff != 3);
	if (opoff == 2)
	{
	    if (ret)
	    {
		irs.gen2(loc, IRpostincscope, ret, property.index);
	    }
	    else
	    {
		//irs.gen2(loc, IRpreincscope, ret, property.index);
		irs.gen3(loc, IRpreincscope, ret, property.index, property.id.toHash());
	    }
	}
	else
	    irs.gen3(loc, (ret ? IRpostinc : IRpreinc) + opoff, ret, base, property.index);
    }
}

/****************************************************************/

class PostDecExp : UnaExp
{
    this(Loc loc, Expression e)
    {
	super(loc, TOKplusplus, e);
    }

    Expression semantic(Scope *sc)
    {
	super.semantic(sc);
	e1.checkLvalue(sc);
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {
	e1.toBuffer(buf);
	buf ~= Token.toString(op);
    }

    void toIR(IRstate *irs, uint ret)
    {
	uint base;
	IR property;
	int opoff;

	//writef("PostDecExp::toIR('%s')\n", toChars());
	e1.toLvalue(irs, base, &property, opoff);
	assert(opoff != 3);
	if (opoff == 2)
	{
	    if (ret)
	    {
		irs.gen2(loc, IRpostdecscope, ret, property.index);
	    }
	    else
	    {
		//irs.gen2(loc, IRpredecscope, ret, property.index);
		irs.gen3(loc, IRpredecscope, ret, property.index, property.id.toHash());
	    }
	}
	else
	    irs.gen3(loc, (ret ? IRpostdec : IRpredec) + opoff, ret, base, property.index);
    }
}

/************************************************************/

class DotExp : UnaExp
{
    Identifier *ident;

    this(Loc loc, Expression e, Identifier *ident)
    {
	super(loc, TOKdot, e);
	this.ident = ident;
    }

    void checkLvalue(Scope *sc)
    {
    }

    void toBuffer(inout tchar[] buf)
    {
	e1.toBuffer(buf);
	buf ~= '.';
	buf ~= ident.toString();
    }

    void toIR(IRstate *irs, uint ret)
    {
	uint base;

	//writef("DotExp::toIR('%s')\n", toChars());
      version (all)
      {
	// Some test cases depend on things like:
	//		foo.bar;
	// generating a property get even if the result is thrown away.
	base = irs.alloc(1);
	e1.toIR(irs, base);
	irs.gen3(loc, IRgets, ret, base, cast(uint)ident);
      }
      else
      {
	if (ret)
	{
	    base = irs.alloc(1);
	    e1.toIR(irs, base);
	    irs.gen3(loc, IRgets, ret, base, cast(uint)ident);
	}
	else
	    e1.toIR(irs, 0);
      }
    }

    void toLvalue(IRstate *irs, out uint base, IR *property, out int opoff)
    {
	base = irs.alloc(1);
	e1.toIR(irs, base);
	property.id = ident;
	opoff = 1;
    }
}

/************************************************************/

class CallExp : UnaExp
{
    Expression[] arguments;

    this(Loc loc, Expression e, Expression[] arguments)
    {
	//writef("CallExp(e1 = %x)\n", e);
	super(loc, TOKcall, e);
	this.arguments = arguments;
    }

    Expression semantic(Scope *sc)
    {   IdentifierExpression ie;

	//writef("CallExp(e1=%x, %d, vptr=%x)\n", e1, e1.op, *(uint *)e1);
	//writef("CallExp(e1='%s')\n", e1.toString());
	e1 = e1.semantic(sc);
	if (e1.op != TOKcall)
	    e1.checkLvalue(sc);

	foreach (inout Expression e; arguments)
	{
	    e = e.semantic(sc);
	}
	if (arguments.length == 1)
	{
	    if (e1.op == TOKidentifier)
	    {
		ie = cast(IdentifierExpression )e1;
		if (ie.ident.toString() == "assert")
		{
		    return new AssertExp(loc, arguments[0]);
		}
	    }
	}
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {
	e1.toBuffer(buf);
	buf ~= '(';
	for (size_t u = 0; u < arguments.length; u++)
	{
	    if (u)
		buf ~= ", ";
	    arguments[u].toBuffer(buf);
	}
	buf ~= ')';
    }

    void toIR(IRstate *irs, uint ret)
    {
	// ret = base.property(argc, argv)
	// CALL ret,base,property,argc,argv
	uint base;
	uint argc;
	uint argv;
	IR property;
	int opoff;

	//writef("CallExp::toIR('%s')\n", toChars());
	e1.toLvalue(irs, base, &property, opoff);

	if (arguments.length)
	{   uint u;

	    argc = arguments.length;
	    argv = irs.alloc(argc);
	    for (u = 0; u < argc; u++)
	    {   Expression e;

		e = arguments[u];
		e.toIR(irs, argv + u * INDEX_FACTOR);
	    }
	    arguments[] = null;		// release to GC
	    arguments = null;
	}
	else
	{
	    argc = 0;
	    argv = 0;
	}

	if (opoff == 3)
	    irs.gen4(loc, IRcallv, ret,base,argc,argv);
	else if (opoff == 2)
	    irs.gen4(loc, IRcallscope, ret,property.index,argc,argv);
	else
	    irs.gen(loc, IRcall + opoff, 5, ret,base,property,argc,argv);
	irs.release(argv, argc);
    }
}

/************************************************************/

class AssertExp : UnaExp
{
    this(Loc loc, Expression e)
    {
	super(loc, TOKassert, e);
    }

    void toBuffer(inout tchar[] buf)
    {
	buf ~= "assert(";
	e1.toBuffer(buf);
	buf ~= ')';
    }

    void toIR(IRstate *irs, uint ret)
    {   uint linnum;
	uint u;
	uint b;

	b = ret ? ret : irs.alloc(1);

	e1.toIR(irs, b);
	u = irs.getIP();
	irs.gen2(loc, IRjt, 0, b);
	linnum = cast(uint)loc;
	irs.gen1(loc, IRassert, linnum);
	irs.patchJmp(u, irs.getIP());

	if (!ret)
	    irs.release(b, 1);
    }
}

/************************* NewExp ***********************************/

class NewExp : UnaExp
{
    Expression[] arguments;

    this(Loc loc, Expression e, Expression[] arguments)
    {
	super(loc, TOKnew, e);
	this.arguments = arguments;
    }

    Expression semantic(Scope *sc)
    {
	e1 = e1.semantic(sc);
	for (size_t a = 0; a < arguments.length; a++)
	{
	    arguments[a] = arguments[a].semantic(sc);
	}
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {
	buf ~= Token.toString(op);
	buf ~= ' ';

	e1.toBuffer(buf);
	buf ~= '(';
	for (size_t a = 0; a < arguments.length; a++)
	{
	    arguments[a].toBuffer(buf);
	}
	buf ~= ')';
    }

    void toIR(IRstate *irs, uint ret)
    {
	// ret = new b(argc, argv)
	// CALL ret,b,argc,argv
	uint b;
	uint argc;
	uint argv;

	//writef("NewExp::toIR('%s')\n", toChars());
	b = irs.alloc(1);
	e1.toIR(irs, b);
	if (arguments.length)
	{   uint u;

	    argc = arguments.length;
	    argv = irs.alloc(argc);
	    for (u = 0; u < argc; u++)
	    {   Expression e;

		e = arguments[u];
		e.toIR(irs, argv + u * INDEX_FACTOR);
	    }
	}
	else
	{
	    argc = 0;
	    argv = 0;
	}

	irs.gen4(loc, IRnew, ret,b,argc,argv);
	irs.release(argv, argc);
	irs.release(b, 1);
    }
}

/************************************************************/

class XUnaExp : UnaExp
{
    uint ircode;

    this(Loc loc, TOK op, uint ircode, Expression e)
    {
	super(loc, op, e);
	this.ircode = ircode;
    }

    void toIR(IRstate *irs, uint ret)
    {
	e1.toIR(irs, ret);
	if (ret)
	    irs.gen1(loc, ircode, ret);
    }
}

class NotExp : XUnaExp
{
    this(Loc loc, Expression e)
    {
	super(loc, TOKnot, IRnot, e);
    }

    int isBooleanResult()
    {
	return true;
    }
}

class DeleteExp : UnaExp
{
    this(Loc loc, Expression e)
    {
	super(loc, TOKdelete, e);
    }

    Expression semantic(Scope *sc)
    {
	e1.checkLvalue(sc);
	return this;
    }

    void toIR(IRstate *irs, uint ret)
    {
	uint base;
	IR property;
	int opoff;

	e1.toLvalue(irs, base, &property, opoff);
	assert(opoff != 3);
	if (opoff == 2)
	    irs.gen2(loc, IRdelscope, ret, property.index);
	else
	    irs.gen3(loc, IRdel + opoff, ret, base, property.index);
    }
}

/************************* CommaExp ***********************************/

class CommaExp : BinExp
{
    this(Loc loc, Expression e1, Expression e2)
    {
	super(loc, TOKcomma, e1, e2);
    }

    void checkLvalue(Scope *sc)
    {
	e2.checkLvalue(sc);
    }

    void toIR(IRstate *irs, uint ret)
    {
	e1.toIR(irs, 0);
	e2.toIR(irs, ret);
    }
}

/************************* ArrayExp ***********************************/

class ArrayExp : BinExp
{
    this(Loc loc, Expression e1, Expression e2)
    {
	super(loc, TOKarray, e1, e2);
    }

    Expression semantic(Scope *sc)
    {
	checkLvalue(sc);
	return this;
    }

    void checkLvalue(Scope *sc)
    {
    }

    void toBuffer(inout tchar[] buf)
    {
	e1.toBuffer(buf);
	buf ~= '[';
	e2.toBuffer(buf);
	buf ~= ']';
    }

    void toIR(IRstate *irs, uint ret)
    {   uint base;
	IR property;
	int opoff;

	if (ret)
	{
	    toLvalue(irs, base, &property, opoff);
	    assert(opoff != 3);
	    if (opoff == 2)
		irs.gen2(loc, IRgetscope, ret, property.index);
	    else
		irs.gen3(loc, IRget + opoff, ret, base, property.index);
	}
	else
	{
	    e1.toIR(irs, 0);
	    e2.toIR(irs, 0);
	}
    }

    void toLvalue(IRstate *irs, out uint base, IR *property, out int opoff)
    {   uint index;

	base = irs.alloc(1);
	e1.toIR(irs, base);
	index = irs.alloc(1);
	e2.toIR(irs, index);
	property.index = index;
	opoff = 0;
    }
}

/************************* AssignExp ***********************************/

class AssignExp : BinExp
{
    this(Loc loc, Expression e1, Expression e2)
    {
	super(loc, TOKassign, e1, e2);
    }

    Expression semantic(Scope *sc)
    {
	//writefln("AssignExp.semantic()");
	super.semantic(sc);
	if (e1.op != TOKcall)		// special case for CallExp lvalue's
	    e1.checkLvalue(sc);
	return this;
    }

    void toIR(IRstate *irs, uint ret)
    {
	uint b;

	//writef("AssignExp::toIR('%s')\n", toChars());
	if (e1.op == TOKcall)		// if CallExp
	{
	    assert(cast(CallExp)(e1));	// make sure we got it right

	    // Special case a function call as an lvalue.
	    // This can happen if:
	    //	foo() = 3;
	    // A Microsoft extension, it means to assign 3 to the default property of
	    // the object returned by foo(). It only has meaning for com objects.
	    // This functionality should be worked into toLvalue() if it gets used
	    // elsewhere.

	    uint base;
	    uint argc;
	    uint argv;
	    IR property;
	    int opoff;
	    CallExp ec = cast(CallExp)e1;

	    if (ec.arguments.length)
		argc = ec.arguments.length + 1;
	    else
		argc = 1;

	    argv = irs.alloc(argc);

	    e2.toIR(irs, argv + (argc - 1) * INDEX_FACTOR);

	    ec.e1.toLvalue(irs, base, &property, opoff);

	    if (ec.arguments.length)
	    {   uint u;

		for (u = 0; u < ec.arguments.length; u++)
		{   Expression e;

		    e = ec.arguments[u];
		    e.toIR(irs, argv + (u + 0) * INDEX_FACTOR);
		}
		ec.arguments[] = null;		// release to GC
		ec.arguments = null;
	    }

	    if (opoff == 3)
		irs.gen4(loc, IRputcallv, ret,base,argc,argv);
	    else if (opoff == 2)
		irs.gen4(loc, IRputcallscope, ret,property.index,argc,argv);
	    else
		irs.gen(loc, IRputcall + opoff, 5, ret,base,property,argc,argv);
	    irs.release(argv, argc);
	}
	else
	{
	    uint base;
	    IR property;
	    int opoff;

	    b = ret ? ret : irs.alloc(1);
	    e2.toIR(irs, b);

	    e1.toLvalue(irs, base, &property, opoff);
	    assert(opoff != 3);
	    if (opoff == 2)
		irs.gen2(loc, IRputscope, b, property.index);
	    else
		irs.gen3(loc, IRput + opoff, b, base, property.index);
	    if (!ret)
		irs.release(b, 1);
	}
    }
}

/************************* AddAssignExp ***********************************/

class AddAssignExp : BinExp
{
    this(Loc loc, Expression e1, Expression e2)
    {
	super(loc, TOKplusass, e1, e2);
    }

    Expression semantic(Scope *sc)
    {
	super.semantic(sc);
	e1.checkLvalue(sc);
	return this;
    }

    void toIR(IRstate *irs, uint ret)
    {
	if (ret == 0 && e2.op == TOKreal &&
	    (cast(RealExpression)e2).value == 1)
	{
	    uint base;
	    IR property;
	    int opoff;

	    //writef("AddAssign to PostInc('%s')\n", toChars());
	    e1.toLvalue(irs, base, &property, opoff);
	    assert(opoff != 3);
	    if (opoff == 2)
		irs.gen2(loc, IRpostincscope, ret, property.index);
	    else
		irs.gen3(loc, IRpostinc + opoff, ret, base, property.index);
	}
	else
	{
	    uint r;
	    uint base;
	    IR property;
	    int opoff;

	    //writef("AddAssignExp::toIR('%s')\n", toChars());
	    e1.toLvalue(irs, base, &property, opoff);
	    assert(opoff != 3);
	    r = ret ? ret : irs.alloc(1);
	    e2.toIR(irs, r);
	    if (opoff == 2)
		irs.gen3(loc, IRaddassscope, r, property.index, property.id.toHash());
	    else
		irs.gen3(loc, IRaddass + opoff, r, base, property.index);
	    if (!ret)
		irs.release(r, 1);
	}
    }
}

/************************* BinAssignExp ***********************************/

class BinAssignExp : BinExp
{
    uint ircode = IRerror;

    this(Loc loc, TOK op, uint ircode, Expression e1, Expression e2)
    {
	super(loc, op, e1, e2);
	this.ircode = ircode;
    }

    Expression semantic(Scope *sc)
    {
	super.semantic(sc);
	e1.checkLvalue(sc);
	return this;
    }

    void toIR(IRstate *irs, uint ret)
    {
	uint b;
	uint c;
	uint r;
	uint base;
	IR property;
	int opoff;

	//writef("BinExp::binAssignIR('%s')\n", toChars());
	e1.toLvalue(irs, base, &property, opoff);
	assert(opoff != 3);
	b = irs.alloc(1);
	if (opoff == 2)
	    irs.gen2(loc, IRgetscope, b, property.index);
	else
	    irs.gen3(loc, IRget + opoff, b, base, property.index);
	c = irs.alloc(1);
	e2.toIR(irs, c);
	r = ret ? ret : irs.alloc(1);
	irs.gen3(loc, ircode, r, b, c);
	if (opoff == 2)
	    irs.gen2(loc, IRputscope, r, property.index);
	else
	    irs.gen3(loc, IRput + opoff, r, base, property.index);
	if (!ret)
	    irs.release(r, 1);
    }
}

/************************* AddExp *****************************/

class AddExp : BinExp
{
    this(Loc loc, Expression e1, Expression e2)
    {
	super(loc, TOKplus, e1, e2);;
    }

    Expression semantic(Scope *sc)
    {
	return this;
    }

    void toIR(IRstate *irs, uint ret)
    {
	binIR(irs, ret, IRadd);
    }
}

/************************* XBinExp ***********************************/

class XBinExp : BinExp
{
    uint ircode = IRerror;

    this(Loc loc, TOK op, uint ircode, Expression e1, Expression e2)
    {
	super(loc, op, e1, e2);
	this.ircode = ircode;
    }

    void toIR(IRstate *irs, uint ret)
    {
	binIR(irs, ret, ircode);
    }
}

/************************* OrOrExp ***********************************/

class OrOrExp : BinExp
{
    this(Loc loc, Expression e1, Expression e2)
    {
	super(loc, TOKoror, e1, e2);
    }

    void toIR(IRstate *irs, uint ret)
    {   uint u;
	uint b;

	if (ret)
	    b = ret;
	else
	    b = irs.alloc(1);

	e1.toIR(irs, b);
	u = irs.getIP();
	irs.gen2(loc, IRjt, 0, b);
	e2.toIR(irs, ret);
	irs.patchJmp(u, irs.getIP());

	if (!ret)
	    irs.release(b, 1);
    }
}

/************************* AndAndExp ***********************************/

class AndAndExp : BinExp
{
    this(Loc loc, Expression e1, Expression e2)
    {
	super(loc, TOKandand, e1, e2);
    }

    void toIR(IRstate *irs, uint ret)
    {   uint u;
	uint b;

	if (ret)
	    b = ret;
	else
	    b = irs.alloc(1);

	e1.toIR(irs, b);
	u = irs.getIP();
	irs.gen2(loc, IRjf, 0, b);
	e2.toIR(irs, ret);
	irs.patchJmp(u, irs.getIP());

	if (!ret)
	    irs.release(b, 1);
    }
}

/************************* CmpExp ***********************************/



class CmpExp : BinExp
{
    uint ircode = IRerror;

    this(Loc loc, TOK tok, uint ircode, Expression e1, Expression e2)
    {
	super(loc, tok, e1, e2);
	this.ircode = ircode;
    }

    int isBooleanResult()
    {
	return true;
    }

    void toIR(IRstate *irs, uint ret)
    {
	binIR(irs, ret, ircode);
    }
}

/*************************** InExp **************************/

class InExp : BinExp
{
    this(Loc loc, Expression e1, Expression e2)
    {
	super(loc, TOKin, e1, e2);
    }
}

/****************************************************************/

class CondExp : BinExp
{
    Expression econd;

    this(Loc loc, Expression econd, Expression e1, Expression e2)
    {
	super(loc, TOKquestion, e1, e2);
	this.econd = econd;
    }

    void toIR(IRstate *irs, uint ret)
    {   uint u1;
	uint u2;
	uint b;

	if (ret)
	    b = ret;
	else
	    b = irs.alloc(1);

	econd.toIR(irs, b);
	u1 = irs.getIP();
	irs.gen2(loc, IRjf, 0, b);
	e1.toIR(irs, ret);
	u2 = irs.getIP();
	irs.gen1(loc, IRjmp, 0);
	irs.patchJmp(u1, irs.getIP());
	e2.toIR(irs, ret);
	irs.patchJmp(u2, irs.getIP());

	if (!ret)
	    irs.release(b, 1);
    }
}