Make function calls tail-recursive

This commit is contained in:
Eelco Dolstra 2013-11-07 17:04:36 +00:00
parent 273322c773
commit c897bac549
4 changed files with 65 additions and 40 deletions

View File

@ -259,6 +259,11 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1, con
throw TypeError(format(s) % s1 % s2); throw TypeError(format(s) % s1 % s2);
} }
LocalNoInlineNoReturn(void throwTypeError(const char * s, const ExprLambda & fun, const Symbol & s2))
{
throw TypeError(format(s) % fun.showNamePos() % s2);
}
LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos)) LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos))
{ {
throw AssertionError(format(s) % pos); throw AssertionError(format(s) % pos);
@ -700,16 +705,13 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v)
void ExprApp::eval(EvalState & state, Env & env, Value & v) void ExprApp::eval(EvalState & state, Env & env, Value & v)
{ {
Value vFun; e1->eval(state, env, v);
e1->eval(state, env, vFun); state.callFunction(v, *(e2->maybeThunk(state, env)), v);
state.callFunction(vFun, *(e2->maybeThunk(state, env)), v);
} }
void EvalState::callFunction(Value & fun, Value & arg, Value & v) void EvalState::callPrimOp(Value & fun, Value & arg, Value & v)
{ {
if (fun.type == tPrimOp || fun.type == tPrimOpApp) {
/* Figure out the number of arguments still needed. */ /* Figure out the number of arguments still needed. */
unsigned int argsDone = 0; unsigned int argsDone = 0;
Value * primOp = &fun; Value * primOp = &fun;
@ -736,11 +738,19 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
if (countCalls) primOpCalls[primOp->primOp->name]++; if (countCalls) primOpCalls[primOp->primOp->name]++;
primOp->primOp->fun(*this, vArgs, v); primOp->primOp->fun(*this, vArgs, v);
} else { } else {
Value * fun2 = allocValue();
*fun2 = fun;
v.type = tPrimOpApp; v.type = tPrimOpApp;
v.primOpApp.left = allocValue(); v.primOpApp.left = fun2;
*v.primOpApp.left = fun;
v.primOpApp.right = &arg; v.primOpApp.right = &arg;
} }
}
void EvalState::callFunction(Value & fun, Value & arg, Value & v)
{
if (fun.type == tPrimOp || fun.type == tPrimOpApp) {
callPrimOp(fun, arg, v);
return; return;
} }
@ -772,7 +782,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
Bindings::iterator j = arg.attrs->find(i->name); Bindings::iterator j = arg.attrs->find(i->name);
if (j == arg.attrs->end()) { if (j == arg.attrs->end()) {
if (!i->def) throwTypeError("%1% called without required argument `%2%'", if (!i->def) throwTypeError("%1% called without required argument `%2%'",
fun.lambda.fun->showNamePos(), i->name); *fun.lambda.fun, i->name);
env2.values[displ++] = i->def->maybeThunk(*this, env2); env2.values[displ++] = i->def->maybeThunk(*this, env2);
} else { } else {
attrsUsed++; attrsUsed++;
@ -787,20 +797,32 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
user. */ user. */
foreach (Bindings::iterator, i, *arg.attrs) foreach (Bindings::iterator, i, *arg.attrs)
if (fun.lambda.fun->formals->argNames.find(i->name) == fun.lambda.fun->formals->argNames.end()) if (fun.lambda.fun->formals->argNames.find(i->name) == fun.lambda.fun->formals->argNames.end())
throwTypeError("%1% called with unexpected argument `%2%'", fun.lambda.fun->showNamePos(), i->name); throwTypeError("%1% called with unexpected argument `%2%'", *fun.lambda.fun, i->name);
abort(); // can't happen abort(); // can't happen
} }
} }
nrFunctionCalls++; nrFunctionCalls++;
if (countCalls) functionCalls[fun.lambda.fun]++; if (countCalls) incrFunctionCall(fun.lambda.fun);
fun.lambda.fun->body->eval(*this, env2, v);
#if 0
try { try {
fun.lambda.fun->body->eval(*this, env2, v); fun.lambda.fun->body->eval(*this, env2, v);
} catch (Error & e) { } catch (Error & e) {
addErrorPrefix(e, "while evaluating %1%:\n", fun.lambda.fun->showNamePos()); addErrorPrefix(e, "while evaluating %1%:\n", fun.lambda.fun->showNamePos());
throw; throw;
} }
#endif
}
// Lifted out of callFunction() because it creates a temporary that
// prevents tail-call optimisation.
void EvalState::incrFunctionCall(ExprLambda * fun)
{
functionCalls[fun]++;
} }

View File

@ -227,6 +227,7 @@ public:
bool eqValues(Value & v1, Value & v2); bool eqValues(Value & v1, Value & v2);
void callFunction(Value & fun, Value & arg, Value & v); void callFunction(Value & fun, Value & arg, Value & v);
void callPrimOp(Value & fun, Value & arg, Value & v);
/* Automatically call a function for which each argument has a /* Automatically call a function for which each argument has a
default value or has a binding in the `args' map. */ default value or has a binding in the `args' map. */
@ -268,6 +269,8 @@ private:
typedef std::map<ExprLambda *, unsigned int> FunctionCalls; typedef std::map<ExprLambda *, unsigned int> FunctionCalls;
FunctionCalls functionCalls; FunctionCalls functionCalls;
void incrFunctionCall(ExprLambda * fun);
typedef std::map<Pos, unsigned int> AttrSelects; typedef std::map<Pos, unsigned int> AttrSelects;
AttrSelects attrSelects; AttrSelects attrSelects;

View File

@ -330,7 +330,7 @@ void ExprLambda::setName(Symbol & name)
} }
string ExprLambda::showNamePos() string ExprLambda::showNamePos() const
{ {
return (format("%1% at %2%") % (name.set() ? "`" + (string) name + "'" : "an anonymous function") % pos).str(); return (format("%1% at %2%") % (name.set() ? "`" + (string) name + "'" : "an anonymous function") % pos).str();
} }

View File

@ -205,7 +205,7 @@ struct ExprLambda : Expr
% arg % pos); % arg % pos);
}; };
void setName(Symbol & name); void setName(Symbol & name);
string showNamePos(); string showNamePos() const;
COMMON_METHODS COMMON_METHODS
}; };