/*****
* stm.cc
* Andy Hammerlindl 2002/8/30
*
* Statements are everything in the language that do something on their
* own. Statements are different from declarations in that statements
* do not modify the environment. Translation of a statement puts the
* stack code to run it into the instruction stream.
*****/
void stm::prettyprint(ostream &out, Int indent)
{
prettyname(out,"stm",indent, getPos());
}
void emptyStm::prettyprint(ostream &out, Int indent)
{
prettyname(out,"emptyStm",indent, getPos());
}
void blockStm::prettyprint(ostream &out, Int indent)
{
prettyname(out,"blockStm",indent, getPos());
base->prettyprint(out, indent+1);
}
void expStm::prettyprint(ostream &out, Int indent)
{
prettyname(out,"expStm",indent, getPos());
body->prettyprint(out, indent+1);
}
void baseExpTrans(coenv &e, exp *expr)
{
types::ty_kind kind = expr->trans(e)->kind;
if (kind != types::ty_void)
// Remove any value it puts on the stack.
e.c.encodePop();
}
// For an object such as currentpicture, write 'picture currentpicture' to
// give some information. Only do this when the object has a name.
void tryToWriteTypeOfExp(types::ty *t, exp *body)
{
symbol name=body->getName();
if (!name)
return;
// Store the value in a new variable of the proper type.
varEntry *v = makeVarEntry(expr->getPos(), e, 0, t);
e.e.addVar(symbol::trans("operator answer"), v);
v->getLocation()->encode(WRITE, expr->getPos(), e.c);
e.c.encodePop();
}
position pos=expr->getPos();
baseExpTrans(e, new callExp(pos, new nameExp(pos, "write"),
new nameExp(pos, "operator answer")));
}
void tryToWriteExp(coenv &e, exp *expr)
{
position pos=expr->getPos();
types::ty *t=expr->cgetType(e);
if(!t) return;
// If the original expression is bad, just print the errors.
// If it is a function which returns void, just call the function.
if (t->kind == ty_error || t->kind == ty_void) {
baseExpTrans(e, expr);
return;
}
types::ty *ct=call->getType(e);
if (ct->kind == ty_error || ct->kind == ty_overloaded) {
if (t->kind == ty_overloaded) {
// Translate the expr in order to print the ambiguity error first.
expr->trans(e);
em.sync(true);
assert(em.errors());
// Then, write out all of the types.
tryToWriteTypeOfExp(t, expr);
}
else {
// Write the type of the expression and, since it is unique, assign it to
// 'operator answer' even though its value isn't printed.
tryToWriteTypeOfExp(t, expr);
storeExp(e, t, expr);
}
}
else if (t->kind == ty_overloaded) {
// If the exp is overloaded, but the act of writing makes it
// unambiguous, add a suffix to the output to warn the user of this.
exp *suffix=new nameExp(pos,
symbol::trans("overloadedMessage"));
exp *callWithSuffix=new callExp(pos,
callee, expr, suffix);
void expStm::interactiveTrans(coenv &e)
{
// First check if it is the kind of expression that should be written.
if (body->writtenToPrompt() &&
settings::getSetting<bool>("interactiveWrite"))
tryToWriteExp(e, body);
else
baseExpTrans(e, body);
}
void ifStm::prettyprint(ostream &out, Int indent)
{
prettyname(out,"ifStm",indent, getPos());
test->prettyprint(out, indent+1);
onTrue->prettyprint(out, indent+1);
if (onFalse)
onFalse->prettyprint(out, indent+1);
}
void transLoopBody(coenv &e, stm *body) {
// The semantics of the language are defined so that any variable declared
// inside a loop are new variables for each iteration of the loop. For
// instance, the code
//
// int f();
// for (int i = 0; i < 10; ++i) {
// int j=10*i;
// if (i == 5)
// f = new int() { return j; };
// }
// write(f());
//
// will write 50. This is implemented by allocating a new frame for each
// iteration. However, this can have a big performance hit, so we first
// translate the code without the frame, check if it needed the closure, and
// rewrite the code if necessary.
label start = e.c.defNewLabel();
// Encode a no-op, in case we need to jump over the default implementation
// to a special case.
e.c.encode(inst::nop);
body->markTrans(e);
// Don't re-translate if there were errors.
if (em.errors())
return;
if (e.c.usesClosureSinceLabel(start)){
// Jump over the old section.
label end = e.c.defNewLabel();
e.c.encodePatch(start, end);
// Let coder know that break and continue need to pop the frame.
e.c.loopPushesFrame();
void forStm::prettyprint(ostream &out, Int indent)
{
prettyname(out,"forStm",indent, getPos());
if (init) init->prettyprint(out, indent+1);
if (test) test->prettyprint(out, indent+1);
if (update) update->prettyprint(out, indent+1);
body->prettyprint(out, indent+1);
}
void forStm::trans(coenv &e)
{
// Any vardec in the initializer needs its own scope.
e.e.beginScope();
if (init)
init->markTrans(e);
label ctarget = e.c.fwdLabel();
label end = e.c.fwdLabel();
e.c.pushLoop(ctarget, end);
void extendedForStm::trans(coenv &e) {
// Translate into the syntax:
//
// start[] a = set;
// for (int i=0; i < a.length; ++i) {
// start var=a[i];
// body
// }
position pos=getPos();
// Use gensyms for the variable names so as not to pollute the namespace.
symbol a=symbol::gensym("a");
symbol i=symbol::gensym("i");
// Get the start type. Handle type inference as a special case.
types::ty *t = start->trans(e, true);
if (t->kind == types::ty_inferred) {
// First ensure the array expression is an unambiguous array.
types::ty *at = set->cgetType(e);
if (at->kind != ty_array) {
em.error(set->getPos());
em << "expression is not an array of inferable type";
// On failure, don't bother trying to translate the loop.
return;
}
// var a=set;
tyEntryTy tet(pos, primInferred());
decid dec1(pos, new decidstart(pos, a), set);
vardec(pos, &tet, &dec1).trans(e);
}
else {
// start[] a=set;
arrayTy at(pos, start, new dimensions(pos));
decid dec1(pos, new decidstart(pos, a), set);
vardec(pos, &at, &dec1).trans(e);
}
// { start var=a[i]; body }
block b(pos);
decid dec2(pos,
new decidstart(pos, var),
new subscriptExp(pos, new nameExp(pos, a),
new nameExp(pos, i)));
b.add(new vardec(pos, start, &dec2));
b.add(body);
// for (int i=0; i < a.length; ++i)
// <block>
forStm(pos,
new vardec(pos, new tyEntryTy(pos, primInt()),
new decid(pos,
new decidstart(pos, i),
new intExp(pos, 0))),
new binaryExp(pos,
new nameExp(pos, i),
SYM_LT,
new nameExp(pos,
new qualifiedName(pos,
new simpleName(pos, a),
symbol::trans("length")))),
new expStm(pos, new prefixExp(pos, new nameExp(pos, i), SYM_PLUS)),
new blockStm(pos, &b)).trans(e);
}
void breakStm::prettyprint(ostream &out, Int indent)
{
prettyname(out,"breakStm",indent, getPos());
}
void breakStm::trans(coenv &e)
{
if (!e.c.encodeBreak()) {
em.error(getPos());
em << "break statement outside of a loop";
}
}
void continueStm::prettyprint(ostream &out, Int indent)
{
prettyname(out,"continueStm",indent, getPos());
}
void continueStm::trans(coenv &e)
{
if (!e.c.encodeContinue()) {
em.error(getPos());
em << "continue statement outside of a loop";
}
}
void returnStm::prettyprint(ostream &out, Int indent)
{
prettyname(out, "returnStm",indent, getPos());
if (t->kind == ty_void) {
if (value) {
em.error(getPos());
em << "function cannot return a value";
}
if (e.c.isRecord())
e.c.encode(inst::pushclosure);
}
else {
if (value) {
value->transToType(e, t);
}
else {
em.error(getPos());
em << "function must return a value";
}
}
// NOTE: Currently, a return statement in a module definition will end
// the initializer. Should this be allowed?
e.c.encode(inst::ret);
}
void stmExpList::prettyprint(ostream &out, Int indent)
{
prettyname(out, "stmExpList",indent, getPos());
for (mem::list<stm *>::iterator p = stms.begin(); p != stms.end(); ++p)
(*p)->prettyprint(out, indent+1);
}
void stmExpList::trans(coenv &e)
{
for (mem::list<stm *>::iterator p = stms.begin(); p != stms.end(); ++p)
(*p)->markTrans(e);
}