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

SameQ[] and Expression module tweaks #1079

Merged
merged 5 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
9 changes: 7 additions & 2 deletions .github/workflows/consistency-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,15 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
sudo apt update -qq && sudo apt install llvm-dev
sudo apt update -qq && sudo apt install llvm-dev remake
python -m pip install --upgrade pip
# We can comment out after next Mathics-Scanner release
python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
# python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
git clone https://github.com/Mathics3/mathics-scanner.git
cd mathics-scanner/
pip install -e .
cd ..

- name: Install Mathics with minimum dependencies
run: |
make develop
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/osx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,18 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Install OS dependencies
run: |
brew install llvm@14 tesseract
brew install llvm@14 tesseract remake
python -m pip install --upgrade pip
- name: Install Mathics3 with full Python dependencies
run: |
# We can comment out after next Mathics-Scanner release
python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
git clone https://github.com/Mathics3/mathics-scanner.git
cd mathics-scanner/
pip install -e .
cd ..
# python -m pip install Mathics-Scanner[full]
make develop-full
remake -x develop-full
- name: Test Mathics3
run: |
make -j3 check
7 changes: 6 additions & 1 deletion .github/workflows/ubuntu-cython.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ jobs:
sudo apt-get update -qq && sudo apt-get install -qq liblapack-dev llvm-dev tesseract-ocr
python -m pip install --upgrade pip
# We can comment out after next Mathics-Scanner release
python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
# python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
git clone https://github.com/Mathics3/mathics-scanner.git
cd mathics-scanner/
pip install -e .
cd ..

# python -m pip install Mathics-Scanner[full]
- name: Install Mathics with full dependencies
run: |
Expand Down
11 changes: 8 additions & 3 deletions .github/workflows/ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,19 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Install OS dependencies
run: |
sudo apt-get update -qq && sudo apt-get install -qq liblapack-dev llvm-dev tesseract-ocr
sudo apt-get update -qq && sudo apt-get install -qq liblapack-dev llvm-dev tesseract-ocr remake
- name: Install Mathics3 with full dependencies
run: |
python -m pip install --upgrade pip
# We can comment out after next Mathics-Scanner release
python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
# python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
git clone https://github.com/Mathics3/mathics-scanner.git
cd mathics-scanner/
pip install -e .
cd ..

# python -m pip install Mathics-Scanner[full]
make develop-full
remake -x develop-full
- name: Test Mathics
run: |
make -j3 check
8 changes: 7 additions & 1 deletion .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ jobs:
- name: Install Mathics3 with Python dependencies
run: |
# We can comment out after next Mathics-Scanner release
python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
# python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]

git clone https://github.com/Mathics3/mathics-scanner.git
cd mathics-scanner
pip install -e .
cd ..

# python -m pip install Mathics-Scanner[full]
make develop-full
- name: Test Mathics3
Expand Down
4 changes: 0 additions & 4 deletions mathics/builtin/assignments/assign_binaryop.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ class AddTo(BinaryOperator):
attributes = A_HOLD_FIRST | A_PROTECTED
grouping = "Right"
operator = "+="
precedence = 100

rules = {
"x_ += dx_": "x = x + dx",
Expand Down Expand Up @@ -68,7 +67,6 @@ class Decrement(PostfixOperator):
"""

operator = "--"
precedence = 660
attributes = A_HOLD_FIRST | A_PROTECTED | A_READ_PROTECTED

rules = {
Expand Down Expand Up @@ -100,7 +98,6 @@ class DivideBy(BinaryOperator):
attributes = A_HOLD_FIRST | A_PROTECTED
grouping = "Right"
operator = "/="
precedence = 100

rules = {
"x_ /= dx_": "x = x / dx",
Expand Down Expand Up @@ -131,7 +128,6 @@ class Increment(PostfixOperator):
"""

operator = "++"
precedence = 660
attributes = A_HOLD_FIRST | A_PROTECTED | A_READ_PROTECTED

rules = {
Expand Down
10 changes: 5 additions & 5 deletions mathics/builtin/atomic/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ class CharacterEncoding(Predefined):

Setting its value to 'None' restore the value to \
'$SystemCharacterEncoding':
>> $CharacterEncoding = None;
>> $CharacterEncoding = None;
>> $SystemCharacterEncoding == $CharacterEncoding
= True

Expand Down Expand Up @@ -411,14 +411,14 @@ class HexadecimalCharacter(Builtin):

# This isn't your normal Box class. We'll keep this here rather than
# in mathics.builtin.box for now.
class InterpretedBox(PrefixOperator):
class InterpretationBox(PrefixOperator):
r"""
<url>
:WMA link:
https://reference.wolfram.com/language/ref/InterpretedBox.html</url>
https://reference.wolfram.com/language/ref/InterpretationBox.html</url>

<dl>
<dt>'InterpretedBox[$box$]'
<dt>'InterpretationBox[$box$]'
<dd>is the ad hoc fullform for \! $box$. just for internal use...
</dl>

Expand All @@ -431,7 +431,7 @@ class InterpretedBox(PrefixOperator):
summary_text = "interpret boxes as an expression"

def eval(self, boxes, evaluation: Evaluation):
"""InterpretedBox[boxes_]"""
"""InterpretationBox[boxes_]"""
# TODO: the following is a very raw and dummy way to
# handle these expressions.
# In the first place, this should handle different kind
Expand Down
8 changes: 4 additions & 4 deletions mathics/builtin/exp_structure/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
from mathics.eval.parts import python_levelspec, walk_levels


class ApplyLevel(BinaryOperator):
class MapApply(BinaryOperator):
"""
<url>
:WMA link:
https://reference.wolfram.com/language/ref/ApplyLevel.html</url>
https://reference.wolfram.com/language/ref/MapApply.html</url>

<dl>
<dt>'ApplyLevel[$f$, $expr$]'
<dt>'MapApply[$f$, $expr$]'

<dt>'$f$ @@@ $expr$'
<dd>is equivalent to 'Apply[$f$, $expr$, {1}]'.
Expand All @@ -36,7 +36,7 @@ class ApplyLevel(BinaryOperator):
precedence = 620

rules = {
"ApplyLevel[f_, expr_]": "Apply[f, expr, {1}]",
"MapApply[f_, expr_]": "Apply[f, expr, {1}]",
}

summary_text = "apply a function to a list, at the top level"
Expand Down
70 changes: 52 additions & 18 deletions mathics/core/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import importlib
import os.path as osp
import re
from abc import ABC
from functools import lru_cache, total_ordering
from itertools import chain
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union, cast
Expand Down Expand Up @@ -77,12 +78,12 @@
ROOT_DIR = pkg_resources.resource_filename("mathics", "")

# Load the conversion tables from disk
characters_path = osp.join(ROOT_DIR, "data", "operator-tables.json")
operator_tables_path = osp.join(ROOT_DIR, "data", "operator-tables.json")
assert osp.exists(
characters_path
), f"Operator precedence tables are missing; expected to be in {characters_path}"
with open(characters_path, "r") as f:
operator_data = ujson.load(f)
operator_tables_path
), f"Internal error: Operator precedence tables are missing; expected to be in {operator_tables_path}"
with open(operator_tables_path, "r") as f:
OPERATOR_DATA = ujson.load(f)


class Builtin:
Expand Down Expand Up @@ -248,7 +249,7 @@ def contribute(self, definitions, is_pymodule=False):
check_options = None
else:
raise ValueError(
"illegal option mode %s; check $OptionSyntax." % option_syntax
f"illegal option mode {option_syntax}; check $OptionSyntax."
)

rules = []
Expand Down Expand Up @@ -518,7 +519,7 @@ def get_sympy_names(self) -> List[str]:

# This has to come before MPMathFunction
class SympyFunction(SympyObject):
def eval(self, z, evaluation):
def eval(self, z, evaluation: Evaluation):
# Note: we omit a docstring here, so as not to confuse
# function signature collector ``contribute``.

Expand Down Expand Up @@ -964,14 +965,29 @@ def eval_multi(self, expr, first, sequ, evaluation):
return to_expression(name, to_expression(name, expr, *sequ), first)


class Operator(Builtin):
class Operator(Builtin, ABC):
"""
Base Class for operators: binary, unary, nullary, prefix postfix, ...
"""

operator: Optional[str] = None
precedence: Optional[int] = None
precedence_parse = None
needs_verbatim = False

default_formats = True

def get_precedence(self, name: str) -> int:
operator_info = OPERATOR_DATA.get("operator-precedence")
assert isinstance(
operator_info, dict
), 'Internal error: "operator-precedence" should be found in operators.json'
precedence = operator_info.get(name)
assert isinstance(
precedence, int
), f'Internal error: "precedence" field for "{name}" should be an integer is {precedence}'
return precedence

def get_operator(self) -> Optional[str]:
return self.operator

Expand All @@ -995,9 +1011,14 @@ def get_functions(self, prefix="eval", is_pymodule=False) -> List[Callable]:


class UnaryOperator(Operator):
"""
Class for Unary Operators, (e.g. Not, Factorial)
"""

def __init__(self, format_function, *args, **kwargs):
super().__init__(*args, **kwargs)
name = self.get_name()
name = self.get_name(short=True)
self.precedence = self.get_precedence(name)
if self.needs_verbatim:
name = f"Verbatim[{name}"
if self.default_formats:
Expand All @@ -1008,57 +1029,70 @@ def __init__(self, format_function, *args, **kwargs):
form = '%s[{HoldForm[item]},"%s",%d]' % (
format_function,
operator,
operator_data["operator-precedence"].get(name, self.precedence),
self.precedence,
)
self.formats[op_pattern] = form


class PrefixOperator(UnaryOperator):
"""
Class for Bultin Prefix Unary Operators, e.g. Not ("¬")
"""

def __init__(self, *args, **kwargs):
super().__init__("Prefix", *args, **kwargs)


class PostfixOperator(UnaryOperator):
"""
Class for Bultin Postfix Unary Operators, e.g. Factorial (!)
"""

def __init__(self, *args, **kwargs):
super().__init__("Postfix", *args, **kwargs)


class BinaryOperator(Operator):
"""
Class for Builtin Binary Operators, e.g. Plus (+)
"""

grouping = "System`None" # NonAssociative, None, Left, Right

def __init__(self, *args, **kwargs):
super(BinaryOperator, self).__init__(*args, **kwargs)
name = self.get_name()
name = self.get_name(short=True)
self.precedence = self.get_precedence(name)

# Prevent pattern matching symbols from gaining meaning here using
# Verbatim
name = "Verbatim[%s]" % name
name = f"Verbatim[{name}]"

# For compatibility, allow grouping symbols in builtins to be
# specified without System`.
self.grouping = ensure_context(self.grouping)

if self.grouping in ("System`None", "System`NonAssociative"):
op_pattern = "%s[items__]" % name
op_pattern = f"{name}[items__]"
replace_items = "items"
else:
op_pattern = "%s[x_, y_]" % name
op_pattern = f"{name}[x_, y_]"
replace_items = "x, y"

operator = ascii_operator_to_symbol.get(self.operator, self.__class__.__name__)

if self.default_formats:
formatted = "MakeBoxes[Infix[{%s}, %s, %d,%s], form]" % (
replace_items,
operator,
operator_data["operator-precedence"].get(name, self.precedence),
self.precedence,
self.grouping,
)
default_rules = {
"MakeBoxes[{0}, form:StandardForm|TraditionalForm]".format(
op_pattern
): formatted,
"MakeBoxes[{0}, form:InputForm|OutputForm]".format(
op_pattern
): formatted,
f"MakeBoxes[{op_pattern}, form:InputForm|OutputForm]": formatted,
}
default_rules.update(self.rules)
self.rules = default_rules
Expand Down
8 changes: 4 additions & 4 deletions mathics/core/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Here we have the base class and related function for element inside an Expression.
"""


from abc import ABC
from typing import Any, Optional, Tuple

from mathics.core.attributes import A_NO_ATTRIBUTES
Expand Down Expand Up @@ -193,7 +193,7 @@ def __ne__(self, other) -> bool:
) or self.get_sort_key() != other.get_sort_key()


class BaseElement(KeyComparable):
class BaseElement(KeyComparable, ABC):
"""
This is the base class from which all other Expressions are
derived from. If you think of an Expression as tree-like, then a
Expand Down Expand Up @@ -290,7 +290,7 @@ def get_float_value(self, permit_complex=False):
def get_int_value(self):
return None

def get_lookup_name(self):
def get_lookup_name(self) -> str:
"""
Returns symbol name of leftmost head. This method is used
to determine which definition must be asked for rules
Expand Down Expand Up @@ -398,7 +398,7 @@ def is_free(self, form, evaluation) -> bool:
def is_inexact(self) -> bool:
return self.get_precision() is not None

def sameQ(self, rhs) -> bool:
def sameQ(self, rhs: "BaseElement") -> bool:
"""Mathics SameQ"""
return id(self) == id(rhs)

Expand Down
Loading