# How to convert a string having nested parentheses into an expression?

I am a Wolfram Language/Mathematica beginner and trying to convert STEP ISO10303 commands into geometrical regions. A simple command looks like

(vector(a,(b,c)),line(d,(e,f))


I tried with String commands to convert it into a valid ToExpression string like

{vector[a,{b,c}],line[d,{e,f}]}


allowing then to convert it with Rules into geometrical objects.

I managed to get a first prototype running but struggled finding a generic way of converting nested parentheses.

As I am a mediocre Perl programmer I would use the recipe https://stackoverflow.com/questions/4445674/can-i-use-perl-regular-expressions-to-match-balanced-text but I could not find information if Mathematica regex allows using longest match without backtracking and reference to sub-pattern?

Literature says that one either needs recursive regex usage or stack implementation when trying to parse nested objects.

Any help on how to tackle this problem is much appreciated.

Regards Markus

I'd do the minimum necessary to make a legitimate, unambiguous Mathematica expression, and then let Mathematica rewrite it.

stepexpr[s_] := ToExpression[StringReplace[s, {"(" -> " dummy[", ")" -> "]"}]] /.
op_Symbol dummy[args__] -> op[args] /. dummy -> List


This replaces () expressions with a dummy[] function in the string, making a legal expression. Then, in Mathematica, it transforms op dummy[args] into op[args]. Finally, it makes Lists of the remaining dummy functions.

stepexpr["(vector(a,(b,c)),line(d,(e,f)))"]
(* {vector[a, {b, c}], line[d, {e, f}]} *)

• Very clever! I am really impressed by the power of Mathematica. It is good that I can learn from all of you. Thanks for sharing. – Markus Sonderegger Jan 8 '18 at 20:00
• I saw another solution using Times=Sequence and tried to follow up on it with inline Block[{Times = Sequence}, step = StringReplace["(vector(a,(b,c)))", {"(" -> "{", ")" -> "}"}] // ToExpression; step /. {w_, c_} :> w[c] ] Unfortunately I did not manage to get this solution work with a list of commands like shown in my question. I experimented with Apply working on a pair of list but did not manage to get it run. May be you can show me how to do this. – Markus Sonderegger Jan 8 '18 at 20:06
• Sequence is a rather peculiar object, and I don't know how to employ it to do what you want. – John Doty Jan 9 '18 at 21:12

In your example you need [] if and only if two letters precede.
If that fact extends to the generel case, then

ToExpression[With[{s1 = StringReplace[string, {
a_ ~~ b_ ~~ "(" /; LetterQ[a] && LetterQ[b] :> a <> b <> "[",
"(" -> "{"}]},
StringReplacePart[s1,
StringCases[s1, {"[", "{"}] /. {"{" -> "}", "[" -> "]"},
StringPosition[s1, ")"]]]]

{vector[a, {b, c}], line[d, {e, f}]}


In the case of that example:

str = "(vector(a,(b,c)),line(d,(e,f)))";
StringCases[str, {
"vector(" ~~ Shortest[arg1__] ~~ ",(" ~~ Shortest[arg2__] ~~ "," ~~ Shortest[arg3__] ~~ "))" :> vector[arg1, {arg2, arg3}],
"line(" ~~ Shortest[arg1__] ~~ ",(" ~~ Shortest[arg2__] ~~ "," ~~ Shortest[arg3__] ~~ "))" :> line[arg1, {arg2, arg3}]
}]


{vector["a", {"b", "c"}], line["d", {"e", "f"}]}

Feel free to tell me if this is not general enough or if there is another problem.

If you have lists of varying length so that you cannot encode it directly in the pattern:

StringCases[
"f(a,b,c,d)",
"f(" ~~ args__ ~~ ")" :> StringSplit[args, ","]
]


{{"a", "b", "c", "d"}}

Starting from this great answer we can do this in two steps using recursive regular expressions:

str = "(vector(a,(b,c,vector(a,(b,c)))),line(d,(e,f)))";
sqBrPos = StringPosition[str,
WordBoundary ~~ RegularExpression@"(?P<a>$([^\$$$]|(?P>a))*\$$)"];
str2 = StringReplacePart[StringReplacePart[str, "[", {#1, #1} & @@@ sqBrPos],
"]", {#2, #2} & @@@ sqBrPos]
curBrPos = StringPosition[str2, RegularExpression@"(?P<a>$([^\$$$]|(?P>a))*\$$)"];
str3 = StringReplacePart[StringReplacePart[str2, "{", {#1, #1} & @@@ curBrPos],
"}", {#2, #2} & @@@ curBrPos]
ToExpression[str3, InputForm, Hold]

"(vector[a,(b,c,vector[a,(b,c)])],line[d,(e,f)])"

"{vector[a,{b,c,vector[a,{b,c}]}],line[d,{e,f}]}"

Hold[{vector[a, {b, c, vector[a, {b, c}]}], line[d, {e, f}]}]


Strongly related:

• Good to know that recursive regular expressions work. Do you know which regex engine is used in Mathematica as the Documentation Center is not stating that one can use ?P It looks to me like Perl with the fact that each backslash has to be escaped in Mathemitca. – Markus Sonderegger Jan 8 '18 at 19:59
• @MarkusSonderegger Mathematica uses the PCRE library, which currently (starting from Mathematica version 10.1) supports different styles for backreferences. You should read the official PCRE docs for the syntax. And yes, you must additionally escape every backslash inside of RegularExpression. – Alexey Popkov Jan 8 '18 at 20:14
• @MarkusSonderegger About escaping in RegularExpression you can be interested in this answer. – Alexey Popkov Jan 10 '18 at 5:02