Search code examples
wolfram-mathematicamathlink

How to make an analog of InString[]?


I have discovered that InString[] does not work in MathLink mode when sending input with EnterExpressionPacket header. So I need to define my own function that returns previous input line. One way I have developed here does not work in some cases:

In[1]:= Unevaluated[2 + 2]
With[{line = $Line - 1}, HoldForm[In[line]]] /. (DownValues[In])
Out[1]= Unevaluated[2 + 2]
Out[2]= 2 + 2

This is because RuleDelayed has no HoldAllComplete attribute. Adding this attribute makes this OK:

In[1]:= Unprotect[RuleDelayed];
SetAttributes[RuleDelayed, HoldAllComplete];
Protect[RuleDelayed];
Unevaluated[2 + 2]
With[{line = $Line - 1}, HoldForm[In[line]]] /. DownValues[In]

Out[4]= Unevaluated[2 + 2]

Out[5]= Unevaluated[2 + 2]

But modifying built-in functions generally is not a good idea. Is there a better way to do this?


Solution

  • It seems that I have solved the problem. Here is the function:

    In[1]:=
    getLastInput := Module[{num, f},
        f = Function[{u, v},
            {u /. {In -> num, HoldPattern -> First}, HoldForm[v]}, HoldAllComplete];
        First@Cases[
            Block[{RuleDelayed = f}, DownValues[In]],
            {$Line - 1, x_} -> x, {1}, 1]]
    
    In[2]:=
    Unevaluated[2+2]
    getLastInput
    
    Out[2]=
    Unevaluated[2+2]
    
    Out[3]=
    Unevaluated[2+2]
    

    And I just have got the answer to the question on InString in MathLink mode from Todd Gayley (Wolfram Research):

    InString is only assigned when using EnterTextPacket, not EnterExpressionPacket. There is no string form of the input when sending EnterExpressionPacket (whose content is, by definition, already an expression).

    EDIT:

    I just have found that my code does not work with input expressions with head Evaluate. The solution is to replace HoldForm by HoldComplete in my code:

    getLastInput := Module[{num, f},
        f = Function[{u, v},
            {u /. {In -> num, HoldPattern -> First}, HoldComplete[v]}, HoldAllComplete];
        First@Cases[
            Block[{RuleDelayed = f}, DownValues[In]],
            {$Line - 1, x_} -> x, {1}, 1]]
    

    This works well. Another approach would be to unprotect HoldForm and set up attribute HoldAllComplete on it. I'm wondering why HoldForm does not have this attribute by default?

    EDIT 2:

    In the comments for the main question Leonid Shifrin suggested much better solution:

    getLastInput := 
     Block[{RuleDelayed},SetAttributes[RuleDelayed,HoldAllComplete];
      With[{line=$Line-1},HoldComplete[In[line]]/.DownValues[In]]]
    

    See comments for details.

    EDIT 3: The last code can be made even better for by replacing HoldComplete by double HoldForm:

    getLastInput := 
     Block[{RuleDelayed},SetAttributes[RuleDelayed,HoldAllComplete];
      With[{line=$Line-1},HoldForm@HoldForm[In[line]]/.DownValues[In]]]
    

    The idea is taken from presentation by Robby Villegas of Wolfram Research at the 1999 Developer Conference. See subsection "HoldCompleteForm: a non-printing variant of HoldComplete (just as HoldForm is to Hold)" in "Working With Unevaluated Expressions" notebook posted here.