diff --git a/CHANGES.rst b/CHANGES.rst
index 0665179f6..5c31d9e7c 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,7 +1,8 @@
.. contents::
CHANGES
-=======
+========
+
5.0.3dev0
---------
@@ -42,6 +43,8 @@ Bugs
# ``0`` with a given precision (like in ```0`3```) is now parsed as ``0``, an integer number.
#. ``RandomSample`` with one list argument now returns a random ordering of the list items. Previously it would return just one item.
+#. Improving parsing ``RowBox`` expressions including ``FormBox`` tags (``` \` ```) inside.
+
Enhancements
diff --git a/mathics/builtin/makeboxes.py b/mathics/builtin/makeboxes.py
index 290eb2e1f..ff58d5cea 100644
--- a/mathics/builtin/makeboxes.py
+++ b/mathics/builtin/makeboxes.py
@@ -339,38 +339,38 @@ class BoxForms_(Predefined):
class MakeBoxes(Builtin):
- """
+ r"""
- 'MakeBoxes[$expr$]'
-
- is a low-level formatting primitive that converts $expr$
- to box form, without evaluating it.
-
- '\\( ... \\)'
+
- is a low-level formatting primitive that converts $expr$ to box form, without evaluating it.
+
+
- '\( ... \)'
- directly inputs box objects.
String representation of boxes
- >> \\(x \\^ 2\\)
+ >> \(x \^ 2\)
= SuperscriptBox[x, 2]
- >> \\(x \\_ 2\\)
+ >> \(x \_ 2\)
= SubscriptBox[x, 2]
- >> \\( a \\+ b \\% c\\)
+ >> \( a \+ b \% c\)
= UnderoverscriptBox[a, b, c]
- >> \\( a \\& b \\% c\\)
+ >> \( a \& b \% c\)
= UnderoverscriptBox[a, c, b]
- #> \\( \\@ 5 \\)
+ #> \( \@ 5 \)
= SqrtBox[5]
- >> \\(x \\& y \\)
+ >> \(x \& y \)
= OverscriptBox[x, y]
- >> \\(x \\+ y \\)
+ >> \(x \+ y \)
= UnderscriptBox[x, y]
- #> \\( x \\^ 2 \\_ 4 \\)
+ #> \( x \^ 2 \_ 4 \)
= SuperscriptBox[x, SubscriptBox[2, 4]]
## Tests for issue 151 (infix operators in heads)
diff --git a/mathics/core/parser/parser.py b/mathics/core/parser/parser.py
index 7e681b63c..e8a916629 100644
--- a/mathics/core/parser/parser.py
+++ b/mathics/core/parser/parser.py
@@ -308,13 +308,53 @@ def p_RawLeftAssociation(self, token):
def p_LeftRowBox(self, token):
self.consume()
children = []
- self.box_depth += 1
- self.bracket_depth += 1
+ # If this does not happen, it would be because
+ # it was called when a `FormBox` (or any other)
+ # was found. More generally, we could use
+ # ``token.tag == "LeftRowBox"
+ # if there were other Tokens with a
+ # similar behaviour (see bellow).
+
+ if token.tag != "FormBox":
+ self.box_depth += 1
+ self.bracket_depth += 1
+
token = self.next()
- while token.tag not in ("RightRowBox", "OtherscriptBox"):
+ while token.tag not in ("RightRowBox", "OtherscriptBox", "FormBox"):
newnode = self.parse_box(0)
children.append(newnode)
token = self.next()
+
+ # FormBox token has a particular behaviour: if it is found inside
+ # a RowBox, it splits the Rowbox in two pieces: the part at the
+ # left is taken as a "Format" and the part at the right is parsed
+ # as the Box to be formatted, in a way that
+ # \(a_1, a_2 ... \` b_1 b_2 ... \) is parsed as
+ # FormBox[RowBox[{b_1, b_2,...}], RowBox[a_1, a_2, ...]]
+ # This kind of parsing is not supported by the standard mechanism,
+ # so we need to processing it here instead of using a p_FormBox
+ # method.
+ # The strategy to deal with is that, when a "FormBox" tag is found,
+ # the collected elements are used to build the second argument of the
+ # output, and then the method is called again to parse the rest of the
+ # box as it had started at the "FormBox" tag. In this new call,
+ # neither `self.box_depth` or `self.bracket_depth` are going to be
+ # incremented, but are decremented at the end of the child expression.
+ if token.tag == "FormBox":
+ if len(children) == 0:
+ fmt = Symbol("StandardForm")
+ elif len(children) == 1:
+ fmt = children[0]
+ if type(fmt) is String:
+ fmt_name = fmt.value
+ if is_symbol_name(fmt_name):
+ fmt = Symbol(fmt_name)
+ else:
+ fmt = Node("Removed", String("$$Failure"))
+ else:
+ fmt = Node("RowBox", Node("List", *children))
+ rest = self.p_LeftRowBox(token)
+ return Node("FormBox", rest, fmt)
if len(children) == 0:
result = String("")
elif len(children) == 1:
@@ -840,20 +880,6 @@ def b_FractionBox(self, box1, token, p):
box2 = self.parse_box(q + 1)
return Node("FractionBox", box1, box2)
- def b_FormBox(self, box1, token, p):
- q = misc_ops["FormBox"]
- if q < p:
- return None
- if box1 is None:
- box1 = Symbol("StandardForm") # RawForm
- elif is_symbol_name(box1.value):
- box1 = Symbol(box1.value, context=None)
- else:
- box1 = Node("Removed", String("$$Failure"))
- self.consume()
- box2 = self.parse_box(q)
- return Node("FormBox", box2, box1)
-
def b_OverscriptBox(self, box1, token, p):
q = misc_ops["OverscriptBox"]
if q < p:
diff --git a/test/core/parser/test_parser.py b/test/core/parser/test_parser.py
index 629f2a1ce..bce249c4c 100644
--- a/test/core/parser/test_parser.py
+++ b/test/core/parser/test_parser.py
@@ -694,10 +694,14 @@ def testFraction(self):
self.check("\\( \\/ \\)", 'FractionBox["", ""]')
def testFormBox(self):
- self.check("\\( 1 \\` b \\)", 'FormBox["b", Removed["$$Failure"]]')
- self.check("\\( \\` b \\)", 'FormBox["b", StandardForm]')
- self.check("\\( a \\` b \\)", 'FormBox["b", a]')
- self.check("\\( a \\` \\)", 'FormBox["", a]')
+ self.check(r"\( \` b \)", 'FormBox["b", StandardForm]')
+ self.check(r"\( a \` b \)", 'FormBox["b", a]')
+ self.check(r"\( a \` \)", 'FormBox["", a]')
+ self.check(r"\( a \` b + c \)", 'FormBox[RowBox[{"b", "+", "c"}], a]')
+ self.check(r"\( a \` b \` c \)", 'FormBox[FormBox["c", b], a]')
+ self.check(r'\( "a" \` b \)', 'FormBox["b", Removed["$$Failure"]]')
+ self.check(r"\( 3.2 \` b \)", 'FormBox["b", Removed["$$Failure"]]')
+ self.check(r"\( 3.2 + a \` b \)", 'FormBox["b", RowBox[{"3.2", "+", "a"}]]')
def testRow(self):
self.check("\\( \\)", String(""))