diff --git a/test/test_mathics_precedence.py b/test/test_mathics_precedence.py index 975bfa5..05e35b5 100644 --- a/test/test_mathics_precedence.py +++ b/test/test_mathics_precedence.py @@ -1,5 +1,44 @@ """ -Test precedences +Test operator precedences +========================= + +Precedence values reported in the Mathics Scanner tables do not always match +with the values reported by `Precedence[...]` in WMA. As it was +pointed out by Robert L. Jacobson in +[https://www.robertjacobson.dev/posts/2018-09-03-generalizing-pemdas-what-is-an-operator/] +and [https://www.robertjacobson.dev/posts/2018-09-04-defining-the-wolfram-language-part-2-operator-properties/] + +The behavior of the parser and the formatter of WMA do not always is consistent with the values +reported by `Precedence[...]`. Jacobson mentions that in the most of the cases, the behaviour is more +consistent with the values reported by `WolframLanguageData`, which are given following a different +numeric convention. + +Some examples of these inconsistencies are: +* In WMA, `Precedence` reports the same value (215) for all the boolean operators +(`Not`, `And`, `Nand`, `Xor`, `Or`, `Nor`) but behaves as +``` +Precedence[Not]>Precedence[And]==Precedence[Nand]>Precedence[Xor]>Precedence[Or]==Precedence[Nor] +``` + +In other cases, precedence values of some operators are reported to be the default value (670) +while its behavior is different (Ej: `GreaterSlantEqual` and `LessSlantEqual` behaves as +their precedence were the same that the one for `LessEqual` and `GreaterEqual`). + +In any case, the relevant information is the order relation that `Precedence` induce over the +operator and their associated symbols, and this is the information that we want to curate in +the MathicsScanner tables. + +This module test that the *order* induced by the precedence values assigned to each operator/symbol +to be consistent with the one of a list built from WMA. This list was build by sorting the elements +according to their WMA `Precedence` values, and then modified in a way the ordering be consistent +with the way in which expressions involving these symbols / operators are parsed and formatter +by `OutputForm`. This consistency was tested in WMA using the functions defined in +the `testprecedence.m` module. + +Notice also that the tests of this module does not tries to check the behavior +of the parser or the formatters in Mathics-core, but just that the information +that MathicsScanner reports to be consistent with the behavior of WMA. + """ try: @@ -11,267 +50,8 @@ import pytest -WMA_PRECEDENCES = { - "CompoundExpression": 10.0, - "Put": 30.0, - "PutAppend": 30.0, - "Set": 40.0, - "SetDelayed": 40.0, - "UpSet": 40.0, - "UpSetDelayed": 40.0, - "Because": 50.0, - "Therefore": 50.0, - "Postfix": 70.0, - "Colon": 80.0, - "Function": 90.0, - "AddTo": 100.0, - "DivideBy": 100.0, - "SubtractFrom": 100.0, - "TimesBy": 100.0, - "ReplaceAll": 110.0, - "ReplaceRepeated": 110.0, - "RuleDelayed": 120.0, - "Rule": 120.0, - "Condition": 130.0, - "StringExpression": 135.0, - "Optional": 140.0, - "Alternatives": 160.0, - "Repeated": 170.0, - "RepeatedNull": 170.0, - "SuchThat": 180.0, - "DoubleLeftTee": 190.0, - "DoubleRightTee": 190.0, - "DownTee": 190.0, - "LeftTee": 190.0, - "Perpendicular": 190.0, - "RightTee": 190.0, - "UpTee": 190.0, - "Implies": 200.0, - "Equivalent": 205.0, - "And": 215.0, - "Nand": 215.0, - "Nor": 215.0, - "Or": 215.0, - "Xor": 215.0, - "Not": 230.0, - "RoundImplies": 240.0, - "NotReverseElement": 250.0, - "NotSquareSubsetEqual": 250.0, - "NotSquareSupersetEqual": 250.0, - "NotSubset": 250.0, - "NotSubsetEqual": 250.0, - "NotSuperset": 250.0, - "NotSupersetEqual": 250.0, - "ReverseElement": 250.0, - "SquareSubset": 250.0, - "SquareSubsetEqual": 250.0, - "SquareSuperset": 250.0, - "SquareSupersetEqual": 250.0, - "Subset": 250.0, - "SubsetEqual": 250.0, - "Superset": 250.0, - "SupersetEqual": 250.0, - "DoubleLeftArrow": 270.0, - "DoubleLeftRightArrow": 270.0, - "DoubleRightArrow": 270.0, - "DownLeftRightVector": 270.0, - "DownLeftTeeVector": 270.0, - "DownLeftVector": 270.0, - "DownLeftVectorBar": 270.0, - "DownRightTeeVector": 270.0, - "DownRightVector": 270.0, - "DownRightVectorBar": 270.0, - "LeftArrow": 270.0, - "LeftArrowBar": 270.0, - "LeftArrowRightArrow": 270.0, - "LeftRightArrow": 270.0, - "LeftRightVector": 270.0, - "LeftTeeArrow": 270.0, - "LeftTeeVector": 270.0, - "LeftVector": 270.0, - "LeftVectorBar": 270.0, - "LowerLeftArrow": 270.0, - "LowerRightArrow": 270.0, - "RightArrow": 270.0, - "RightArrowBar": 270.0, - "RightArrowLeftArrow": 270.0, - "RightTeeArrow": 270.0, - "RightTeeVector": 270.0, - "RightVector": 270.0, - "RightVectorBar": 270.0, - "ShortLeftArrow": 270.0, - "ShortRightArrow": 270.0, - "UpperLeftArrow": 270.0, - "UpperRightArrow": 270.0, - "DoubleVerticalBar": 280.0, - "NotDoubleVerticalBar": 280.0, - "VerticalBar": 280.0, - "Equal": 290.0, - "Greater": 290.0, - "GreaterEqual": 290.0, - "Less": 290.0, - "LessEqual": 290.0, - "SameQ": 290.0, - "Unequal": 290.0, - "UnsameQ": 290.0, - "Congruent": 290.0, - "CupCap": 290.0, - "DotEqual": 290.0, - "EqualTilde": 290.0, - "Equilibrium": 290.0, - "GreaterEqualLess": 290.0, - "GreaterFullEqual": 290.0, - "GreaterGreater": 290.0, - "GreaterLess": 290.0, - "GreaterTilde": 290.0, - "HumpDownHump": 290.0, - "HumpEqual": 290.0, - "LeftTriangle": 290.0, - "LeftTriangleBar": 290.0, - "LeftTriangleEqual": 290.0, - "LessEqualGreater": 290.0, - "LessFullEqual": 290.0, - "LessGreater": 290.0, - "LessLess": 290.0, - "LessTilde": 290.0, - "NestedGreaterGreater": 290.0, - "NestedLessLess": 290.0, - "NotCongruent": 290.0, - "NotCupCap": 290.0, - "NotGreater": 290.0, - "NotGreaterEqual": 290.0, - "NotGreaterFullEqual": 290.0, - "NotGreaterLess": 290.0, - "NotGreaterTilde": 290.0, - "NotLeftTriangle": 290.0, - "NotLeftTriangleEqual": 290.0, - "NotLess": 290.0, - "NotLessEqual": 290.0, - "NotLessFullEqual": 290.0, - "NotLessGreater": 290.0, - "NotLessTilde": 290.0, - "NotPrecedes": 290.0, - "NotPrecedesSlantEqual": 290.0, - "NotPrecedesTilde": 290.0, - "NotRightTriangle": 290.0, - "NotRightTriangleEqual": 290.0, - "NotSucceeds": 290.0, - "NotSucceedsSlantEqual": 290.0, - "NotSucceedsTilde": 290.0, - "NotTilde": 290.0, - "NotTildeEqual": 290.0, - "NotTildeFullEqual": 290.0, - "NotTildeTilde": 290.0, - "Precedes": 290.0, - "PrecedesEqual": 290.0, - "PrecedesSlantEqual": 290.0, - "PrecedesTilde": 290.0, - "Proportion": 290.0, - "Proportional": 290.0, - "ReverseEquilibrium": 290.0, - "RightTriangle": 290.0, - "RightTriangleBar": 290.0, - "RightTriangleEqual": 290.0, - "Succeeds": 290.0, - "SucceedsEqual": 290.0, - "SucceedsSlantEqual": 290.0, - "SucceedsTilde": 290.0, - "Tilde": 290.0, - "TildeEqual": 290.0, - "TildeFullEqual": 290.0, - "TildeTilde": 290.0, - "DirectedEdge": 295.0, - "UndirectedEdge": 295.0, - "SquareUnion": 300.0, - "UnionPlus": 300.0, - "Span": 305.0, - "SquareIntersection": 305.0, - "MinusPlus": 310.0, - "PlusMinus": 310.0, - "Plus": 310.0, - "Subtract": 310.0, - "CircleMinus": 330.0, - "CirclePlus": 330.0, - "Cup": 340.0, - "Cap": 350.0, - "Coproduct": 360.0, - "VerticalTilde": 370.0, - "Star": 390.0, - "Times": 400.0, - "CenterDot": 410.0, - "CircleTimes": 420.0, - "Vee": 430.0, - "Wedge": 440.0, - "Diamond": 450.0, - "Backslash": 460.0, - "Divide": 470.0, - "Minus": 480.0, - "Dot": 490.0, - "CircleDot": 520.0, - "SmallCircle": 530.0, - "Square": 540.0, - "CapitalDifferentialD": 550.0, - "Del": 550.0, - "DifferentialD": 550.0, - "DoubleDownArrow": 580.0, - "DoubleLongLeftArrow": 580.0, - "DoubleLongLeftRightArrow": 580.0, - "DoubleLongRightArrow": 580.0, - "DoubleUpArrow": 580.0, - "DoubleUpDownArrow": 580.0, - "DownArrow": 580.0, - "DownArrowBar": 580.0, - "DownArrowUpArrow": 580.0, - "DownTeeArrow": 580.0, - "LeftDownTeeVector": 580.0, - "LeftDownVector": 580.0, - "LeftDownVectorBar": 580.0, - "LeftUpDownVector": 580.0, - "LeftUpTeeVector": 580.0, - "LeftUpVector": 580.0, - "LeftUpVectorBar": 580.0, - "LongLeftArrow": 580.0, - "LongLeftRightArrow": 580.0, - "LongRightArrow": 580.0, - "ReverseUpEquilibrium": 580.0, - "RightDownTeeVector": 580.0, - "RightDownVector": 580.0, - "RightDownVectorBar": 580.0, - "RightUpDownVector": 580.0, - "RightUpTeeVector": 580.0, - "RightUpVector": 580.0, - "RightUpVectorBar": 580.0, - "ShortDownArrow": 580.0, - "ShortUpArrow": 580.0, - "UpArrow": 580.0, - "UpArrowBar": 580.0, - "UpArrowDownArrow": 580.0, - "UpDownArrow": 580.0, - "UpEquilibrium": 580.0, - "UpTeeArrow": 580.0, - "Power": 590.0, - "StringJoin": 600.0, - "Factorial": 610.0, - "Factorial2": 610.0, - "Apply": 620.0, - "Map": 620.0, - "Prefix": 640.0, - "Decrement": 660.0, - "Increment": 660.0, - "PreDecrement": 660.0, - "PreIncrement": 660.0, - "Unset": 670.0, - "Information": 670.0, - "GreaterSlantEqual": 670.0, - "LessSlantEqual": 670.0, - "Derivative": 670.0, - "MapApply": 670.0, - "PatternTest": 680.0, - "Get": 720.0, - "MessageName": 750.0, -} -SORTED_SYMBOLS_BY_PRECEDENCE = [ +SYMBOLS_SORTED_BY_PRECEDENCE = [ "CompoundExpression", "Put", "PutAppend", @@ -546,59 +326,27 @@ ] -@pytest.mark.skipif(MATHICS_NOT_INSTALLED, reason="Requires Mathics-core installed") -@pytest.mark.parametrize( - ( - "symbol", - "prec", - ), - list(WMA_PRECEDENCES.items()), -) -@pytest.mark.xfail -def test_precedence_values(symbol, prec): - """ - - TrueRelPrecedence[op1_, op2_] := - Module[{formatted = - ToString[ - HoldForm[ope2[ope1[a, b], c]] /. {ope1 -> op1, ope2 -> op2}, - InputForm]}, - Not[Or[StringPart[formatted, 1] == "(", - StringPart[formatted, -1] == ")"]]] - - Transpose[{a, ALLOPS}] // TableForm""" - - mathics_prec = session.evaluate(f"Precedence[{symbol}]").value - print("Check", f"Precedence[{symbol}]=={prec}") - check_evaluation( - f"Precedence[{symbol}]=={prec}", - "True", - to_string_expr=True, - to_string_expected=True, - hold_expected=True, - failure_message=f"Precendece of {symbol} in mathics {mathics_prec} != WMA value {prec}", - expected_messages=None, - ) - - +# TODO: rewrite this test by reading the table directly. @pytest.mark.skipif(MATHICS_NOT_INSTALLED, reason="Requires Mathics-core installed") def test_precedence_order(): """ Test the precedence order. - This is a slighly flexible test, which does not - requires the numerical coincidence of the Precedence values - with WMA, but just to preserve the order. + + This test checks that the precedence values of the symbols associated + to WL operators follows the order required to make the + that the parser and the OutputForm formatter produce + outputs consistent with the WMA behavior. """ precedence_values = [ session.evaluate(f"Precedence[{symbol}]").value - for symbol in SORTED_SYMBOLS_BY_PRECEDENCE + for symbol in SYMBOLS_SORTED_BY_PRECEDENCE ] fails = [] for i in range(len(precedence_values) - 1): if precedence_values[i] > precedence_values[i + 1]: fails.append( - f"Precedence[{SORTED_SYMBOLS_BY_PRECEDENCE[i]}]={precedence_values[i]}>" - f"{precedence_values[i+1]}=Precedence[{SORTED_SYMBOLS_BY_PRECEDENCE[i+1]}]" + f"Precedence[{SYMBOLS_SORTED_BY_PRECEDENCE[i]}]={precedence_values[i]}>" + f"{precedence_values[i+1]}=Precedence[{SYMBOLS_SORTED_BY_PRECEDENCE[i+1]}]" ) for fail in fails: print(fail)