13
$\begingroup$

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 "[".

$\endgroup$
3
  • 1
    $\begingroup$ Look at SyntaxQ and SyntaxLength - in particular, if the latter returns > length of string, it implies the partial expression is correct syntactically but incomplete.... $\endgroup$
    – ciao
    Aug 5 '15 at 22:49
  • $\begingroup$ @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. $\endgroup$
    – William
    Aug 5 '15 at 22:53
  • 1
    $\begingroup$ 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). $\endgroup$
    – ciao
    Aug 5 '15 at 22:57
20
+50
$\begingroup$

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.

$\endgroup$
2
  • $\begingroup$ 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. $\endgroup$
    – William
    Aug 14 '15 at 1:46
  • $\begingroup$ 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. $\endgroup$
    – WReach
    Aug 15 '15 at 13:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.