diff --git a/SYMBOLS_MANIFEST.txt b/SYMBOLS_MANIFEST.txt
index 54cafb2e5..1369d207f 100644
--- a/SYMBOLS_MANIFEST.txt
+++ b/SYMBOLS_MANIFEST.txt
@@ -187,6 +187,7 @@ System`Brown
System`ButtonBox
System`Byte
System`ByteArray
+System`ByteArrayQ
System`ByteCount
System`ByteOrdering
System`C
diff --git a/mathics/builtin/arithmetic.py b/mathics/builtin/arithmetic.py
index d5ea760ba..1d4e9c52c 100644
--- a/mathics/builtin/arithmetic.py
+++ b/mathics/builtin/arithmetic.py
@@ -73,6 +73,7 @@
SymbolTable,
SymbolUndefined,
)
+from mathics.eval.arithmetic import eval_RealValuedNumberQ
from mathics.eval.inference import get_assumptions_list
from mathics.eval.nevaluator import eval_N
from mathics.eval.numeric import eval_Sign
@@ -961,11 +962,7 @@ class RealValuedNumberQ(Test):
summary_text = "test whether an expression is a real number"
def test(self, expr) -> bool:
- return (
- isinstance(expr, (Integer, Rational, Real))
- or expr.has_form("Underflow", 0)
- or expr.has_form("Overflow", 0)
- )
+ return eval_RealValuedNumberQ(expr)
class Sum(IterationFunction, SympyFunction, PrefixOperator):
diff --git a/mathics/builtin/atomic/atomic.py b/mathics/builtin/atomic/atomic.py
index 71e4df3f5..d36fbb294 100644
--- a/mathics/builtin/atomic/atomic.py
+++ b/mathics/builtin/atomic/atomic.py
@@ -4,7 +4,7 @@
"""
from mathics.core.builtin import Builtin, Test
-from mathics.core.symbols import Atom
+from mathics.eval.atomic.atomic import eval_AtomQ
class AtomQ(Test):
@@ -57,7 +57,7 @@ class AtomQ(Test):
summary_text = "test whether an expression is an atom"
def test(self, expr) -> bool:
- return isinstance(expr, Atom)
+ return eval_AtomQ(expr)
class Head(Builtin):
diff --git a/mathics/builtin/atomic/symbols.py b/mathics/builtin/atomic/symbols.py
index 04b77a67b..628bc0af0 100644
--- a/mathics/builtin/atomic/symbols.py
+++ b/mathics/builtin/atomic/symbols.py
@@ -6,7 +6,7 @@
or namespace, and can have a variety of type of values and attributes.
"""
import re
-from typing import Callable, List, Optional
+from typing import Callable, Optional
from mathics_scanner.tokeniser import NAMES_WILDCARDS, is_symbol_name
@@ -45,20 +45,21 @@
SymbolGrid,
SymbolInputForm,
SymbolLeft,
+ SymbolMissing,
SymbolOptions,
SymbolRule,
SymbolSet,
)
from mathics.doc.online import online_doc_string
+from mathics.eval.atomic.symbols import eval_SymbolQ
from mathics.eval.stackframe import get_eval_Expression
-SymbolMissing = Symbol("System`Missing")
SymbolUnknownSymbol = Symbol("System`UnknownSymbol")
def gather_and_format_definition_rules(
symbol: Symbol, evaluation: Evaluation
-) -> Optional[List[Expression]]:
+) -> Optional[list[Expression]]:
"""Return a list of lines describing the definition of `symbol`"""
lines = []
@@ -727,7 +728,7 @@ class SymbolQ(Test):
summary_text = "test whether is a symbol"
def test(self, expr) -> bool:
- return isinstance(expr, Symbol)
+ return eval_SymbolQ(expr)
class ValueQ(Builtin):
diff --git a/mathics/builtin/binary/__init__.py b/mathics/builtin/binary/__init__.py
index 8916f0d5a..3bd4705cc 100644
--- a/mathics/builtin/binary/__init__.py
+++ b/mathics/builtin/binary/__init__.py
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
-
"""
Binary Data
diff --git a/mathics/builtin/binary/bytearray.py b/mathics/builtin/binary/bytearray.py
index ae3a4e947..8e7af122f 100644
--- a/mathics/builtin/binary/bytearray.py
+++ b/mathics/builtin/binary/bytearray.py
@@ -6,10 +6,12 @@
from typing import Optional
from mathics.core.atoms import ByteArray, Integer, String
-from mathics.core.builtin import Builtin
+from mathics.core.attributes import A_PROTECTED
+from mathics.core.builtin import Builtin, Test
from mathics.core.convert.expression import to_mathics_list
from mathics.core.evaluation import Evaluation
from mathics.core.list import ListExpression
+from mathics.eval.binary.bytearray import eval_ByteArrayQ
class ByteArray_(Builtin):
@@ -54,6 +56,8 @@ class ByteArray_(Builtin):
),
}
+ expected_args = 1
+ eval_error = Builtin.generic_argument_error
name = "ByteArray"
summary_text = "array of bytes"
@@ -88,4 +92,33 @@ def eval_list(self, values, evaluation) -> Optional[ByteArray]:
return ba
-# TODO: BaseEncode, BaseDecode, ByteArrayQ, ByteArrayToString, StringToByteArray, ImportByteArray, ExportByteArray
+class ByteArrayQ(Test):
+ r"""
+ :WMA link:
+ https://reference.wolfram.com/language/ref/ByteArrayQ.html
+
+
+ - 'ByteArrayQ'[{$expr$}]
+
- returns True if $expr$ is a ByteArray object, and False otherwise.
+
+
+ >> ByteArrayQ[ByteArray[Range[16]]]
+ = True
+
+ >> ByteArrayQ[Range[3]]
+ = False
+
+ >> ByteArrayQ[ByteArray["xyz"]]
+ : The argument at position 1 in ByteArray[xyz] should be a vector of unsigned byte values or a Base64-encoded string.
+ = False
+ """
+
+ attributes = A_PROTECTED
+ summary_text = "test whether an expression is a ByteArray"
+
+ def test(self, expr) -> bool:
+ """Return True if expr is a ByteArray atom."""
+ return eval_ByteArrayQ(expr)
+
+
+# TODO: BaseEncode, BaseDecode, ByteArrayToString, StringToByteArray, ImportByteArray, ExportByteArray
diff --git a/mathics/core/builtin.py b/mathics/core/builtin.py
index 0c30758a3..125cc9e5e 100644
--- a/mathics/core/builtin.py
+++ b/mathics/core/builtin.py
@@ -14,18 +14,7 @@
from functools import total_ordering
from itertools import chain
from types import ModuleType
-from typing import (
- Any,
- Callable,
- Dict,
- Iterable,
- List,
- Optional,
- Sequence,
- Tuple,
- Union,
- cast,
-)
+from typing import Any, Callable, Iterable, List, Optional, Sequence, Tuple, Union, cast
import mpmath
import sympy
@@ -205,16 +194,16 @@ def eval_with_options(x, evaluation: Evaluation, options: dict):
_is_numeric: bool = False
attributes: int = A_PROTECTED
context: str = ""
- defaults: Dict[Optional[int], str] = {}
+ defaults: dict[Optional[int], str] = {}
# Number of arguments expected. -1 is used for an arbitrary number.
expected_args: Union[int, Tuple[int, int], range] = -1
- formats: Dict[str, Any] = {}
- messages: Dict[str, Any] = {}
+ formats: dict[str, Any] = {}
+ messages: dict[str, Any] = {}
name: Optional[str] = None
- options: Dict[str, Any] = {}
- rules: Dict[str, Any] = {}
+ options: dict[str, Any] = {}
+ rules: dict[str, Any] = {}
def __getnewargs_ex__(self):
return tuple(), {
@@ -363,7 +352,7 @@ def contextify_form_name(f):
forms = [""]
return forms, pattern
- formatvalues: Dict[str, List[BaseRule]] = {"": []}
+ formatvalues: dict[str, List[BaseRule]] = {"": []}
for pattern, function in self.get_functions("format_"):
forms, pattern = extract_forms(pattern)
pat_attr = attributes if pattern.get_head_name() == name else None
@@ -676,7 +665,7 @@ def is_literal(self) -> bool:
class BuiltinElement(Builtin, BaseElement):
- options: Dict[str, Any]
+ options: dict[str, Any]
def __new__(cls, *args, **kwargs):
new_kwargs = kwargs.copy()
@@ -707,7 +696,7 @@ def __hash__(self):
class SympyObject(Builtin):
sympy_name: Optional[str] = None
- mathics_to_sympy: Dict[str, str] = {}
+ mathics_to_sympy: dict[str, str] = {}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -715,7 +704,7 @@ def __init__(self, *args, **kwargs):
self.sympy_name = strip_context(self.get_name()).lower()
self.mathics_to_sympy[self.__class__.__name__] = self.sympy_name
- def get_sympy_names(self) -> List[str]:
+ def get_sympy_names(self) -> list[str]:
if self.sympy_name:
return [self.sympy_name]
return []
@@ -848,8 +837,8 @@ def eval(self, z, evaluation: Evaluation):
class MPMathMultiFunction(MPMathFunction):
- sympy_names: Optional[Dict[int, str]] = None
- mpmath_names: Optional[Dict[int, str]] = None
+ sympy_names: Optional[dict[int, str]] = None
+ mpmath_names: Optional[dict[int, str]] = None
def get_sympy_names(self):
if self.sympy_names is None:
@@ -972,7 +961,7 @@ def has_option(options, name, evaluation):
return get_option(options, name, evaluation, evaluate=False) is not None
-mathics_to_python: Dict[str, Any] = {} # here we have: name -> string
+mathics_to_python: dict[str, Any] = {} # here we have: name -> string
@total_ordering
@@ -1638,8 +1627,8 @@ def add_no_meaning_builtin_classes(
class PatternObject(BuiltinElement, BasePattern):
needs_verbatim = True
- arg_counts: List[int] = []
- options: Dict[str, Any]
+ arg_counts: list[int] = []
+ options: dict[str, Any]
def init(self, expr: Expression, evaluation: Optional[Evaluation] = None):
super().init(expr, evaluation=evaluation)
@@ -1705,6 +1694,9 @@ def pattern_precedence(self) -> tuple:
class Test(Builtin, ABC):
+ expected_args = 1
+ eval_error = Builtin.generic_argument_error
+
def eval(self, expr, evaluation: Evaluation) -> Optional[BooleanType]:
# Note: in the docstring below, we need to use %(name)s for
# subclasses like ExactNumberQ to work with function-application
diff --git a/mathics/eval/arithmetic.py b/mathics/eval/arithmetic.py
index 8e09d28ea..d552b2f52 100644
--- a/mathics/eval/arithmetic.py
+++ b/mathics/eval/arithmetic.py
@@ -184,6 +184,14 @@ def eval_negate_number(n: Number) -> Number:
return eval_multiply_numbers(IntegerM1, n)
+def eval_RealValuedNumberQ(expr) -> bool:
+ return (
+ isinstance(expr, (Integer, Rational, Real))
+ or expr.has_form("Underflow", 0)
+ or expr.has_form("Overflow", 0)
+ )
+
+
def segregate_numbers(
*elements: BaseElement,
) -> Tuple[List[Number], List[BaseElement]]:
diff --git a/mathics/eval/atomic/atomic.py b/mathics/eval/atomic/atomic.py
new file mode 100644
index 000000000..a8b541016
--- /dev/null
+++ b/mathics/eval/atomic/atomic.py
@@ -0,0 +1,10 @@
+"""
+Evaluation methods for mathics.builtin.atomic.atomic.
+"""
+
+from mathics.core.symbols import Atom
+
+
+def eval_AtomQ(expr) -> bool:
+ """Return True if expr is an Atom."""
+ return isinstance(expr, Atom)
diff --git a/mathics/eval/atomic/symbols.py b/mathics/eval/atomic/symbols.py
new file mode 100644
index 000000000..9d94a58aa
--- /dev/null
+++ b/mathics/eval/atomic/symbols.py
@@ -0,0 +1,10 @@
+"""
+Evaluation methods for mathics.builtin.atomic.atomic.
+"""
+
+from mathics.core.symbols import Symbol
+
+
+def eval_SymbolQ(expr) -> bool:
+ """Return True if expr is an Symbol."""
+ return isinstance(expr, Symbol)
diff --git a/mathics/eval/binary/bytearray.py b/mathics/eval/binary/bytearray.py
new file mode 100644
index 000000000..e28c6f926
--- /dev/null
+++ b/mathics/eval/binary/bytearray.py
@@ -0,0 +1,10 @@
+"""
+Evaluation methods for mathics.builtin.binary.bytearray.
+"""
+
+from mathics.core.atoms import ByteArray
+
+
+def eval_ByteArrayQ(expr) -> bool:
+ """Return True if expr is a ByteArray atom."""
+ return isinstance(expr, ByteArray)
diff --git a/test/builtin/test_binary.py b/test/builtin/test_binary.py
index 0aebbb817..2fbcfbf70 100644
--- a/test/builtin/test_binary.py
+++ b/test/builtin/test_binary.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import sys
-from test.helper import check_evaluation
+from test.helper import check_arg_counts, check_evaluation
import pytest
@@ -456,3 +456,23 @@ def test_type_conversion():
assert expr.value.dtype == np.int64
expr = evaluate('NumericArray[{1,2}, "ComplexReal32"]')
assert expr.value.dtype == np.complex64
+
+
+@pytest.mark.parametrize(
+ ("function_name", "msg_fragment"),
+ [
+ # FIXME: ToString in check_arg_counts() is giving a traceback
+ # for ByteArray[].
+ # (
+ # "ByteArray",
+ # "1 argument is",
+ # ),
+ (
+ "ByteArrayQ",
+ "1 argument is",
+ ),
+ ],
+)
+def test_arg_count_errors(function_name, msg_fragment):
+ """ """
+ check_arg_counts(function_name, msg_fragment)