Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving docstrings #105

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
352 changes: 50 additions & 302 deletions test/test_mathics_precedence.py
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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",
Expand Down Expand Up @@ -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)
Expand Down
Loading