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…
	
	Add table
		Add a link
		
	
		Reference in a new issue