//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 19 Jul 06 Brian Frank Creation
//
**
** Stmt
**
abstract class Stmt : Node
{
//////////////////////////////////////////////////////////////////////////
// Construction
//////////////////////////////////////////////////////////////////////////
new make(Loc loc, StmtId id)
: super(loc)
{
this.id = id
}
//////////////////////////////////////////////////////////////////////////
// Stmt
//////////////////////////////////////////////////////////////////////////
**
** Does this statement always cause us to exit the method (or does it
** cause us to loop forever without a break to the next statement)
**
abstract Bool isExit()
**
** Check for definite assignment where the given function
** returns true for the LHS of an assignment in all code paths.
**
abstract Bool isDefiniteAssign(|Expr lhs->Bool| f)
**
** Is this statement a field initialization for a once storage field.
** This is used in compilerEs to skip initializing fields to "_once_"
**
virtual Bool isOnceFieldInit() { false }
//////////////////////////////////////////////////////////////////////////
// Tree
//////////////////////////////////////////////////////////////////////////
Stmt[]? walk(Visitor v, VisitDepth depth)
{
v.enterStmt(this)
walkChildren(v, depth)
r := v.visitStmt(this)
v.exitStmt(this)
return r
}
virtual Void walkChildren(Visitor v, VisitDepth depth)
{
}
static Expr? walkExpr(Visitor v, VisitDepth depth, Expr? expr)
{
if (depth === VisitDepth.expr && expr != null)
return expr.walk(v)
else
return expr
}
//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////
const StmtId id
}
**************************************************************************
** NopStmt
**************************************************************************
**
** NopStmt is no operation do nothing statement.
**
class NopStmt : Stmt
{
new make(Loc loc) : super(loc, StmtId.nop) {}
override Bool isExit() { false }
override Bool isDefiniteAssign(|Expr lhs->Bool| f) { false }
override Void print(AstWriter out)
{
out.w("nop").nl
}
}
**************************************************************************
** ExprStmt
**************************************************************************
**
** ExprStmt is a statement with a stand along expression such
** as an assignment or method call.
**
class ExprStmt : Stmt
{
new make(Expr expr)
: super(expr.loc, StmtId.expr)
{
this.expr = expr
}
override Bool isExit() { false }
override Bool isDefiniteAssign(|Expr lhs->Bool| f) { expr.isDefiniteAssign(f) }
override Bool isOnceFieldInit()
{
if (expr.id !== ExprId.assign) return false
x := ((BinaryExpr)expr).lhs as FieldExpr
if (x === null) return false
return x.field.isOnce
}
override Void walkChildren(Visitor v, VisitDepth depth)
{
expr = walkExpr(v, depth, expr)
}
override Str toStr() { expr.toStr }
override Void print(AstWriter out)
{
printOpt(out)
}
Void printOpt(AstWriter out, Bool nl := true)
{
expr.print(out)
if (nl) out.nl
}
Expr expr
}
**************************************************************************
** LocalDefStmt
**************************************************************************
**
** LocalDefStmt models a local variable declaration and its
** optional initialization expression.
**
class LocalDefStmt : Stmt
{
new make(Loc loc, CType? ctype := null, Str name := "")
: super(loc, StmtId.localDef)
{
this.ctype = ctype
this.name = name
}
override Bool isExit() { false }
override Bool isDefiniteAssign(|Expr lhs->Bool| f)
{
if (init != null) return init.isDefiniteAssign(f)
return false
}
override Void walkChildren(Visitor v, VisitDepth depth)
{
init = walkExpr(v, depth, init)
}
new makeCatchVar(Catch c)
: super.make(c.loc, StmtId.localDef)
{
ctype = c.errType
name = c.errVariable
isCatchVar = true
}
override Str toStr() { "$ctype $name ($var)" }
override Void print(AstWriter out) { printOpt(out) }
Void printOpt(AstWriter out, Bool nl := true)
{
if (ctype != null) out.w("$ctype ")
out.w(name)
if (init != null) out.w(" init: $init")
if (nl) out.nl
}
CType? ctype // type of the variable (or null if inferred)
Str name // variable name
Expr? init // rhs of init; in ResolveExpr it becomes full assign expr
Bool isCatchVar // is this auto-generated var for "catch (Err x)"
MethodVar? var // variable binding
}
**************************************************************************
** IfStmt
**************************************************************************
**
** IfStmt models an if or if/else statement.
**
class IfStmt : Stmt
{
new make(Loc loc, Expr condition, Block trueBlock)
: super(loc, StmtId.ifStmt)
{
this.condition = condition
this.trueBlock = trueBlock
}
override Bool isExit()
{
if (falseBlock == null) return false
return trueBlock.isExit && falseBlock.isExit
}
override Bool isDefiniteAssign(|Expr lhs->Bool| f)
{
if (!trueBlock.isDefiniteAssign(f)) return false
if (condition.id === ExprId.trueLiteral) return true
if (falseBlock == null) return false
return falseBlock.isDefiniteAssign(f)
}
override Void walkChildren(Visitor v, VisitDepth depth)
{
condition = walkExpr(v, depth, condition)
trueBlock.walk(v, depth)
if (falseBlock != null) falseBlock.walk(v, depth)
}
override Void print(AstWriter out)
{
out.w("if ($condition)").nl
trueBlock.print(out)
if (falseBlock != null)
{
out.w("else").nl
falseBlock.print(out)
}
}
Expr condition // test expression
Block trueBlock // block to execute if condition true
Block? falseBlock // else clause or null
}
**************************************************************************
** ReturnStmt
**************************************************************************
**
** ReturnStmt returns from the method
**
class ReturnStmt : Stmt
{
new make(Loc loc, Expr? expr := null)
: super(loc, StmtId.returnStmt)
{
this.expr = expr
}
static ReturnStmt makeSynthetic(Loc loc, Expr? expr := null)
{
stmt := make(loc, expr)
stmt.isSynthetic = true
return stmt
}
override Bool isExit() { true }
override Bool isDefiniteAssign(|Expr lhs->Bool| f)
{
if (expr == null) return false
return expr.isDefiniteAssign(f)
}
override Void walkChildren(Visitor v, VisitDepth depth)
{
expr = walkExpr(v, depth, expr)
}
override Str toStr() { expr != null ? "return $expr" : "return" }
override Void print(AstWriter out)
{
out.w("return")
if (expr != null) out.w(" $expr")
out.nl
}
Expr? expr // expr to return of null if void return
MethodVar? leaveVar // to stash result for leave from protected region
Bool isSynthetic // was return inserted by compiler
}
**************************************************************************
** ThrowStmt
**************************************************************************
**
** ThrowStmt throws an exception
**
class ThrowStmt : Stmt
{
new make(Loc loc, Expr exception)
: super(loc, StmtId.throwStmt)
{
this.exception = exception
}
override Bool isExit() { true }
override Bool isDefiniteAssign(|Expr lhs->Bool| f) { true }
override Void walkChildren(Visitor v, VisitDepth depth)
{
exception = walkExpr(v, depth, exception)
}
override Void print(AstWriter out)
{
out.w("throw $exception").nl
}
Expr exception // exception to throw
}
**************************************************************************
** ForStmt
**************************************************************************
**
** ForStmt models a for loop of the format:
** for (init; condition; update) block
**
class ForStmt : Stmt
{
new make(Loc loc) : super(loc, StmtId.forStmt) {}
override Bool isExit() { false }
override Bool isDefiniteAssign(|Expr lhs->Bool| f)
{
if (condition.isDefiniteAssign(f)) return true
if (init != null && init.isDefiniteAssign(f)) return true
return false
}
override Void walkChildren(Visitor v, VisitDepth depth)
{
if (init != null) init.walk(v, depth)
condition = walkExpr(v, depth, condition)
update = walkExpr(v, depth, update)
block.walk(v, depth)
}
override Void print(AstWriter out)
{
out.w("for (")
if (init != null) init->printOpt(out, false)
out.w("; ")
if (condition != null) condition.print(out)
out.w("; ")
if (update != null) update.print(out)
out.w(")").nl
block.print(out)
}
Stmt? init // loop initialization
Expr? condition // loop condition
Expr? update // loop update
Block? block // code to run inside loop
}
**************************************************************************
** WhileStmt
**************************************************************************
**
** WhileStmt models a while loop of the format:
** while (condition) block
**
class WhileStmt : Stmt
{
new make(Loc loc, Expr condition, Block block)
: super(loc, StmtId.whileStmt)
{
this.condition = condition
this.block = block
}
override Bool isExit() { false }
override Bool isDefiniteAssign(|Expr lhs->Bool| f)
{
condition.isDefiniteAssign(f)
}
override Void walkChildren(Visitor v, VisitDepth depth)
{
condition = walkExpr(v, depth, condition)
block.walk(v, depth)
}
override Void print(AstWriter out)
{
out.w("while ($condition)").nl
block.print(out)
}
Expr condition // loop condition
Block block // code to run inside loop
}
**************************************************************************
** BreakStmt
**************************************************************************
**
** BreakStmt breaks out of a while/for loop.
**
class BreakStmt : Stmt
{
new make(Loc loc) : super(loc, StmtId.breakStmt) {}
override Bool isExit() { false }
override Bool isDefiniteAssign(|Expr lhs->Bool| f) { false }
override Void print(AstWriter out)
{
out.w("break").nl
}
Stmt? loop // loop to break out of
}
**************************************************************************
** ContinueStmt
**************************************************************************
**
** ContinueStmt continues a while/for loop.
**
class ContinueStmt : Stmt
{
new make(Loc loc) : super(loc, StmtId.continueStmt) {}
override Bool isExit() { false }
override Bool isDefiniteAssign(|Expr lhs->Bool| f) { false }
override Void print(AstWriter out)
{
out.w("continue").nl
}
Stmt? loop // loop to continue
}
**************************************************************************
** TryStmt
**************************************************************************
**
** TryStmt models a try/catch/finally block
**
class TryStmt : Stmt
{
new make(Loc loc)
: super(loc, StmtId.tryStmt)
{
catches = Catch[,]
}
override Bool isExit()
{
if (!block.isExit) return false
return catches.all |Catch c->Bool| { c.block.isExit }
}
override Bool isDefiniteAssign(|Expr lhs->Bool| f)
{
if (finallyBlock != null && finallyBlock.isDefiniteAssign(f)) return true
if (!block.isDefiniteAssign(f)) return false
return catches.all |Catch c->Bool| { c.isDefiniteAssign(f) }
}
override Void walkChildren(Visitor v, VisitDepth depth)
{
block.walk(v, depth)
catches.each |Catch c| { c.block.walk(v, depth) }
if (finallyBlock != null)
{
v.enterFinally(this)
finallyBlock.walk(v, depth)
v.exitFinally(this)
}
}
override Void print(AstWriter out)
{
out.w("try").nl
block.print(out)
catches.each |Catch c| { c.print(out) }
if (finallyBlock != null)
{
out.w("finally").nl
finallyBlock.print(out)
}
}
Expr? exception // expression which leaves exception on stack
Block? block // body of try block
Catch[] catches // list of catch clauses
Block? finallyBlock // body of finally block or null
}
**
** Catch models a single catch clause of a TryStmt
**
class Catch : Node
{
new make(Loc loc)
: super(loc)
{
}
Bool isDefiniteAssign(|Expr lhs->Bool| f)
{
if (block.stmts.last?.id === StmtId.throwStmt) return true
return block.isDefiniteAssign(f)
}
override Void print(AstWriter out)
{
out.w("catch")
if (errType != null) out.w("($errType $errVariable)")
out.nl
block.print(out)
}
TypeRef? errType // Err type to catch or null for catch-all
Str? errVariable // name of err local variable
Block? block // body of catch block
Int start // start offset generated in CodeAsm
Int end // end offset generated in CodeAsm
}
**************************************************************************
** SwitchStmt
**************************************************************************
**
** SwitchStmt models a switch and its case and default block
**
class SwitchStmt : Stmt
{
new make(Loc loc, Expr condition)
: super(loc, StmtId.switchStmt)
{
this.condition = condition
this.cases = Case[,]
}
override Bool isDefiniteAssign(|Expr lhs->Bool| f)
{
if (defaultBlock == null) return false
if (!defaultBlock.isDefiniteAssign(f)) return false
return cases.all |Case c->Bool| { c.block.isDefiniteAssign(f) }
}
override Bool isExit()
{
if (defaultBlock == null) return false
if (!defaultBlock.isExit) return false
return cases.all |Case c->Bool| { c.block.isExit }
}
override Void walkChildren(Visitor v, VisitDepth depth)
{
condition = walkExpr(v, depth, condition)
cases.each |Case c| { c.walk(v, depth) }
if (defaultBlock != null) defaultBlock.walk(v, depth)
}
override Void print(AstWriter out)
{
out.w("switch ($condition)").nl
out.w("{").nl
out.indent
cases.each |Case c| { c.print(out) }
if (defaultBlock != null)
{
out.w("default:").nl
out.indent
defaultBlock.printOpt(out, false)
out.unindent
}
out.unindent
out.w("}").nl
}
Expr condition // test expression
Case[] cases // list of case blocks
Block? defaultBlock // default block (or null)
Bool isTableswitch // just for testing
}
**
** Case models a single case block of a SwitchStmt
**
class Case : Node
{
new make(Loc loc)
: super(loc)
{
cases = Expr[,]
}
Void walk(Visitor v, VisitDepth depth)
{
if (depth === VisitDepth.expr)
cases = Expr.walkExprs(v, cases)
block.walk(v, depth)
}
override Void print(AstWriter out)
{
cases.each |Expr c| { out.w("case $c:").nl }
out.indent
if (block != null) block.printOpt(out, false)
out.unindent
}
Expr[] cases // list of case target (literal expressions)
Block? block // code to run for case
Int startOffset // start offset for CodeAsm
}
**************************************************************************
** StmtId
**************************************************************************
enum class StmtId
{
nop,
expr,
localDef,
ifStmt,
returnStmt,
throwStmt,
forStmt,
whileStmt,
breakStmt,
continueStmt,
tryStmt,
switchStmt
}