diff dmdscript_tango/statement.d @ 0:55c2951c07be

initial, files origin, premoved tree
author saaadel
date Sun, 24 Jan 2010 12:34:47 +0200
parents
children 8363a4bf6a8f
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dmdscript_tango/statement.d	Sun Jan 24 12:34:47 2010 +0200
@@ -0,0 +1,1833 @@
+
+/* 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.statement;
+
+import std.stdio;
+import std.string;
+import std.math;
+
+import dmdscript.script;
+import dmdscript.value;
+import dmdscript.scopex;
+import dmdscript.expression;
+import dmdscript.irstate;
+import dmdscript.symbol;
+import dmdscript.identifier;
+import dmdscript.ir;
+import dmdscript.lexer;
+import dmdscript.errmsgs;
+import dmdscript.functiondefinition;
+import dmdscript.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;
+    }
+}