14

Is their a way to get a more comprehensive return value when running ToExpression?

For example when I run ToExpression["a[b[c,1],2],3]"] all I get from the output is $Failed when I'm looking for something more like Expression "a[b[c,1],2],3]" has no opening "[".

William
  • 7,595
  • 2
  • 22
  • 70
  • 1
    Look at SyntaxQ and SyntaxLength - in particular, if the latter returns > length of string, it implies the partial expression is correct syntactically but incomplete.... – ciao Aug 05 '15 at 22:49
  • @ciao Tell me if I am missing something but it appears that neither return an error message for example Expression "a[b[c,1],2],3]" has no opening "[". ToExpression prints much more descriptive errors. – William Aug 05 '15 at 22:53
  • 1
    They will not return an error message for those, nor should they: SyntaxQ will return False, indicating an invalid expression, SyntaxLength will return 11, indicating the expression up to a[b[c,1],2] is valid. If you want to generate messages, you'll need to build that yourself before submitting to ToExpression (and $SyntaxHandler is regrettably not in play, ToExpression does not use it). – ciao Aug 05 '15 at 22:57

1 Answers1

21

One way would be to redirect all messages issued by ToExpression to a string-stream. Here is an example of that approach, with minimal error-checking:

Needs["Developer`"]

interpret[str_String] :=
  Module[{s = StreamToString[], r, m}
  , Block[{$Messages = {s}}, r = ToExpression[str, InputForm, HoldComplete]]
  ; m = StringFromStream[s]
  ; Close[s]
  ; <| "result" -> r, "messages" -> m |>
  ]

It returns an association with the result and any messages returned by the interpretation. The result is held in unevaluated form when the interpretation is successful.

Here are some examples:

interpret["1+"]

(* <| "result" -> $Failed
    , "messages" -> "ToExpression::sntxi: Incomplete expression; more input is needed ."
    |>
*)

interpret["1+1"]

(* <| "result" -> HoldComplete[1+1], "messages" -> "" |> *)

interpret["a[b[c,1],2],3]"]

(* <| "result" -> $Failed
    , "messages" -> "ToExpression::sntx: Invalid syntax in or before \"a[b[c,1],2],3]\"."
    |>
*)

The exhibited function makes no attempt to handle abort or other non-local exits from ToExpression. Should one of those occur then the return value and messages will be lost, and the string-stream will not be closed. If a bullet-proof function is required, then more elaborate error-handling will be required (perhaps using CheckAll or the like -- see Resource Management in Mathematica for suggestions).

Update

As requested in a comment, here is a different version of interpret with better error handling should ToExpression fail catastrophically:

interpret2[str_String] :=
  withSetup[
    { s = StreamToString[]; Close[s]
    , r = Block[{$Messages = {s}}, ToExpression[str, InputForm, HoldComplete]]
    , m = StringFromStream[s]
    }
  , <| "result" -> r, "messages" -> m |>
  ]

It uses the CheckAll-based function withSetup defined in this answer to ensure that the string-stream is released even if ToExpression takes a non-local exit.

WReach
  • 68,832
  • 4
  • 164
  • 269
  • I added a bounty in hopes you might create a more bullet proof solution. It isn't clear to me add 1st glance what you mean by CheckAll I'm just now reading. – William Aug 14 '15 at 01:46
  • I added an example of what I meant by "more elaborate error-handling". This is probably about as bullet-proof as we can get in high-level Mathematica code, without access to the internal C++ parsing code. – WReach Aug 15 '15 at 13:03