diff --git a/mathics/core/parser/operators.py b/mathics/core/parser/operators.py index 57c6f13f0..238f1c25c 100644 --- a/mathics/core/parser/operators.py +++ b/mathics/core/parser/operators.py @@ -1,5 +1,12 @@ -#!/usr/bin/env python3 # -*- coding: utf-8 -*- +"""Mathics3 Operator tables. + +This information is controlled by data from the MathicsScanner Project, +from YAML tables which are converted to JSON. + +The dictionary from these which are read in here, are used by the +Mathics3 parser. +""" import os.path as osp @@ -12,169 +19,23 @@ except ImportError: import json as ujson # type: ignore[no-redef] -# Load the conversion tables from disk +# Load Mathics3 operator information from JSON. This file is derived from a +# Mathics3 Operator Data YAML file in MathicsScanner. operator_tables_path = osp.join(ROOT_DIR, "data", "operator-tables.json") assert osp.exists( operator_tables_path -), f"Internal error: Operator precedence tables are missing; expected to be in {operator_tables_path}" +), f"Internal error: Mathics3 Operator information are missing; expected to be in {operator_tables_path}" with open(operator_tables_path, "r") as f: OPERATOR_DATA = ujson.load(f) - -prefix_ops = { - "Get": 720, - "PreIncrement": 660, - "PreDecrement": 660, - "Del": 550, - "Minus": 480, - "Square": 540, - "ForAll": 240, - "Exists": 240, - "NotExists": 240, - "Not": 230, - "Information": 5001, - "Definition": 5000, - "InterpretedBox": 670, -} - -postfix_ops = { - "Unset": 670, - "Conjugate": 670, - "Transpose": 670, - "ConjugateTranspose": 670, - "Derivative": 670, - "Increment": 660, - "Decrement": 660, - "Factorial": 610, - "Factorial2": 610, - "Repeated": 170, - "RepeatedNull": 170, - "Function": 90, -} - -left_binary_ops = { - "Divide": 470, - "PlusMinus": 310, - "MinusPlus": 310, - "Subtract": 310, - "LeftTee": 190, - "DoubleLeftTee": 190, - "Condition": 130, - "ReplaceAll": 110, - "ReplaceRepeated": 110, - "Because": 50, - "PutAppend": 30, - "Put": 30, - "Postfix": 70, -} - -right_binary_ops = { - "Apply": 620, - "Map": 620, - "MapAll": 620, - "Power": 590, - "Implies": 200, - "RightTee": 190, - "DoubleRightTee": 190, - "SuchThat": 180, - "Rule": 120, - "RuleDelayed": 120, - "AddTo": 100, - "SubtractFrom": 100, - "TimesBy": 100, - "DivideBy": 100, - "Therefore": 50, - "UpSet": 40, - "Set": 40, - "SetDelayed": 40, - "UpSetDelayed": 40, -} - -flat_binary_ops = { - "MessageName": 750, - "Composition": 625, - "StringJoin": 600, - "SmallCircle": 530, - "CircleDot": 520, - "NonCommutativeMultiply": 510, - "Cross": 500, - "Union": 300, - "Dot": 490, - "Backslash": 460, - "Diamond": 450, - "Wedge": 440, - "Vee": 430, - "CircleTimes": 420, - "CenterDot": 410, - "Times": 400, - "VerticalTilde": 370, - "Coproduct": 360, - "Cap": 350, - "Cup": 340, - "Star": 390, - "CirclePlus": 330, - "CircleMinus": 330, - "Plus": 310, - "Intersection": 305, - "VerticalBar": 280, - "NotVerticalBar": 280, - "DoubleVerticalBar": 280, - "NotDoubleVerticalBar": 280, - "SameQ": 290, - "UnsameQ": 290, - "Equal": 290, - "Unequal": 290, - "Greater": 290, - "Less": 290, - "GreaterEqual": 290, - "LessEqual": 290, - "Element": 250, - "NotElement": 250, - "Subset": 250, - "Superset": 250, - # HACK: although the should be 215 for all boolean_ops we adjust slightly - # to get the subprecedences correct - "And": 225, - "Nand": 225, - "Xor": 220, - "Xnor": 220, - "Or": 215, - "Nor": 215, - "Equivalent": 205, - "Alternatives": 160, - "StringExpression": 135, - "Colon": 80, - "VerticalSeparator": 60, - "CompoundExpression": 10, -} - -nonassoc_binary_ops = { - "UndirectedEdge": 120, - "DirectedEdge": 128, - "PatternTest": 680, -} - -ternary_ops = { - "Span": 305, - "Infix": 630, -} - -misc_ops = { - "DifferentialD": 550, - "Sum": 320, - "Pattern": 150, - "Optional": 140, - "SqrtBox": 670, - "RadicalBox": 670, - "FractionBox": 670, - "OverscriptBox": 710, - "UnderscriptBox": 710, - "SubscriptBox": 695, - "FormBox": 670, - "SuperscriptBox": 590, - "UnderoverscriptBox": 700, - "SubsuperscriptBox": 690, -} +flat_binary_ops = OPERATOR_DATA["flat-binary-operators"] +left_binary_ops = OPERATOR_DATA["left-binary-operators"] +misc_ops = OPERATOR_DATA["miscellaneous-operators"] +nonassoc_binary_ops = OPERATOR_DATA["non-associative-binary-operators"] +postfix_ops = OPERATOR_DATA["postfix-operators"] +prefix_ops = OPERATOR_DATA["prefix-operators"] +right_binary_ops = OPERATOR_DATA["right-binary-operators"] +ternary_ops = OPERATOR_DATA["ternary-operators"] inequality_ops = ["Less", "LessEqual", "Greater", "GreaterEqual", "Equal", "Unequal"] diff --git a/mathics/core/parser/parser.py b/mathics/core/parser/parser.py index 6dce91f88..7d1beb97c 100644 --- a/mathics/core/parser/parser.py +++ b/mathics/core/parser/parser.py @@ -35,7 +35,6 @@ flat_binary_ops, inequality_ops, left_binary_ops, - misc_ops, nonassoc_binary_ops, postfix_ops, prefix_ops, @@ -795,7 +794,7 @@ def b_SqrtBox(self, box0, token: Token, p: int) -> Optional[Node]: if box0 is not None: return None self.consume() - q = misc_ops["SqrtBox"] + q = all_ops["SqrtBox"] box1 = self.parse_box(q) if self.next().tag == "OtherscriptBox": self.consume() @@ -805,7 +804,7 @@ def b_SqrtBox(self, box0, token: Token, p: int) -> Optional[Node]: return Node("SqrtBox", box1) def b_SuperscriptBox(self, box1, token: Token, p: int) -> Optional[Node]: - q = misc_ops["SuperscriptBox"] + q = all_ops["SuperscriptBox"] if q < p: return None if box1 is None: @@ -814,13 +813,13 @@ def b_SuperscriptBox(self, box1, token: Token, p: int) -> Optional[Node]: box2 = self.parse_box(q) if self.next().tag == "OtherscriptBox": self.consume() - box3 = self.parse_box(misc_ops["SubsuperscriptBox"]) + box3 = self.parse_box(all_ops["SubsuperscriptBox"]) return Node("SubsuperscriptBox", box1, box3, box2) else: return Node("SuperscriptBox", box1, box2) def b_SubscriptBox(self, box1, token: Token, p: int) -> Optional[Node]: - q = misc_ops["SubscriptBox"] + q = all_ops["SubscriptBox"] if q < p: return None if box1 is None: @@ -829,13 +828,13 @@ def b_SubscriptBox(self, box1, token: Token, p: int) -> Optional[Node]: box2 = self.parse_box(q) if self.next().tag == "OtherscriptBox": self.consume() - box3 = self.parse_box(misc_ops["SubsuperscriptBox"]) + box3 = self.parse_box(all_ops["SubsuperscriptBox"]) return Node("SubsuperscriptBox", box1, box2, box3) else: return Node("SubscriptBox", box1, box2) def b_UnderscriptBox(self, box1, token: Token, p: int) -> Optional[Node]: - q = misc_ops["UnderscriptBox"] + q = all_ops["UnderscriptBox"] if q < p: return None if box1 is None: @@ -844,13 +843,13 @@ def b_UnderscriptBox(self, box1, token: Token, p: int) -> Optional[Node]: box2 = self.parse_box(q) if self.next().tag == "OtherscriptBox": self.consume() - box3 = self.parse_box(misc_ops["UnderoverscriptBox"]) + box3 = self.parse_box(all_ops["UnderoverscriptBox"]) return Node("UnderoverscriptBox", box1, box2, box3) else: return Node("UnderscriptBox", box1, box2) def b_FractionBox(self, box1, token: Token, p: int) -> Optional[Node]: - q = misc_ops["FractionBox"] + q = all_ops["FractionBox"] if q < p: return None if box1 is None: @@ -860,7 +859,7 @@ def b_FractionBox(self, box1, token: Token, p: int) -> Optional[Node]: return Node("FractionBox", box1, box2) def b_FormBox(self, box1, token: Token, p: int) -> Optional[Node]: - q = misc_ops["FormBox"] + q = all_ops["FormBox"] if q < p: return None if box1 is None: @@ -874,7 +873,7 @@ def b_FormBox(self, box1, token: Token, p: int) -> Optional[Node]: return Node("FormBox", box2, box1) def b_OverscriptBox(self, box1, token: Token, p: int) -> Optional[Node]: - q = misc_ops["OverscriptBox"] + q = all_ops["OverscriptBox"] if q < p: return None if box1 is None: @@ -883,7 +882,7 @@ def b_OverscriptBox(self, box1, token: Token, p: int) -> Optional[Node]: box2 = self.parse_box(q) if self.next().tag == "OtherscriptBox": self.consume() - box3 = self.parse_box(misc_ops["UnderoverscriptBox"]) + box3 = self.parse_box(all_ops["UnderoverscriptBox"]) return Node("UnderoverscriptBox", box1, box3, box2) else: return Node("OverscriptBox", box1, box2)