Make function calls tail-recursive
This commit is contained in:
parent
273322c773
commit
c897bac549
4 changed files with 65 additions and 40 deletions
|
@ -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]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue