/* $Header$ */
/*
* Copyright © 1988-2004 Keith Packard and Bart Massey.
* All Rights Reserved. See the file COPYING in this directory
* for licensing information.
*/
#include "nickle.h"
#include "gram.h"
#undef DEBUG
SymbolPtr CompileNamespace (ExprPtr);
static void
ObjMark (void *object)
{
ObjPtr obj = object;
InstPtr inst;
int i;
MemReference (obj->nonLocal);
inst = ObjCode (obj, 0);
for (i = 0; i < obj->used; i++, inst++)
{
switch (inst->base.opCode) {
case OpGlobal:
case OpGlobalRef:
case OpGlobalRefStore:
case OpTagGlobal:
MemReference (inst->box.box);
break;
case OpBuildStruct:
MemReference (inst->structs.structs);
break;
case OpBuildArrayInd:
case OpBuildArray:
MemReference (inst->array.type);
break;
case OpBuildHash:
MemReference (inst->hash.type);
break;
case OpConst:
MemReference (inst->constant.constant);
break;
case OpObj:
MemReference (inst->code.code);
break;
case OpFork:
MemReference (inst->obj.obj);
break;
case OpCatch:
MemReference (inst->catch.exception);
break;
case OpRaise:
MemReference (inst->raise.exception);
break;
case OpFarJump:
MemReference (inst->farJump.farJump);
break;
default:
break;
}
}
for (i = 0; i < obj->used_stat; i++)
MemReference (ObjStat (obj, i)->stat);
}
DataType ObjType = { ObjMark, 0, "ObjType" };
static ObjPtr
NewObj (int size, int size_stat)
{
ENTER ();
ObjPtr obj;
obj = ALLOCATE (&ObjType,
sizeof (Obj) +
size * sizeof (Inst) +
size_stat * sizeof (Stat));
obj->size = size;
obj->used = 0;
obj->size_stat = size_stat;
obj->used_stat = 0;
obj->error = False;
obj->nonLocal = 0;
RETURN (obj);
}
#define OBJ_INCR 32
#define OBJ_STAT_INCR 16
static ObjPtr
AddInst (ObjPtr obj, ExprPtr stat)
{
ENTER ();
ObjPtr nobj;
int need_stat = 1;
if (obj->used_stat && ObjStat(obj, obj->used_stat - 1)->stat == stat)
need_stat = 0;
if (obj->used == obj->size || obj->used_stat + need_stat > obj->size_stat)
{
int nsize = obj->size, nsize_stat = obj->size_stat;
if (obj->used == obj->size)
nsize = obj->size + OBJ_INCR;
if (obj->used_stat + need_stat > obj->size_stat)
nsize_stat = obj->size_stat + OBJ_STAT_INCR;
nobj = NewObj (nsize, nsize_stat);
memcpy (ObjCode (nobj, 0), ObjCode (obj, 0), obj->used * sizeof (Inst));
memcpy (ObjStat (nobj, 0), ObjStat (obj, 0), obj->used_stat * sizeof (Stat));
nobj->used = obj->used;
nobj->used_stat = obj->used_stat;
nobj->error = obj->error;
nobj->nonLocal = obj->nonLocal;
obj = nobj;
}
if (need_stat)
{
StatPtr s = ObjStat (obj, obj->used_stat);
s->inst = obj->used;
s->stat = stat;
obj->used_stat++;
}
obj->used++;
RETURN (obj);
}
static ObjPtr
AppendObj (ObjPtr first, ObjPtr last)
{
int i;
InstPtr firsti, lasti;
for (i = 0; i < last->used; i++)
{
lasti = ObjCode (last, i);
first = AddInst (first, ObjStatement (last, lasti));
firsti = ObjCode (first, ObjLast (first));
*firsti = *lasti;
}
if (last->error)
first->error = True;
return first;
}
ExprPtr
ObjStatement (ObjPtr obj, InstPtr inst)
{
int i = inst - ObjCode(obj, 0);
int low = 0, high = obj->used_stat - 1;
while (low < high - 1)
{
int mid = (low + high) >> 1;
if (ObjStat(obj,mid)->inst <= i)
low = mid;
else
high = mid - 1;
}
while (low <= high)
{
StatPtr s = ObjStat(obj, high);
if (s->inst <= i)
return s->stat;
high--;
}
return 0;
}
static void
ResetInst (ObjPtr obj, int i)
{
obj->used = i;
while (obj->used_stat && ObjStat(obj, obj->used_stat - 1)->inst > i)
obj->used_stat--;
}
/*
* Set branch offsets to zero so that CompileIsReachable can
* use them before the real values get filled in. This is correct
* because the reachability targets are always in nested blocks which
* can never be the target for this instruction
*/
#define NewInst(_o,_op,_i,_stat) \
{\
InstPtr __inst__; \
(_o) = AddInst(_o, _stat); \
(_i) = ObjLast(_o); \
__inst__ = ObjCode (_o, _i); \
__inst__->base.opCode = (_op); \
__inst__->base.flags = 0; \
__inst__->branch.offset = 0; \
}
#define BuildInst(_o,_op,_inst,_stat) \
{\
(_o) = AddInst (_o, _stat); \
(_inst) = ObjCode(_o, ObjLast(_o)); \
(_inst)->base.opCode = (_op); \
(_inst)->base.flags = 0; \
(_inst)->branch.offset = 0; \
}
#define SetFlag(_o,_f) ((_o)->used ? (ObjCode((_o), \
ObjLast(_o))->base.flags |= (_f)) \
: 0)
#define SetPush(_o) SetFlag(_o,InstPush)
#define SetAInit(_o) SetFlag(_o,InstAInit)
/*
* Select the correct code body depending on whether
* we're compiling a static initializer
*/
#define CodeBody(c) ((c)->func.inStaticInit ? &(c)->func.staticInit : &(c)->func.body)
typedef enum _tail { TailNever, TailVoid, TailAlways } Tail;
ObjPtr CompileLvalue (ObjPtr obj, ExprPtr expr, ExprPtr stat, CodePtr code, Bool createIfNecessary, Bool assign, Bool initialize, Bool amper, Bool auto_reference);
ObjPtr CompileBinOp (ObjPtr obj, ExprPtr expr, BinaryOp op, ExprPtr stat, CodePtr code);
ObjPtr CompileBinFunc (ObjPtr obj, ExprPtr expr, BinaryFunc func, ExprPtr stat, CodePtr code, char *name);
ObjPtr CompileUnOp (ObjPtr obj, ExprPtr expr, UnaryOp op, ExprPtr stat, CodePtr code);
ObjPtr CompileUnFunc (ObjPtr obj, ExprPtr expr, UnaryFunc func, ExprPtr stat, CodePtr code, char *name);
ObjPtr CompileAssign (ObjPtr obj, ExprPtr expr, Bool initialize, ExprPtr stat, CodePtr code);
ObjPtr CompileAssignOp (ObjPtr obj, ExprPtr expr, BinaryOp op, ExprPtr stat, CodePtr code);
ObjPtr CompileAssignFunc (ObjPtr obj, ExprPtr expr, BinaryFunc func, ExprPtr stat, CodePtr code, char *name);
ObjPtr CompileArrayIndex (ObjPtr obj, ExprPtr expr, TypePtr indexType, ExprPtr stat, CodePtr code, int *ndimp);
ObjPtr CompileCall (ObjPtr obj, ExprPtr expr, Tail tail, ExprPtr stat, CodePtr code, Bool auto_reference);
ObjPtr _CompileExpr (ObjPtr obj, ExprPtr expr, Bool evaluate, ExprPtr stat, CodePtr code);
ObjPtr _CompileBoolExpr (ObjPtr obj, ExprPtr expr, Bool evaluate, ExprPtr stat, CodePtr code);
void CompilePatchLoop (ObjPtr obj, int start,
int continue_offset,
int break_offset,
int catch_offset);
ObjPtr _CompileStat (ObjPtr obj, ExprPtr expr, Bool last, CodePtr code);
ObjPtr CompileFunc (ObjPtr obj, CodePtr code, ExprPtr stat, CodePtr previous, NonLocalPtr nonLocal);
ObjPtr CompileDecl (ObjPtr obj, ExprPtr decls, Bool evaluate, ExprPtr stat, CodePtr code);
ObjPtr CompileFuncCode (CodePtr code,
ExprPtr stat,
CodePtr previous,
NonLocalPtr nonLocal);
void CompileError (ObjPtr obj, ExprPtr stat, char *s, ...);
static Bool CompileIsReachable (ObjPtr obj, int i);
static ObjPtr
CompileArrayDimValue (ObjPtr obj, TypePtr type, Bool lvalue, ExprPtr stat, CodePtr code);
static ObjPtr
CompileType (ObjPtr obj, ExprPtr decls, TypePtr type, ExprPtr stat, CodePtr code);
/*
* Set storage information for new symbols
*/
static void
CompileStorage (ObjPtr obj, ExprPtr stat, SymbolPtr symbol, CodePtr code)
{
ENTER ();
if (!symbol)
obj->error = True;
/*
* For symbols hanging from a frame (statics, locals and args),
* locate the frame and set their element value
*/
else if (ClassFrame(symbol->symbol.class))
{
switch (symbol->symbol.class) {
case class_static:
symbol->local.element = AddBoxType (&code->func.statics,
symbol->symbol.type);
symbol->local.staticScope = True;
symbol->local.code = code;
break;
case class_arg:
case class_auto:
symbol->local.element = AddBoxType (&CodeBody (code)->dynamics,
symbol->symbol.type);
symbol->local.staticScope = code->func.inStaticInit;
symbol->local.code = code;
break;
default:
break;
}
}
EXIT ();
}
/*
* Set storage information for array dimensions
*/
static void
CompileDimensionStorage (ObjPtr obj, Class class, TypePtr type, CodePtr code)
{
ENTER ();
if (class == class_typedef)
class = code ? class_auto : class_global;
switch (class) {
case class_global:
case class_const:
type->array.storage = DimStorageGlobal;
type->array.u.global = NewBox (True, False, 1, typeArrayInt);
break;
case class_static:
type->array.storage = DimStorageStatic;
type->array.u.frame.element = AddBoxType (&code->func.statics,
typeArrayInt);
type->array.u.frame.staticScope = True;
type->array.u.frame.code = code;
break;
case class_arg:
case class_auto:
type->array.storage = DimStorageAuto;
type->array.u.frame.element = AddBoxType (&CodeBody (code)->dynamics,
typeArrayInt);
type->array.u.frame.staticScope = code->func.inStaticInit;
type->array.u.frame.code = code;
break;
default:
break;
}
EXIT ();
}
/*
* Make sure a symbol is valid
*/
static SymbolPtr
CompileCheckSymbol (ObjPtr obj, ExprPtr stat, ExprPtr name, CodePtr code,
int *depth, Bool createIfNecessary)
{
ENTER ();
SymbolPtr s;
int d;
CodePtr c;
s = name->atom.symbol;
if (!s)
{
if (name->atom.atom == AtomId ("[]"))
CompileError (obj, stat, "Using [] outside of comprehension scope");
else
CompileError (obj, stat, "No visible symbol \"%A\" in scope%s",
name->atom.atom,
name->atom.privateFound ? " (found non-public symbol)" : "");
RETURN (0);
}
/*
* For args and autos, make sure we're not compiling a static
* initializer, in that case, the locals will not be in dynamic
* namespace
*/
d = 0;
switch (s->symbol.class) {
case class_static:
case class_arg:
case class_auto:
/*
* See if the name is above a global init scope
*/
for (c = code; c; c = c->base.previous)
if (c->func.inGlobalInit)
break;
for (; c; c = c->base.previous)
if (c == s->local.code)
break;
if (c)
{
CompileError (obj, stat, "\"%A\" not in global initializer scope",
name->atom.atom);
break;
}
c = s->local.code;
if (!c)
{
CompileError (obj, stat, "Class %C invalid at global scope",
s->symbol.class);
break;
}
/*
* Ensure the dynamic scope will exist
*/
if (c->func.inStaticInit && !s->local.staticScope)
{
CompileError (obj, stat, "\"%A\" not in static initializer scope",
name->atom.atom);
break;
}
/*
* Compute the static link offset
*/
d = 0;
for (c = code; c && c != s->local.code; c = c->base.previous)
d++;
break;
default:
break;
}
*depth = d;
RETURN (s);
}
void
CompileError (ObjPtr obj, ExprPtr stat, char *s, ...)
{
va_list args;
FilePrintf (FileStderr, "-> ");
PrettyStat (FileStderr, stat, False);
if (stat->base.file)
FilePrintf (FileStderr, "%A:%d: ", stat->base.file, stat->base.line);
va_start (args, s);
FileVPrintf (FileStderr, s, args);
va_end (args);
FilePrintf (FileStderr, "\n");
obj->error = True;
}
static TypePtr
CompileRefType (ObjPtr obj, ExprPtr expr, TypePtr t)
{
t = TypeCanon (t);
if (!t) {
CompileError(obj, expr, "reference to incomplete type");
return 0;
}
if (t->base.tag == type_ref && !t->ref.pointer)
return t->ref.ref;
return 0;
}
static TypePtr
CompileIndexType (ExprPtr expr)
{
TypePtr type = expr->base.type, indexType = typePoly;
if (type)
{
switch (type->base.tag) {
case type_array:
indexType = typePrim[rep_integer];
break;
case type_hash:
indexType = type->hash.keyType;
break;
default:
break;
}
}
return indexType;
}
/*
* Compile the left side of an assignment statement.
* The result is a 'ref' left in the value register
* The type is the type of the refered value, not a reference
* to that type.
*/
ObjPtr
CompileLvalue (ObjPtr obj, ExprPtr expr, ExprPtr stat, CodePtr code,
Bool createIfNecessary, Bool assign, Bool initialize,
Bool amper, Bool auto_reference)
{
ENTER ();
InstPtr inst = 0;
SymbolPtr s;
int depth;
int ndim;
TypePtr t;
Bool flipTypes = False;
switch (expr->base.tag) {
case VAR:
obj = CompileDecl (obj, expr, False, stat, code);
{
DeclListPtr decl;
s = 0;
for (decl = expr->decl.decl; decl; decl = decl->next)
s = decl->symbol;
}
/* the symbol was compiled in this frame */
depth = 0;
goto isName;
case NAME:
s = CompileCheckSymbol (obj, stat, expr, code,
&depth, createIfNecessary);
isName:
if (!s)
{
expr->base.type = typePoly;
break;
}
inst = 0;
switch (s->symbol.class) {
case class_const:
if (!initialize) {
CompileError (obj, stat, "Attempt to assign to static variable \"%A\"",
expr->atom.atom);
expr->base.type = typePoly;
break;
}
/* fall through ... */
case class_global:
BuildInst (obj, OpGlobalRef, inst, stat);
inst->box.box = s->global.value;
break;
case class_static:
BuildInst (obj, OpStaticRef, inst, stat);
inst->frame.staticLink = depth;
inst->frame.element = s->local.element;
break;
case class_arg:
case class_auto:
BuildInst (obj, OpLocalRef, inst, stat);
inst->frame.staticLink = depth;
inst->frame.element = s->local.element;
break;
default:
CompileError (obj, stat, "Invalid use of %C \"%A\"",
s->symbol.class, expr->atom.atom);
expr->base.type = typePoly;
break;
}
if (!inst)
break;
expr->base.type = s->symbol.type;
flipTypes = True;
break;
case AMPER:
obj = CompileLvalue (obj, expr->tree.left, stat, code,
createIfNecessary, assign, initialize,
True, auto_reference);
expr->base.type = expr->tree.left->base.type;
break;
case COLONCOLON:
obj = CompileLvalue (obj, expr->tree.right, stat, code, False, assign, initialize,
amper, auto_reference);
expr->base.type = expr->tree.right->base.type;
amper = False; /* has been dealt with in nested call */
break;
case DOT:
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
expr->base.type = TypeCombineStruct (expr->tree.left->base.type,
expr->base.tag,
expr->tree.right->atom.atom);
if (!expr->base.type)
{
CompileError (obj, stat, "Object left of '.' is not a struct or union containing \"%A\"",
expr->tree.right->atom.atom);
expr->base.type = typePoly;
break;
}
BuildInst (obj, OpDotRef, inst, stat);
inst->atom.atom = expr->tree.right->atom.atom;
flipTypes = True;
break;
case ARROW:
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
expr->base.type = TypeCombineStruct (expr->tree.left->base.type,
expr->base.tag,
expr->tree.right->atom.atom);
if (!expr->base.type)
{
CompileError (obj, stat, "Object left of '->' is not a struct or union containing \"%A\"",
expr->tree.right->atom.atom);
expr->base.type = typePoly;
break;
}
BuildInst (obj, OpArrowRef, inst, stat);
inst->atom.atom = expr->tree.right->atom.atom;
break;
case OS:
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
obj = CompileArrayIndex (obj, expr->tree.right,
CompileIndexType (expr->tree.left),
stat, code, &ndim);
if (!ndim)
{
expr->base.type = typePoly;
break;
}
expr->base.type = TypeCombineArray (expr->tree.left->base.type,
ndim,
True);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible type '%T', for %d dimension operation",
expr->tree.left->base.type, ndim);
expr->base.type = typePoly;
break;
}
BuildInst (obj, OpArrayRef, inst, stat);
inst->ints.value = ndim;
flipTypes = True;
break;
case STAR:
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
expr->base.type = TypeCombineUnary (expr->tree.left->base.type, expr->base.tag);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible type, value '%T', for * operation",
expr->tree.left->base.type);
expr->base.type = typePoly;
break;
}
break;
case OP:
if (auto_reference)
{
obj = CompileCall (obj, expr, TailNever, stat, code, True);
break;
}
default:
if (auto_reference)
{
obj = _CompileExpr (obj, expr, True, stat, code);
BuildInst (obj, OpUnFunc, inst, stat);
inst->unfunc.func = do_reference;
}
else
{
CompileError (obj, stat, "Invalid lvalue");
expr->base.type = typePoly;
}
break;
}
if (flipTypes)
{
t = CompileRefType (obj, expr, expr->base.type);
if (amper)
{
if (t)
{
/*
* reference to a reference type; that
* means just reference the variable itself, but
* switch the expression type to '*foo' instead of
* '&foo'
*/
expr->base.type = NewTypeRef (t, True);
if (assign)
inst->base.opCode++;
amper = False;
}
}
else
{
if (t)
{
/*
* access to a reference type; that means
* fetch the value of the reference
*/
inst->base.opCode--;
expr->base.type = t;
}
else
{
/*
* access to a non-reference type; that
* means just reference the variable itself and
* leave the type alone
*/
if (assign)
inst->base.opCode++;
}
}
}
/*
* Handle any remaining & from above
*/
if (amper)
{
if (auto_reference)
{
BuildInst (obj, OpUnFunc, inst, stat);
inst->unfunc.func = do_reference;
expr->base.type = NewTypeRef (expr->base.type, True);
}
else
{
/*
* reference to a non-reference type. Error
*/
CompileError (obj, stat, "Object right of '&' is not of ref type");
expr->base.type = typePoly;
}
}
assert (expr->base.type);
RETURN (obj);
}
static void
_CompileCheckException (ObjPtr obj, ExprPtr stat)
{
SymbolPtr except = CheckStandardException ();
if (except)
CompileError (obj, stat, "Exception \"%A\" raised during compilation",
except->symbol.name);
}
/*
* Compile a binary operator --
* compile the left side, push, compile the right and then
* add the operator
*/
ObjPtr
CompileBinOp (ObjPtr obj, ExprPtr expr, BinaryOp op, ExprPtr stat, CodePtr code)
{
ENTER ();
InstPtr inst;
int left, right;
left = obj->used;
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
SetPush (obj);
right = obj->used;
obj = _CompileExpr (obj, expr->tree.right, True, stat, code);
expr->base.type = TypeCombineBinary (expr->tree.left->base.type,
expr->base.tag,
expr->tree.right->base.type);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible types, left '%T', right '%T', for %O operation",
expr->tree.left->base.type,
expr->tree.right->base.type,
op);
expr->base.type = typePoly;
}
else if (obj->used == left + 2 &&
ObjCode (obj, left)->base.opCode == OpConst &&
ObjCode (obj, right)->base.opCode == OpConst &&
!signalException)
{
inst = ObjCode (obj, left);
inst->constant.constant = BinaryOperate (ObjCode(obj, left)->constant.constant,
ObjCode(obj, right)->constant.constant,
op);
_CompileCheckException (obj, stat);
inst->base.flags &= ~InstPush;
obj->used = left + 1;
}
else
{
BuildInst (obj, OpBinOp, inst, stat);
inst->binop.op = op;
}
RETURN (obj);
}
ObjPtr
CompileBinFunc (ObjPtr obj, ExprPtr expr, BinaryFunc func, ExprPtr stat, CodePtr code, char *name)
{
ENTER ();
InstPtr inst;
int left, right;
left = obj->used;
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
SetPush (obj);
right = obj->used;
obj = _CompileExpr (obj, expr->tree.right, True, stat, code);
expr->base.type = TypeCombineBinary (expr->tree.left->base.type,
expr->base.tag,
expr->tree.right->base.type);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible types, left '%T', right '%T', for %s operation",
expr->tree.left->base.type,
expr->tree.right->base.type,
name);
expr->base.type = typePoly;
}
else if (obj->used == left + 2 &&
ObjCode (obj, left)->base.opCode == OpConst &&
ObjCode (obj, right)->base.opCode == OpConst &&
!signalException)
{
inst = ObjCode (obj, left);
inst->constant.constant = (*func) (ObjCode(obj, left)->constant.constant,
ObjCode(obj, right)->constant.constant);
_CompileCheckException (obj, stat);
inst->base.flags &= ~InstPush;
obj->used = left + 1;
}
else
{
BuildInst (obj, OpBinFunc, inst, stat);
inst->binfunc.func = func;
}
RETURN (obj);
}
/*
* Unaries are easy --
* compile the operand and add the operator
*/
ObjPtr
CompileUnOp (ObjPtr obj, ExprPtr expr, UnaryOp op, ExprPtr stat, CodePtr code)
{
ENTER ();
InstPtr inst;
ExprPtr down;
int d;
if (expr->tree.right)
down = expr->tree.right;
else
down = expr->tree.left;
d = obj->used;
obj = _CompileExpr (obj, down, True, stat, code);
expr->base.type = TypeCombineUnary (down->base.type, expr->base.tag);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible type, value '%T', for %U operation",
down->base.type, op);
expr->base.type = typePoly;
}
else if (obj->used == d + 1 &&
ObjCode (obj, d)->base.opCode == OpConst &&
!signalException)
{
inst = ObjCode (obj, d);
inst->constant.constant = UnaryOperate (ObjCode(obj, d)->constant.constant,
op);
_CompileCheckException (obj, stat);
inst->base.flags &= ~InstPush;
obj->used = d + 1;
}
else
{
BuildInst (obj, OpUnOp, inst, stat);
inst->unop.op = op;
}
RETURN (obj);
}
ObjPtr
CompileUnFunc (ObjPtr obj, ExprPtr expr, UnaryFunc func, ExprPtr stat, CodePtr code, char *name)
{
ENTER ();
InstPtr inst;
ExprPtr down;
int d;
if (expr->tree.right)
down = expr->tree.right;
else
down = expr->tree.left;
d = obj->used;
obj = _CompileExpr (obj, down, True, stat, code);
expr->base.type = TypeCombineUnary (down->base.type, expr->base.tag);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible type, value '%T', for %s operation",
down->base.type, name);
expr->base.type = typePoly;
}
else if (obj->used == d + 1 &&
ObjCode (obj, d)->base.opCode == OpConst &&
!signalException)
{
inst = ObjCode (obj, d);
inst->constant.constant = (*func) (ObjCode(obj, d)->constant.constant);
_CompileCheckException (obj, stat);
inst->base.flags &= ~InstPush;
obj->used = d + 1;
}
else
{
BuildInst (obj, OpUnFunc, inst, stat);
inst->unfunc.func = func;
}
RETURN (obj);
}
/*
* Assignement --
* compile the value, build a ref for the LHS and add the operator
*/
ObjPtr
CompileAssign (ObjPtr obj, ExprPtr expr, Bool initialize, ExprPtr stat, CodePtr code)
{
ENTER ();
InstPtr inst;
obj = CompileLvalue (obj, expr->tree.left, stat, code, True, True, initialize,
False, False);
SetPush (obj);
obj = _CompileExpr (obj, expr->tree.right, True, stat, code);
expr->base.type = TypeCombineBinary (expr->tree.left->base.type,
expr->base.tag,
expr->tree.right->base.type);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible types, left '%T', right '%T', for = operation",
expr->tree.left->base.type,
expr->tree.right->base.type);
expr->base.type = typePoly;
}
BuildInst (obj, OpAssign, inst, stat);
inst->assign.initialize = initialize;
RETURN (obj);
}
ObjPtr
CompileAssignOp (ObjPtr obj, ExprPtr expr, BinaryOp op, ExprPtr stat, CodePtr code)
{
ENTER ();
InstPtr inst;
obj = CompileLvalue (obj, expr->tree.left, stat, code, False, False, False,
False, False);
SetPush (obj);
BuildInst (obj, OpFetch, inst, stat);
SetPush (obj);
obj = _CompileExpr (obj, expr->tree.right, True, stat, code);
expr->base.type = TypeCombineBinary (expr->tree.left->base.type,
expr->base.tag,
expr->tree.right->base.type);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible types, left '%T', right '%T', for %O= operation",
expr->tree.left->base.type,
expr->tree.right->base.type,
op);
expr->base.type = typePoly;
}
BuildInst (obj, OpAssignOp, inst, stat);
inst->binop.op = op;
RETURN (obj);
}
ObjPtr
CompileAssignFunc (ObjPtr obj, ExprPtr expr, BinaryFunc func, ExprPtr stat, CodePtr code, char *name)
{
ENTER ();
InstPtr inst;
obj = CompileLvalue (obj, expr->tree.left, stat, code, False, False, False,
False, False);
SetPush (obj);
BuildInst (obj, OpFetch, inst, stat);
SetPush (obj);
obj = _CompileExpr (obj, expr->tree.right, True, stat, code);
expr->base.type = TypeCombineBinary (expr->tree.left->base.type,
expr->base.tag,
expr->tree.right->base.type);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible types, left '%T', right '%T', for %s= operation",
expr->tree.left->base.type,
expr->tree.right->base.type,
name);
expr->base.type = typePoly;
}
BuildInst (obj, OpAssignFunc, inst, stat);
inst->binfunc.func = func;
RETURN (obj);
}
static ObjPtr
CompileArgs (ObjPtr obj, int *argcp, Bool *varactualp, ExprPtr arg, Bool pushValue, ExprPtr stat, CodePtr code)
{
ENTER ();
int argc;
argc = 0;
*varactualp = False;
while (arg)
{
if (pushValue)
SetPush (obj);
if (arg->tree.left->base.tag == DOTDOTDOT)
{
InstPtr inst;
obj = _CompileExpr (obj, arg->tree.left->tree.left, True, stat, code);
BuildInst (obj, OpVarActual, inst, stat);
*varactualp = True;
}
else
{
obj = _CompileExpr (obj, arg->tree.left, True, stat, code);
}
arg = arg->tree.right;
pushValue = True;
argc++;
}
*argcp = argc;
RETURN(obj);
}
/*
* Typecheck function object and arguments
*/
static Bool
CompileTypecheckArgs (ObjPtr obj,
Type *type,
ExprPtr args,
int argc,
ExprPtr stat)
{
ENTER ();
Bool ret = True;
ArgType *argt;
ExprPtr arg;
Type *func_type;
Type *actual_type;
int i;
Bool varactual;
func_type = TypeCombineFunction (type);
if (!func_type)
{
CompileError (obj, stat, "Incompatible type, value '%T', for call",
type);
EXIT ();
return False;
}
if (func_type->base.tag == type_func)
{
argt = func_type->func.args;
arg = args;
i = 0;
varactual = False;
while ((arg && !varactual) || (argt && !argt->varargs))
{
if (!argt)
{
CompileError (obj, stat, "Too many parameters for function type '%T'", func_type);
ret = False;
break;
}
if (!arg)
{
CompileError (obj, stat, "Too few parameters for function type '%T'", func_type);
ret = False;
break;
}
varactual = arg->tree.left->base.tag == DOTDOTDOT;
if (varactual)
actual_type = TypeCombineArray (arg->tree.left->tree.left->base.type, 1, False);
else
actual_type = arg->tree.left->base.type;
if (!TypeIsOrdered (argt->type, actual_type))
{
CompileError (obj, stat, "Incompatible types, formal '%T', actual '%T', for argument %d",
argt->type, arg->tree.left->base.type, i);
ret = False;
}
i++;
if (!argt->varargs)
argt = argt->next;
if (arg && (!varactual || !argt))
arg = arg->tree.right;
}
}
EXIT ();
return ret;
}
static void
MarkNonLocal (void *object)
{
NonLocal *nl = object;
MemReference (nl->prev);
}
DataType NonLocalType = { MarkNonLocal, 0, "NonLocalType" };
static NonLocal *
NewNonLocal (NonLocal *prev, NonLocalKind kind, int target)
{
ENTER();
NonLocal *nl;
nl = ALLOCATE (&NonLocalType, sizeof (NonLocal));
nl->prev = prev;
nl->kind = kind;
nl->target = target;
RETURN (nl);
}
/*
* Compile a function call --
*
* + compile the code that generates a function object
* + compile the args, pushing value on the stack
* + Typecheck the arguments. Must be done here so that
* the type of the function is available
* + Add the OpCall
* + Add an OpNoop in case the result must be pushed; otherwise there's
* no place to hang a push bit
*/
ObjPtr
CompileCall (ObjPtr obj, ExprPtr expr, Tail tail, ExprPtr stat, CodePtr code, Bool auto_reference)
{
ENTER ();
InstPtr inst;
int argc;
Bool varactual;
TypePtr t;
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
obj = CompileArgs (obj, &argc, &varactual, expr->tree.right, True, stat, code);
if (!CompileTypecheckArgs (obj, expr->tree.left->base.type,
expr->tree.right, argc, stat))
{
expr->base.type = typePoly;
RETURN (obj);
}
expr->base.type = TypeCombineReturn (expr->tree.left->base.type);
t = CompileRefType (obj, expr, expr->base.type);
if ((t && !auto_reference) || (!t && auto_reference))
tail = TailNever;
if ((tail == TailAlways &&
!TypePoly (expr->base.type) &&
TypeIsSupertype (code->base.type, expr->base.type)) ||
(tail == TailVoid &&
TypeCanon (expr->base.type) == typePrim[rep_void]))
{
BuildInst (obj, OpTailCall, inst, stat);
inst->ints.value = varactual ? -argc : argc;
}
else
{
BuildInst (obj, OpCall, inst, stat);
inst->ints.value = varactual ? -argc : argc;
if (t && !auto_reference)
{
BuildInst (obj, OpUnFunc, inst, stat);
inst->unfunc.func = Dereference;
expr->base.type = t;
}
else if (!t && auto_reference)
{
BuildInst (obj, OpUnFunc, inst, stat);
inst->unfunc.func = do_reference;
/*
* this is called from CompileLvalue which
* must return a value of the type pointed to, not the
* type of the object itself, so don't create a pointer
* type here. Someday we'll figure all of this out...
*/
/* expr->base.type = NewTypeRef (expr->base.type, True); */
}
else
BuildInst (obj, OpNoop, inst, stat);
}
RETURN (obj);
}
/*
* Compile an exception --
*
* + lookup the name
* + compile the args, pushing
* + typecheck the args
* + Add the OpRaise
*/
static ObjPtr
CompileRaise (ObjPtr obj, ExprPtr expr, ExprPtr stat, CodePtr code)
{
ENTER();
int argc;
ExprPtr name;
SymbolPtr sym;
InstPtr inst;
Bool varactual;
if (expr->tree.left->base.tag == COLONCOLON)
name = expr->tree.left->tree.right;
else
name = expr->tree.left;
sym = name->atom.symbol;
if (!sym)
{
CompileError (obj, stat, "No exception '%A' in scope",
name->atom.atom);
RETURN (obj);
}
if (sym->symbol.class != class_exception)
{
CompileError (obj, stat, "'%A' is not an exception",
name->atom.atom);
RETURN (obj);
}
obj = CompileArgs (obj, &argc, &varactual, expr->tree.right, False, stat, code);
if (!CompileTypecheckArgs (obj, sym->symbol.type, expr->tree.right, argc, stat))
RETURN(obj);
expr->base.type = typePoly;
BuildInst (obj, OpRaise, inst, stat);
inst->raise.argc = varactual ? -argc : argc;
inst->raise.exception = sym;
RETURN (obj);
}
/*
* Compile a twixt --
*
* twixt (enter; leave) body
*
* enter:
* enter
* OpEnterDone
* OpTwixt enter: leave:
* body
* OpTwixtDone
* leave:
* leave
* OpLeaveDone
*
*/
static ObjPtr
CompileTwixt (ObjPtr obj, ExprPtr expr, ExprPtr stat, CodePtr code)
{
ENTER ();
int enter_inst, twixt_inst;
InstPtr inst;
enter_inst = obj->used;
/* Compile enter expression */
if (expr->tree.left->tree.left)
obj = _CompileExpr (obj, expr->tree.left->tree.left, True, stat, code);
BuildInst (obj, OpEnterDone, inst, stat);
/* here's where the twixt instruction goes */
NewInst (obj, OpTwixt, twixt_inst, stat);
obj->nonLocal = NewNonLocal (obj->nonLocal, NonLocalTwixt, 0);
/* Compile the body */
obj = _CompileStat (obj, expr->tree.right->tree.left, False, code);
obj->nonLocal = obj->nonLocal->prev;
BuildInst (obj, OpTwixtDone, inst, stat);
/* finish the twixt instruction */
inst = ObjCode (obj, twixt_inst);
inst->twixt.enter = enter_inst - twixt_inst;
inst->twixt.leave = obj->used - twixt_inst;
/* Compile leave expression */
if (expr->tree.left->tree.right)
obj = _CompileExpr (obj, expr->tree.left->tree.right, False, stat, code);
BuildInst (obj, OpLeaveDone, inst, stat);
RETURN (obj);
}
/*
* Compile an array index expression tree
*/
ObjPtr
CompileArrayIndex (ObjPtr obj, ExprPtr expr, TypePtr indexType,
ExprPtr stat, CodePtr code, int *ndimp)
{
ENTER ();
int ndim;
ndim = 0;
while (expr)
{
SetPush (obj);
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
if (!TypeIsOrdered (indexType,
expr->tree.left->base.type))
{
CompileError (obj, stat, "Incompatible expression type '%T', for index %d type '%T'",
expr->tree.left->base.type, ndim, indexType);
break;
}
expr = expr->tree.right;
ndim++;
}
*ndimp = ndim;
RETURN (obj);
}
/*
* Return an expression that will build an
* initializer for a fully specified composite
* type
*/
/*
* Calculate the number of dimensions in an array by looking at
* the initializers
*/
static int
CompileCountInitDimensions (TypePtr type, ExprPtr expr)
{
int ndimMax, ndimSub, ndim;
switch (expr->base.tag) {
case ANONINIT:
type = TypeCanon (type);
if (type->base.tag == type_struct)
ndim = 0;
else
ndim = 1;
break;
case ARRAY:
expr = expr->tree.left;
ndimMax = 0;
while (expr)
{
if (expr->tree.left && expr->tree.left->base.tag != DOTDOTDOT)
{
ndimSub = CompileCountInitDimensions (type, expr->tree.left);
if (ndimSub < 0)
return ndimSub;
if (ndimMax && ndimSub != ndimMax)
return -1;
ndimMax = ndimSub;
}
expr = expr->tree.right;
}
ndim = ndimMax + 1;
break;
default:
ndim = 0;
break;
}
return ndim;
}
static int
CompileCountDeclDimensions (ExprPtr expr)
{
int ndim;
ndim = 0;
while (expr)
{
expr = expr->tree.right;
ndim++;
}
return ndim;
}
static int
CompileCountImplicitDimensions (ExprPtr expr)
{
switch (expr->base.tag) {
case ARRAY:
return 1 + CompileCountImplicitDimensions (expr->tree.left);
case ANONINIT:
return 0;
case COMMA:
return CompileCountImplicitDimensions (expr->tree.left);
default:
return 0;
}
}
static ObjPtr
CompileBuildArray (ObjPtr obj, ExprPtr expr, TypePtr type,
ExprPtr dim, int ndim,
ExprPtr stat, CodePtr code)
{
ENTER ();
InstPtr inst;
if (dim)
{
while (dim)
{
obj = _CompileExpr (obj, dim->tree.left, True, stat, code);
SetPush (obj);
dim = dim->tree.right;
}
BuildInst (obj, OpBuildArray, inst, stat);
}
else
{
obj = CompileArrayDimValue (obj, type, False, stat, code);
BuildInst (obj, OpBuildArrayInd, inst, stat);
}
inst->array.ndim = ndim;
inst->array.type = type->array.type;
inst->array.resizable = type->array.resizable;
RETURN (obj);
}
static Bool
CompileSizeDimensions (ExprPtr expr, int *dims, int ndims)
{
int dim;
if (!expr)
dim = 0;
else switch (expr->base.tag) {
case ARRAY:
dim = 0;
expr = expr->tree.left;
while (expr)
{
if (expr->tree.left->base.tag == DOTDOTDOT)
return False;
if (ndims != 1)
{
CompileSizeDimensions (expr->tree.left, dims + 1, ndims - 1);
if (dims[1])
dim++;
}
else
dim++;
expr = expr->tree.right;
}
break;
case COMP:
return False;
case ANONINIT:
dim = 0;
break;
default:
dim = 1;
if (expr->tree.left->base.tag == DOTDOTDOT)
return False;
if (ndims != 1)
CompileSizeDimensions (expr, dims + 1, ndims - 1);
break;
}
if (dim > *dims)
*dims = dim;
return True;
}
static ExprPtr
CompileImplicitArray (ObjPtr obj, ExprPtr stat, ExprPtr inits, int ndim)
{
ENTER ();
ExprPtr sub;
int *dims;
int n;
dims = AllocateTemp (ndim * sizeof (int));
memset (dims, '\0', ndim * sizeof (int));
if (!CompileSizeDimensions (inits, dims, ndim))
{
CompileError (obj, stat, "Implicit dimensioned array with variable initializers");
RETURN (0);
}
sub = 0;
for (n = ndim - 1; n >= 0; n--)
{
sub = NewExprTree (COMMA,
NewExprConst (TEN_NUM, NewInt (dims[n])),
sub);
}
RETURN(sub);
}
static ObjPtr
CompileArrayInit (ObjPtr obj, ExprPtr expr, Type *type,
ExprPtr stat, CodePtr code);
static ObjPtr
CompileHashInit (ObjPtr obj, ExprPtr expr, Type *type,
ExprPtr stat, CodePtr code);
static ObjPtr
CompileStructUnionInit (ObjPtr obj, ExprPtr expr, Type *type,
ExprPtr stat, CodePtr code);
static ExprPtr
CompileImplicitInit (Type *type);
static ObjPtr
CompileInit (ObjPtr obj, ExprPtr expr, Type *type,
ExprPtr stat, CodePtr code)
{
ENTER ();
type = TypeCanon (type);
if (!expr || expr->base.tag == ANONINIT)
{
switch (type->base.tag) {
case type_array:
obj = CompileArrayInit (obj, 0, type, stat, code);
break;
case type_hash:
obj = CompileHashInit (obj, 0, type, stat, code);
break;
case type_struct:
obj = CompileStructUnionInit (obj, 0, type, stat, code);
break;
case type_union:
default:
CompileError (obj, stat, "Invalid empty initializer , type '%T'", type);
break;
}
}
else switch (expr->base.tag) {
case ARRAY:
case COMP:
if (type->base.tag != type_array)
CompileError (obj, stat, "Array initializer type mismatch, type '%T'",
type);
else
obj = CompileArrayInit (obj, expr, type, stat, code);
break;
case HASH:
if (type->base.tag != type_hash)
CompileError (obj, stat, "Hash initializer type mismatch, type '%T'",
type);
else
obj = CompileHashInit (obj, expr, type, stat, code);
break;
case STRUCT:
if (type->base.tag != type_struct && type->base.tag != type_union)
CompileError (obj, stat, "Struct/union initializer type mismatch, type '%T'",
type);
else
obj = CompileStructUnionInit (obj, expr, type, stat, code);
break;
default:
obj = _CompileExpr (obj, expr, True, stat, code);
if (!TypeCombineBinary (type, ASSIGN, expr->base.type))
CompileError (obj, stat, "Incompatible types, storage '%T', value '%T', for initializer",
type, expr->base.type);
}
RETURN (obj);
}
static ObjPtr
CompileArrayInits (ObjPtr obj, ExprPtr expr, TypePtr type,
int ndim, ExprPtr stat, CodePtr code,
AInitMode mode)
{
ENTER ();
InstPtr inst;
ExprPtr e;
if (ndim == 0)
{
obj = CompileInit (obj, expr, type, stat, code);
}
else
{
ExprPtr next;
switch (expr->base.tag) {
case ARRAY:
for (e = expr->tree.left; e; e = next)
{
AInitMode subMode = AInitModeElement;
next = e->tree.right;
if (next && next->tree.left->base.tag == DOTDOTDOT)
{
subMode = AInitModeRepeat;
next = next->tree.right;
}
obj = CompileArrayInits (obj, e->tree.left, type,
ndim-1, stat, code, subMode);
}
break;
case ANONINIT:
break;
default:
CompileError (obj, stat, "Not enough initializer dimensions");
break;
}
}
BuildInst (obj, OpInitArray, inst, stat);
inst->ainit.dim = ndim;
inst->ainit.mode = mode;
RETURN (obj);
}
static ExprPtr
CompileArrayInitArgs (int ndim)
{
ExprPtr a = NewExprConst (TEN_NUM, One);
a->base.type = typePrim[rep_integer];
if (!ndim)
return 0;
return NewExprTree (COMMA, a, CompileArrayInitArgs (ndim - 1));
}
static ArgType *
CompileComprehensionArgs (ExprPtr e)
{
ArgType *down = 0;
if (e->base.tag == COMMA)
{
down = CompileComprehensionArgs (e->tree.right);
e = e->tree.left;
}
down = NewArgType (typePrim[rep_integer],
False, e->atom.atom,
e->atom.symbol, down);
return down;
}
static ObjPtr
CompileComprehension (ObjPtr obj,
TypePtr type,
ExprPtr expr,
ExprPtr stat,
CodePtr code)
{
ENTER ();
ExprPtr body = expr->tree.right;
ExprPtr lambda;
ArgType *args;
/*
* Convert a single expression into a block containing a
* return statement
*/
switch (body->base.tag) {
case STRUCT:
case COMP:
case ARRAY:
case ANONINIT:
body = NewExprTree (NEW, body, 0);
body->base.type = type;
}
if (body->base.tag != OC)
body = NewExprTree (OC,
NewExprTree (RETURNTOK, 0, body),
NewExprTree (OC, 0, 0));
/*
* Convert the args
*/
args = CompileComprehensionArgs (expr->tree.left->tree.left);
/*
* Compile [] symbol
*/
CompileStorage (obj, stat, expr->tree.left->tree.right->atom.symbol,
code);
/*
* Build a func expression
*/
lambda = NewExprCode (NewFuncCode (type,
args,
body,
Void),
0);
obj = _CompileExpr (obj, lambda, True, stat, code);
expr->tree.left->base.type = lambda->base.type;
RETURN(obj);
}
/*
* typedef struct { int x; } foo;
* typedef struct { foo[2,2] q; } bar;
* bar y = { q = { { { x = 1 } ... } ... } };
*
*
* ARRAY
* / \
* COMMA 0
* / \
* ARRAY COMMA
* / \ | \
* COMMA 0 DOTDOTDOT 0
* / \
* STRUCT COMMA
* / \ | \
* COMMA 0 DOTDOTDOT 0
* / \
* ASSIGN 0
* | \
* NAME TEN_NUM
* "x" 1
*/
static ObjPtr
CompileArrayInit (ObjPtr obj, ExprPtr expr, Type *type, ExprPtr stat, CodePtr code)
{
ENTER ();
int ndim;
Type *sub = type->array.type;
Expr *dimensions;
ndim = CompileCountDeclDimensions (type->array.dimensions);
if (!ndim)
{
if (expr)
ndim = CompileCountImplicitDimensions (expr);
if (!ndim)
{
CompileError (obj, stat, "Cannot compute number of array dimensions");
RETURN (obj);
}
}
if (type->array.dimensions && type->array.dimensions->tree.left)
dimensions = 0;
else
{
dimensions = CompileImplicitArray (obj, stat, expr, ndim);
if (!dimensions)
RETURN (obj);
}
if (expr && expr->base.tag == COMP)
{
ExprPtr args = CompileArrayInitArgs (ndim);
Type *retType;
obj = CompileComprehension (obj, sub, expr, stat, code);
if (!CompileTypecheckArgs (obj, expr->tree.left->base.type,
args, ndim, stat))
{
RETURN(obj);
}
retType = TypeCombineReturn (expr->tree.left->base.type);
if (!TypeCombineBinary (sub, ASSIGN, retType))
{
CompileError (obj, stat, "Incompatible types, array '%T', return '%T', for initializer",
sub, expr->base.type);
RETURN(obj);
}
SetPush (obj);
obj = CompileLvalue (obj,
expr->tree.left->tree.right,
stat, code, False, True, True, False, False);
SetPush (obj);
}
obj = CompileBuildArray (obj, expr, type, dimensions, ndim, stat, code);
if (expr)
{
InstPtr inst;
if (expr->base.tag == COMP)
{
int start_inst;
int top_inst;
/*
* Comprehension:
*
* Obj ^ (obj)
* InitArray ndim (Start)
* Branch L1
* L2: InitArray ndim (Func)
* Call
* InitArray 0 (Element)
* L1: InitArray n (Test)
* BranchFalse L2
* InitArray n (Element)
*/
BuildInst (obj, OpAssign, inst, stat);
inst->assign.initialize = True;
BuildInst (obj, OpInitArray, inst, stat);
inst->ainit.mode = AInitModeStart;
inst->ainit.dim = ndim;
/* Branch L1 */
NewInst (obj, OpBranch, start_inst, stat);
top_inst = obj->used;
BuildInst (obj, OpInitArray, inst, stat);
inst->ainit.dim = ndim;
inst->ainit.mode = AInitModeFunc;
BuildInst (obj, OpCall, inst, stat);
inst->ints.value = ndim;
BuildInst (obj, OpInitArray, inst, stat);
inst->ainit.dim = 0;
inst->ainit.mode = AInitModeElement;
/* Patch Branch L1 */
inst = ObjCode (obj, start_inst);
inst->branch.offset = obj->used - start_inst;
inst->branch.mod = BranchModNone;
BuildInst (obj, OpInitArray, inst, stat);
inst->ainit.dim = 0;
inst->ainit.mode = AInitModeTest;
/* Branch L2 */
BuildInst (obj, OpBranchFalse, inst, stat);
inst->branch.offset = top_inst - ObjLast(obj);
inst->branch.mod = BranchModNone;
/* Finish up */
BuildInst (obj, OpInitArray, inst, stat);
inst->ainit.dim = ndim;
inst->ainit.mode = AInitModeFuncDone;
}
else
{
int ninitdim;
if (expr->base.tag != ARRAY && expr->base.tag != ANONINIT)
{
CompileError (obj, stat, "Non array initializer");
RETURN (obj);
}
ninitdim = CompileCountInitDimensions (sub, expr);
if (ninitdim < 0)
{
CompileError (obj, stat, "Inconsistent array initializer dimensionality");
RETURN (obj);
}
if (ndim > ninitdim ||
(ndim < ninitdim && TypeCanon(sub)->base.tag != type_array))
{
CompileError (obj, stat, "Array dimension mismatch %d != %d\n",
ndim, ninitdim);
RETURN (obj);
}
BuildInst (obj, OpInitArray, inst, stat);
inst->ainit.mode = AInitModeStart;
inst->ainit.dim = ndim;
obj = CompileArrayInits (obj, expr, sub, ndim, stat, code,
AInitModeElement);
}
}
RETURN (obj);
}
static ObjPtr
CompileHashInit (ObjPtr obj, ExprPtr expr, Type *type,
ExprPtr stat, CodePtr code)
{
ENTER ();
InstPtr inst;
ExprPtr inits = expr ? expr->tree.left : 0;
ExprPtr init;
if (type->base.tag == type_hash)
{
BuildInst (obj, OpBuildHash, inst, stat);
inst->hash.type = type;
if (expr)
expr->base.type = type;
/*
* Initialize any elements given values
*/
for (init = inits; init; init = init->tree.right)
{
ExprPtr key = init->tree.left->tree.left;
ExprPtr value = init->tree.left->tree.right;
SetPush (obj); /* push the hash */
if (key)
{
/*
* Compute the key
*/
obj = CompileInit (obj, key, type->hash.keyType, stat, code);
if (!TypeIsOrdered (type->hash.keyType, key->base.type))
{
CompileError (obj, stat, "Incompatible expression type '%T', for hash index type '%T'",
key->base.type, type->hash.keyType);
RETURN (obj);
}
SetPush (obj); /* push the key */
}
/*
* Compute the value
*/
obj = CompileInit (obj, value, type->hash.type, stat, code);
/*
* Store the pair
*/
BuildInst (obj, key ? OpInitHash : OpInitHashDef, inst, stat);
}
}
RETURN (obj);
}
/*
* Construct an implicit initializer expression for the specified type
*/
static ExprPtr
CompileImplicitInit (Type *type)
{
ENTER ();
ExprPtr init = 0;
Type *sub;
int dim;
StructTypePtr structs;
TypePtr *types;
Atom *atoms;
int i;
type = TypeCanon (type);
switch (type->base.tag) {
case type_array:
if (type->array.dimensions)
{
if (type->array.resizable)
{
init = NewExprTree (ANONINIT, 0, 0);
}
else if (type->array.dimensions->tree.left)
{
sub = type->array.type;
init = CompileImplicitInit (sub);
if (init)
{
dim = CompileCountDeclDimensions (type->array.dimensions);
while (--dim >= 0)
{
init = NewExprTree (ARRAY,
NewExprTree (COMMA,
init,
NewExprTree (COMMA,
NewExprTree (DOTDOTDOT, 0, 0),
0)),
0);
}
}
else
init = NewExprTree (ANONINIT, 0, 0);
}
}
break;
case type_hash:
init = NewExprTree (HASH, 0, 0);
break;
case type_struct:
structs = type->structs.structs;
types = BoxTypesElements (structs->types);
atoms = StructTypeAtoms (structs);
init = 0;
for (i = 0; i < structs->nelements; i++)
{
ExprPtr member;
sub = types[i];
member = CompileImplicitInit (sub);
if (member)
{
init = NewExprTree (COMMA,
NewExprTree (ASSIGN,
NewExprAtom (atoms[i], 0, False),
member),
init);
}
}
if (init)
init = NewExprTree (STRUCT, init, 0);
else
init = NewExprTree (ANONINIT, 0, 0);
break;
default:
break;
}
RETURN (init);
}
static Bool
CompileStructInitElementIncluded (ExprPtr expr, Atom atom)
{
while (expr)
{
if (atom == expr->tree.left->tree.left->atom.atom)
return True;
expr = expr->tree.right;
}
return False;
}
static ObjPtr
CompileStructUnionInit (ObjPtr obj, ExprPtr expr, Type *type,
ExprPtr stat, CodePtr code)
{
ENTER ();
StructType *structs = type->structs.structs;
InstPtr inst;
ExprPtr inits = expr ? expr->tree.left : 0;
ExprPtr init;
Type *mem_type;
int i;
TypePtr *types = BoxTypesElements (structs->types);
Atom *atoms = StructTypeAtoms (structs);
if (type->base.tag == type_struct)
{
BuildInst (obj, OpBuildStruct, inst, stat);
inst->structs.structs = structs;
/*
* Initialize any elements which were given explicit values
*/
for (init = inits; init; init = init->tree.right)
{
mem_type = StructMemType (structs, init->tree.left->tree.left->atom.atom);
if (!mem_type)
{
CompileError (obj, stat, "Type '%T' is not a struct or union containing \"%A\"",
type, init->tree.left->tree.left->atom.atom);
continue;
}
SetPush (obj); /* push the struct */
/*
* Compute the initializer value
*/
obj = CompileInit (obj, init->tree.left->tree.right, mem_type, stat, code);
/*
* Assign to the member
*/
BuildInst (obj, OpInitStruct, inst, stat);
inst->atom.atom = init->tree.left->tree.left->atom.atom;
}
/*
* Implicitly initialize any remaining elements
*/
for (i = 0; i < structs->nelements; i++)
{
TypePtr type = TypeCanon (types[i]);
if (!inits || !CompileStructInitElementIncluded (inits, atoms[i]))
{
ExprPtr init = CompileImplicitInit (type);
if (init)
{
SetPush (obj);
obj = CompileInit (obj, init, type, stat, code);
BuildInst (obj, OpInitStruct, inst, stat);
inst->atom.atom = atoms[i];
}
}
}
}
else
{
init = inits;
if (!init)
{
CompileError (obj, stat, "Empty initializer for union '%T'",
type);
RETURN (obj);
}
if (init->tree.right)
{
CompileError (obj, stat, "Multiple initializers for union '%T'",
type);
RETURN (obj);
}
mem_type = StructMemType (structs, init->tree.left->tree.left->atom.atom);
if (!mem_type)
{
CompileError (obj, stat, "Type '%T' is not a struct or union containing \"%A\"",
type, init->tree.left->tree.left->atom.atom);
RETURN (obj);
}
/*
* Compute the initializer value
*/
obj = CompileInit (obj, init->tree.left->tree.right, mem_type, stat, code);
SetPush (obj); /* push the initializer value */
BuildInst (obj, OpBuildUnion, inst, stat);
inst->structs.structs = structs;
BuildInst (obj, OpInitUnion, inst, stat);
inst->atom.atom = init->tree.left->tree.left->atom.atom;
}
RETURN (obj);
}
static int
CompileCountCatches (ExprPtr catches)
{
int c = 0;
while (catches)
{
c++;
catches = catches->tree.left;
}
return c;
}
static ObjPtr
CompileCatch (ObjPtr obj, ExprPtr catches, ExprPtr body,
ExprPtr stat, CodePtr code, int nest)
{
ENTER ();
int catch_inst, exception_inst;
InstPtr inst;
ExprPtr catch;
ExprPtr name;
SymbolPtr exception;
Type *catch_type;
NonLocal *nonLocal;
int nest_tmp;
if (catches)
{
catch = catches->tree.right;
/*
* try a catch b
*
* CATCH b OpCall EXCEPTION a ENDCATCH
* +----------------------+
* +-----------------+
*/
if (catch->code.code->base.name->base.tag == COLONCOLON)
name = catch->code.code->base.name->tree.right;
else
name = catch->code.code->base.name;
exception = name->atom.symbol;
if (!exception)
{
CompileError (obj, stat, "No exception '%A' in scope",
name->atom.atom);
RETURN(obj);
}
if (exception->symbol.class != class_exception)
{
CompileError (obj, stat, "Invalid use of %C \"%A\" as exception",
exception->symbol.class, catch->code.code->base.name->atom);
RETURN (obj);
}
catch_type = NewTypeFunc (typePoly, catch->code.code->base.args);
if (!TypeIsOrdered (exception->symbol.type, catch_type))
{
CompileError (obj, stat, "Incompatible types, formal '%T', actual '%T', for catch",
exception->symbol.type, catch_type);
RETURN (obj);
}
NewInst (obj, OpCatch, catch_inst, stat);
/*
* Pop peer catch blocks from non local list while
* compiling exception handler
*/
nonLocal = obj->nonLocal;
if (nest)
{
REFERENCE (nonLocal);
nest_tmp = nest;
while (nest_tmp-- > 0)
obj->nonLocal = obj->nonLocal->prev;
}
/*
* Exception arguments are sitting in value, push
* them on the stack
*/
BuildInst (obj, OpNoop, inst, stat);
SetPush (obj);
/*
* Compile the exception handler and the
* call to get to it.
*/
catch->code.code->base.func = code ? code->base.func : 0;
obj = CompileFunc (obj, catch->code.code, stat, code,
NewNonLocal (obj->nonLocal,
NonLocalCatch,
NON_LOCAL_RETURN));
/*
* Patch non local returns inside
*/
CompilePatchLoop (obj, catch_inst, -1, -1, -1);
/*
* Unwind any peer catch blocks while executing catch
*/
if (nest)
{
BuildInst (obj, OpUnwind, inst, stat);
inst->unwind.twixt = 0;
inst->unwind.catch = nest;
/* replace peer catch blocks */
obj->nonLocal = nonLocal;
}
BuildInst (obj, OpExceptionCall, inst, stat);
exception_inst = obj->used;
BuildInst (obj, OpBranch, inst, stat);
inst->branch.offset = 0;
inst->branch.mod = BranchModCatch;
inst = ObjCode (obj, catch_inst);
inst->catch.offset = obj->used - catch_inst;
inst->catch.exception = exception;
obj->nonLocal = NewNonLocal (obj->nonLocal, NonLocalTry, 0);
obj = CompileCatch (obj, catches->tree.left, body, stat, code, nest+1);
obj->nonLocal = obj->nonLocal->prev;
if (!nest)
{
BuildInst (obj, OpEndCatch, inst, stat);
inst->ints.value = CompileCountCatches (catches);
/*
* Patch Catch branches inside
*/
CompilePatchLoop (obj, exception_inst, -1, -1, obj->used);
}
}
else
obj = _CompileStat (obj, body, False, code);
RETURN (obj);
}
ObjPtr
_CompileExpr (ObjPtr obj, ExprPtr expr, Bool evaluate, ExprPtr stat, CodePtr code)
{
ENTER ();
int ndim;
int top_inst, test_inst, middle_inst;
InstPtr inst;
SymbolPtr s;
Type *t;
int staticLink;
Bool bool_const;
switch (expr->base.tag) {
case NAME:
s = CompileCheckSymbol (obj, stat, expr, code, &staticLink, False);
if (!s)
{
expr->base.type = typePoly;
break;
}
switch (s->symbol.class) {
case class_const:
case class_global:
BuildInst (obj, OpGlobal, inst, stat);
inst->box.box = s->global.value;
assert (s->global.value);
#if 0
inst->var.name = s;
inst->var.staticLink = 0;
#endif
break;
case class_static:
BuildInst (obj, OpStatic, inst, stat);
inst->frame.staticLink = staticLink;
inst->frame.element = s->local.element;
break;
case class_arg:
case class_auto:
BuildInst (obj, OpLocal, inst, stat);
inst->frame.staticLink = staticLink;
inst->frame.element = s->local.element;
break;
default:
CompileError (obj, stat, "Invalid use of %C \"%A\"",
s->symbol.class, expr->atom.atom);
expr->base.type = typePoly;
inst = 0;
break;
}
if (!inst)
break;
expr->base.type = s->symbol.type;
t = CompileRefType (obj, expr, expr->base.type);
if (t)
{
BuildInst (obj, OpUnFunc, inst, stat);
inst->unfunc.func = Dereference;
expr->base.type = t;
}
break;
case VAR:
obj = CompileDecl (obj, expr, evaluate, stat, code);
break;
case NEW:
if (expr->base.type)
obj = CompileType (obj, 0, expr->base.type, stat, code);
obj = CompileInit (obj, expr->tree.left, expr->base.type, stat, code);
break;
case UNION:
if (expr->tree.right)
obj = _CompileExpr (obj, expr->tree.right, True, stat, code);
else
{
BuildInst (obj, OpConst, inst, stat);
inst->constant.constant = Void;
}
SetPush (obj);
t = TypeCanon (expr->base.type);
if (t && t->base.tag == type_union)
{
StructType *st = t->structs.structs;
Type *mt;
expr->tree.left->base.type = StructMemType (st, expr->tree.left->atom.atom);
if (!expr->tree.left->base.type)
{
CompileError (obj, stat, "Union type '%T' has no member \"%A\"",
expr->base.type,
expr->tree.left->atom.atom);
break;
}
mt = TypeCanon (expr->tree.left->base.type);
BuildInst (obj, OpBuildUnion, inst, stat);
inst->structs.structs = st;
if (expr->tree.right)
{
if (mt == typePrim[rep_void])
{
CompileError (obj, stat, "Union type '%T', member '%A' requires no constructor value",
expr->base.type,
expr->tree.left->atom.atom);
break;
}
if (!TypeCombineBinary (expr->tree.left->base.type,
ASSIGN,
expr->tree.right->base.type))
{
CompileError (obj, stat, "Incompatible types, member '%T', value '%T', for union constructor",
expr->tree.left->base.type,
expr->tree.right->base.type);
break;
}
}
else
{
if (mt != typePrim[rep_void])
{
CompileError (obj, stat, "Union member '%A' requires constructor value",
expr->tree.left->atom.atom);
break;
}
}
BuildInst (obj, OpInitUnion, inst, stat);
inst->atom.atom = expr->tree.left->atom.atom;
}
else
{
CompileError (obj, stat, "Incompatible type, type '%T', for union constructor", expr->base.type);
expr->base.type = typePoly;
break;
}
break;
case TEN_NUM:
case OCTAL0_NUM:
case OCTAL_NUM:
case BINARY_NUM:
case HEX_NUM:
case CHAR_CONST:
BuildInst (obj, OpConst, inst, stat);
inst->constant.constant = expr->constant.constant;
expr->base.type = typePrim[rep_integer];
break;
case TEN_FLOAT:
case OCTAL_FLOAT:
case BINARY_FLOAT:
case HEX_FLOAT:
BuildInst (obj, OpConst, inst, stat);
inst->constant.constant = expr->constant.constant;
if (ValueRep(expr->constant.constant) == &IntRep)
expr->base.type = typePrim[rep_integer];
else
expr->base.type = typePrim[ValueTag(expr->constant.constant)];
break;
case STRING_CONST:
BuildInst (obj, OpConst, inst, stat);
inst->constant.constant = expr->constant.constant;
expr->base.type = typePrim[rep_string];
break;
case THREAD_CONST:
BuildInst (obj, OpConst, inst, stat);
inst->constant.constant = expr->constant.constant;
expr->base.type = typePrim[rep_thread];
break;
case VOIDVAL:
BuildInst (obj, OpConst, inst, stat);
inst->constant.constant = expr->constant.constant;
expr->base.type = typePrim[rep_void];
break;
case BOOLVAL:
BuildInst (obj, OpConst, inst, stat);
inst->constant.constant = expr->constant.constant;
expr->base.type = typePrim[rep_bool];
break;
case POLY_CONST:
BuildInst (obj, OpConst, inst, stat);
inst->constant.constant = expr->constant.constant;
expr->base.type = typePoly; /* FIXME composite const type */
break;
case OS:
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
obj = CompileArrayIndex (obj, expr->tree.right,
CompileIndexType (expr->tree.left),
stat, code, &ndim);
if (!ndim)
{
expr->base.type = typePoly;
break;
}
expr->base.type = TypeCombineArray (expr->tree.left->base.type,
ndim,
False);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible type '%T', for %d dimension operation",
expr->tree.left->base.type, ndim);
expr->base.type = typePoly;
break;
}
BuildInst (obj, OpArray, inst, stat);
inst->ints.value = ndim;
break;
case OP: /* function call */
obj = CompileCall (obj, expr, TailNever, stat, code, False);
break;
case COLONCOLON:
obj = _CompileExpr (obj, expr->tree.right, evaluate, stat, code);
expr->base.type = expr->tree.right->base.type;
break;
case DOT:
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
expr->base.type = TypeCombineStruct (expr->tree.left->base.type,
expr->base.tag,
expr->tree.right->atom.atom);
if (!expr->base.type)
{
CompileError (obj, stat, "Type '%T' is not a struct or union containing \"%A\"",
expr->tree.left->base.type,
expr->tree.right->atom.atom);
expr->base.type = typePoly;
break;
}
BuildInst (obj, OpDot, inst, stat);
inst->atom.atom = expr->tree.right->atom.atom;
t = CompileRefType (obj, expr, expr->base.type);
if (t)
{
BuildInst (obj, OpUnFunc, inst, stat);
inst->unfunc.func = Dereference;
expr->base.type = t;
}
break;
case ARROW:
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
expr->base.type = TypeCombineStruct (expr->tree.left->base.type,
expr->base.tag,
expr->tree.right->atom.atom);
if (!expr->base.type)
{
CompileError (obj, stat, "Type '%T' is not a struct or union ref containing \"%A\"",
expr->tree.left->base.type,
expr->tree.right->atom.atom);
expr->base.type = typePoly;
break;
}
BuildInst (obj, OpArrow, inst, stat);
inst->atom.atom = expr->tree.right->atom.atom;
t = CompileRefType (obj, expr, expr->base.type);
if (t)
{
BuildInst (obj, OpUnFunc, inst, stat);
inst->unfunc.func = Dereference;
expr->base.type = t;
}
break;
case FUNC:
obj = CompileFunc (obj, expr->code.code, stat, code, 0);
expr->base.type = NewTypeFunc (expr->code.code->base.type,
expr->code.code->base.args);
break;
case STAR: obj = CompileUnFunc (obj, expr, Dereference, stat, code,"*"); break;
case AMPER:
obj = CompileLvalue (obj, expr->tree.left, stat, code, False, False, False,
False, True);
t = CompileRefType (obj, expr->tree.left, expr->tree.left->base.type);
if (!t)
t = expr->tree.left->base.type;
expr->base.type = NewTypeRef (t, True);
if (!expr->base.type)
{
CompileError (obj, stat, "Type '%T' cannot be an l-value",
expr->tree.left->base.type);
expr->base.type = typePoly;
break;
}
break;
case UMINUS: obj = CompileUnOp (obj, expr, NegateOp, stat, code); break;
case LNOT: obj = CompileUnFunc (obj, expr, Lnot, stat, code,"~"); break;
case BANG: obj = CompileUnFunc (obj, expr, Not, stat, code,"!"); break;
case FACT: obj = CompileUnFunc (obj, expr, Factorial, stat, code,"!"); break;
case INC:
if (expr->tree.left)
{
obj = CompileLvalue (obj, expr->tree.left, stat, code, False, False, False,
False, False);
expr->base.type = TypeCombineBinary (expr->tree.left->base.type,
ASSIGNPLUS,
typePrim[rep_int]);
BuildInst (obj, OpPreOp, inst, stat);
}
else
{
obj = CompileLvalue (obj, expr->tree.right, stat, code,
False, False, False, False, False);
expr->base.type = TypeCombineBinary (expr->tree.right->base.type,
ASSIGNPLUS,
typePrim[rep_int]);
BuildInst (obj, OpPostOp, inst, stat);
}
inst->binop.op = PlusOp;
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible type, value '%T', for ++ operation ",
expr->tree.left ? expr->tree.left->base.type :
expr->tree.right->base.type);
expr->base.type = typePoly;
break;
}
break;
case DEC:
if (expr->tree.left)
{
obj = CompileLvalue (obj, expr->tree.left, stat, code,
False, False, False, False, False);
expr->base.type = TypeCombineBinary (expr->tree.left->base.type,
ASSIGNMINUS,
typePrim[rep_int]);
BuildInst (obj, OpPreOp, inst, stat);
}
else
{
obj = CompileLvalue (obj, expr->tree.right, stat, code,
False, False, False, False, False);
expr->base.type = TypeCombineBinary (expr->tree.right->base.type,
ASSIGNMINUS,
typePrim[rep_int]);
BuildInst (obj, OpPostOp, inst, stat);
}
inst->binop.op = MinusOp;
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible type, value '%T', for -- operation",
expr->tree.left ? expr->tree.left->base.type :
expr->tree.right->base.type);
expr->base.type = typePoly;
break;
}
break;
case PLUS: obj = CompileBinOp (obj, expr, PlusOp, stat, code); break;
case MINUS: obj = CompileBinOp (obj, expr, MinusOp, stat, code); break;
case TIMES: obj = CompileBinOp (obj, expr, TimesOp, stat, code); break;
case DIVIDE: obj = CompileBinOp (obj, expr, DivideOp, stat, code); break;
case DIV: obj = CompileBinOp (obj, expr, DivOp, stat, code); break;
case MOD: obj = CompileBinOp (obj, expr, ModOp, stat, code); break;
case POW:
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
SetPush (obj);
obj = _CompileExpr (obj, expr->tree.right->tree.left, True, stat, code);
SetPush (obj);
obj = _CompileExpr (obj, expr->tree.right->tree.right, True, stat, code);
expr->base.type = TypeCombineBinary (expr->tree.right->tree.left->base.type,
expr->base.tag,
expr->tree.right->tree.right->base.type);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible types, left '%T', right '%T', for ** operation",
expr->tree.right->tree.left->base.type,
expr->tree.right->tree.right->base.type);
expr->base.type = typePoly;
break;
}
BuildInst (obj, OpCall, inst, stat);
inst->ints.value = 2;
BuildInst (obj, OpNoop, inst, stat);
break;
case SHIFTL: obj = CompileBinFunc (obj, expr, ShiftL, stat, code, "<<"); break;
case SHIFTR: obj = CompileBinFunc (obj, expr, ShiftR, stat, code, ">>"); break;
case QUEST:
/*
* a ? b : c
*
* a QUEST b COLON c
* +-------------+
* +-------+
*/
top_inst = obj->used;
obj = _CompileBoolExpr (obj, expr->tree.left, True, stat, code);
if (obj->used == top_inst + 1 &&
(inst = ObjCode (obj, top_inst))->base.opCode == OpConst)
{
test_inst = -1;
bool_const = True (inst->constant.constant);
obj->used = top_inst;
}
else
{
NewInst (obj, OpBranchFalse, test_inst, stat);
bool_const = False;
}
top_inst = obj->used;
obj = _CompileExpr (obj, expr->tree.right->tree.left, evaluate, stat, code);
if (test_inst == -1)
{
middle_inst = -1;
if (!bool_const)
obj->used = top_inst;
}
else
{
NewInst (obj, OpBranch, middle_inst, stat);
inst = ObjCode (obj, test_inst);
inst->branch.offset = obj->used - test_inst;
inst->branch.mod = BranchModNone;
}
top_inst = obj->used;
obj = _CompileExpr (obj, expr->tree.right->tree.right, evaluate, stat, code);
if (middle_inst == -1)
{
if (bool_const)
obj->used = top_inst;
}
else
{
inst = ObjCode (obj, middle_inst);
inst->branch.offset = obj->used - middle_inst;
inst->branch.mod = BranchModNone;
BuildInst (obj, OpNoop, inst, stat);
}
expr->base.type = TypeCombineBinary (expr->tree.right->tree.left->base.type,
COLON,
expr->tree.right->tree.right->base.type);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible types, true '%T', false '%T', for ?: operation",
expr->tree.right->tree.left->base.type,
expr->tree.right->tree.right->base.type);
expr->base.type = typePoly;
break;
}
break;
case LXOR: obj = CompileBinFunc (obj, expr, Lxor, stat, code, "^"); break;
case LAND: obj = CompileBinOp (obj, expr, LandOp, stat, code); break;
case LOR: obj = CompileBinOp (obj, expr, LorOp, stat, code); break;
case AND:
/*
* a && b
*
* a ANDAND b
* +--------+
*/
top_inst = obj->used;
obj = _CompileBoolExpr (obj, expr->tree.left, True, stat, code);
if (obj->used == top_inst + 1 &&
(inst = ObjCode (obj, top_inst))->base.opCode == OpConst)
{
test_inst = -1;
bool_const = True (inst->constant.constant);
if (bool_const)
obj->used = top_inst;
}
else
{
NewInst (obj, OpBranchFalse, test_inst, stat);
bool_const = True;
}
middle_inst = obj->used;
/*
* Always compile the RHS to check for errors
*/
obj = _CompileBoolExpr (obj, expr->tree.right, evaluate, stat, code);
/*
* Smash any instructions if they'll be skipped
*/
if (!bool_const)
obj->used = middle_inst;
if (test_inst >= 0)
{
inst = ObjCode (obj, test_inst);
inst->branch.offset = obj->used - test_inst;
inst->branch.mod = BranchModNone;
BuildInst (obj, OpNoop, inst, stat);
}
expr->base.type = TypeCombineBinary (expr->tree.left->base.type,
AND,
expr->tree.right->base.type);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible types, left '%T', right '%T', for && operation",
expr->tree.left->base.type,
expr->tree.right->base.type);
expr->base.type = typePoly;
break;
}
break;
case OR:
/*
* a || b
*
* a OROR b
* +--------+
*/
top_inst = obj->used;
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
if (obj->used == top_inst + 1 &&
(inst = ObjCode (obj, top_inst))->base.opCode == OpConst)
{
test_inst = -1;
bool_const = True (inst->constant.constant);
if (!bool_const)
obj->used = top_inst;
}
else
{
NewInst (obj, OpBranchTrue, test_inst, stat);
bool_const = False;
}
middle_inst = obj->used;
/*
* Always compile the RHS to check for errors
*/
obj = _CompileExpr (obj, expr->tree.right, evaluate, stat, code);
/*
* Smash any instructions if they'll be skipped
*/
if (bool_const)
obj->used = middle_inst;
if (test_inst >= 0)
{
inst = ObjCode (obj, test_inst);
inst->branch.offset = obj->used - test_inst;
inst->branch.mod = BranchModNone;
BuildInst (obj, OpNoop, inst, stat);
}
expr->base.type = TypeCombineBinary (expr->tree.left->base.type,
OR,
expr->tree.right->base.type);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible types, left '%T', right, '%T', for || operation",
expr->tree.left->base.type,
expr->tree.right->base.type);
expr->base.type = typePoly;
break;
}
break;
case ASSIGN: obj = CompileAssign (obj, expr, False, stat, code); break;
case ASSIGNPLUS: obj = CompileAssignOp (obj, expr, PlusOp, stat, code); break;
case ASSIGNMINUS: obj = CompileAssignOp (obj, expr, MinusOp, stat, code); break;
case ASSIGNTIMES: obj = CompileAssignOp (obj, expr, TimesOp, stat, code); break;
case ASSIGNDIVIDE: obj = CompileAssignOp (obj, expr, DivideOp, stat, code); break;
case ASSIGNDIV: obj = CompileAssignOp (obj, expr, DivOp, stat, code); break;
case ASSIGNMOD: obj = CompileAssignOp (obj, expr, ModOp, stat, code); break;
case ASSIGNPOW:
obj = _CompileExpr (obj, expr->tree.left, True, stat, code);
SetPush (obj);
obj = CompileLvalue (obj, expr->tree.right->tree.left, stat, code,
False, False, False, False, False);
SetPush (obj);
obj = _CompileExpr (obj, expr->tree.right->tree.right, True, stat, code);
expr->base.type = TypeCombineBinary (expr->tree.right->tree.left->base.type,
expr->base.tag,
expr->tree.right->tree.right->base.type);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible types, left '%T', right '%T', for **= operation",
expr->tree.right->tree.left->base.type,
expr->tree.right->tree.right->base.type);
expr->base.type = typePoly;
break;
}
BuildInst (obj, OpCall, inst, stat);
inst->ints.value = 2;
BuildInst (obj, OpNoop, inst, stat);
break;
case ASSIGNSHIFTL: obj = CompileAssignFunc (obj, expr, ShiftL, stat, code, "<<"); break;
case ASSIGNSHIFTR: obj = CompileAssignFunc (obj, expr, ShiftR, stat, code, ">>"); break;
case ASSIGNLXOR: obj = CompileAssignFunc (obj, expr, Lxor, stat, code, "^"); break;
case ASSIGNLAND: obj = CompileAssignOp (obj, expr, LandOp, stat, code); break;
case ASSIGNLOR: obj = CompileAssignOp (obj, expr, LorOp, stat, code); break;
case ASSIGNAND:
/*
* a &&= b
*
* a ASSIGNAND b
* +--------+
*/
top_inst = obj->used;
obj = CompileLvalue (obj, expr->tree.left, stat, code,
False, False, False, False, False);
SetPush (obj);
NewInst (obj, OpFetch, middle_inst, stat);
NewInst (obj, OpBranchFalse, test_inst, stat);
/* no short circuit */
obj = _CompileBoolExpr (obj, expr->tree.right, True, stat, code);
if (test_inst >= 0)
{
inst = ObjCode (obj, test_inst);
inst->branch.offset = obj->used - test_inst;
inst->branch.mod = BranchModNone;
BuildInst (obj, OpAssign, inst, stat);
NewInst(obj, OpBranch, test_inst, stat);
} else {
NewInst (obj, OpAssign, middle_inst, stat);
NewInst(obj, OpBranch, test_inst, stat);
}
/* short circuit */
NewInst(obj, OpDrop, middle_inst, stat);
inst = ObjCode(obj, test_inst);
inst->branch.offset = obj->used - test_inst;
inst->branch.mod = BranchModNone;
/* exit: is this Noop necessary? */
BuildInst (obj, OpNoop, inst, stat);
expr->base.type = TypeCombineBinary (expr->tree.left->base.type,
AND,
expr->tree.right->base.type);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible types, left '%T', right '%T', for &&= operation",
expr->tree.left->base.type,
expr->tree.right->base.type);
expr->base.type = typePoly;
break;
}
break;
case ASSIGNOR:
/*
* a ||= b
*
* a ASSIGNOR b
* +--------+
*/
top_inst = obj->used;
obj = CompileLvalue (obj, expr->tree.left, stat, code,
False, False, False, False, False);
SetPush (obj);
NewInst (obj, OpFetch, middle_inst, stat);
NewInst (obj, OpBranchTrue, test_inst, stat);
/* no short circuit */
obj = _CompileBoolExpr (obj, expr->tree.right, True, stat, code);
if (test_inst >= 0)
{
inst = ObjCode (obj, test_inst);
inst->branch.offset = obj->used - test_inst;
inst->branch.mod = BranchModNone;
BuildInst (obj, OpAssign, inst, stat);
NewInst(obj, OpBranch, test_inst, stat);
} else {
NewInst (obj, OpAssign, middle_inst, stat);
NewInst(obj, OpBranch, test_inst, stat);
}
/* short circuit */
NewInst(obj, OpDrop, middle_inst, stat);
inst = ObjCode(obj, test_inst);
inst->branch.offset = obj->used - test_inst;
inst->branch.mod = BranchModNone;
/* exit: is this Noop necessary? */
BuildInst (obj, OpNoop, inst, stat);
expr->base.type = TypeCombineBinary (expr->tree.left->base.type,
OR,
expr->tree.right->base.type);
if (!expr->base.type)
{
CompileError (obj, stat, "Incompatible types, left '%T', right '%T', for ||= operation",
expr->tree.left->base.type,
expr->tree.right->base.type);
expr->base.type = typePoly;
break;
}
break;
case EQ: obj = CompileBinOp (obj, expr, EqualOp, stat, code); break;
case NE: obj = CompileBinFunc (obj, expr, NotEqual, stat, code,"!="); break;
case LT: obj = CompileBinOp (obj, expr, LessOp, stat, code); break;
case GT: obj = CompileBinFunc (obj, expr, Greater, stat, code,">"); break;
case LE: obj = CompileBinFunc (obj, expr, LessEqual, stat, code,"<="); break;
case GE: obj = CompileBinFunc (obj, expr, GreaterEqual, stat, code,">="); break;
case COMMA:
top_inst = obj->used;
obj = _CompileExpr (obj, expr->tree.left, False, stat, code);
if (obj->used == top_inst + 1 &&
(inst = ObjCode (obj, top_inst))->base.opCode == OpConst)
{
obj->used = top_inst;
}
expr->base.type = expr->tree.left->base.type;
if (expr->tree.right)
{
obj = _CompileExpr (obj, expr->tree.right, evaluate, stat, code);
expr->base.type = expr->tree.right->base.type;
}
break;
case FORK:
BuildInst (obj, OpFork, inst, stat);
inst->obj.obj = CompileExpr (expr->tree.right, code);
expr->base.type = typePrim[rep_thread];
break;
case THREAD:
obj = CompileCall (obj, NewExprTree (OP,
expr->tree.right,
NewExprTree (COMMA,
expr->tree.left,
(Expr *) 0)),
TailNever,
stat, code, False);
expr->base.type = typePrim[rep_thread];
break;
case DOLLAR:
{
ExprPtr value, new;
if (expr->tree.left)
value = expr->tree.left;
else
value = NewExprConst (TEN_NUM, Zero);
new = BuildCall ("History", "fetch", 1, value);
obj = _CompileExpr (obj, new, True, stat, code);
}
expr->base.type = typePoly;
break;
case EXPR:
/* reposition statement reference so top-level errors are nicer*/
obj = _CompileExpr (obj, expr->tree.left, evaluate, expr, code);
expr->base.type = expr->tree.left->base.type;
break;
case OC:
/* statement block embedded in an expression */
obj = _CompileStat (obj, expr, True, code);
BuildInst (obj, OpConst, inst, stat);
inst->constant.constant = Void;
expr->base.type = typePrim[rep_void];
break;
default:
assert(0);
}
assert (!evaluate || expr->base.type);
RETURN (obj);
}
void
CompilePatchLoop (ObjPtr obj,
int start,
int continue_offset,
int break_offset,
int catch_offset)
{
InstPtr inst;
while (start < obj->used)
{
inst = ObjCode (obj, start);
switch (inst->base.opCode) {
case OpBranch:
if (inst->branch.offset == 0)
{
switch (inst->branch.mod) {
case BranchModBreak:
inst->branch.offset = break_offset - start;
break;
case BranchModContinue:
if (continue_offset >= 0)
inst->branch.offset = continue_offset - start;
break;
case BranchModCatch:
if (catch_offset >= 0)
inst->branch.offset = catch_offset - start;
break;
default:
break;
}
}
break;
case OpFarJump:
if (inst->farJump.farJump->inst == -1)
{
switch (inst->farJump.mod) {
case BranchModBreak:
inst->farJump.farJump->inst = break_offset;
break;
case BranchModContinue:
inst->farJump.farJump->inst = continue_offset;
break;
case BranchModReturn:
case BranchModReturnVoid:
inst->farJump.farJump->inst = -2;
break;
case BranchModNone:
case BranchModCatch:
break;
}
}
break;
case OpObj:
if (!inst->code.code->base.builtin &&
inst->code.code->func.body.obj->nonLocal)
{
if (inst->code.code->func.body.obj)
CompilePatchLoop (inst->code.code->func.body.obj, 0,
continue_offset,
break_offset,
-1);
if (inst->code.code->func.staticInit.obj)
CompilePatchLoop (inst->code.code->func.staticInit.obj, 0,
continue_offset,
break_offset, -1);
}
break;
default:
break;
}
++start;
}
}
static void
CompileMoveObj (ObjPtr obj,
int start,
int depth,
int amount)
{
InstPtr inst;
while (start < obj->used)
{
inst = ObjCode (obj, start);
switch (inst->base.opCode) {
case OpFarJump:
if (inst->farJump.farJump->frame == depth &&
inst->farJump.farJump->inst >= 0)
{
inst->farJump.farJump->inst += amount;
}
break;
case OpObj:
if (!inst->code.code->base.builtin &&
inst->code.code->func.body.obj->nonLocal)
{
if (inst->code.code->func.body.obj)
CompileMoveObj (inst->code.code->func.body.obj, 0,
depth + 1, amount);
if (inst->code.code->func.staticInit.obj)
CompileMoveObj (inst->code.code->func.staticInit.obj, 0,
depth + 1, amount);
}
break;
default:
break;
}
++start;
}
}
static ObjPtr
_CompileNonLocal (ObjPtr obj, BranchMod mod, ExprPtr expr, CodePtr code)
{
ENTER ();
int twixt = 0, catch = 0, frame = 0;
NonLocal *nl;
InstPtr inst;
int target;
switch (mod) {
case BranchModBreak: target = NON_LOCAL_BREAK; break;
case BranchModContinue: target = NON_LOCAL_CONTINUE; break;
case BranchModReturn:
case BranchModReturnVoid: target = NON_LOCAL_RETURN; break;
case BranchModNone:
default: RETURN(obj);
}
for (nl = obj->nonLocal; nl; nl = nl->prev)
{
if (nl->target & target)
break;
switch (nl->kind) {
case NonLocalTwixt:
twixt++;
break;
case NonLocalTry:
catch++;
break;
case NonLocalCatch:
frame++;
break;
case NonLocalControl:
break;
}
}
if (!nl)
{
switch (target) {
case NON_LOCAL_BREAK:
CompileError (obj, expr, "break not in loop/switch/twixt");
break;
case NON_LOCAL_CONTINUE:
CompileError (obj, expr, "continue not in loop");
break;
case NON_LOCAL_RETURN:
break;
}
}
if (twixt || catch || frame)
{
BuildInst (obj, OpFarJump, inst, expr);
inst->farJump.farJump = 0;
inst->farJump.farJump = NewFarJump (-1, twixt, catch, frame);
inst->farJump.mod = mod;
}
else
{
switch (mod) {
case BranchModReturn:
BuildInst (obj, OpReturn, inst, expr);
break;
case BranchModReturnVoid:
BuildInst (obj, OpReturnVoid, inst, expr);
break;
case BranchModBreak:
case BranchModContinue:
BuildInst (obj, OpBranch, inst, expr);
inst->branch.offset = 0; /* filled in by PatchLoop */
inst->branch.mod = mod;
case BranchModNone:
case BranchModCatch:
break;
}
}
RETURN (obj);
}
/*
* Check if a return foo () can be turned into a tail call.
*/
static Bool
_CompileCanTailCall (ObjPtr obj, CodePtr code)
{
/* not in a function ("can't" happen) */
if (!code)
return False;
/* if profiling, disable tail calls to avoid losing information */
if (profiling)
return False;
/* Check for enclosing non-local branch targets */
if (obj->nonLocal != 0)
return False;
/* Check for compiling in a nested exception handler */
if (code->base.func != code)
return False;
return True;
}
ObjPtr
_CompileBoolExpr (ObjPtr obj, ExprPtr expr, Bool evaluate, ExprPtr stat, CodePtr code)
{
obj = _CompileExpr (obj, expr, evaluate, stat, code);
if (!TypePoly (expr->base.type) && !TypeBool (expr->base.type))
{
CompileError (obj, expr, "Incompatible type, value '%T', for boolean", expr->base.type);
}
return obj;
}
ObjPtr
_CompileStat (ObjPtr obj, ExprPtr expr, Bool last, CodePtr code)
{
ENTER ();
int start_inst, top_inst, continue_inst, test_inst, middle_inst;
ExprPtr c;
int ncase, *case_inst, icase;
Bool has_default;
InstPtr inst;
StructType *st;
ObjPtr cobj, bobj;
switch (expr->base.tag) {
case IF:
/*
* if (a) b
*
* a BRANCHFALSE b
* +-------------+
*/
top_inst = obj->used;
obj = _CompileBoolExpr (obj, expr->tree.left, True, expr, code);
if (obj->used == top_inst + 1 &&
(inst = ObjCode (obj, top_inst))->base.opCode == OpConst)
{
Bool t = True (inst->constant.constant);
obj->used = top_inst;
if (t)
obj = _CompileStat (obj, expr->tree.right, last, code);
}
else
{
NewInst (obj, OpBranchFalse, test_inst, expr);
obj = _CompileStat (obj, expr->tree.right, last, code);
inst = ObjCode (obj, test_inst);
inst->branch.offset = obj->used - test_inst;
inst->branch.mod = BranchModNone;
}
break;
case ELSE:
/*
* if (a) b else c
*
* a BRANCHFALSE b BRANCH c
* +--------------------+
* +--------+
*/
top_inst = obj->used;
obj = _CompileBoolExpr (obj, expr->tree.left, True, expr, code);
if (obj->used == top_inst + 1 &&
(inst = ObjCode (obj, top_inst))->base.opCode == OpConst)
{
Bool t = True (inst->constant.constant);
obj->used = top_inst;
/*
* Check which side wins
*/
if (t)
obj = _CompileStat (obj, expr->tree.right->tree.left, last, code);
else
obj = _CompileStat (obj, expr->tree.right->tree.right, last, code);
}
else
{
NewInst (obj, OpBranchFalse, test_inst, expr);
/*
* Compile b
*/
obj = _CompileStat (obj, expr->tree.right->tree.left, last, code);
/*
* Branch around else if reachable
*/
if (CompileIsReachable (obj, obj->used))
{
NewInst (obj, OpBranch, middle_inst, expr);
}
else
middle_inst = -1;
/*
* Fix up branch on a
*/
inst = ObjCode (obj, test_inst);
inst->branch.offset = obj->used - test_inst;
inst->branch.mod = BranchModNone;
/*
* Compile c
*/
obj = _CompileStat (obj, expr->tree.right->tree.right, last, code);
/*
* Fix up branch around else if necessary
*/
if (middle_inst != -1)
{
inst = ObjCode (obj, middle_inst);
inst->branch.offset = obj->used - middle_inst;
inst->branch.mod = BranchModNone;
}
}
break;
case WHILE:
/*
* while (a) b
*
* a BRANCHFALSE b BRANCH
* +--------------------+
* +---------------+
*/
cobj = NewObj (OBJ_INCR, OBJ_STAT_INCR);
cobj = _CompileBoolExpr (cobj, expr->tree.left, True, expr, code);
if (cobj->used == 1 &&
(inst = ObjCode (cobj, 0))->base.opCode == OpConst)
{
Bool t = True (inst->constant.constant);
if (!t)
{
/* strip out the whole while loop */
break;
}
start_inst = -1;
}
else
{
NewInst (obj, OpBranch, start_inst, expr);
}
top_inst = obj->used;
obj->nonLocal = NewNonLocal (obj->nonLocal, NonLocalControl,
NON_LOCAL_BREAK|NON_LOCAL_CONTINUE);
obj = _CompileStat (obj, expr->tree.right, False, code);
obj->nonLocal = obj->nonLocal->prev;
continue_inst = obj->used;
if (start_inst != -1)
{
inst = ObjCode (obj, start_inst);
inst->branch.offset = obj->used - start_inst;
inst->branch.mod = BranchModNone;
middle_inst = obj->used;
obj = AppendObj (obj, cobj);
CompileMoveObj (obj, middle_inst, 0, middle_inst);
BuildInst (obj, OpBranchTrue, inst, expr);
inst->branch.offset = top_inst - ObjLast(obj);
inst->branch.mod = BranchModNone;
}
else
{
BuildInst (obj, OpBranch, inst, expr);
inst->branch.offset = top_inst - ObjLast(obj);
inst->branch.mod = BranchModNone;
}
CompilePatchLoop (obj, top_inst, continue_inst, obj->used, -1);
break;
case DO:
/*
* do a while (b);
*
* a b DO
* +---+
*/
top_inst = obj->used;
obj->nonLocal = NewNonLocal (obj->nonLocal, NonLocalControl,
NON_LOCAL_BREAK|NON_LOCAL_CONTINUE);
obj = _CompileStat (obj, expr->tree.left, False, code);
obj->nonLocal = obj->nonLocal->prev;
continue_inst = obj->used;
obj = _CompileBoolExpr (obj, expr->tree.right, True, expr, code);
if (obj->used == continue_inst + 1 &&
(inst = ObjCode (obj, continue_inst))->base.opCode == OpConst)
{
Bool t = True (inst->constant.constant);
obj->used = continue_inst;
if (t)
{
BuildInst (obj, OpBranch, inst, expr);
inst->branch.offset = top_inst - ObjLast(obj);
inst->branch.mod = BranchModNone;
}
}
else
{
BuildInst (obj, OpBranchTrue, inst, expr);
inst->branch.offset = top_inst - ObjLast(obj);
inst->branch.mod = BranchModNone;
}
CompilePatchLoop (obj, top_inst, continue_inst, obj->used, -1);
break;
case FOR:
/*
* for (a; b; c) d
*
* a BRANCH d c b BRANCHTRUE
* +----------+
* +-----+
*/
/* a */
if (expr->tree.left->tree.left)
obj = _CompileExpr (obj, expr->tree.left->tree.left, False, expr, code);
test_inst = -1;
start_inst = -1;
/* check for b */
bobj = 0;
if (expr->tree.left->tree.right->tree.left)
{
bobj = NewObj (OBJ_INCR, OBJ_STAT_INCR);
bobj = _CompileBoolExpr (bobj,
expr->tree.left->tree.right->tree.left,
True, expr, code);
NewInst (obj, OpBranch, start_inst, expr);
}
/* check for c */
cobj = 0;
if (expr->tree.left->tree.right->tree.right->tree.left)
{
cobj = NewObj (OBJ_INCR, OBJ_STAT_INCR);
cobj = _CompileExpr (cobj, expr->tree.left->tree.right->tree.right->tree.left, False, expr, code);
}
top_inst = obj->used;
/* d */
obj->nonLocal = NewNonLocal (obj->nonLocal, NonLocalControl,
NON_LOCAL_BREAK|NON_LOCAL_CONTINUE);
obj = _CompileStat (obj, expr->tree.right, False, code);
obj->nonLocal = obj->nonLocal->prev;
/* glue c into place */
continue_inst = obj->used;
if (cobj)
{
middle_inst = obj->used;
obj = AppendObj (obj, cobj);
CompileMoveObj (obj, middle_inst, 0, middle_inst);
}
/* glue b into place */
if (bobj)
{
int middle_inst = obj->used;
obj = AppendObj (obj, bobj);
CompileMoveObj (obj, middle_inst, 0, middle_inst);
if (obj->used == middle_inst + 1 &&
(inst = ObjCode (obj, middle_inst))->base.opCode == OpConst)
{
Bool t = True(inst->constant.constant);
obj->used = middle_inst;
if (t)
{
BuildInst (obj, OpBranch, inst, expr);
inst->branch.offset = top_inst - ObjLast (obj);
inst->branch.mod = BranchModNone;
}
else
{
/* delete whole for body */
ResetInst (obj, start_inst);
continue_inst = top_inst = start_inst;
}
}
else
{
BuildInst (obj, OpBranchTrue, inst, expr);
inst->branch.offset = top_inst - ObjLast(obj);
inst->branch.mod = BranchModNone;
}
/* patch start branch */
inst = ObjCode (obj, start_inst);
inst->branch.offset = middle_inst - start_inst;
inst->branch.mod = BranchModNone;
}
else
{
BuildInst (obj, OpBranch, inst, expr);
inst->branch.offset = top_inst - ObjLast (obj);
inst->branch.mod = BranchModNone;
}
CompilePatchLoop (obj, top_inst, continue_inst, obj->used, -1);
break;
case SWITCH:
case UNION:
/*
* switch (a) { case b: c; case d: e; }
*
* a b CASE d CASE DEFAULT c e
* +------------------+
* +-------------+
* +--------+
*/
obj = _CompileExpr (obj, expr->tree.left, True, expr, code);
st = 0;
if (expr->base.tag == SWITCH)
SetPush (obj);
else
{
if (expr->tree.left->base.type)
{
Type *t;
t = TypeCanon (expr->tree.left->base.type);
if (t->base.tag == type_union)
st = t->structs.structs;
else if (!TypePoly (t))
{
CompileError (obj, expr, "Union switch type '%T' not union",
expr->tree.left->base.type);
}
}
}
c = expr->tree.right;
has_default = False;
ncase = 0;
while (c)
{
if (!c->tree.left->tree.left)
has_default = True;
else
ncase++;
c = c->tree.right;
}
/*
* Check to see if the union switch covers
* all possible values
*/
test_inst = 0;
if (expr->base.tag == UNION && st)
{
Bool missing = False;
int i;
Atom *atoms = StructTypeAtoms(st);
/*
* See if every member of the union has a case
*/
for (i = 0; i < st->nelements; i++)
{
c = expr->tree.right;
while (c)
{
ExprPtr pair = c->tree.left->tree.left;
if (pair)
{
Atom tag = pair->tree.left->atom.atom;
if (tag == atoms[i])
break;
}
c = c->tree.right;
}
if (!c)
{
missing = True;
break;
}
}
if (!missing)
{
test_inst = -1;
if (has_default)
CompileError (obj, expr, "Union switch has unreachable default");
}
if (missing && !has_default)
CompileError (obj, expr, "Union switch missing elements with no default");
}
case_inst = AllocateTemp (ncase * sizeof (int));
/*
* Compile the comparisons
*/
c = expr->tree.right;
icase = 0;
while (c)
{
if (c->tree.left->tree.left)
{
if (expr->base.tag == SWITCH)
obj = _CompileExpr (obj, c->tree.left->tree.left, True, c, code);
NewInst (obj, expr->base.tag == SWITCH ? OpCase : OpTagCase,
case_inst[icase], expr);
icase++;
}
c = c->tree.right;
}
/* add default case at the bottom */
if (test_inst == 0)
{
/* don't know what the opcode is yet */
NewInst (obj, expr->base.tag == SWITCH ? OpDefault : OpBranch,
test_inst, expr);
}
top_inst = obj->used;
obj->nonLocal = NewNonLocal (obj->nonLocal, NonLocalControl,
NON_LOCAL_BREAK);
/*
* Compile the statements
*/
c = expr->tree.right;
icase = 0;
while (c)
{
ExprPtr s = c->tree.left->tree.right;
/*
* Patch the branch
*/
if (c->tree.left->tree.left)
{
inst = ObjCode (obj, case_inst[icase]);
if (expr->base.tag == SWITCH)
{
inst->branch.offset = obj->used - case_inst[icase];
inst->branch.mod = BranchModNone;
}
else
{
ExprPtr pair = c->tree.left->tree.left;
Atom tag = pair->tree.left->atom.atom;
Type *mt = typePoly;
/*
* Find the member type
*/
if (st)
{
mt = StructMemType (st, tag);
if (!mt)
{
mt = typePoly;
CompileError (obj, expr, "Union case tag '%A' not in type '%T'",
tag, expr->tree.left->base.type);
}
}
/*
* Make sure there's no fall-through
*/
if (icase > 0 && pair->tree.right &&
CompileIsReachable (obj, obj->used))
{
CompileError (obj, expr,
"Fall-through case with variant value");
}
inst->tagcase.offset = obj->used - case_inst[icase];
inst->tagcase.tag = tag;
/*
* this side holds the name to assign the
* switch value to. Set it's type and
* build the assignment
*/
if (pair->tree.right)
{
SymbolPtr name = pair->tree.right->atom.symbol;
InstPtr assign;
name->symbol.type = mt;
CompileStorage (obj, expr, name, code);
if (ClassFrame (name->symbol.class))
{
BuildInst (obj, OpTagLocal, assign, expr);
assign->frame.staticLink = 0;
assign->frame.element = name->local.element;
}
else
{
BuildInst (obj, OpTagGlobal, assign, expr);
assign->box.box = name->global.value;
}
}
}
icase++;
}
else if (test_inst >= 0)
{
inst = ObjCode (obj, test_inst);
if (expr->base.tag == SWITCH)
inst->base.opCode = OpDefault;
else
inst->base.opCode = OpBranch;
inst->branch.offset = obj->used - test_inst;
inst->branch.mod = BranchModNone;
test_inst = -1;
}
while (s->tree.left)
{
obj = _CompileStat (obj, s->tree.left, False, code);
s = s->tree.right;
}
c = c->tree.right;
}
obj->nonLocal = obj->nonLocal->prev;
/*
* Add a default branch if necessary
*/
if (test_inst >= 0)
{
inst = ObjCode (obj, test_inst);
inst->branch.offset = obj->used - test_inst;
inst->branch.mod = BranchModNone;
}
CompilePatchLoop (obj, top_inst, -1, obj->used, -1);
break;
case FUNC:
obj = CompileDecl (obj, expr, False, expr, code);
break;
case TYPEDEF:
if (expr->tree.left->decl.type)
obj = CompileType (obj, expr->tree.left, expr->tree.left->decl.type,
expr, code);
break;
case OC:
while (expr->tree.left)
{
obj = _CompileStat (obj, expr->tree.left, last && !expr->tree.right->tree.left, code);
expr = expr->tree.right;
}
break;
case BREAK:
obj = _CompileNonLocal (obj, BranchModBreak, expr, code);
break;
case CONTINUE:
obj = _CompileNonLocal (obj, BranchModContinue, expr, code);
break;
case RETURNTOK:
if (!code || !code->base.func)
{
CompileError (obj, expr, "return not in function");
break;
}
if (expr->tree.right)
{
if (expr->tree.right->base.tag == OP &&
_CompileCanTailCall (obj, code))
{
obj = CompileCall (obj, expr->tree.right, TailAlways, expr, code, False);
}
else
{
obj = _CompileExpr (obj, expr->tree.right, True, expr, code);
}
if (ObjCode (obj, ObjLast (obj))->base.opCode != OpTailCall)
obj = _CompileNonLocal (obj, BranchModReturn, expr, code);
expr->base.type = expr->tree.right->base.type;
}
else
{
obj = _CompileNonLocal (obj, BranchModReturnVoid, expr, code);
expr->base.type = typePrim[rep_void];
}
if (!TypeCombineBinary (code->base.func->base.type, ASSIGN, expr->base.type))
{
CompileError (obj, expr, "Incompatible types, formal '%T', actual '%T', for return",
code->base.type, expr->base.type);
break;
}
break;
case EXPR:
if (last && expr->tree.left->base.tag == OP && !profiling)
obj = CompileCall (obj, expr->tree.left, TailVoid, expr, code, False);
else
obj = _CompileExpr (obj, expr->tree.left, False, expr, code);
break;
case SEMI:
break;
case NAMESPACE:
obj = _CompileStat (obj, expr->tree.right, last, code);
break;
case IMPORT:
break;
case CATCH:
obj = CompileCatch (obj, expr->tree.left, expr->tree.right, expr, code, 0);
break;
case RAISE:
obj = CompileRaise (obj, expr, expr, code);
break;
case TWIXT:
obj = CompileTwixt (obj, expr, expr, code);
break;
}
RETURN (obj);
}
static Bool
CompileIsUnconditional (InstPtr inst)
{
switch (inst->base.opCode) {
case OpBranch:
case OpFarJump:
case OpDefault:
case OpReturn:
case OpReturnVoid:
case OpTailCall:
case OpCatch:
case OpRaise:
return True;
default:
return False;
}
}
static Bool
CompileIsBranch (InstPtr inst)
{
switch (inst->base.opCode) {
case OpBranch:
case OpBranchFalse:
case OpBranchTrue:
case OpFarJump:
case OpCase:
case OpTagCase:
case OpDefault:
case OpCatch:
return True;
default:
return False;
}
}
static Bool
CompileIsReachable (ObjPtr obj, int target)
{
InstPtr inst;
int i;
for (i = 0; i < obj->used; i++)
{
inst = ObjCode (obj, i);
if (CompileIsBranch (inst) && i + inst->branch.offset == target)
return True;
if (i == target - 1 && !CompileIsUnconditional (inst))
return True;
}
return False;
}
ObjPtr
CompileFuncCode (CodePtr code,
ExprPtr stat,
CodePtr previous,
NonLocalPtr nonLocal)
{
ENTER ();
ObjPtr obj;
InstPtr inst;
Bool needReturn;
code->base.previous = previous;
obj = NewObj (OBJ_INCR, OBJ_STAT_INCR);
obj->nonLocal = nonLocal;
obj = _CompileStat (obj, code->func.code, True, code);
needReturn = False;
if (!obj->used || CompileIsReachable (obj, obj->used))
needReturn = True;
if (needReturn)
{
/*
* If control reaches the end of the function,
* flag an error for non-void functions,
* don't complain about void functions or catch blocks
*/
if (!nonLocal &&
!TypeCombineBinary (code->base.func->base.type, ASSIGN,
typePrim[rep_void]))
{
CompileError (obj, stat, "Control reaches end of function with type '%T'",
code->base.func->base.type);
}
BuildInst (obj, OpReturnVoid, inst, stat);
}
#ifdef DEBUG
ObjDump (obj, 0);
FileFlush (FileStdout, True);
#endif
RETURN (obj);
}
ObjPtr
CompileFunc (ObjPtr obj,
CodePtr code,
ExprPtr stat,
CodePtr previous,
NonLocalPtr nonLocal)
{
ENTER ();
InstPtr inst;
ArgType *args;
ObjPtr staticInit;
for (args = code->base.args; args; args = args->next)
{
CompileStorage (obj, stat, args->symbol, code);
if (!args->varargs)
code->base.argc++;
}
code->func.body.obj = CompileFuncCode (code, stat, previous, nonLocal);
obj->error |= code->func.body.obj->error;
BuildInst (obj, OpObj, inst, stat);
inst->code.code = code;
if ((staticInit = code->func.staticInit.obj))
{
SetPush (obj);
BuildInst (staticInit, OpStaticDone, inst, stat);
/* BuildInst (staticInit, OpEnd, inst, stat); */
#ifdef DEBUG
ObjDump (staticInit, 1);
FileFlush (FileStdout, True);
#endif
code->func.staticInit.obj = staticInit;
BuildInst (obj, OpStaticInit, inst, stat);
BuildInst (obj, OpNoop, inst, stat);
obj->error |= staticInit->error;
}
RETURN (obj);
}
/*
* Get the class, defaulting as appropriate
*/
static Class
CompileDeclClass (ExprPtr decls, CodePtr code)
{
Class class;
class = decls ? decls->decl.class : class_undef;
if (class == class_undef)
class = code ? class_auto : class_global;
return class;
}
/*
* Find the code object to compile the declaration in
*/
static CodePtr
CompileDeclCodeCompile (Class class, CodePtr code)
{
CodePtr code_compile = 0;
switch (class) {
case class_global:
case class_const:
/*
* Globals are compiled in the static initializer for
* the outermost enclosing function.
*/
code_compile = code;
while (code_compile && code_compile->base.previous)
code_compile = code_compile->base.previous;
break;
case class_static:
/*
* Statics are compiled in the static initializer for
* the nearest enclosing function
*/
code_compile = code;
break;
case class_auto:
case class_arg:
/*
* Autos are compiled where they lie; just make sure a function
* exists somewhere to hang them from
*/
break;
default:
break;
}
return code_compile;
}
static ObjPtr *
CompileDeclInitObjStart (ObjPtr *obj, CodePtr code, CodePtr code_compile)
{
ObjPtr *initObj = obj;
if (code_compile)
{
if (!code_compile->func.staticInit.obj)
code_compile->func.staticInit.obj = NewObj (OBJ_INCR, OBJ_STAT_INCR);
initObj = &code_compile->func.staticInit.obj;
code_compile->func.inStaticInit = True;
if (code != code_compile)
code->func.inGlobalInit = True;
}
return initObj;
}
static void
CompileDeclInitObjFinish (ObjPtr *obj, ObjPtr *initObj, CodePtr code, CodePtr code_compile)
{
if (code_compile)
{
code_compile->func.inStaticInit = False;
code->func.inGlobalInit = False;
}
}
/*
* Compile a type. This consists only of compiling array dimension expressions
* so those values can be used later
*/
static ObjPtr
CompileArrayDimValue (ObjPtr obj, TypePtr type, Bool lvalue, ExprPtr stat, CodePtr code)
{
ENTER ();
InstPtr inst = 0;
int d;
CodePtr c;
switch (type->array.storage) {
case DimStorageNone:
assert (0);
break;
case DimStorageGlobal:
BuildInst (obj, OpGlobal, inst, stat);
inst->box.box = type->array.u.global;
break;
case DimStorageStatic:
case DimStorageAuto:
d = 0;
for (c = code; c && c != type->array.u.frame.code; c = c->base.previous)
d++;
if (type->array.storage == DimStorageStatic)
{
BuildInst (obj, OpStatic, inst, stat);
}
else
{
BuildInst (obj, OpLocal, inst, stat);
}
inst->frame.staticLink = d;
inst->frame.element = type->array.u.frame.element;
break;
}
if (lvalue)
inst->base.opCode += 2;
RETURN (obj);
}
static ObjPtr
CompileArrayDims (ObjPtr obj, ExprPtr dim, ExprPtr stat, CodePtr code)
{
ENTER ();
if (dim)
{
InstPtr inst;
obj = CompileArrayDims (obj, dim->tree.right, stat, code);
obj = _CompileExpr (obj, dim->tree.left, True, stat, code);
BuildInst (obj, OpInitArray, inst, stat);
inst->ainit.dim = 0;
inst->ainit.mode = AInitModeElement;
}
RETURN (obj);
}
static ObjPtr
CompileArrayType (ObjPtr obj, ExprPtr decls, TypePtr type, ExprPtr stat, CodePtr code)
{
ENTER ();
type->array.dims = CompileCountDeclDimensions (type->array.dimensions);
if (type->array.dims && type->array.dimensions->tree.left)
{
Class class = CompileDeclClass (decls, code);
CodePtr code_compile = CompileDeclCodeCompile (class, code);
ExprPtr dim = type->array.dimensions;
InstPtr inst;
ObjPtr *initObj;
CompileDimensionStorage (obj, class, type, code);
initObj = CompileDeclInitObjStart (&obj, code, code_compile);
/*
* Prepare the lvalue for assignment
*/
*initObj = CompileArrayDimValue (*initObj, type, True, stat, code);
/*
* Allocate an array for the dimension information
*/
SetPush (*initObj);
BuildInst (*initObj, OpConst, inst, stat);
inst->constant.constant = NewInt (type->array.dims);
SetPush (*initObj);
BuildInst (*initObj, OpBuildArray, inst, stat);
inst->array.ndim = 1;
inst->array.resizable = False;
inst->array.type = typePrim[rep_integer];
/*
* Initialize the dimension array
*/
BuildInst (*initObj, OpInitArray, inst, stat);
inst->ainit.mode = AInitModeStart;
inst->ainit.dim = 1;
*initObj = CompileArrayDims (*initObj, dim, stat, code);
BuildInst (*initObj, OpInitArray, inst, stat);
inst->ainit.dim = 1;
inst->ainit.mode = AInitModeElement;
/*
* Assign it
*/
BuildInst (*initObj, OpAssign, inst, stat);
inst->assign.initialize = True;
}
RETURN (obj);
}
static ObjPtr
CompileType (ObjPtr obj, ExprPtr decls, TypePtr type, ExprPtr stat, CodePtr code)
{
ENTER();
ArgType *at;
StructType *st;
TypeElt *et;
int i;
switch (type->base.tag) {
case type_prim:
break;
case type_name:
break;
case type_ref:
obj = CompileType (obj, decls, type->ref.ref, stat, code);
break;
case type_func:
obj = CompileType (obj, decls, type->func.ret, stat, code);
for (at = type->func.args; at; at = at->next)
obj = CompileType (obj, decls, at->type, stat, code);
break;
case type_array:
obj = CompileArrayType (obj, decls, type, stat, code);
obj = CompileType (obj, decls, type->array.type, stat, code);
break;
case type_hash:
obj = CompileType (obj, decls, type->hash.type, stat, code);
obj = CompileType (obj, decls, type->hash.keyType, stat, code);
break;
case type_struct:
case type_union:
st = type->structs.structs;
for (i = 0; i < st->nelements; i++)
obj = CompileType (obj, decls, BoxTypesElements(st->types)[i], stat, code);
break;
case type_types:
for (et = type->types.elt; et; et = et->next)
obj = CompileType (obj, decls, et->type, stat, code);
break;
}
RETURN (obj);
}
/*
* Compile a declaration expression. Allocate storage for the symbol,
* Typecheck and compile initializers, make sure a needed value
* is left in the accumulator
*/
ObjPtr
CompileDecl (ObjPtr obj, ExprPtr decls,
Bool evaluate, ExprPtr stat, CodePtr code)
{
ENTER ();
SymbolPtr s = 0;
DeclListPtr decl;
TypePtr type = decls->decl.type;
Class class = CompileDeclClass (decls, code);
CodePtr code_compile = CompileDeclCodeCompile (class, code);
ObjPtr *initObj;
if (ClassFrame (class) && !code)
{
CompileError (obj, decls, "Invalid storage class %C", class);
decls->base.type = typePoly;
RETURN (obj);
}
if (type)
obj = CompileType (obj, decls, type, stat, code);
for (decl = decls->decl.decl; decl; decl = decl->next) {
ExprPtr init;
s = decl->symbol;
CompileStorage (obj, decls, s, code);
/*
* Automatically build initializers for composite types
* which fully specify the storage
*/
init = decl->init;
if (!init && s)
init = CompileImplicitInit (s->symbol.type);
if (init)
{
InstPtr inst;
ExprPtr lvalue;
/*
* Compile the initializer value
*/
initObj = CompileDeclInitObjStart (&obj, code, code_compile);
/*
* Assign it
*/
lvalue = NewExprAtom (decl->name, decl->symbol, False);
*initObj = CompileLvalue (*initObj, lvalue,
decls, code, False, True, True,
CompileRefType (obj, lvalue, s->symbol.type) != 0,
False);
SetPush (*initObj);
*initObj = CompileInit (*initObj, init, s->symbol.type, stat, code);
CompileDeclInitObjFinish (&obj, initObj, code, code_compile);
BuildInst (*initObj, OpAssign, inst, stat);
inst->assign.initialize = True;
}
}
if (evaluate)
{
if (s)
{
InstPtr inst;
switch (class) {
case class_global:
case class_const:
BuildInst (obj, OpGlobal, inst, stat);
inst->box.box = s->global.value;
break;
case class_static:
BuildInst (obj, OpStatic, inst, stat);
inst->frame.staticLink = 0;
inst->frame.element = s->local.element;
break;
case class_auto:
case class_arg:
BuildInst (obj, OpLocal, inst, stat);
inst->frame.staticLink = 0;
inst->frame.element = s->local.element;
break;
default:
break;
}
decls->base.type = s->symbol.type;
}
else
decls->base.type = typePoly;
}
RETURN (obj);
}
ObjPtr
CompileStat (ExprPtr expr, CodePtr code)
{
ENTER ();
ObjPtr obj;
InstPtr inst;
obj = NewObj (OBJ_INCR, OBJ_STAT_INCR);
obj = _CompileStat (obj, expr, False, code);
BuildInst (obj, OpEnd, inst, expr);
#ifdef DEBUG
ObjDump (obj, 0);
FileFlush (FileStdout, True);
#endif
RETURN (obj);
}
ObjPtr
CompileExpr (ExprPtr expr, CodePtr code)
{
ENTER ();
ObjPtr obj;
InstPtr inst;
ExprPtr stat;
stat = NewExprTree (EXPR, expr, 0);
obj = NewObj (OBJ_INCR, OBJ_STAT_INCR);
obj = _CompileExpr (obj, expr, True, stat, code);
BuildInst (obj, OpEnd, inst, stat);
#ifdef DEBUG
ObjDump (obj, 0);
FileFlush (FileStdout, True);
#endif
RETURN (obj);
}
const char *const OpNames[] = {
"Noop",
/*
* Statement op codes
*/
"Branch",
"BranchFalse",
"BranchTrue",
"Case",
"TagCase",
"TagGlobal",
"TagLocal",
"Default",
"Return",
"ReturnVoid",
"Fork",
"Catch",
"EndCatch",
"Raise",
"OpTwixt",
"OpTwixtDone",
"OpEnterDone",
"OpLeaveDone",
"OpFarJump",
"OpUnwind",
/*
* Expr op codes
*/
"Global",
"GlobalRef",
"GlobalRefStore",
"Static",
"StaticRef",
"StaticRefStore",
"Local",
"LocalRef",
"LocalRefStore",
"Fetch",
"Const",
"BuildArray",
"BuildArrayInd",
"InitArray",
"BuildHash",
"InitHash",
"InitHashDef",
"BuildStruct",
"InitStruct",
"BuildUnion",
"InitUnion",
"Array",
"ArrayRef",
"ArrayRefStore",
"VarActual",
"Call",
"TailCall",
"ExceptionCall",
"Dot",
"DotRef",
"DotRefStore",
"Arrow",
"ArrowRef",
"ArrowRefStore",
"Obj",
"StaticInit",
"StaticDone",
"BinOp",
"BinFunc",
"UnOp",
"UnFunc",
"PreOp",
"PostOp",
"Assign",
"AssignOp",
"AssignFunc",
"End",
"Drop",
};
static char *
ObjBinFuncName (BinaryFunc func)
{
static const struct {
BinaryFunc func;
char *name;
} funcs[] = {
{ ShiftL, "ShiftL" },
{ ShiftR, "ShiftR" },
{ Lxor, "Lxor" },
{ NotEqual, "NotEqual" },
{ Greater, "Greater" },
{ LessEqual, "LessEqual" },
{ GreaterEqual, "GreaterEqual" },
{ 0, 0 }
};
int i;
for (i = 0; funcs[i].func; i++)
if (funcs[i].func == func)
return funcs[i].name;
return "<unknown>";
}
static char *
ObjUnFuncName (UnaryFunc func)
{
static const struct {
UnaryFunc func;
char *name;
} funcs[] = {
{ Dereference, "Dereference" },
{ Lnot, "Lnot" },
{ Not, "Not" },
{ Factorial, "Factorial" },
{ do_reference, "do_reference" },
{ 0, 0 }
};
int i;
for (i = 0; funcs[i].func; i++)
if (funcs[i].func == func)
return funcs[i].name;
return "<unknown>";
}
static void
ObjIndent (int indent)
{
int j;
for (j = 0; j < indent; j++)
FilePrintf (FileStdout, " ");
}
static char *
BranchModName (BranchMod mod)
{
switch (mod) {
case BranchModNone: return "BranchModNone";
case BranchModBreak: return "BranchModBreak";
case BranchModContinue: return "BranchModContinue";
case BranchModReturn: return "BranchModReturn";
case BranchModReturnVoid: return "BranchModReturnVoid";
case BranchModCatch: return "BranchModCatch";
}
return "?";
}
void
InstDump (InstPtr inst, int indent, int i, int *branch, int maxbranch)
{
int j;
Bool realBranch = False;
#ifdef DEBUG
FilePrintf (FileStdout, "%x: ", (int) inst);
#endif
ObjIndent (indent);
FilePrintf (FileStdout, "%s%s %c ",
OpNames[inst->base.opCode],
" " + strlen(OpNames[inst->base.opCode]),
inst->base.flags & InstPush ? '^' : ' ');
switch (inst->base.opCode) {
case OpTagCase:
FilePrintf (FileStdout, "(%A) ", inst->tagcase.tag);
goto branch;
case OpCatch:
FilePrintf (FileStdout, "\"%A\" ",
inst->catch.exception->symbol.name);
goto branch;
case OpBranch:
case OpBranchFalse:
case OpBranchTrue:
case OpCase:
case OpDefault:
realBranch = True;
branch:
if (branch)
{
j = i + inst->branch.offset;
if (0 <= j && j < maxbranch)
FilePrintf (FileStdout, "branch L%d", branch[j]);
else
FilePrintf (FileStdout, "Broken branch %d", inst->branch.offset);
}
else
FilePrintf (FileStdout, "branch %d", inst->branch.offset);
if (realBranch)
FilePrintf (FileStdout, " %s",
BranchModName (inst->branch.mod));
break;
case OpReturn:
case OpReturnVoid:
break;
case OpFork:
FilePrintf (FileStdout, "\n");
ObjDump (inst->obj.obj, indent+1);
break;
case OpEndCatch:
FilePrintf (FileStdout, " %d catches", inst->ints.value);
break;
case OpRaise:
FilePrintf (FileStdout, "%A", inst->raise.exception->symbol.name);
FilePrintf (FileStdout, " argc %d", inst->raise.argc);
break;
case OpTwixt:
if (branch)
{
j = i + inst->twixt.enter;
if (0 <= j && j < maxbranch)
FilePrintf (FileStdout, "enter L%d", branch[j]);
else
FilePrintf (FileStdout, "Broken enter %d", inst->branch.offset);
j = i + inst->twixt.leave;
if (0 <= j && j < maxbranch)
FilePrintf (FileStdout, " leave L%d", branch[j]);
else
FilePrintf (FileStdout, " Broken leave %d", inst->branch.offset);
}
else
{
FilePrintf (FileStdout, "enter %d leave %d",
inst->twixt.enter, inst->twixt.leave);
}
break;
case OpFarJump:
FilePrintf (FileStdout, "twixt %d catch %d frame %d inst %d mod %s",
inst->farJump.farJump->twixt,
inst->farJump.farJump->catch,
inst->farJump.farJump->frame,
inst->farJump.farJump->inst,
BranchModName (inst->farJump.mod));
break;
case OpUnwind:
FilePrintf (FileStdout, "twixt %d catch %d",
inst->unwind.twixt, inst->unwind.catch);
break;
case OpStatic:
case OpStaticRef:
case OpStaticRefStore:
case OpLocal:
case OpLocalRef:
case OpLocalRefStore:
case OpTagLocal:
FilePrintf (FileStdout, " (link %d elt %d)",
inst->frame.staticLink, inst->frame.element);
break;
case OpConst:
FilePrintf (FileStdout, "%v", inst->constant.constant);
break;
case OpCall:
case OpTailCall:
FilePrintf (FileStdout, "%d args", inst->ints.value);
break;
case OpBuildArray:
FilePrintf (FileStdout, "%d dims %sresizable",
inst->array.ndim,
inst->array.resizable ? "" : "un");
break;
case OpInitArray:
FilePrintf (FileStdout, "%d %s",
inst->ainit.dim,
inst->ainit.mode == AInitModeStart ? "start":
inst->ainit.mode == AInitModeElement ? "element":
inst->ainit.mode == AInitModeRepeat ? "repeat":
inst->ainit.mode == AInitModeFunc ? "func":
inst->ainit.mode == AInitModeTest ? "test": "?");
break;
case OpDot:
case OpDotRef:
case OpDotRefStore:
case OpArrow:
case OpArrowRef:
case OpArrowRefStore:
case OpInitStruct:
case OpInitUnion:
FilePrintf (FileStdout, "%s", AtomName (inst->atom.atom));
break;
case OpObj:
FilePrintf (FileStdout, "\n");
if (inst->code.code->func.staticInit.obj)
{
ObjIndent (indent);
FilePrintf (FileStdout, "Static initializer:\n");
ObjDump (inst->code.code->func.staticInit.obj, indent+1);
ObjIndent (indent);
FilePrintf (FileStdout, "Function body:\n");
}
ObjDump (inst->code.code->func.body.obj, indent+1);
break;
case OpBinOp:
case OpPreOp:
case OpPostOp:
case OpAssignOp:
FilePrintf (FileStdout, "%O", inst->binop.op);
break;
case OpBinFunc:
case OpAssignFunc:
FilePrintf (FileStdout, "%s", ObjBinFuncName (inst->binfunc.func));
break;
case OpUnOp:
FilePrintf (FileStdout, "%U", inst->unop.op);
break;
case OpUnFunc:
FilePrintf (FileStdout, "%s", ObjUnFuncName (inst->unfunc.func));
break;
default:
break;
}
FilePrintf (FileStdout, "\n");
}
void
ObjDump (ObjPtr obj, int indent)
{
int i, j;
InstPtr inst;
ExprPtr stat;
int *branch;
int b;
branch = AllocateTemp (obj->used * sizeof (int));
memset (branch, '\0', obj->used * sizeof (int));
ObjIndent (indent);
FilePrintf (FileStdout, "%d instructions %d statements (0x%x)\n",
obj->used, obj->used_stat, ObjCode(obj,0));
b = 0;
for (i = 0; i < obj->used; i++)
{
inst = ObjCode(obj, i);
if (CompileIsBranch (inst))
{
j = i + inst->branch.offset;
if (0 <= j && j < obj->used)
if (!branch[j])
branch[j] = ++b;
}
if (inst->base.opCode == OpTwixt)
{
j = i + inst->twixt.enter;
if (0 <= j && j < obj->used)
if (!branch[j])
branch[j] = ++b;
j = i + inst->twixt.leave;
if (0 <= j && j < obj->used)
if (!branch[j])
branch[j] = ++b;
}
}
b = 0;
stat = 0;
for (i = 0; i < obj->used; i++)
{
ExprPtr nextStat = ObjStatement (obj, inst = ObjCode (obj, i));
if (nextStat && nextStat != stat)
{
stat = nextStat;
FilePrintf (FileStdout, " ");
PrettyStat (FileStdout, stat, False);
}
if (branch[i])
FilePrintf (FileStdout, "L%d:\n", branch[i]);
InstDump (inst, indent, i, branch, obj->used);
}
}
syntax highlighted by Code2HTML, v. 0.9.1