This question and its answers explains two methods which allow to import the content of a package file as held expressions. That is very useful as one then can manipulate that imported held code as expressions before evaluating it or writing it back to a new package file.
While making use of that functionality I discovered that something like:
codestr="Format[bin[x_,y_],StandardForm] := MatrixForm[{{x},{y}}]"
(example is from the Format documentation page) will be imported as:
{HoldComplete[RowBox[{"bin", "(", RowBox[{"x_", ",", "y_"}], ")"}] :=
MatrixForm[{{x}, {y}}]; ]}
with both ImportString[codestr,{"Package","HeldExpressions"}]
or ToExpression[codestr,InputForm,HoldComplete]
. The problem seems to be that neither of them will really hold Format
which seems to be evaluated at parse time (is that maybe unavoidable?).
Altogether that breaks the intended workflow to read, convert to held expression, manipulate and write the result back to a package file like shown at the end of this question.
I know the following workarounds, but none of it looks very satisfactory:
- rewrite the
Format
expression to use e.g.MakeBoxes
- String-Manipulate before converting to held expression, then treat result before evaluating or writing back
- Use
Import
as"InactivatedExpression"
(which keepsFormat
inactivated) and work with the result, which seems much more complicated to me and might have additional problems when the code itself makes use ofInactive
and friends.
The following is a simplified example which would rename the symbol name in code which makes some definitions for it (for easier experiments I'm getting the code from strings instead of files and also convert back to strings instead of files). Does anyone have a better suggestion how I could get that to work?
codestr="
Format[bin[x_,y_],TraditionalForm]:=MatrixForm[{{x},{y}}]
bin[x_?NumericQ,y_?NumericQ]:=Binomial[x,y];
";
origcodexpr=ImportString[codestr,{"Package","HeldExpressions"}];
newcodeexpr=origcodexpr/.HoldPattern[bin]:>mybinomial;
newcodeexpr /. {HoldComplete ->
Function[c,ToString[Unevaluated[c],InputForm],HoldAllComplete]}
The last line returns:
{"RowBox[{\"mybinomial\", \"(\",
RowBox[{\"x_\", \",\", \"y_\"}], \")\"}] := MatrixForm[{{x}, {y}}]; ",
"mybinomial[(x_)?NumericQ, (y_)?NumericQ] := Binomial[x, y]; "}
but what I would want is:
{"Format[mybinomial[x_,y_],TraditionalForm]:= MatrixForm[{{x}, {y}}]; ",
"mybinomial[(x_)?NumericQ, (y_)?NumericQ] := Binomial[x, y]; "}
Of course I am also interested whether there are other functions/constructs than Format
which would make the above break...
EDIT
As Leonid (who else) immediatly clarified the problem is actually not the import which works as intendend and advertised. The problem is that when I looked at it in InputForm
in the notebook the Format
was interpreted. If I had looked at FullForm
instead, I would have seen that both ways to import unevaluated expressions does indeed HoldComplete
.
Now my problem turns out to be that I don't know how to convert these held expressions back to a string which I can export to a file (of course direct export to a file would also be acceptable...). I tried to follow Leonids idea but unfortunately I can't manage to produce a FullForm
string of the unevaluated expressions. To clarify: the ultimate goal for my current use case would be to write the manipulated held expressions to a package file which can then be loaded with Get
or Needs
. So my original idea to write back as InputForm
is stricktly speaking not so important, writing FullForm
would be just as good. I could even imagine to compress the unevaluated expression and write that to a file with a wrapper which uncompresses and evaluates the code. Any other suggestions would be appreciated, and of course for other applications it would be nice to know if it would be possible to write out as human readable InputForm
nevertheless...
FullForm
(likeToExpression[codestr, InputForm, HoldComplete] // FullForm
), and you wiull see hat everything has actually been imported correctly, it's just the print form that plays tricks with you. $\endgroup$FullForm
:ToString[FullForm@expr]
. $\endgroup$Read[..., HoldComplete[Expression]]
. Then you can read and evaluate expression by expression, which preserves contexts. $\endgroup$