view dmdscript_tango/statement.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-2007 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.statement;

import std.stdio;
import std.string;
import std.math;

import dmdscript_tango.script;
import dmdscript_tango.value;
import dmdscript_tango.scopex;
import dmdscript_tango.expression;
import dmdscript_tango.irstate;
import dmdscript_tango.symbol;
import dmdscript_tango.identifier;
import dmdscript_tango.ir;
import dmdscript_tango.lexer;
import dmdscript_tango.textgen.errmsgs;
import dmdscript_tango.functiondefinition;
import dmdscript_tango.opcodes;

enum
{
    TOPSTATEMENT,
    FUNCTIONDEFINITION,
    EXPSTATEMENT,
    VARSTATEMENT,
}


/******************************** TopStatement ***************************/

class TopStatement
{
    const uint TOPSTATEMENT_SIGNATURE = 0xBA3FE1F3;
    uint signature = TOPSTATEMENT_SIGNATURE;

    Loc loc;
    int done;		// 0: parsed
			// 1: semantic
			// 2: toIR
    int st;

    this(Loc loc)
    {
	this.loc = loc;
	this.done = 0;
	this.st = TOPSTATEMENT;
    }

    invariant
    {
	assert(signature == TOPSTATEMENT_SIGNATURE);
    }

    void toBuffer(inout tchar[] buf)
    {
	buf ~= "TopStatement.toBuffer()\n";
    }

    Statement semantic(Scope *sc)
    {
	writefln("TopStatement.semantic(%p)", this);
	return null;
    }

    void toIR(IRstate *irs)
    {
	writefln("TopStatement.toIR(%p)", this);
    }

    void error(Scope *sc, int msgnum)
    {
	error(sc, errmsgtbl[msgnum]);
    }

    void error(Scope *sc, ...)
    {
	tchar[] buf;
	tchar[] sourcename;

	if (sc.funcdef)
	{   if (sc.funcdef.isanonymous)
		sourcename = "anonymous";
	    else if (sc.funcdef.name)
		sourcename ~= sc.funcdef.name.toString();
	}
	buf = std.string.format("%s(%d) : Error: ", sourcename, loc);

	void putc(dchar c)
	{
	    std.utf.encode(buf, c);
	}

	std.format.doFormat(&putc, _arguments, _argptr);


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

    TopStatement ImpliedReturn()
    {
	return this;
    }
}

/******************************** Statement ***************************/

class Statement : TopStatement
{
    LabelSymbol *label;

    this(Loc loc)
    {
	super(loc);
	this.loc = loc;
    }

    void toBuffer(inout tchar[] buf)
    {
	buf ~= "Statement.toBuffer()\n";
    }

    Statement semantic(Scope *sc)
    {
	writef("Statement.semantic(%p)\n", this);
	return this;
    }

    void toIR(IRstate *irs)
    {
	writef("Statement.toIR(%p)\n", this);
    }

    uint getBreak()
    {
	assert(0);
	return 0;
    }

    uint getContinue()
    {
	assert(0);
	return 0;
    }

    uint getGoto()
    {
	assert(0);
	return 0;
    }

    uint getTarget()
    {
	assert(0);
	return 0;
    }

    ScopeStatement getScope()
    {
	return null;
    }
}

/******************************** EmptyStatement ***************************/

class EmptyStatement : Statement
{
    this(Loc loc)
    {
	super(loc);
	this.loc = loc;
    }

    void toBuffer(inout tchar[] buf)
    {
	buf ~= ";\n";
    }

    Statement semantic(Scope *sc)
    {
	//writef("EmptyStatement.semantic(%p)\n", this);
	return this;
    }

    void toIR(IRstate *irs)
    {
    }
}

/******************************** ExpStatement ***************************/

class ExpStatement : Statement
{
    Expression exp;

    this(Loc loc, Expression exp)
    {
	//writef("ExpStatement.ExpStatement(this = %x, exp = %x)\n", this, exp);
	super(loc);
	st = EXPSTATEMENT;
	this.exp = exp;
    }

    void toBuffer(inout tchar[] buf)
    {
	if (exp)
	    exp.toBuffer(buf);
	buf ~= ";\n";
    }

    Statement semantic(Scope *sc)
    {
	//writef("exp = '%s'\n", exp.toString());
	//writef("ExpStatement.semantic(this = %x, exp = %x, exp.vptr = %x, %x, %x)\n", this, exp, ((uint *)exp)[0], /*(*(uint **)exp)[12],*/ *(uint *)(*(uint **)exp)[12]);
	if (exp)
	    exp = exp.semantic(sc);
	//writef("-semantic()\n");
	return this;
    }

    TopStatement ImpliedReturn()
    {
	return new ImpliedReturnStatement(loc, exp);
    }

    void toIR(IRstate *irs)
    {
	//writef("ExpStatement.toIR(%p)\n", exp);
	if (exp)
	{   uint marksave = irs.mark();

	    assert(exp);
	    exp.toIR(irs, 0);
	    irs.release(marksave);

	    exp = null;		// release to garbage collector
	}
    }
}

/****************************** VarDeclaration ******************************/

class VarDeclaration
{
    Loc loc;
    Identifier *name;
    Expression init;

    this(Loc loc, Identifier *name, Expression init)
    {
	this.loc = loc;
	this.init = init;
	this.name = name;
    }
}

/******************************** VarStatement ***************************/

class VarStatement : Statement
{
    VarDeclaration[] vardecls;

    this(Loc loc)
    {
	super(loc);
	st = VARSTATEMENT;
    }

    Statement semantic(Scope *sc)
    {   FunctionDefinition fd;
	uint i;

	// Collect all the Var statements in order in the function
	// declaration, this is so it is easy to instantiate them
	fd = sc.funcdef;
	//fd.varnames.reserve(vardecls.length);

	for (i = 0; i < vardecls.length; i++)
	{
	    VarDeclaration vd;

	    vd = vardecls[i];
	    if (vd.init)
		vd.init = vd.init.semantic(sc);
	    fd.varnames ~= vd.name;
	}

	return this;
    }

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

	if (vardecls.length)
	{
	    buf ~= "var ";

	    for (i = 0; i < vardecls.length; i++)
	    {
		VarDeclaration vd;

		vd = vardecls[i];
		buf ~= vd.name.toString();
		if (vd.init)
		{   buf ~= " = ";
		    vd.init.toBuffer(buf);
		}
	    }
	    buf ~= ";\n";
	}
    }

    void toIR(IRstate *irs)
    {
	uint i;
	uint ret;

	if (vardecls.length)
	{   uint marksave;

	    marksave = irs.mark();
	    ret = irs.alloc(1);

	    for (i = 0; i < vardecls.length; i++)
	    {
		VarDeclaration vd;

		vd = vardecls[i];

		// This works like assignment statements:
		//	name = init;
		IR property;

		if (vd.init)
		{
		    vd.init.toIR(irs, ret);
		    property.id = Identifier.build(vd.name.toString());
		    irs.gen2(loc, IRputthis, ret, property.index);
		}
	    }
	    irs.release(marksave);
	    vardecls[] = null;		// help gc
	}
    }
}

/******************************** BlockStatement ***************************/

class BlockStatement : Statement
{
    Statement[] statements;

    this(Loc loc)
    {
	super(loc);
    }

    Statement semantic(Scope *sc)
    {   uint i;

	//writefln("BlockStatement.semantic()");
	for (i = 0; i < statements.length; i++)
	{   Statement s;

	    s = statements[i];
	    assert(s);
	    statements[i] = s.semantic(sc);
	}

	return this;
    }

    TopStatement ImpliedReturn()
    {
	uint i = statements.length;

	if (i)
	{
	    TopStatement ts = statements[i - 1];
	    ts = ts.ImpliedReturn();
	    statements[i - 1] = cast(Statement)ts;
	}
	return this;
    }


    void toBuffer(inout tchar[] buf)
    {
	buf ~= "{\n";

	foreach (Statement s; statements)
	{
	    s.toBuffer(buf);
	}

	buf ~= "}\n";
    }

    void toIR(IRstate *irs)
    {
	foreach (Statement s; statements)
	{
	    s.toIR(irs);
	}

	// Release to garbage collector
	statements[] = null;
	statements = null;
    }
}

/******************************** LabelStatement ***************************/

class LabelStatement : Statement
{
    Identifier* ident;
    Statement statement;
    uint gotoIP;
    uint breakIP;
    ScopeStatement scopeContext;

    this(Loc loc, Identifier *ident, Statement statement)
    {
	//writef("LabelStatement.LabelStatement(%p, '%s', %p)\n", this, ident.toChars(), statement);
	super(loc);
	this.ident = ident;
	this.statement = statement;
	gotoIP = ~0u;
	breakIP = ~0u;
	scopeContext = null;
    }

    Statement semantic(Scope *sc)
    {
	LabelSymbol ls;

	//writef("LabelStatement.semantic('%ls')\n", ident.toString());
	scopeContext = sc.scopeContext;
	ls = sc.searchLabel(ident);
	if (ls)
	{
	    // Ignore multiple definition errors
	    //if (ls.statement)
		//error(sc, "label '%s' is already defined", ident.toString());
	    ls.statement = this;
	}
	else
	{
	    ls = new LabelSymbol(loc, ident, this);
	    sc.insertLabel(ls);
	}
	if (statement)
	    statement = statement.semantic(sc);
	return this;
    }

    TopStatement ImpliedReturn()
    {
	if (statement)
	    statement = cast(Statement)statement.ImpliedReturn();
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {
	buf ~= ident.toString();
	buf ~= ": ";
	if (statement)
	    statement.toBuffer(buf);
	else
	    buf ~= '\n';
    }

    void toIR(IRstate *irs)
    {
	gotoIP = irs.getIP();
	statement.toIR(irs);
	breakIP = irs.getIP();
    }

    uint getGoto()
    {
	return gotoIP;
    }

    uint getBreak()
    {
	return breakIP;
    }

    uint getContinue()
    {
	return statement.getContinue();
    }

    ScopeStatement getScope()
    {
	return scopeContext;
    }
}

/******************************** IfStatement ***************************/

class IfStatement : Statement
{
    Expression condition;
    Statement ifbody;
    Statement elsebody;

    this(Loc loc, Expression condition, Statement ifbody, Statement elsebody)
    {
	super(loc);
	this.condition = condition;
	this.ifbody = ifbody;
	this.elsebody = elsebody;
    }

    Statement semantic(Scope *sc)
    {
	//writef("IfStatement.semantic(%p)\n", sc);
	assert(condition);
	condition = condition.semantic(sc);
	ifbody = ifbody.semantic(sc);
	if (elsebody)
	    elsebody = elsebody.semantic(sc);

	return this;
    }

    TopStatement ImpliedReturn()
    {
	assert(condition);
	ifbody = cast(Statement)ifbody.ImpliedReturn();
	if (elsebody)
	    elsebody = cast(Statement)elsebody.ImpliedReturn();
	return this;
    }



    void toIR(IRstate *irs)
    {
	uint c;
	uint u1;
	uint u2;

	assert(condition);
	c = irs.alloc(1);
	condition.toIR(irs, c);
	u1 = irs.getIP();
	irs.gen2(loc, (condition.isBooleanResult() ? IRjfb : IRjf), 0, c);
	irs.release(c, 1);
	ifbody.toIR(irs);
	if (elsebody)
	{
	    u2 = irs.getIP();
	    irs.gen1(loc, IRjmp, 0);
	    irs.patchJmp(u1, irs.getIP());
	    elsebody.toIR(irs);
	    irs.patchJmp(u2, irs.getIP());
	}
	else
	{
	    irs.patchJmp(u1, irs.getIP());
	}

	// Help GC
	condition = null;
	ifbody = null;
	elsebody = null;
    }
}

/******************************** SwitchStatement ***************************/

class SwitchStatement : Statement
{
    Expression condition;
    Statement bdy;
    uint breakIP;
    ScopeStatement scopeContext;

    DefaultStatement swdefault;
    CaseStatement[] cases;

    this(Loc loc, Expression c, Statement b)
    {
	super(loc);
	condition = c;
	bdy = b;
	breakIP = ~0u;
	scopeContext = null;

	swdefault = null;
	cases = null;
    }

    Statement semantic(Scope *sc)
    {
	condition = condition.semantic(sc);

	SwitchStatement switchSave = sc.switchTarget;
	Statement breakSave = sc.breakTarget;

	scopeContext = sc.scopeContext;
	sc.switchTarget = this;
	sc.breakTarget = this;

	bdy = bdy.semantic(sc);

	sc.switchTarget = switchSave;
	sc.breakTarget = breakSave;

	return this;
    }

    void toIR(IRstate *irs)
    {   uint c;
	uint udefault;
	uint marksave;

	//writef("SwitchStatement.toIR()\n");
	marksave = irs.mark();
	c = irs.alloc(1);
	condition.toIR(irs, c);

	// Generate a sequence of cmp-jt
	// Not the most efficient, but we await a more formal
	// specification of switch before we attempt to optimize

	if (cases.length)
	{   uint x;

	    x = irs.alloc(1);
	    for (uint i = 0; i < cases.length; i++)
	    {
		CaseStatement cs;

		x = irs.alloc(1);
		cs = cases[i];
		cs.exp.toIR(irs, x);
		irs.gen3(loc, IRcid, x, c, x);
		cs.patchIP = irs.getIP();
		irs.gen2(loc, IRjt, 0, x);
	    }
	}
	udefault = irs.getIP();
	irs.gen1(loc, IRjmp, 0);

	Statement breakSave = irs.breakTarget;
	irs.breakTarget = this;
	bdy.toIR(irs);

	irs.breakTarget = breakSave;
	breakIP = irs.getIP();

	// Patch jump addresses
	if (cases.length)
	{
	    for (uint i = 0; i < cases.length; i++)
	    {   CaseStatement cs;

		cs = cases[i];
		irs.patchJmp(cs.patchIP, cs.caseIP);
	    }
	}
	if (swdefault)
	    irs.patchJmp(udefault, swdefault.defaultIP);
	else
	    irs.patchJmp(udefault, breakIP);
	irs.release(marksave);

	// Help gc
	condition = null;
	bdy = null;
    }

    uint getBreak()
    {
	return breakIP;
    }

    ScopeStatement getScope()
    {
	return scopeContext;
    }
}


/******************************** CaseStatement ***************************/

class CaseStatement : Statement
{
    Expression exp;
    uint caseIP;
    uint patchIP;

    this(Loc loc, Expression exp)
    {
	super(loc);
	this.exp = exp;
	caseIP = ~0u;
	patchIP = ~0u;
    }

    Statement semantic(Scope *sc)
    {
	//writef("CaseStatement.semantic(%p)\n", sc);
	exp = exp.semantic(sc);
	if (sc.switchTarget)
	{
	    SwitchStatement sw = sc.switchTarget;
	    uint i;

	    // Look for duplicate
	    for (i = 0; i < sw.cases.length; i++)
	    {
		CaseStatement cs = sw.cases[i];

		if (exp == cs.exp)
		{
		    error(sc, errmsgtbl[ERR_SWITCH_REDUNDANT_CASE], exp.toString());
		    return null;
		}
	    }
	    sw.cases ~= this;
	}
	else
	{   error(sc, errmsgtbl[ERR_MISPLACED_SWITCH_CASE], exp.toString());
	    return null;
	}
	return this;
    }

    void toIR(IRstate *irs)
    {
	caseIP = irs.getIP();
    }
}

/******************************** DefaultStatement ***************************/

class DefaultStatement : Statement
{
    uint defaultIP;

    this(Loc loc)
    {
	super(loc);
	defaultIP = ~0u;
    }

    Statement semantic(Scope *sc)
    {
	if (sc.switchTarget)
	{
	    SwitchStatement sw = sc.switchTarget;

	    if (sw.swdefault)
	    {
		error(sc, ERR_SWITCH_REDUNDANT_DEFAULT);
		return null;
	    }
	    sw.swdefault = this;
	}
	else
	{   error(sc, ERR_MISPLACED_SWITCH_DEFAULT);
	    return null;
	}
	return this;
    }

    void toIR(IRstate *irs)
    {
	defaultIP = irs.getIP();
    }
}

/******************************** DoStatement ***************************/

class DoStatement : Statement
{
    Statement bdy;
    Expression condition;
    uint breakIP;
    uint continueIP;
    ScopeStatement scopeContext;

    this(Loc loc, Statement b, Expression c)
    {
	super(loc);
	bdy = b;
	condition = c;
	breakIP = ~0u;
	continueIP = ~0u;
	scopeContext = null;
    }

    Statement semantic(Scope *sc)
    {
	Statement continueSave = sc.continueTarget;
	Statement breakSave = sc.breakTarget;

	scopeContext = sc.scopeContext;
	sc.continueTarget = this;
	sc.breakTarget = this;

	bdy = bdy.semantic(sc);
	condition = condition.semantic(sc);

	sc.continueTarget = continueSave;
	sc.breakTarget = breakSave;

	return this;
    }

    TopStatement ImpliedReturn()
    {
	if (bdy)
	    bdy = cast(Statement)bdy.ImpliedReturn();
	return this;
    }

    void toIR(IRstate *irs)
    {   uint c;
	uint u1;
	Statement continueSave = irs.continueTarget;
	Statement breakSave = irs.breakTarget;
	uint marksave;

	irs.continueTarget = this;
	irs.breakTarget = this;

	marksave = irs.mark();
	u1 = irs.getIP();
	bdy.toIR(irs);
	c = irs.alloc(1);
	continueIP = irs.getIP();
	condition.toIR(irs, c);
	irs.gen2(loc, (condition.isBooleanResult() ? IRjtb : IRjt), u1 - irs.getIP(), c);
	breakIP = irs.getIP();
	irs.release(marksave);

	irs.continueTarget = continueSave;
	irs.breakTarget = breakSave;

	// Help GC
	condition = null;
	bdy = null;
    }

    uint getBreak()
    {
	return breakIP;
    }

    uint getContinue()
    {
	return continueIP;
    }

    ScopeStatement getScope()
    {
	return scopeContext;
    }
}

/******************************** WhileStatement ***************************/

class WhileStatement : Statement
{
    Expression condition;
    Statement bdy;
    uint breakIP;
    uint continueIP;
    ScopeStatement scopeContext;

    this(Loc loc, Expression c, Statement b)
    {
	super(loc);
	condition = c;
	bdy = b;
	breakIP = ~0u;
	continueIP = ~0u;
	scopeContext = null;
    }

    Statement semantic(Scope *sc)
    {
	Statement continueSave = sc.continueTarget;
	Statement breakSave = sc.breakTarget;

	scopeContext = sc.scopeContext;
	sc.continueTarget = this;
	sc.breakTarget = this;

	condition = condition.semantic(sc);
	bdy = bdy.semantic(sc);

	sc.continueTarget = continueSave;
	sc.breakTarget = breakSave;

	return this;
    }

    TopStatement ImpliedReturn()
    {
	if (bdy)
	    bdy = cast(Statement)bdy.ImpliedReturn();
	return this;
    }

    void toIR(IRstate *irs)
    {   uint c;
	uint u1;
	uint u2;

	Statement continueSave = irs.continueTarget;
	Statement breakSave = irs.breakTarget;
	uint marksave = irs.mark();

	irs.continueTarget = this;
	irs.breakTarget = this;

	u1 = irs.getIP();
	continueIP = u1;
	c = irs.alloc(1);
	condition.toIR(irs, c);
	u2 = irs.getIP();
	irs.gen2(loc, (condition.isBooleanResult() ? IRjfb : IRjf), 0, c);
	bdy.toIR(irs);
	irs.gen1(loc, IRjmp, u1 - irs.getIP());
	irs.patchJmp(u2, irs.getIP());
	breakIP = irs.getIP();

	irs.release(marksave);
	irs.continueTarget = continueSave;
	irs.breakTarget = breakSave;

	// Help GC
	condition = null;
	bdy = null;
    }

    uint getBreak()
    {
	return breakIP;
    }

    uint getContinue()
    {
	return continueIP;
    }

    ScopeStatement getScope()
    {
	return scopeContext;
    }
}

/******************************** ForStatement ***************************/

class ForStatement : Statement
{
    Statement init;
    Expression condition;
    Expression increment;
    Statement bdy;
    uint breakIP;
    uint continueIP;
    ScopeStatement scopeContext;

    this(Loc loc, Statement init, Expression condition, Expression increment, Statement bdy)
    {
	super(loc);
	this.init = init;
	this.condition = condition;
	this.increment = increment;
	this.bdy = bdy;
	breakIP = ~0u;
	continueIP = ~0u;
	scopeContext = null;
    }

    Statement semantic(Scope *sc)
    {
	Statement continueSave = sc.continueTarget;
	Statement breakSave = sc.breakTarget;

	if (init)
	    init = init.semantic(sc);
	if (condition)
	    condition = condition.semantic(sc);
	if (increment)
	    increment = increment.semantic(sc);

	scopeContext = sc.scopeContext;
	sc.continueTarget = this;
	sc.breakTarget = this;

	bdy = bdy.semantic(sc);

	sc.continueTarget = continueSave;
	sc.breakTarget = breakSave;

	return this;
    }

    TopStatement ImpliedReturn()
    {
	if (bdy)
	    bdy = cast(Statement)bdy.ImpliedReturn();
	return this;
    }

    void toIR(IRstate *irs)
    {
	uint u1;
	uint u2 = 0;	// unneeded initialization keeps lint happy

	Statement continueSave = irs.continueTarget;
	Statement breakSave = irs.breakTarget;
	uint marksave = irs.mark();

	irs.continueTarget = this;
	irs.breakTarget = this;

	if (init)
	    init.toIR(irs);
	u1 = irs.getIP();
	if (condition)
	{
	    if (condition.op == TOKless || condition.op == TOKlessequal)
	    {
		BinExp be = cast(BinExp)condition;
		RealExpression re;
		uint b;
		uint c;

		b = irs.alloc(1);
		be.e1.toIR(irs, b);
		re = cast(RealExpression )be.e2;
		if (be.e2.op == TOKreal && !isnan(re.value))
		{
		    u2 = irs.getIP();
		    irs.gen(loc, (condition.op == TOKless) ? IRjltc : IRjlec, 4, 0, b, re.value);
		}
		else
		{
		    c = irs.alloc(1);
		    be.e2.toIR(irs, c);
		    u2 = irs.getIP();
		    irs.gen3(loc, (condition.op == TOKless) ? IRjlt : IRjle, 0, b, c);
		}
	    }
	    else
	    {   uint c;

		c = irs.alloc(1);
		condition.toIR(irs, c);
		u2 = irs.getIP();
		irs.gen2(loc, (condition.isBooleanResult() ? IRjfb : IRjf), 0, c);
	    }
	}
	bdy.toIR(irs);
	continueIP = irs.getIP();
	if (increment)
	    increment.toIR(irs, 0);
	irs.gen1(loc, IRjmp, u1 - irs.getIP());
	if (condition)
	    irs.patchJmp(u2, irs.getIP());

	breakIP = irs.getIP();

	irs.release(marksave);
	irs.continueTarget = continueSave;
	irs.breakTarget = breakSave;

	// Help GC
	init = null;
	condition = null;
	bdy = null;
	increment = null;
    }

    uint getBreak()
    {
	return breakIP;
    }

    uint getContinue()
    {
	return continueIP;
    }

    ScopeStatement getScope()
    {
	return scopeContext;
    }
}

/******************************** ForInStatement ***************************/

class ForInStatement : Statement
{
    Statement init;
    Expression inexp;
    Statement bdy;
    uint breakIP;
    uint continueIP;
    ScopeStatement scopeContext;

    this(Loc loc, Statement init, Expression inexp, Statement bdy)
    {
	super(loc);
	this.init = init;
	this.inexp = inexp;
	this.bdy = bdy;
	breakIP = ~0u;
	continueIP = ~0u;
	scopeContext = null;
    }

    Statement semantic(Scope *sc)
    {
	Statement continueSave = sc.continueTarget;
	Statement breakSave = sc.breakTarget;

	init = init.semantic(sc);

	if (init.st == EXPSTATEMENT)
	{   ExpStatement es;

	    es = cast(ExpStatement)(init);
	    es.exp.checkLvalue(sc);
	}
	else if (init.st == VARSTATEMENT)
	{
	}
	else
	{
	    error(sc, ERR_INIT_NOT_EXPRESSION);
	    return null;
	}

	inexp = inexp.semantic(sc);

	scopeContext = sc.scopeContext;
	sc.continueTarget = this;
	sc.breakTarget = this;

	bdy = bdy.semantic(sc);

	sc.continueTarget = continueSave;
	sc.breakTarget = breakSave;

	return this;
    }

    TopStatement ImpliedReturn()
    {
	bdy = cast(Statement)bdy.ImpliedReturn();
	return this;
    }

    void toIR(IRstate *irs)
    {
	uint e;
	uint iter;
	ExpStatement es;
	VarStatement vs;
	uint base;
	IR property;
	int opoff;
	uint marksave = irs.mark();

	e = irs.alloc(1);
	inexp.toIR(irs, e);
	iter = irs.alloc(1);
	irs.gen2(loc, IRiter, iter, e);

	Statement continueSave = irs.continueTarget;
	Statement breakSave = irs.breakTarget;

	irs.continueTarget = this;
	irs.breakTarget = this;

	if (init.st == EXPSTATEMENT)
	{
	    es = cast(ExpStatement)(init);
	    es.exp.toLvalue(irs, base, &property, opoff);
	}
	else if (init.st == VARSTATEMENT)
	{
	    VarDeclaration vd;

	    vs = cast(VarStatement)(init);
	    assert(vs.vardecls.length == 1);
	    vd = vs.vardecls[0];

	    property.id = Identifier.build(vd.name.toString());
	    opoff = 2;
	    base = ~0u;
	}
	else
	{   // Error already reported by semantic()
	    return;
	}

	continueIP = irs.getIP();
	if (opoff == 2)
	    irs.gen3(loc, IRnextscope, 0, property.index, iter);
	else
	    irs.gen(loc, IRnext + opoff, 4, 0, base, property.index, iter);
	bdy.toIR(irs);
	irs.gen1(loc, IRjmp, continueIP - irs.getIP());
	irs.patchJmp(continueIP, irs.getIP());

	breakIP = irs.getIP();

	irs.continueTarget = continueSave;
	irs.breakTarget = breakSave;
	irs.release(marksave);

	// Help GC
	init = null;
	inexp = null;
	bdy = null;
    }

    uint getBreak()
    {
	return breakIP;
    }

    uint getContinue()
    {
	return continueIP;
    }

    ScopeStatement getScope()
    {
	return scopeContext;
    }
}

/******************************** ScopeStatement ***************************/

class ScopeStatement : Statement
{
    ScopeStatement enclosingScope;
    int depth;			// syntactical nesting level of ScopeStatement's
    int npops;			// how many items added to scope chain

    this(Loc loc)
    {
	super(loc);
	enclosingScope = null;
	depth = 1;
	npops = 1;
    }
}

/******************************** WithStatement ***************************/

class WithStatement : ScopeStatement
{
    Expression exp;
    Statement bdy;

    this(Loc loc, Expression exp, Statement bdy)
    {
	super(loc);
	this.exp = exp;
	this.bdy = bdy;
    }

    Statement semantic(Scope *sc)
    {
	exp = exp.semantic(sc);

	enclosingScope = sc.scopeContext;
	sc.scopeContext = this;

	// So enclosing FunctionDeclaration knows how deep the With's
	// can nest
	if (enclosingScope)
	    depth = enclosingScope.depth + 1;
	if (depth > sc.funcdef.withdepth)
	    sc.funcdef.withdepth = depth;

	sc.nestDepth++;
	bdy = bdy.semantic(sc);
	sc.nestDepth--;

	sc.scopeContext = enclosingScope;
	return this;
    }

    TopStatement ImpliedReturn()
    {
	bdy = cast(Statement)bdy.ImpliedReturn();
	return this;
    }



    void toIR(IRstate *irs)
    {
	uint c;
	uint marksave = irs.mark();

	irs.scopeContext = this;

	c = irs.alloc(1);
	exp.toIR(irs, c);
	irs.gen1(loc, IRpush, c);
	bdy.toIR(irs);
	irs.gen0(loc, IRpop);

	irs.scopeContext = enclosingScope;
	irs.release(marksave);

	// Help GC
	exp = null;
	bdy = null;
    }
}

/******************************** ContinueStatement ***************************/

class ContinueStatement : Statement
{
    Identifier *ident;
    Statement target;

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

    Statement semantic(Scope *sc)
    {
	if (ident == null)
	{
	    target = sc.continueTarget;
	    if (!target)
	    {
		error(sc, ERR_MISPLACED_CONTINUE);
		return null;
	    }
	}
	else
	{   LabelSymbol ls;

	    ls = sc.searchLabel(ident);
	    if (!ls || !ls.statement)
	    {   error(sc, errmsgtbl[ERR_UNDEFINED_STATEMENT_LABEL], ident.toString());
		return null;
	    }
	    else
		target = ls.statement;
	}
	return this;
    }

    void toIR(IRstate *irs)
    {   ScopeStatement w;
	ScopeStatement tw;

	tw = target.getScope();
	for (w = irs.scopeContext; !(w is tw); w = w.enclosingScope)
	{
	    assert(w);
	    irs.pops(w.npops);
	}
	irs.addFixup(irs.getIP());
	irs.gen1(loc, IRjmp, cast(uint)cast(void*)this);
    }

    uint getTarget()
    {
	assert(target);
	return target.getContinue();
    }
}

/******************************** BreakStatement ***************************/

class BreakStatement : Statement
{
    Identifier *ident;
    Statement target;

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

    Statement semantic(Scope *sc)
    {
	//writef("BreakStatement.semantic(%p)\n", sc);
	if (ident == null)
	{
	    target = sc.breakTarget;
	    if (!target)
	    {
		error(sc, ERR_MISPLACED_BREAK);
		return null;
	    }
	}
	else
	{   LabelSymbol ls;

	    ls = sc.searchLabel(ident);
	    if (!ls || !ls.statement)
	    {   error(sc, errmsgtbl[ERR_UNDEFINED_STATEMENT_LABEL], ident.toString());
		return null;
	    }
	    else
		target = ls.statement;
	}
	return this;
    }

    void toIR(IRstate *irs)
    {   ScopeStatement w;
	ScopeStatement tw;

	assert(target);
	tw = target.getScope();
	for (w = irs.scopeContext; !(w is tw); w = w.enclosingScope)
	{
	    assert(w);
	    irs.pops(w.npops);
	}

	irs.addFixup(irs.getIP());
	irs.gen1(loc, IRjmp, cast(uint)cast(void*)this);
    }

    uint getTarget()
    {
	assert(target);
	return target.getBreak();
    }
}

/******************************** GotoStatement ***************************/

class GotoStatement : Statement
{
    Identifier *ident;
    LabelSymbol label;

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

    Statement semantic(Scope *sc)
    {
	LabelSymbol ls;

	ls = sc.searchLabel(ident);
	if (!ls)
	{
	    ls = new LabelSymbol(loc, ident, null);
	    sc.insertLabel(ls);
	}
	label = ls;
	return this;
    }

    void toIR(IRstate *irs)
    {
	assert(label);

	// Determine how many with pops we need to do
	for (ScopeStatement w = irs.scopeContext; ; w = w.enclosingScope)
	{
	    if (!w)
	    {
		if (label.statement.scopeContext)
		{
		    assert(0); // BUG: should do next statement instead
		    //script.error(errmsgtbl[ERR_GOTO_INTO_WITH]);
		}
		break;
	    }
	    if (w is label.statement.scopeContext)
		break;
	    irs.pops(w.npops);
	}

	irs.addFixup(irs.getIP());
	irs.gen1(loc, IRjmp, cast(uint)cast(void*)this);
    }

    uint getTarget()
    {
	return label.statement.getGoto();
    }
}

/******************************** ReturnStatement ***************************/

class ReturnStatement : Statement
{
    Expression exp;

    this(Loc loc, Expression exp)
    {
	super(loc);
	this.exp = exp;
    }

    Statement semantic(Scope *sc)
    {
	if (exp)
	    exp = exp.semantic(sc);

	// Don't allow return from eval functions or global function
	if (sc.funcdef.iseval || sc.funcdef.isglobal)
	    error(sc, ERR_MISPLACED_RETURN);

	return this;
    }

    void toBuffer(inout tchar[] buf)
    {
	//writef("ReturnStatement.toBuffer()\n");
	buf ~= "return ";
	if (exp)
	    exp.toBuffer(buf);
	buf ~= ";\n";
    }

    void toIR(IRstate *irs)
    {   ScopeStatement w;
	int npops;

	npops = 0;
	for (w = irs.scopeContext; w; w = w.enclosingScope)
	    npops += w.npops;

	if (exp)
	{   uint e;

	    e = irs.alloc(1);
	    exp.toIR(irs, e);
	    if (npops)
	    {
		irs.gen1(loc, IRimpret, e);
		irs.pops(npops);
		irs.gen0(loc, IRret);
	    }
	    else
		irs.gen1(loc, IRretexp, e);
	    irs.release(e, 1);
	}
	else
	{
	    if (npops)
		irs.pops(npops);
	    irs.gen0(loc, IRret);
	}

	// Help GC
	exp = null;
    }
}

/******************************** ImpliedReturnStatement ***************************/

// Same as ReturnStatement, except that the return value is set but the
// function does not actually return. Useful for setting the return
// value for loop bodies.

class ImpliedReturnStatement : Statement
{
    Expression exp;

    this(Loc loc, Expression exp)
    {
	super(loc);
	this.exp = exp;
    }

    Statement semantic(Scope *sc)
    {
	if (exp)
	    exp = exp.semantic(sc);
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {
	if (exp)
	    exp.toBuffer(buf);
	buf ~= ";\n";
    }

    void toIR(IRstate *irs)
    {
	if (exp)
	{   uint e;

	    e = irs.alloc(1);
	    exp.toIR(irs, e);
	    irs.gen1(loc, IRimpret, e);
	    irs.release(e, 1);

	    // Help GC
	    exp = null;
	}
    }
}

/******************************** ThrowStatement ***************************/

class ThrowStatement : Statement
{
    Expression exp;

    this(Loc loc, Expression exp)
    {
	super(loc);
	this.exp = exp;
    }

    Statement semantic(Scope *sc)
    {
	if (exp)
	    exp = exp.semantic(sc);
	else
	{
	    error(sc, ERR_NO_THROW_EXPRESSION);
	    return new EmptyStatement(loc);
	}
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {
	buf ~= "throw ";
	if (exp)
	    exp.toBuffer(buf);
	buf ~= ";\n";
    }

    void toIR(IRstate *irs)
    {
	uint e;

	assert(exp);
	e = irs.alloc(1);
	exp.toIR(irs, e);
	irs.gen1(loc, IRthrow, e);
	irs.release(e, 1);

	// Help GC
	exp = null;
    }
}

/******************************** TryStatement ***************************/

class TryStatement : ScopeStatement
{
    Statement bdy;
    Identifier* catchident;
    Statement catchbdy;
    Statement finalbdy;

    this(Loc loc, Statement bdy,
	    Identifier *catchident, Statement catchbdy,
	    Statement finalbdy)
    {
	super(loc);
	this.bdy = bdy;
	this.catchident = catchident;
	this.catchbdy = catchbdy;
	this.finalbdy = finalbdy;
	if (catchbdy && finalbdy)
	    npops = 2;		// 2 items in scope chain
    }

    Statement semantic(Scope *sc)
    {
	enclosingScope = sc.scopeContext;
	sc.scopeContext = this;

	// So enclosing FunctionDeclaration knows how deep the With's
	// can nest
	if (enclosingScope)
	    depth = enclosingScope.depth + 1;
	if (depth > sc.funcdef.withdepth)
	    sc.funcdef.withdepth = depth;

	bdy.semantic(sc);
	if (catchbdy)
	    catchbdy.semantic(sc);
	if (finalbdy)
	    finalbdy.semantic(sc);

	sc.scopeContext = enclosingScope;
	return this;
    }

    void toBuffer(inout tchar[] buf)
    {
	buf ~= "try\n";
	bdy.toBuffer(buf);
	if (catchident)
	{
	    buf ~= "catch (" ~
		   catchident.toString() ~
		   ")\n";
	}
	if (catchbdy)
	    catchbdy.toBuffer(buf);
	if (finalbdy)
	{
	    buf ~= "finally\n";
	    finalbdy.toBuffer(buf);
	}
    }

    void toIR(IRstate *irs)
    {   uint f;
	uint c;
	uint e;
	uint e2;
	uint marksave = irs.mark();

	irs.scopeContext = this;
	if (finalbdy)
	{
	    f = irs.getIP();
	    irs.gen1(loc, IRtryfinally, 0);
	    if (catchbdy)
	    {
		c = irs.getIP();
		irs.gen2(loc, IRtrycatch, 0, cast(uint)Identifier.build(catchident.toString()));
		bdy.toIR(irs);
		irs.gen0(loc, IRpop);		// remove catch clause
		irs.gen0(loc, IRpop);		// call finalbdy

		e = irs.getIP();
		irs.gen1(loc, IRjmp, 0);
		irs.patchJmp(c, irs.getIP());
		catchbdy.toIR(irs);
		irs.gen0(loc, IRpop);		// remove catch object
		irs.gen0(loc, IRpop);		// call finalbdy code
		e2 = irs.getIP();
		irs.gen1(loc, IRjmp, 0);	// jmp past finalbdy

		irs.patchJmp(f, irs.getIP());
		irs.scopeContext = enclosingScope;
		finalbdy.toIR(irs);
		irs.gen0(loc, IRfinallyret);
		irs.patchJmp(e, irs.getIP());
		irs.patchJmp(e2, irs.getIP());
	    }
	    else // finalbdy only
	    {
		bdy.toIR(irs);
		irs.gen0(loc, IRpop);
		e = irs.getIP();
		irs.gen1(loc, IRjmp, 0);
		irs.patchJmp(f, irs.getIP());
		irs.scopeContext = enclosingScope;
		finalbdy.toIR(irs);
		irs.gen0(loc, IRfinallyret);
		irs.patchJmp(e, irs.getIP());
	    }
	}
	else // catchbdy only
	{
	    c = irs.getIP();
	    irs.gen2(loc, IRtrycatch, 0, cast(uint)Identifier.build(catchident.toString()));
	    bdy.toIR(irs);
	    irs.gen0(loc, IRpop);
	    e = irs.getIP();
	    irs.gen1(loc, IRjmp, 0);
	    irs.patchJmp(c, irs.getIP());
	    catchbdy.toIR(irs);
	    irs.gen0(loc, IRpop);
	    irs.patchJmp(e, irs.getIP());
	}
	irs.scopeContext = enclosingScope;
	irs.release(marksave);

	// Help GC
	bdy = null;
	catchident = null;
	catchbdy = null;
	finalbdy = null;
    }
}