(* :Title: PartialEvaluation *)

(* :Author: Mark Fisher *)

(* :Context: As currently implemented, the package does not set up
	its own Context. *)

(* :Mathematica Version: 4.0 *)

(* :Package Version: 1.0 September 2002 *)

(* :Summary: A debugging tool.
	PartialEvaluation[f[args], n] evaluates the first n expressions in
	the "main" CompoundExpression in f[args]. (The position of the
	"main" CompoundExpression can be specified by an option.) An
	arbitrary expression can be appended as an optional third
	argument: PartialEvaluation[f[args], n, expr].
*)

(* :Discussion: Here is the code without the error checking for
	PartialEvaluation[f[args], n, expr]:

	(* get the DownValues and "turn them off" *)
	dv = DownValues[f];
	DownValues[f] = {};

	(* find the rule that matches *)
	matches = Position[MatchQ[f[args], #]& /@ dv[[All, 1]], True];
	match = dv[[ matches[[1, 1]] ]];

	(* find the "main" CompoundExpression *)
	ppos = Position[match, HoldPattern[CompoundExpression[__]]];
	pos = First[Sort[ppos]];

	(* extract, truncate, append to, and reinsert it *)
	held = Extract[match, pos, Hold];
	held = ReplacePart[held, Sequence, {1, 0}];
	held = Take[held, n];
	held = Join[held, Hold[expr]];
	held = ReplacePart[Hold[Evaluate[held]], CompoundExpression, {1, 0}];
	match = ReplacePart[match, held, pos, 1];

	(* apply the modified rule and restore DownValues *)
	eval = f[args] /. match;
	DownValues[f] = dv;
	eval

*)

PartialEvaluation::usage = "PartialEvaluation[f[args], n] returns
f[args] where only the first n expressions are evaluated in the \"main\"
CompoundExpression in DownValues[f]. For example:\n\t
Clear[f]\n\t
f[x_] := Module[{a,b}, a=3; b=4; a b x]\n\t
PartialEvaluation[f[10], 2]\n
returns 4. PartialEvaluation takes an optional third argument, an
expression that is appended to the truncated CompoundExpression and
thus returned by f[args]. For example\n\t
ParitalEvaluation[f[10], 2, {a, b, x}]\n
returns {3, 4, 10}."

PartialEvaluation::nomatch = "`1` does not match any of the rules in
DownValues[`2`]."

PartialEvaluation::noce = "There are no CompoundExpressions in ``."

PartialEvaluation::badpos = "There is no CompoundExpression at the
specified position."

PartialEvaluation::toobig = "There are only `1` expressions in
the CompoundExpression in `2`."

SetAttributes[PartialEvaluation, HoldAll]

Options[PartialEvaluation] = {Position -> Automatic}

PartialEvaluation[f_[args__], n_Integer, expr_:Null,
		opts___?OptionQ] /; (!OptionQ[expr]) :=
	Module[{dv, unprotected, result, matches, match, ppos, pos, held},
	dv = DownValues[f];
	unprotected = Unprotect[f];
	DownValues[f] = {};
	result = Catch[ (* for error checking *)
	matches = Position[MatchQ[f[args], #]& /@ dv[[All, 1]], True];
	If[matches == {}, (* error checking *)
		Message[PartialEvaluation::nomatch, f[args], f];
		Throw[HoldForm[f[args]]]
		];
	match = dv[[ matches[[1, 1]] ]];
	ppos = Position[match, HoldPattern[CompoundExpression[__]]];
	If[ppos == {}, (* error checking *)
		Message[PartialEvaluation::noce, f[args]];
		Throw[HoldForm[f[args]]]
		];
	pos = Position /. {opts} /. Options[PartialEvaluation];
	If[pos === Automatic, pos = First[Sort[ppos]]];
	If[FreeQ[ppos, pos], (* error checking *)
		Message[PartialEvaluation::badpos];
		Throw[HoldForm[f[args]]]
		];
	held = Extract[match, pos, Hold];
	held = ReplacePart[held, Sequence, {1, 0}];
	If[Abs[n] > Length[held], (* error checking *)
		Message[PartialEvaluation::toobig, Length[held], HoldForm[f[args]]];
		Throw[HoldForm[f[args]]]
		];
	held = Take[held, n];
	If[expr =!= Null, held = Join[held, Hold[expr]]];
	held = ReplacePart[Hold[Evaluate[held]], CompoundExpression, {1, 0}];
	match = ReplacePart[match, held, pos, 1];
	f[args] /. match
	]; (* end Catch *)
	DownValues[f] = dv;
	Protect[Evaluate[unprotected]];
	result
	]

PossiblePositions::usage = "PossiblePositions[f] returns a list
of positions of CompoundExpression[__] in DownValues[f]."

PossiblePositions[f_Symbol] :=
	Position[DownValues[f], HoldPattern[CompoundExpression[__]]]


