4

It is known that output expressions are passed through MakeBoxes to turn the graphics expressions into the box language which the front end uses to represent graphics (when $Output has default option FormatType->StandardForm). For example, if we evaluate:

HoldComplete[Graphics[Disk[]]]

we get a disk wrapped by HoldComplete:

screenshot

This is because HoldComplete does not stop MakeBoxes from converting its contents to typeset expression:

In[4]:= MakeBoxes@HoldComplete[Graphics[Disk[]]]
Out[4]= RowBox[{"HoldComplete", "[", GraphicsBox[DiskBox[{0, 0}]], "]"}]

So my question is: is it possible to make some additional definitions to MakeBoxes such that wrapping any expression with head MakeBoxesStop will prevent MakeBoxes from converting this expression to typeset form? In this case the expression should look in output as any other expression with no rules associated with symbols in it; in the above case:

screenshot

P.S. Please do not suggest to use InputForm since I am not satisfied with its default behavior.

Community
  • 1
  • 1
Alexey Popkov
  • 9,355
  • 4
  • 42
  • 93
  • 1
    As I mentioned several times in our past conversations, the phrase *HoldComplete does not stop MakeBoxes*... is confusing. `HoldComplete` matters at evaluation stage, and for the purposes of rendering (conversion to boxes) is just a normal wrapper. I don't understand why you mentioned it here at all. What matters for the rendering/FE is the resulting box form of an expression, and this is completely separate topic from evaluation happening in the kernel. – Leonid Shifrin Jun 30 '11 at 13:05
  • @Leonnid @Alexey - the comment about 'rendering in the FE vs. what's happening in the kernel' reminded me of [this](http://forums.wolfram.com/mathgroup/archive/2008/Jan/msg00427.html) reply to a MathGroup post by John Fultz. Would forcing legacy graphics rendering help? (probably a dumb suggestion, but thought I'd share) – telefunkenvf14 Jun 30 '11 at 16:17
  • @telefunkenvf14 I am the wrong person to ask about this. But IMO sending the expression in the box form to the FE even when it is graphics is cleaner than sending an image, so I would not force the legacy graphics rendering unless really necessary. – Leonid Shifrin Jun 30 '11 at 16:30
  • Strongly related: [Prevent graphics render inside held expression](http://mathematica.stackexchange.com/a/32314/280). – Alexey Popkov Sep 15 '13 at 01:45

2 Answers2

3

This function seems to do it:

Clear[MakeBoxesStop];
MakeBoxesStop /: MakeBoxes[MakeBoxesStop[expr_], form_] :=
  Module[{heldHeads = 
     Join @@ Cases[expr,s_Symbol[___] :> HoldComplete[s], {0, Infinity}, 
      Heads -> True], 
    modified, direct,  tempContext = ToString[Unique[]] <> "`"},
   Block[{$ContextPath = $ContextPath, $Packages  = $Packages},
     BeginPackage[tempContext];
       modified = 
        Join @@ Map[
          Function[head,
             ToExpression[ToLowerCase[ToString[Unevaluated[head]]],InputForm, HoldComplete],     
             HoldAllComplete], 
          heldHeads];
     EndPackage[];
     With[{newexpr = 
       expr /. (List @@ Thread[HoldPattern /@ heldHeads -> modified, HoldComplete])},
       With[{result = 
        MakeBoxes[newexpr, form] /. 
           Thread[Rule @@ 
              Map[List @@ 
                 Map[Function[head, ToString[Unevaluated[head]], HoldAllComplete], #] &,
                 {modified , heldHeads}]]
            },
            Remove @@ Names[tempContext <> "*"];
            result]]]];

It won't win the elegance contests, and may be not very clean, but it seems to do what you requested:

In[270]:= MakeBoxesStop[Graphics[Disk[]]]

Out[270]= Graphics[Disk[List[0, 0]]]

If you don't want expression inside MakeBoxesStop to evaluate, add the appropriate attributes and Unevaluated wrappers in the body.

EDIT

The following simple box-making function is based on the Mathematica parser posted here:

Clear[toBoxes];
toBoxes[expr_] :=
  First[parse[tokenize[ToString@FullForm[expr]]] //. {
    head_String[elem_] :>    RowBox[{head, "[", elem, "]"}], 
    head_String[elems___] :>  RowBox[{head, "[", RowBox[Riffle[{elems}, ","]], "]"}]}]

Then, we need:

Clear[MakeBoxesStopAlt];
MakeBoxesStopAlt /: MakeBoxes[MakeBoxesStopAlt[expr_], form_] :=  toBoxes[expr]

For example:

In[327]:= MakeBoxesStopAlt[Graphics[Disk[]]]

Out[327]= Graphics[Disk[List[0, 0]]]
Community
  • 1
  • 1
Leonid Shifrin
  • 22,449
  • 4
  • 68
  • 100
  • I published the first working version. I can very well imagine that it can be made much shorter and that wrapping things all the time in `HoldComplete` is not always needed (particularly for generated symbols). – Leonid Shifrin Jun 30 '11 at 12:36
  • @Leonid It seems that it is not necessary to `ToLowerCase` all symbols: moving them in another context [is sufficient](http://stackoverflow.com/questions/6224185/making-customized-inputform-and-shortinputform/6230478#6230478). I do not know exact details on how `MakeBoxes` is applied recursively to the original expression but it seems that this process is controlled by `MakeBoxes` itself and I feel that it is possible to stop this recursive process at arbitrary head by adding an appropriate definition to `MakeBoxes`. – Alexey Popkov Jun 30 '11 at 13:02
  • @Leonid Of course, this definition must convert the expression further to `BoxForm`s but irrespective to `FormatValues` of symbols like `Graphics` etc. – Alexey Popkov Jun 30 '11 at 13:04
  • @Alexey You are right about `ToLowerCase` - this is an artifact of the first version which did not localize the new symbols. As for your strategy to stop `MakeBoxes` - well, good luck. All I can say is that since you still need it to work for inner parts inside the heads where you want to "stop" it, I don't see much point in this strategy (besides it sounds complicated and dependent on implementation details of `MakeBoxes`). In this solution, I attempted to reuse the `MakeBoxes` on the highest possible level. If you need a list of specific symbols, you can also do it with my code. – Leonid Shifrin Jun 30 '11 at 13:13
  • @Alexey Actually I looked at your code and while moving symbols to another context works for you, it does not work for me unless I use longer names, since I don't want to give up the `System'` context. Using longer names will effectively bring the same mess as using `ToLowerCase`, so I think I don't care which one is used. – Leonid Shifrin Jun 30 '11 at 13:19
  • @Leonid In really I wish something like `MakeBoxesStop /: MakeBoxes[MakeBoxesStop[expr_], form_] := myMakeBoxes[expr]` where `myMakeBoxes` will reproduce only one aspect of the default behavior of `MakeBoxes`: `Trace[MakeBoxes[graphics[disk[]], StandardForm], TraceInternal -> True]` (in other words, `myMakeBoxes` just has no any of the `UpValues`/`FormatValues` of `MakeBoxes`). – Alexey Popkov Jun 30 '11 at 13:23
  • @Alexey I don't think you can make a *reliable* (that is, not relying too much on `MakeBoxes` implementation) "hybrid" solution, which will use some parts of `MakeBoxes` but not others. You can, of course, write your own converter to boxes, which will do things the way you like. But this is a heavier approach. – Leonid Shifrin Jun 30 '11 at 13:29
  • @Leonid Your last comment is confusing: you say that you do not want to give up the ``System``` context but at the same time in your code you do it: `BeginPackage[tempContext]; ToExpression[ToLowerCase[ToString[Unevaluated[head]]], InputForm, HoldComplete];`. What do you mean? – Alexey Popkov Jun 30 '11 at 13:32
  • @Leonid Is it really too hard to make an analog of `MakeBoxes` which reproduces just the mentioned behavior: `Trace[MakeBoxes[graphics[disk[]], StandardForm], TraceInternal -> True]`? It is very non-standard task but probably the solution may be short although it is not clear for me how to find a way to deal with it. – Alexey Popkov Jun 30 '11 at 13:36
  • @Alexey When you call `BeginPackage`, the `System'` context remains on the `$ContextPath`, otherwise one would not be able to use any of the system functions by their short names inside a package. – Leonid Shifrin Jun 30 '11 at 13:37
  • @Alexey I don't know how hard it is :) Probably not too hard if what you want is also not too advanced. You just have to know the box language rather well, and then write a recursive function. But this is going exactly into those low-level details that I'd like to avoid. Of course, if you have a good reason, why not? I am sure many people would benefit if you code a light-weight converter to boxes and publish it here as an answer. – Leonid Shifrin Jun 30 '11 at 13:40
  • @Leonid As I understand for reproducing this behavior we need to know only the `RowBox` syntax. What is really confusing is how to convert `disk[]` to `RowBox[{"disk", "[", "]"}]` without using `ToString` (i.e. avoiding parsing of string representation of expression). – Alexey Popkov Jun 30 '11 at 13:45
  • @Alexey If you only need `RowBox`-s, then yes. But you may soon find that you'd like some more boxes, and things may quickly get more complicated. I don't see the conversion to string as a problem. You can of course convert entire expression to string via `ToString[FullForm[expr]]`, and then write a simple tokenizer/parser which would not involve `ToString`-`ToExpression` cycles. Question is how to make that parser efficient. I wrote a rather efficient breadth-first parser for the mma expressions in `FullForm` . See my edit for a function based on it. – Leonid Shifrin Jun 30 '11 at 14:23
  • @Alexey On the linked page, look for my second solution, somewhere at the bottom of the page. – Leonid Shifrin Jun 30 '11 at 14:30
0

Starting from Mathematica 11.0, we have DisableFormatting wrapper which prevents formatting inside of held expressions:

Hold[DisableFormatting@Graphics[Disk[]]]

>     Hold[DisableFormatting[Graphics[Disk[]]]]

Strongly related answer by Carl Woll:

Alexey Popkov
  • 9,355
  • 4
  • 42
  • 93