views:

148

answers:

3

Suppose I want a function that takes a number and returns it as a string, exactly as it was given. The following doesn't work:

SetAttributes[foo, HoldAllComplete];
foo[x_] := ToString[Unevaluated@x]

The output for foo[.2] and foo[.20] is identical.

The reason I want to do this is that I want a function that can understand dates with dots as delimiters, eg, f[2009.10.20]. I realize that's a bizarre abuse of Mathematica but I'm making a domain-specific language and want to use Mathematica as the parser for it by just doing an eval (ToExpression). I can actually make this work if I can rely on double-digit days and months, like 2009.01.02 but I want to also allow 2009.1.2 and that ends up boiling down to the above question.

I suspect the only answer is to pass the thing in as a string and then parse it, but perhaps there's some trick I don't know. Note that this is related to this question: http://stackoverflow.com/questions/1616592/mathematica-unevaluated

+1  A: 

Floats are, IIRC, parsed by Mathematica into actual Floats, so there's no real way to do what you want.

pavpanchekha
+4  A: 

I wouldn't rely on Mathematica's float-parsing. Instead I'd define rules on MakeExpression for foo. This allows you to intercept the input, as boxes, prior to it being parsed into floats. This pair of rules should be a good starting place, at least for StandardForm:

MakeExpression[RowBox[{"foo", "[", dateString_, "]"}], StandardForm] :=
  With[{args = Sequence @@ Riffle[StringSplit[dateString, "."], ","]},
    MakeExpression[RowBox[{"foo", "[", "{", args, "}", "]"}], StandardForm]]

MakeExpression[RowBox[{"foo", "[", RowBox[{yearMonth_, day_}], "]"}], 
    StandardForm] :=
  With[{args = 
    Sequence @@ Riffle[Append[StringSplit[yearMonth, "."], day], ","]},
      MakeExpression[RowBox[{"foo", "[", "{", args, "}", "]"}], StandardForm]]

I needed the second rule because the notebook interface will "helpfully" insert a space if you try to put a second decimal place in a number.

EDIT: In order to use this from the kernel, you'll need to use a front end, but that's often pretty easy in version 7. If you can get your expression as a string, use UsingFrontEnd in conjunction with ToExpression:

 UsingFrontEnd[ToExpression["foo[2009.09.20]", StandardForm]

EDIT 2: There's a lot of possibilities if you want to play with $PreRead, which allows you to apply special processing to the input, as strings, before they're parsed.

Pillsy
Holy crap, you're amazing! I had thought this was impossible. One hitch: I'm not using the front end, but just plain text processed by the kernel. In that case do you reckon I'm out of luck after all? (I'll experiment more myself shortly. This is a great lead. Thanks so much!)
dreeves
+1  A: 
$PreRead = If[$FrontEnd =!= Null, #1, 
StringReplace[#,x:NumberString /; StringMatchQ[x,"*.*0"] :>
     StringJoin[x, "`", ToString[
       StringLength[StringReplace[x, "-" -> ""]] - 
        Switch[StringTake[StringReplace[x, 
           "-" -> ""], 1], "0", 2, ".", 1, _, 
         1]]]]] & ; 

will display foo[.20] as foo[0.20]. The InputForm of it will be foo[0.2`2.]

I find parsing and displaying number formats in Mathematica more difficult than it should be...

Rolf Mertig