parsimathious 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: parsimathious
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A mathematical expression parser supporting arithmetic, functions, and complex numbers
|
|
5
|
+
Keywords: math,parser,expression,arithmetic,complex numbers
|
|
6
|
+
Author: Simone Sturniolo
|
|
7
|
+
Author-email: Simone Sturniolo <simonesturniolo@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
17
|
+
Requires-Dist: parsimonious>=0.11.0
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Project-URL: Homepage, https://github.com/stur86/parsimathious
|
|
20
|
+
Project-URL: Repository, https://github.com/stur86/parsimathious
|
|
21
|
+
Project-URL: Issues, https://github.com/stur86/parsimathious/issues
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# parsimathious
|
|
25
|
+
|
|
26
|
+
[](https://pypi.org/project/parsimathious/)
|
|
27
|
+
[](https://pypi.org/project/parsimathious/)
|
|
28
|
+
[](https://github.com/stur86/parsimathious/actions/workflows/test.yml)
|
|
29
|
+
[](https://opensource.org/licenses/MIT)
|
|
30
|
+
|
|
31
|
+
`parsimathious` is a simple mathematical expression parser implemented with [`parsimonious`](https://github.com/erikrose/parsimonious). It supports basic arithmetic operations, parentheses, unary functions, constants, and complex numbers.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
You can install `parsimathious` using pip:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install parsimathious
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
Import the `ExpressionParser` and create an instance:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from parsimathious import ExpressionParser
|
|
47
|
+
|
|
48
|
+
parser = ExpressionParser()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Then you can parse and evaluate expressions:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
result = parser("sin(pi / 2) + 1")
|
|
55
|
+
print(result) # Output: 2.0
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Supported functions and constants
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
On top of basic arithmetic operations, `parsimathious` supports the following unary functions and constants by default:
|
|
62
|
+
|
|
63
|
+
| Name | Python Implementation | Description |
|
|
64
|
+
|----------|--------------------------------|------------------------------------|
|
|
65
|
+
| `sin` | math.sin | Sine |
|
|
66
|
+
| `cos` | math.cos | Cosine |
|
|
67
|
+
| `tan` | math.tan | Tangent |
|
|
68
|
+
| `log` | math.log | Natural logarithm (base e) |
|
|
69
|
+
| `sqrt` | math.sqrt | Square root |
|
|
70
|
+
| `exp` | math.exp | Exponential (e^x) |
|
|
71
|
+
| `log10` | math.log10 | Logarithm base 10 |
|
|
72
|
+
| `abs` | abs | Absolute value |
|
|
73
|
+
| `floor` | math.floor | Floor (round down) |
|
|
74
|
+
| `ceil` | math.ceil | Ceiling (round up) |
|
|
75
|
+
| `round` | round | Round to nearest integer |
|
|
76
|
+
| `sinh` | math.sinh | Hyperbolic sine |
|
|
77
|
+
| `cosh` | math.cosh | Hyperbolic cosine |
|
|
78
|
+
| `tanh` | math.tanh | Hyperbolic tangent |
|
|
79
|
+
| `asin` | math.asin | Arc sine |
|
|
80
|
+
| `acos` | math.acos | Arc cosine |
|
|
81
|
+
| `atan` | math.atan | Arc tangent |
|
|
82
|
+
| `asinh` | math.asinh | Inverse hyperbolic sine |
|
|
83
|
+
| `acosh` | math.acosh | Inverse hyperbolic cosine |
|
|
84
|
+
| `atanh` | math.atanh | Inverse hyperbolic tangent |
|
|
85
|
+
| `sec` | lambda x: 1 / math.cos(x) | Secant |
|
|
86
|
+
| `csc` | lambda x: 1 / math.sin(x) | Cosecant |
|
|
87
|
+
| `cot` | lambda x: 1 / math.tan(x) | Cotangent |
|
|
88
|
+
|
|
89
|
+
### Constants
|
|
90
|
+
|
|
91
|
+
| Name | Value | Description |
|
|
92
|
+
|------|----------------------|----------------------------|
|
|
93
|
+
| `pi` | math.pi | The mathematical constant π |
|
|
94
|
+
| `e` | math.e | The mathematical constant e |
|
|
95
|
+
| `i` | 1j | The imaginary unit |
|
|
96
|
+
|
|
97
|
+
## Custom Unary Functions
|
|
98
|
+
|
|
99
|
+
It's also possible to support custom unary functions by passing a dictionary of function names to their implementations when creating the `ExpressionParser`:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
import math
|
|
103
|
+
from parsimathious import ExpressionParser, UnaryFunctionMap
|
|
104
|
+
|
|
105
|
+
custom_functions: UnaryFunctionMap = {
|
|
106
|
+
"log2": math.log2, # Logarithm base 2
|
|
107
|
+
"cube": lambda x: x ** 3, # Cube function
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
parser = ExpressionParser(unary_functions=custom_functions)
|
|
111
|
+
result = parser("log2(8) + cube(3)")
|
|
112
|
+
print(result) # Output: 35.0
|
|
113
|
+
```
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# parsimathious
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/parsimathious/)
|
|
4
|
+
[](https://pypi.org/project/parsimathious/)
|
|
5
|
+
[](https://github.com/stur86/parsimathious/actions/workflows/test.yml)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
`parsimathious` is a simple mathematical expression parser implemented with [`parsimonious`](https://github.com/erikrose/parsimonious). It supports basic arithmetic operations, parentheses, unary functions, constants, and complex numbers.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
You can install `parsimathious` using pip:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install parsimathious
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
Import the `ExpressionParser` and create an instance:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from parsimathious import ExpressionParser
|
|
24
|
+
|
|
25
|
+
parser = ExpressionParser()
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Then you can parse and evaluate expressions:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
result = parser("sin(pi / 2) + 1")
|
|
32
|
+
print(result) # Output: 2.0
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Supported functions and constants
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
On top of basic arithmetic operations, `parsimathious` supports the following unary functions and constants by default:
|
|
39
|
+
|
|
40
|
+
| Name | Python Implementation | Description |
|
|
41
|
+
|----------|--------------------------------|------------------------------------|
|
|
42
|
+
| `sin` | math.sin | Sine |
|
|
43
|
+
| `cos` | math.cos | Cosine |
|
|
44
|
+
| `tan` | math.tan | Tangent |
|
|
45
|
+
| `log` | math.log | Natural logarithm (base e) |
|
|
46
|
+
| `sqrt` | math.sqrt | Square root |
|
|
47
|
+
| `exp` | math.exp | Exponential (e^x) |
|
|
48
|
+
| `log10` | math.log10 | Logarithm base 10 |
|
|
49
|
+
| `abs` | abs | Absolute value |
|
|
50
|
+
| `floor` | math.floor | Floor (round down) |
|
|
51
|
+
| `ceil` | math.ceil | Ceiling (round up) |
|
|
52
|
+
| `round` | round | Round to nearest integer |
|
|
53
|
+
| `sinh` | math.sinh | Hyperbolic sine |
|
|
54
|
+
| `cosh` | math.cosh | Hyperbolic cosine |
|
|
55
|
+
| `tanh` | math.tanh | Hyperbolic tangent |
|
|
56
|
+
| `asin` | math.asin | Arc sine |
|
|
57
|
+
| `acos` | math.acos | Arc cosine |
|
|
58
|
+
| `atan` | math.atan | Arc tangent |
|
|
59
|
+
| `asinh` | math.asinh | Inverse hyperbolic sine |
|
|
60
|
+
| `acosh` | math.acosh | Inverse hyperbolic cosine |
|
|
61
|
+
| `atanh` | math.atanh | Inverse hyperbolic tangent |
|
|
62
|
+
| `sec` | lambda x: 1 / math.cos(x) | Secant |
|
|
63
|
+
| `csc` | lambda x: 1 / math.sin(x) | Cosecant |
|
|
64
|
+
| `cot` | lambda x: 1 / math.tan(x) | Cotangent |
|
|
65
|
+
|
|
66
|
+
### Constants
|
|
67
|
+
|
|
68
|
+
| Name | Value | Description |
|
|
69
|
+
|------|----------------------|----------------------------|
|
|
70
|
+
| `pi` | math.pi | The mathematical constant π |
|
|
71
|
+
| `e` | math.e | The mathematical constant e |
|
|
72
|
+
| `i` | 1j | The imaginary unit |
|
|
73
|
+
|
|
74
|
+
## Custom Unary Functions
|
|
75
|
+
|
|
76
|
+
It's also possible to support custom unary functions by passing a dictionary of function names to their implementations when creating the `ExpressionParser`:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import math
|
|
80
|
+
from parsimathious import ExpressionParser, UnaryFunctionMap
|
|
81
|
+
|
|
82
|
+
custom_functions: UnaryFunctionMap = {
|
|
83
|
+
"log2": math.log2, # Logarithm base 2
|
|
84
|
+
"cube": lambda x: x ** 3, # Cube function
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
parser = ExpressionParser(unary_functions=custom_functions)
|
|
88
|
+
result = parser("log2(8) + cube(3)")
|
|
89
|
+
print(result) # Output: 35.0
|
|
90
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "parsimathious"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "A mathematical expression parser supporting arithmetic, functions, and complex numbers"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Simone Sturniolo", email = "simonesturniolo@gmail.com" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
keywords = ["math", "parser", "expression", "arithmetic", "complex numbers"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3.11",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"Programming Language :: Python :: 3.13",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Intended Audience :: Science/Research",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Mathematics"
|
|
21
|
+
]
|
|
22
|
+
dependencies = [
|
|
23
|
+
"parsimonious>=0.11.0",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/stur86/parsimathious"
|
|
28
|
+
Repository = "https://github.com/stur86/parsimathious"
|
|
29
|
+
Issues = "https://github.com/stur86/parsimathious/issues"
|
|
30
|
+
|
|
31
|
+
[build-system]
|
|
32
|
+
requires = ["uv_build>=0.10.8,<0.11.0"]
|
|
33
|
+
build-backend = "uv_build"
|
|
34
|
+
|
|
35
|
+
[dependency-groups]
|
|
36
|
+
dev = [
|
|
37
|
+
"pytest>=9.0.3",
|
|
38
|
+
"taskipy>=1.14.1",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[tool.taskipy.tasks]
|
|
42
|
+
test = "pytest tests"
|
|
43
|
+
build = "uv build"
|
|
44
|
+
publish = "uv publish"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from parsimonious import Grammar, ParseError
|
|
3
|
+
from parsimonious.nodes import Node
|
|
4
|
+
from typing import Dict, Callable
|
|
5
|
+
|
|
6
|
+
UnaryFunction = Callable[[float], float]
|
|
7
|
+
UnaryFunctionMap = Dict[str, UnaryFunction]
|
|
8
|
+
|
|
9
|
+
_DEFAULT_UNARY_FUNCTIONS: UnaryFunctionMap = {
|
|
10
|
+
"sin": math.sin,
|
|
11
|
+
"cos": math.cos,
|
|
12
|
+
"tan": math.tan,
|
|
13
|
+
"log": math.log,
|
|
14
|
+
"sqrt": math.sqrt,
|
|
15
|
+
"exp": math.exp,
|
|
16
|
+
"log10": math.log10,
|
|
17
|
+
"abs": abs,
|
|
18
|
+
"floor": math.floor,
|
|
19
|
+
"ceil": math.ceil,
|
|
20
|
+
"round": round,
|
|
21
|
+
"sinh": math.sinh,
|
|
22
|
+
"cosh": math.cosh,
|
|
23
|
+
"tanh": math.tanh,
|
|
24
|
+
"asin": math.asin,
|
|
25
|
+
"acos": math.acos,
|
|
26
|
+
"atan": math.atan,
|
|
27
|
+
"asinh": math.asinh,
|
|
28
|
+
"acosh": math.acosh,
|
|
29
|
+
"atanh": math.atanh,
|
|
30
|
+
"sec": lambda x: 1 / math.cos(x),
|
|
31
|
+
"csc": lambda x: 1 / math.sin(x),
|
|
32
|
+
"cot": lambda x: 1 / math.tan(x),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class ExpressionGrammar:
|
|
36
|
+
_grammar: Grammar
|
|
37
|
+
|
|
38
|
+
def __init__(self, unary_functions: UnaryFunctionMap = _DEFAULT_UNARY_FUNCTIONS):
|
|
39
|
+
grammar_definition = """
|
|
40
|
+
expression = sum / unary_number
|
|
41
|
+
sum = term (add_op term)*
|
|
42
|
+
term = factor (mul_op factor)*
|
|
43
|
+
factor = exp_factor (exp_op exp_factor)*
|
|
44
|
+
exp_factor = atom
|
|
45
|
+
unary_number = unary_op number
|
|
46
|
+
parenthesized_expression = "(" expression ")"
|
|
47
|
+
add_op = "+" / "-"
|
|
48
|
+
mul_op = "*" / "/"
|
|
49
|
+
exp_op = "^"
|
|
50
|
+
unary_op = "+" / "-"
|
|
51
|
+
number = ~r"\\d+(\\.\\d+)?"
|
|
52
|
+
constant = "pi" / "e"
|
|
53
|
+
imaginary_unit = "i"
|
|
54
|
+
imaginary_number = number imaginary_unit
|
|
55
|
+
complex_number = imaginary_number / number / imaginary_unit / constant
|
|
56
|
+
"""
|
|
57
|
+
if len(unary_functions) > 0:
|
|
58
|
+
function_keys = list(unary_functions.keys())
|
|
59
|
+
# Sort them from longest to shortest to ensure correct parsing (e.g., "log10" before "log")
|
|
60
|
+
function_keys.sort(key=len, reverse=True)
|
|
61
|
+
function_names = " / ".join(f'"{name}"' for name in function_keys)
|
|
62
|
+
grammar_definition += f"""atom = function_call / parenthesized_expression / complex_number
|
|
63
|
+
function_call = function_name parenthesized_expression
|
|
64
|
+
function_name = {function_names}"""
|
|
65
|
+
else:
|
|
66
|
+
grammar_definition += "atom = parenthesized_expression / complex_number"
|
|
67
|
+
|
|
68
|
+
self._grammar = Grammar(grammar_definition)
|
|
69
|
+
|
|
70
|
+
def __call__(self, expression: str) -> Node:
|
|
71
|
+
# Strip out all spaces
|
|
72
|
+
expression = expression.replace(" ", "")
|
|
73
|
+
try:
|
|
74
|
+
return self._grammar.parse(expression)
|
|
75
|
+
except ParseError as e:
|
|
76
|
+
raise Exception(f"Syntax error at position {e.pos}: {e.text[e.pos:e.pos+20]}") from e
|
|
77
|
+
except Exception as e:
|
|
78
|
+
raise Exception(f"Error parsing expression: {e}") from e
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if __name__ == "__main__":
|
|
82
|
+
while True:
|
|
83
|
+
grammar = ExpressionGrammar()
|
|
84
|
+
try:
|
|
85
|
+
expr = input("Enter an expression (or 'exit' to quit): ")
|
|
86
|
+
if expr.lower() == "exit":
|
|
87
|
+
break
|
|
88
|
+
ast = grammar(expr)
|
|
89
|
+
except Exception as e:
|
|
90
|
+
print(f"Error: {e}")
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from typing import Any, Sequence
|
|
3
|
+
|
|
4
|
+
from parsimonious import NodeVisitor
|
|
5
|
+
from parsimonious.nodes import Node
|
|
6
|
+
from .grammar import ExpressionGrammar, UnaryFunctionMap, _DEFAULT_UNARY_FUNCTIONS
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ExpressionVisitor(NodeVisitor):
|
|
10
|
+
def __init__(self, unary_functions: UnaryFunctionMap):
|
|
11
|
+
super().__init__()
|
|
12
|
+
self._unary_functions = unary_functions
|
|
13
|
+
|
|
14
|
+
def visit_complex_number(self, node, visited_children):
|
|
15
|
+
return visited_children[0]
|
|
16
|
+
|
|
17
|
+
def visit_imaginary_number(self, node, visited_children):
|
|
18
|
+
number_node, _ = visited_children
|
|
19
|
+
if number_node is None:
|
|
20
|
+
return complex(0, 1) # Just "i" means 0 + 1i
|
|
21
|
+
else:
|
|
22
|
+
return complex(0, float(number_node)) # e.g., "2i" means 0 + 2i
|
|
23
|
+
|
|
24
|
+
def visit_number(self, node, visited_children):
|
|
25
|
+
return float(node.text)
|
|
26
|
+
|
|
27
|
+
def visit_imaginary_unit(self, node, visited_children):
|
|
28
|
+
return 1.0j
|
|
29
|
+
|
|
30
|
+
def visit_constant(self, node, visited_children):
|
|
31
|
+
constant_map = {
|
|
32
|
+
"pi": math.pi,
|
|
33
|
+
"e": math.e,
|
|
34
|
+
}
|
|
35
|
+
try:
|
|
36
|
+
return constant_map[node.text]
|
|
37
|
+
except KeyError:
|
|
38
|
+
raise ValueError(f"Unknown constant: {node.text}")
|
|
39
|
+
|
|
40
|
+
def visit_atom(self, node, visited_children):
|
|
41
|
+
return visited_children[0]
|
|
42
|
+
|
|
43
|
+
def visit_exp_factor(self, node, visited_children):
|
|
44
|
+
return visited_children[0]
|
|
45
|
+
|
|
46
|
+
def visit_factor(self, node, visited_children):
|
|
47
|
+
base = visited_children[0]
|
|
48
|
+
for op, exponent in visited_children[1]:
|
|
49
|
+
if op.text != "^":
|
|
50
|
+
raise ValueError(f"Unexpected operator in factor: {op.text}")
|
|
51
|
+
base = base ** exponent
|
|
52
|
+
return base
|
|
53
|
+
|
|
54
|
+
def visit_term(self, node, visited_children):
|
|
55
|
+
result = visited_children[0]
|
|
56
|
+
for [op], factor in visited_children[1]:
|
|
57
|
+
if op.text == "*":
|
|
58
|
+
result *= factor
|
|
59
|
+
elif op.text == "/":
|
|
60
|
+
result /= factor
|
|
61
|
+
else:
|
|
62
|
+
raise ValueError(f"Unexpected operator in term: {op.text}")
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
def visit_sum(self, node, visited_children):
|
|
66
|
+
result = visited_children[0]
|
|
67
|
+
for [op], term in visited_children[1]:
|
|
68
|
+
if op.text == "+":
|
|
69
|
+
result += term
|
|
70
|
+
elif op.text == "-":
|
|
71
|
+
result -= term
|
|
72
|
+
else:
|
|
73
|
+
raise ValueError(f"Unexpected operator in expression: {op.text}")
|
|
74
|
+
return result
|
|
75
|
+
|
|
76
|
+
def visit_parenthesized_expression(self, node, visited_children):
|
|
77
|
+
_, expr, _ = visited_children
|
|
78
|
+
return expr[0]
|
|
79
|
+
|
|
80
|
+
def visit_function_name(self, node, visited_children):
|
|
81
|
+
return node
|
|
82
|
+
|
|
83
|
+
def visit_function_call(self, node, visited_children):
|
|
84
|
+
function_name_node, arg_node = visited_children
|
|
85
|
+
function_name = function_name_node.text
|
|
86
|
+
if function_name not in self._unary_functions:
|
|
87
|
+
raise ValueError(f"Unknown function: {function_name}")
|
|
88
|
+
func = self._unary_functions[function_name]
|
|
89
|
+
arg_value = arg_node # The argument is the first child of the parenthesized expression
|
|
90
|
+
return func(arg_value)
|
|
91
|
+
|
|
92
|
+
def generic_visit(self, node: Node, visited_children: Sequence[Any]):
|
|
93
|
+
return visited_children or node
|
|
94
|
+
|
|
95
|
+
class ExpressionParser:
|
|
96
|
+
def __init__(self, unary_functions: UnaryFunctionMap = _DEFAULT_UNARY_FUNCTIONS):
|
|
97
|
+
self._grammar = ExpressionGrammar(unary_functions)
|
|
98
|
+
self._visitor = ExpressionVisitor(unary_functions)
|
|
99
|
+
|
|
100
|
+
def __call__(self, expression: str) -> float | complex:
|
|
101
|
+
ast = self._grammar(expression)
|
|
102
|
+
return self._visitor.visit(ast)
|