diff --git a/parser/.gitignore b/parser/.gitignore new file mode 100644 index 0000000..7e99e36 --- /dev/null +++ b/parser/.gitignore @@ -0,0 +1 @@ +*.pyc \ No newline at end of file diff --git a/parser/README.md b/parser/README.md new file mode 100644 index 0000000..3e743d3 --- /dev/null +++ b/parser/README.md @@ -0,0 +1,63 @@ +# Math expression parser + +Math expression evaluation library. It supports most of useful math operations and functions. Expressions can contain variables which can be substituted with `int`s, `float`s or `numpy.ndarray`s. + +## Example usage + +```python +from parser import Parser + +parser = Parser("(-b + sqrt(b^2-4a c))/(2a)") + +parser.variables_names # {'c', 'a', 'b'} + +parser.evaluate({"a": 1, "b": -3, "c": 2}) # 1.0 + +parser.evaluate({"a": [1, 1, 1], "b": [-5, -6, -9], "c": [6, 9, 20]}) # [2. 3. 4.] +``` +## Expression syntax + +Expression can contain numbers or variable names with functions applyed to them, separated with operators or united with braces. + +Theese are supported: + +Functions: + +|name| math | +|--|--| +| `abs` | $\|x\|$ | +| `acos` | $\cos^{-1}(x)$ | +| `acosh` | $\cosh^{-1}(x)$ | +| `acot` | $\cot^{-1}(x)$ | +| `asin` | $\sin^{-1}(x)$ | +| `asinh` | $\sinh^{-1}(x)$ | +| `atan` | $\tan^{-1}(x)$ | +| `avg` | $\overline x$ | +| `cos` | $\cos(x)$ | +| `cosh` | $\cosh(x)$ | +| `cot` | $\cot(x)$ | +| `exp` | $\exp(x)$ | +| `lg` | $\lg(x)$ | +| `ln` | $\ln(x)$ | +| `log10` | $\log_{10}(x)$ | +| `log2` | $\log_2(x)$ | +| `prod` | $\displaystyle \prod_{i=0}^n x_i$ | +| `sgn` | $sgn(x)$ | +| `sin` | $\sin(x)$ | +| `sinh` | $\sinh(x)$ | +| `sqrt` | $\sqrt{x}$ | +| `sum` | $\displaystyle\sum_{i=0}^n x_i$ | +| `tan` | $\tan(x)$ | +| `tanh` | $\tanh(x)$ | + +Operators: `+`, `-`, `*`, `/`, `^`, `%` + +Braces: `()`, `[]`, `{}` + +Floating points: `.`, `,` + +Functions have only one argument, provided in braces. Operators must have two operands except minus (if it is the first character of equation or braced expression). + +`sum` and `prod` sums and multiplies all elements in variable if it is `numpy.ndarray` and produces a `float`. + +**! There is no error handling yet !** diff --git a/parser/parser/__init__.py b/parser/parser/__init__.py new file mode 100644 index 0000000..3385e94 --- /dev/null +++ b/parser/parser/__init__.py @@ -0,0 +1,21 @@ +from .parser import ( + BinaryExpression, + Expression, + Operation, + Parser, + Token, + Tokenizer, + UnaryExpression, + ValueExpression, +) + +__all__ = ( + "BinaryExpression", + "Expression", + "Operation", + "Parser", + "Token", + "Tokenizer", + "UnaryExpression", + "ValueExpression", +) diff --git a/parser/parser/__main__.py b/parser/parser/__main__.py new file mode 100644 index 0000000..bbfe661 --- /dev/null +++ b/parser/parser/__main__.py @@ -0,0 +1,16 @@ +from parser import Parser + +expression = input("Input math expression: ") + +parser = Parser(expression) + +print("Variables in your expression: " + ", ".join(parser.variables_names)) + +variables = {} + +for key in parser.variables_names: + variables[key] = float(input(f"Input '{key}' variable value: ")) + +res = parser.evaluate(variables) + +print(f"Evaluation result is: {res}") diff --git a/parser/parser/expression.py b/parser/parser/expression.py new file mode 100644 index 0000000..8563d76 --- /dev/null +++ b/parser/parser/expression.py @@ -0,0 +1,52 @@ +import abc +from collections.abc import Callable, Mapping + +from .types import FunctionType, OperatorType, ValueType + + +class Expression(abc.ABC): + _evaluator: Callable[[Mapping[str, ValueType]], ValueType] + + def evaluate(self, variables: Mapping[str, ValueType]): + return self._evaluator(variables) + + +class ValueExpression(Expression): + def __init__(self, a: str | ValueType): + self.__debug_a = a + + if isinstance(a, str): + self._evaluator = lambda vars: vars[a] + else: + self._evaluator = lambda _: a + + def __repr__(self): + return f"<{self.__debug_a}>" + + +class UnaryExpression(Expression): + def __init__(self, function: FunctionType, a: Expression): + self.__debug_f = function.__name__ + self.__debug_a = repr(a) + + self._evaluator = lambda vars: function(a.evaluate(vars)) + + def __repr__(self): + return f"<{self.__debug_f}({self.__debug_a})>" + + +class BinaryExpression(Expression): + def __init__( + self, + function: OperatorType, + a: Expression, + b: Expression, + ): + self.__debug_f = function.__name__ + self.__debug_a = repr(a) + self.__debug_b = repr(b) + + self._evaluator = lambda vars: function(a.evaluate(vars), b.evaluate(vars)) + + def __repr__(self): + return f"<{self.__debug_a} {self.__debug_f} {self.__debug_b}>" diff --git a/parser/parser/operation.py b/parser/parser/operation.py new file mode 100644 index 0000000..7ff47cd --- /dev/null +++ b/parser/parser/operation.py @@ -0,0 +1,84 @@ +from dataclasses import dataclass + +import numpy as np + +from .types import FunctionType, OperatorType, ValueType + + +def acot(x: ValueType): + return np.arctan(1 / x) + + +def cot(x: ValueType): + return 1 / np.tan(x) + + +functions: dict[str, FunctionType] = { + "abs": np.abs, + "acos": np.arccos, + "acosh": np.arccosh, + "acot": acot, + "asin": np.arcsin, + "asinh": np.arcsinh, + "atan": np.arctan, + "avg": np.average, + "cos": np.cos, + "cosh": np.cosh, + "cot": cot, + "exp": np.exp, + "lg": np.log10, + "ln": np.log, + "log10": np.log10, + "log2": np.log2, + "prod": np.prod, + "sgn": np.sign, + "sin": np.sin, + "sinh": np.sinh, + "sqrt": np.sqrt, + "sum": np.sum, + "tan": np.tan, + "tah": np.tanh, +} + +operators: dict[str, OperatorType] = { + "+": np.add, + "-": np.subtract, + "*": np.multiply, + "/": np.divide, + "%": np.mod, + "^": np.float_power, +} + +priorities: dict[str, int] = { + "(": 0, + "+": 1, + "-": 1, + "*": 2, + "/": 2, + "%": 2, + "^": 3, + "f": 4, # function + ")": 5, +} + + +@dataclass +class Operation: + evaluator: (FunctionType | OperatorType | str) + priority: int + size: int + + +class FunctionOperation(Operation): + def __init__(self, name: str): + super().__init__(functions[name], priorities["f"], 1) + + +class BraceOperation(Operation): + def __init__(self, name: str): + super().__init__(name, priorities[name], 0) + + +class OperatorOperation(Operation): + def __init__(self, name: str): + super().__init__(operators[name], priorities[name], 2) diff --git a/parser/parser/parser.py b/parser/parser/parser.py new file mode 100644 index 0000000..71eaa1c --- /dev/null +++ b/parser/parser/parser.py @@ -0,0 +1,81 @@ +from collections.abc import Mapping + +from .expression import BinaryExpression, Expression, UnaryExpression, ValueExpression +from .operation import ( + BraceOperation, + FunctionOperation, + Operation, + OperatorOperation, + priorities, +) +from .tokenizer import Token, Tokenizer +from .types import ValueType + + +class Parser: + def __init__(self, input_expr: str): + self.input_expr = input_expr + self.variables_names: set[str] = set() + + self.tokenize() + self.parse() + + def tokenize(self): + self.tokens = Tokenizer(self.input_expr) + + def parse(self): + self.val_stack: list[Expression] = [] + self.op_stack: list[Operation] = [] + + for t_val, t_type in self.tokens: + if t_type in (Token.Number, Token.Variable): + self.val_stack.append(ValueExpression(t_val)) + + if t_type == Token.Variable: + self.variables_names.add(t_val) + + elif t_type == Token.Function: + self.op_stack.append(FunctionOperation(t_val)) + + elif t_type == Token.LBrace: + self.op_stack.append(BraceOperation("(")) + + elif t_type == Token.RBrace: + while len(self.op_stack) > 0 and not ( + self.op_stack[-1].size == 0 and self.op_stack[-1].priority == 0 + ): # until next in stack is lbrace + self.do_one() + self.op_stack.pop() # pop lbrace + + elif t_type == Token.Operator: + t_priority = priorities[t_val] + + while ( + len(self.op_stack) > 0 and self.op_stack[-1].priority > t_priority + ): + self.do_one() + + self.op_stack.append(OperatorOperation(t_val)) + + while len(self.op_stack) > 0: + self.do_one() + + self._evaluator = self.val_stack[0].evaluate + + self.__debug_expr = repr(self.val_stack) + + def evaluate(self, variables: Mapping[str, ValueType]): + return self._evaluator(variables) + + def do_one(self): + op = self.op_stack.pop() + + if op.size == 1: + a = self.val_stack.pop() + self.val_stack.append(UnaryExpression(op.evaluator, a)) + elif op.size == 2: + b, a = self.val_stack.pop(), self.val_stack.pop() # inversed pop order + self.val_stack.append(BinaryExpression(op.evaluator, a, b)) + + def __repr__(self): + return self.__debug_expr diff --git a/parser/parser/tokenizer.py b/parser/parser/tokenizer.py new file mode 100644 index 0000000..60501fb --- /dev/null +++ b/parser/parser/tokenizer.py @@ -0,0 +1,106 @@ +from collections.abc import Generator +from dataclasses import dataclass +from typing import Optional + +from .operation import functions, operators +from .types import Token, TokenType + + +@dataclass +class Tokenizer: + expression: str + + def __iter__(self) -> Generator[TokenType, None, None]: + accumulator = "" + prev = None + + for ch in self.expression: + if (breaker_type := self.is_breaker(ch)) is not None: + if len(accumulator) > 0: + # ch is `(` after function name + if breaker_type == Token.LBrace and self.is_function(accumulator): + yield accumulator, Token.Function + prev = Token.Function + accumulator = "" + else: + value, token_type = self.detect_number(accumulator) + yield value, token_type + prev = token_type + accumulator = "" + + # `(` after variable or number + if breaker_type == Token.LBrace: + yield "*", Token.Operator + prev = Token.Operator + + # Unary minus case + if ch == "-" and (prev == Token.LBrace or prev is None): + yield 0, Token.Number + prev = Token.Number + + # `(expr)(expr)` case + if breaker_type == Token.LBrace and prev == Token.RBrace: + yield "*", Token.Operator + prev = Token.Operator + + if breaker_type != Token.Space: + yield ch, breaker_type + prev = breaker_type + else: + # Variable or function name after braced expr or variable and space + if prev == Token.RBrace or prev == Token.Variable: + yield "*", Token.Operator + prev = Token.Operator + + # Floating point number + if ch in ",.": + accumulator += "." + continue + + # Variable or function name after number + if ( + not ch.isdecimal() + and (num := Tokenizer.is_number(accumulator)) is not None + ): + yield num, Token.Number + yield "*", Token.Operator + prev = Token.Operator + accumulator = "" + + accumulator += ch + if len(accumulator) > 0: + yield self.detect_number(accumulator) + + @staticmethod + def is_breaker(character) -> Optional[Token]: + if character in operators: + return Token.Operator + if character in "([{": + return Token.LBrace + if character in ")]}": + return Token.RBrace + if character == " ": + return Token.Space + + return None + + @staticmethod + def is_number(string) -> Optional[float]: + try: + return float(string) + except ValueError: + return None + + @staticmethod + def detect_number(string) -> TokenType: + if (num := Tokenizer.is_number(string)) is not None: + return num, Token.Number + else: + return string, Token.Variable + + @staticmethod + def is_function(lexeme: str) -> bool: + if lexeme in functions: + return True + + return False diff --git a/parser/parser/types.py b/parser/parser/types.py new file mode 100644 index 0000000..f451f39 --- /dev/null +++ b/parser/parser/types.py @@ -0,0 +1,23 @@ +from collections.abc import Callable +from enum import Enum, auto + +import numpy as np + +ValueType = int | float | np.ndarray + +FunctionType = Callable[[ValueType], ValueType] + +OperatorType = Callable[[ValueType, ValueType], ValueType] + + +class Token(Enum): + Variable = auto() + Number = auto() + Function = auto() + Operator = auto() + LBrace = auto() + RBrace = auto() + Space = auto() + + +TokenType = tuple[str | float, Token] diff --git a/parser/poetry.lock b/parser/poetry.lock new file mode 100644 index 0000000..28b912f --- /dev/null +++ b/parser/poetry.lock @@ -0,0 +1,436 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + +[[package]] +name = "attrs" +version = "23.1.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] + +[[package]] +name = "black" +version = "23.9.1" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"}, + {file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"}, + {file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"}, + {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"}, + {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"}, + {file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"}, + {file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"}, + {file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"}, + {file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"}, + {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"}, + {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "cattrs" +version = "23.1.2" +description = "Composable complex class support for attrs and dataclasses." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cattrs-23.1.2-py3-none-any.whl", hash = "sha256:b2bb14311ac17bed0d58785e5a60f022e5431aca3932e3fc5cc8ed8639de50a4"}, + {file = "cattrs-23.1.2.tar.gz", hash = "sha256:db1c821b8c537382b2c7c66678c3790091ca0275ac486c76f3c8f3920e83c657"}, +] + +[package.dependencies] +attrs = ">=20" + +[package.extras] +bson = ["pymongo (>=4.2.0,<5.0.0)"] +cbor2 = ["cbor2 (>=5.4.6,<6.0.0)"] +msgpack = ["msgpack (>=1.0.2,<2.0.0)"] +orjson = ["orjson (>=3.5.2,<4.0.0)"] +pyyaml = ["PyYAML (>=6.0,<7.0)"] +tomlkit = ["tomlkit (>=0.11.4,<0.12.0)"] +ujson = ["ujson (>=5.4.0,<6.0.0)"] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "docstring-to-markdown" +version = "0.12" +description = "On the fly conversion of Python docstrings to markdown" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "docstring-to-markdown-0.12.tar.gz", hash = "sha256:40004224b412bd6f64c0f3b85bb357a41341afd66c4b4896709efa56827fb2bb"}, + {file = "docstring_to_markdown-0.12-py3-none-any.whl", hash = "sha256:7df6311a887dccf9e770f51242ec002b19f0591994c4783be49d24cdc1df3737"}, +] + +[[package]] +name = "isort" +version = "5.12.0" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, + {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, +] + +[package.extras] +colors = ["colorama (>=0.4.3)"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] + +[[package]] +name = "jedi" +version = "0.19.1" +description = "An autocompletion tool for Python that can be used for text editors." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, +] + +[package.dependencies] +parso = ">=0.8.3,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + +[[package]] +name = "jedi-language-server" +version = "0.41.1" +description = "A language server for Jedi!" +category = "dev" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "jedi_language_server-0.41.1-py3-none-any.whl", hash = "sha256:ca9b3e7f48b70f0988d85ffde4f01dd1ab94c8e0f69e8c6424e6657117b44f91"}, + {file = "jedi_language_server-0.41.1.tar.gz", hash = "sha256:3f15ca5cc28e728564f7d63583e171b418025582447ce023512e3f2b2d71ebae"}, +] + +[package.dependencies] +cattrs = ">=23.1.2" +docstring-to-markdown = "<1.0.0" +jedi = ">=0.19.0,<0.20.0" +lsprotocol = ">=2022.0.0a9" +pygls = ">=1.0.1,<2.0.0" + +[[package]] +name = "lsprotocol" +version = "2023.0.0b1" +description = "Python implementation of the Language Server Protocol." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "lsprotocol-2023.0.0b1-py3-none-any.whl", hash = "sha256:ade2cd0fa0ede7965698cb59cd05d3adbd19178fd73e83f72ef57a032fbb9d62"}, + {file = "lsprotocol-2023.0.0b1.tar.gz", hash = "sha256:f7a2d4655cbd5639f373ddd1789807450c543341fa0a32b064ad30dbb9f510d4"}, +] + +[package.dependencies] +attrs = ">=21.3.0" +cattrs = "*" + +[[package]] +name = "mypy" +version = "1.5.1" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70"}, + {file = "mypy-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0"}, + {file = "mypy-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12"}, + {file = "mypy-1.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d"}, + {file = "mypy-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25"}, + {file = "mypy-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4"}, + {file = "mypy-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4"}, + {file = "mypy-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243"}, + {file = "mypy-1.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275"}, + {file = "mypy-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315"}, + {file = "mypy-1.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb"}, + {file = "mypy-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373"}, + {file = "mypy-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161"}, + {file = "mypy-1.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a"}, + {file = "mypy-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1"}, + {file = "mypy-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65"}, + {file = "mypy-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160"}, + {file = "mypy-1.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2"}, + {file = "mypy-1.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb"}, + {file = "mypy-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f"}, + {file = "mypy-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a"}, + {file = "mypy-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14"}, + {file = "mypy-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb"}, + {file = "mypy-1.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693"}, + {file = "mypy-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770"}, + {file = "mypy-1.5.1-py3-none-any.whl", hash = "sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5"}, + {file = "mypy-1.5.1.tar.gz", hash = "sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "numpy" +version = "1.26.0" +description = "Fundamental package for array computing in Python" +category = "main" +optional = false +python-versions = "<3.13,>=3.9" +files = [ + {file = "numpy-1.26.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8db2f125746e44dce707dd44d4f4efeea8d7e2b43aace3f8d1f235cfa2733dd"}, + {file = "numpy-1.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0621f7daf973d34d18b4e4bafb210bbaf1ef5e0100b5fa750bd9cde84c7ac292"}, + {file = "numpy-1.26.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51be5f8c349fdd1a5568e72713a21f518e7d6707bcf8503b528b88d33b57dc68"}, + {file = "numpy-1.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:767254ad364991ccfc4d81b8152912e53e103ec192d1bb4ea6b1f5a7117040be"}, + {file = "numpy-1.26.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:436c8e9a4bdeeee84e3e59614d38c3dbd3235838a877af8c211cfcac8a80b8d3"}, + {file = "numpy-1.26.0-cp310-cp310-win32.whl", hash = "sha256:c2e698cb0c6dda9372ea98a0344245ee65bdc1c9dd939cceed6bb91256837896"}, + {file = "numpy-1.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:09aaee96c2cbdea95de76ecb8a586cb687d281c881f5f17bfc0fb7f5890f6b91"}, + {file = "numpy-1.26.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:637c58b468a69869258b8ae26f4a4c6ff8abffd4a8334c830ffb63e0feefe99a"}, + {file = "numpy-1.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:306545e234503a24fe9ae95ebf84d25cba1fdc27db971aa2d9f1ab6bba19a9dd"}, + {file = "numpy-1.26.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6adc33561bd1d46f81131d5352348350fc23df4d742bb246cdfca606ea1208"}, + {file = "numpy-1.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e062aa24638bb5018b7841977c360d2f5917268d125c833a686b7cbabbec496c"}, + {file = "numpy-1.26.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:546b7dd7e22f3c6861463bebb000646fa730e55df5ee4a0224408b5694cc6148"}, + {file = "numpy-1.26.0-cp311-cp311-win32.whl", hash = "sha256:c0b45c8b65b79337dee5134d038346d30e109e9e2e9d43464a2970e5c0e93229"}, + {file = "numpy-1.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:eae430ecf5794cb7ae7fa3808740b015aa80747e5266153128ef055975a72b99"}, + {file = "numpy-1.26.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:166b36197e9debc4e384e9c652ba60c0bacc216d0fc89e78f973a9760b503388"}, + {file = "numpy-1.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f042f66d0b4ae6d48e70e28d487376204d3cbf43b84c03bac57e28dac6151581"}, + {file = "numpy-1.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5e18e5b14a7560d8acf1c596688f4dfd19b4f2945b245a71e5af4ddb7422feb"}, + {file = "numpy-1.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6bad22a791226d0a5c7c27a80a20e11cfe09ad5ef9084d4d3fc4a299cca505"}, + {file = "numpy-1.26.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4acc65dd65da28060e206c8f27a573455ed724e6179941edb19f97e58161bb69"}, + {file = "numpy-1.26.0-cp312-cp312-win32.whl", hash = "sha256:bb0d9a1aaf5f1cb7967320e80690a1d7ff69f1d47ebc5a9bea013e3a21faec95"}, + {file = "numpy-1.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:ee84ca3c58fe48b8ddafdeb1db87388dce2c3c3f701bf447b05e4cfcc3679112"}, + {file = "numpy-1.26.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4a873a8180479bc829313e8d9798d5234dfacfc2e8a7ac188418189bb8eafbd2"}, + {file = "numpy-1.26.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:914b28d3215e0c721dc75db3ad6d62f51f630cb0c277e6b3bcb39519bed10bd8"}, + {file = "numpy-1.26.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c78a22e95182fb2e7874712433eaa610478a3caf86f28c621708d35fa4fd6e7f"}, + {file = "numpy-1.26.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86f737708b366c36b76e953c46ba5827d8c27b7a8c9d0f471810728e5a2fe57c"}, + {file = "numpy-1.26.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b44e6a09afc12952a7d2a58ca0a2429ee0d49a4f89d83a0a11052da696440e49"}, + {file = "numpy-1.26.0-cp39-cp39-win32.whl", hash = "sha256:5671338034b820c8d58c81ad1dafc0ed5a00771a82fccc71d6438df00302094b"}, + {file = "numpy-1.26.0-cp39-cp39-win_amd64.whl", hash = "sha256:020cdbee66ed46b671429c7265cf00d8ac91c046901c55684954c3958525dab2"}, + {file = "numpy-1.26.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0792824ce2f7ea0c82ed2e4fecc29bb86bee0567a080dacaf2e0a01fe7654369"}, + {file = "numpy-1.26.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d484292eaeb3e84a51432a94f53578689ffdea3f90e10c8b203a99be5af57d8"}, + {file = "numpy-1.26.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:186ba67fad3c60dbe8a3abff3b67a91351100f2661c8e2a80364ae6279720299"}, + {file = "numpy-1.26.0.tar.gz", hash = "sha256:f93fc78fe8bf15afe2b8d6b6499f1c73953169fad1e9a8dd086cdff3190e7fdf"}, +] + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + +[[package]] +name = "pathspec" +version = "0.11.2" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, +] + +[[package]] +name = "platformdirs" +version = "3.11.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, + {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, +] + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] + +[[package]] +name = "pygls" +version = "1.1.1" +description = "A pythonic generic language server (pronounced like 'pie glass')" +category = "dev" +optional = false +python-versions = ">=3.7.9,<4" +files = [ + {file = "pygls-1.1.1-py3-none-any.whl", hash = "sha256:330704551a335b443bf1cdfb0507f121608591095898d451f0007eeb1510067c"}, + {file = "pygls-1.1.1.tar.gz", hash = "sha256:b1b4ddd6f800a5573f61f0ec2cd3bc7a859d171f48142b46e1de35a1357c00fe"}, +] + +[package.dependencies] +lsprotocol = "2023.0.0b1" +typeguard = ">=3.0.0,<4.0.0" + +[package.extras] +ws = ["websockets (>=11.0.3,<12.0.0)"] + +[[package]] +name = "ruff" +version = "0.0.292" +description = "An extremely fast Python linter, written in Rust." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.0.292-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:02f29db018c9d474270c704e6c6b13b18ed0ecac82761e4fcf0faa3728430c96"}, + {file = "ruff-0.0.292-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:69654e564342f507edfa09ee6897883ca76e331d4bbc3676d8a8403838e9fade"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c3c91859a9b845c33778f11902e7b26440d64b9d5110edd4e4fa1726c41e0a4"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4476f1243af2d8c29da5f235c13dca52177117935e1f9393f9d90f9833f69e4"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be8eb50eaf8648070b8e58ece8e69c9322d34afe367eec4210fdee9a555e4ca7"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9889bac18a0c07018aac75ef6c1e6511d8411724d67cb879103b01758e110a81"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6bdfabd4334684a4418b99b3118793f2c13bb67bf1540a769d7816410402a205"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7c77c53bfcd75dbcd4d1f42d6cabf2485d2e1ee0678da850f08e1ab13081a8"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e087b24d0d849c5c81516ec740bf4fd48bf363cfb104545464e0fca749b6af9"}, + {file = "ruff-0.0.292-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f160b5ec26be32362d0774964e218f3fcf0a7da299f7e220ef45ae9e3e67101a"}, + {file = "ruff-0.0.292-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ac153eee6dd4444501c4bb92bff866491d4bfb01ce26dd2fff7ca472c8df9ad0"}, + {file = "ruff-0.0.292-py3-none-musllinux_1_2_i686.whl", hash = "sha256:87616771e72820800b8faea82edd858324b29bb99a920d6aa3d3949dd3f88fb0"}, + {file = "ruff-0.0.292-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b76deb3bdbea2ef97db286cf953488745dd6424c122d275f05836c53f62d4016"}, + {file = "ruff-0.0.292-py3-none-win32.whl", hash = "sha256:e854b05408f7a8033a027e4b1c7f9889563dd2aca545d13d06711e5c39c3d003"}, + {file = "ruff-0.0.292-py3-none-win_amd64.whl", hash = "sha256:f27282bedfd04d4c3492e5c3398360c9d86a295be00eccc63914438b4ac8a83c"}, + {file = "ruff-0.0.292-py3-none-win_arm64.whl", hash = "sha256:7f67a69c8f12fbc8daf6ae6d36705037bde315abf8b82b6e1f4c9e74eb750f68"}, + {file = "ruff-0.0.292.tar.gz", hash = "sha256:1093449e37dd1e9b813798f6ad70932b57cf614e5c2b5c51005bf67d55db33ac"}, +] + +[[package]] +name = "typeguard" +version = "3.0.2" +description = "Run-time type checker for Python" +category = "dev" +optional = false +python-versions = ">=3.7.4" +files = [ + {file = "typeguard-3.0.2-py3-none-any.whl", hash = "sha256:bbe993854385284ab42fd5bd3bee6f6556577ce8b50696d6cb956d704f286c8e"}, + {file = "typeguard-3.0.2.tar.gz", hash = "sha256:fee5297fdb28f8e9efcb8142b5ee219e02375509cd77ea9d270b5af826358d5a"}, +] + +[package.extras] +doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["mypy (>=0.991)", "pytest (>=7)"] + +[[package]] +name = "typing-extensions" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, +] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.11,<3.13" +content-hash = "3180680d07071b1bed28aa242a0feb41baa73446b6edfefab15e2252da5dfdbf" diff --git a/parser/pyproject.toml b/parser/pyproject.toml new file mode 100644 index 0000000..36b5ad1 --- /dev/null +++ b/parser/pyproject.toml @@ -0,0 +1,25 @@ +[tool.poetry] +name = "parser" +version = "0.1.0" +description = "" +authors = ["dm1sh "] +readme = "README.md" + +[tool.poetry.dependencies] +python = ">=3.11,<3.13" +numpy = "^1.26.0" + + +[tool.poetry.group.dev.dependencies] +black = "^23.9.1" +mypy = "^1.5.1" +ruff = "^0.0.292" +jedi-language-server = "^0.41.1" +isort = "^5.12.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.isort] +profile = "black" \ No newline at end of file diff --git a/parser/tests/__init__.py b/parser/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/parser/tests/test_parser.py b/parser/tests/test_parser.py new file mode 100644 index 0000000..2bca931 --- /dev/null +++ b/parser/tests/test_parser.py @@ -0,0 +1,14 @@ +from parser import Parser + + +def test_Parser(): + parser = Parser("(-b + sqrt(b^2-4a c))/(2a)") + + assert parser.variables_names == {"c", "a", "b"} + + assert parser.evaluate({"a": 1, "b": -3, "c": 2}) == 1.0 + + assert all( + parser.evaluate({"a": [1, 1, 1], "b": [-5, -6, -9], "c": [6, 9, 20]}) + == [2.0, 3.0, 4.0] + )