# Converting expressions to InputForm strings without spaces after commas

One of the little utility packages I use needs to convert Mathematica expressions to InputForm text that is interpolated into a custom file format which is then parsed by another tool. That other tool, for various reasons, has to actually parse the InputForm expressions, but uses its own parser to do so, and that parser doesn't deal very will with commas in Mathematica expressions. It will choke on something like this:

{{1, 2, 3}, {4, 5, 6}}


but works fine with this:

{{1,2,3},{4,5,6}}


By default, ToString, with the InputForm format, will stick spaces in after the commas. Currently, I use StringReplace to work around the problem:

stringifyForExport[form_] :=
StringReplace[
ToString[form, InputString, PageWidth -> Infinity,
CharacterEncoding -> "ASCII"],
" " -> ""];


I'm not very satisfied with this solution because it won't work if we have any strings in form that contain spaces. So far this hasn't caused serious problems, but I know it's just a matter of time. Is there a better way to get rid of those spaces?

-
Is there any way to use Copy As... > Plain Text for this? I believe that gives the format you want. –  Mr.Wizard May 17 '12 at 6:59
@Mr.Wizard I'm afraid not; I'm doing this from a command line script in a lot of cases. –  Pillsy May 17 '12 at 7:23

This is a disgusting solution, but it can keep you company while you get better answers

ClearAll[trimSp];
trimSp[st_] :=
Module[{stringStore, i = 0,
weirdString = "sldfkj8394w3%$=%F(U%)GUG$%W%FWOGUN "},
ToString @@ {Unevaluated @@ (Hold[st] /.
s_String :>
Block[{}, stringStore[++i] = s; weirdString /; True]),
InputForm} //
StringReplace[#, ", " :> ","] & //
(i = 0; StringReplace[#, weirdString :> stringStore[++i]]) &]


It replaces each instance of a string in the expression by some string you hope to never come accross, and stores the original string somewhere. Then it turns it all into string in InputForm, and then puts the strings back in their places. This works because InputForm (I think) doesn't seem to change the order of the arguments. So, Map[f, {2}] turns into f/@2, and the order you come into your arguments in the string coincides with the depth-first ReplaceAll stroll in the expression.

Another way could be

trimSpV2[st_String] :=
Module[{i = 0},
StringReplace[
st, (("\"" /; (++i; False)) | ", " /; (EvenQ[i])) :> ","]]


The first one, you use it by passing the expression, such as trimSp[{1, 2, 3}]. The second one receives the string already and only trims the ", ":>"," if it has come into an even number of explicit " in the string. So you would use it trimSpV2@ToString[{1, 2, 3}, InputForm]. This second one seems nice but would fail with strings with explicit " inside

Another option. This tags the strings, formats them in input form by adding an extra space after ", ", and then converts the full expression to InputForm removing a space after ", "

Module[{stringTag},
Format[stringTag[expr_String], InputForm] :=
StringReplace[expr, ", " :> ",  "];
trimSpV3[expr_] :=
ToString[expr /. s_String :> stringTag[s], InputForm]~
StringReplace~(", " :> ",");
]

-
I think this approach is probably the right one, unless there's an undocumented "NoSpacesWhilePrettyPrintingDammit" option. +1 –  Pillsy May 17 '12 at 1:42
Accepted. The last answer is probably the least gross, and it's nice and simple. –  Pillsy May 23 '12 at 13:43
@Pillsy, yes, I also think the last one is the best. Thanks –  Rojo May 23 '12 at 16:29

I'm beginning to think that any solution to this particular problem is going to be a bit on the gross side, but like in Rojo's answer, I think using placeholders is the way to go. I generate rules to do replacement, and use symbols as placeholders, but it's pretty similar overall.

expungeSpaces[form_] :=
With[{rules =
Cases[form, s_String :> (s -> Unique["placeholder\$"]), Infinity,
toString =
ToString[#, InputForm, PageWidth -> Infinity,
CharacterEncoding -> "ASCII"] &
},
StringReplace[
StringReplace[
Apply[
Function[held,
toString[Unevaluated[held]],
HoldFirst],
Hold[form] /. Dispatch@rules],
" " -> ""],
rules /. (s_String -> p_Symbol) :> toString@p -> toString@s]];


Results:

expungeSpaces[
wongle["bongle"][{"pillsy pillsy pillsy", 2, 3, 4, 5}]] // InputForm

"wongle[\"bongle\"][{\"pillsy pillsy pillsy\",2,3,4,5}]"

-
I just came back and added another option, see what you think –  Rojo May 17 '12 at 3:04