clex-py 0.1.0__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.
clex_py-0.1.0/LICENSE ADDED
File without changes
clex_py-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: clex-py
3
+ Version: 0.1.0
4
+ Summary: An alternative to functional programming
5
+ Author-email: cfarr2019@gmail.com
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Dynamic: license-file
File without changes
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "clex-py"
7
+ version = "0.1.0"
8
+ description = "An alternative to functional programming"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ authors = [
12
+ { name = "", email = "cfarr2019@gmail.com" }
13
+ ]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+
20
+ [tool.setuptools.packages.find]
21
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,42 @@
1
+ class Engine:
2
+ def __init__(self, AST):
3
+ self.AST = AST
4
+
5
+ def __call__(self, *inputs):
6
+ return self.eval(inputs)
7
+
8
+ def eval(self, **inputs):
9
+ actions = {
10
+ 'ADD': lambda x, y: [a + b for a, b in zip(x, y)],
11
+ 'SUB': lambda x, y: [a - b for a, b in zip(x, y)],
12
+ 'MUL': lambda x, y: [a * b for a, b in zip(x, y)],
13
+ 'DIV': lambda x, y: [a / b for a, b in zip(x, y)],
14
+ 'MOD': lambda x, y: [a % b for a, b in zip(x, y)],
15
+
16
+ 'EQ': lambda x, y: [a == b for a, b in zip(x, y)],
17
+ 'GT': lambda x, y: [a > b for a, b in zip(x, y)],
18
+ 'LT': lambda x, y: [a < b for a, b in zip(x, y)],
19
+ 'GE': lambda x, y: [a >= b for a, b in zip(x, y)],
20
+ 'LE': lambda x, y: [a <= b for a, b in zip(x, y)],
21
+ 'NE': lambda x, y: [a != b for a, b in zip(x, y)],
22
+
23
+ 'IDX': lambda x, y: list(x[y]),
24
+ 'PARSE': lambda x, y: [element for element, condition in zip(x, y) if condition],
25
+ 'NPARSE': lambda x, y: [element for element, condition in zip(x, y) if not condition],
26
+ 'IN': lambda x, y: list(set(x) & set(y)),
27
+ 'NIN': lambda x, y: list(set(x) - set(y))
28
+ }
29
+
30
+ _inputs = {**inputs}
31
+ _val_buffer = list()
32
+
33
+ for func in self.AST:
34
+ op, v1, v2, ret = func
35
+ input = _inputs[v1[1]]
36
+ is_v2_input = (v2[0] == 'INPUT')
37
+ v2_data = _inputs[v2[1]] if is_v2_input else [v2[1]] * len(input)
38
+
39
+ _val_buffer = actions[op](input, v2_data)
40
+ _inputs[ret] = _val_buffer
41
+
42
+ return _val_buffer
@@ -0,0 +1,139 @@
1
+ # (x + y; r)
2
+ # () define the function
3
+ # letters define inputs or values to be processed
4
+ # a function is composed of 1+inputs, an operation a value, and a return value
5
+ # (input1 op constant or input)
6
+ # @ could retrieve the element at index v2 e.g: (x @ 2:)
7
+
8
+ # {} could define the use of outside functions exposed to the engine
9
+
10
+ from string import digits, ascii_letters, punctuation
11
+ from engine import Engine
12
+
13
+ class ParserError(Exception):
14
+ pass
15
+
16
+
17
+ def expression(functions):
18
+ parser = {
19
+ "split_values": [c for c in ascii_letters + digits],
20
+ "funcs": _get_functions(functions),
21
+ "cursor": 0,
22
+ "AST": [],
23
+ }
24
+
25
+ while _should_parse(parser):
26
+ _match(parser, '(')
27
+ parser["AST"].append(_parse_expression(parser))
28
+ _match(parser, ')')
29
+
30
+ return Engine(parser["AST"]).eval
31
+
32
+
33
+ def _parse_value_or_input(parser):
34
+ if _current(parser) in ascii_letters:
35
+ var_name = _match(parser, parser["split_values"])
36
+ return ('INPUT', var_name)
37
+
38
+ val = _match(parser, digits)
39
+ return ('VALUE', int(val))
40
+
41
+
42
+ def _parse_return(parser):
43
+ match _current(parser):
44
+ case ':':
45
+ _next(parser)
46
+ return '_'
47
+ case ';':
48
+ _next(parser)
49
+ return _match(parser, ascii_letters)
50
+ case _:
51
+ raise ParserError(f"Expected ';' or ':' instead got {_current(parser)}")
52
+
53
+
54
+ def _parse_expression(parser):
55
+ OP_MAP = {
56
+ '+': 'ADD',
57
+ '-': 'SUB',
58
+ '*': 'MUL',
59
+ '/': 'DIV',
60
+ '%': 'MOD',
61
+ '>': 'GT',
62
+ '<': 'LT',
63
+ '>=': 'GE',
64
+ '<=': 'LE',
65
+ '=': 'EQ',
66
+ '!=': 'NE',
67
+ '@': 'IDX',
68
+ '&': 'PARSE',
69
+ '!&': 'NPARSE',
70
+ '^': 'IN',
71
+ '!^': 'NIN',
72
+ }
73
+ sorted_ops = sorted(OP_MAP.keys(), key=len, reverse=True)
74
+
75
+ v1 = _parse_value_or_input(parser)
76
+ op = OP_MAP[_match(parser, sorted_ops)]
77
+ v2 = _parse_value_or_input(parser)
78
+ ret = _parse_return(parser)
79
+
80
+ return (op, v1, v2, ret)
81
+
82
+
83
+ def _should_parse(parser):
84
+ return parser["cursor"] < len(parser["funcs"])
85
+
86
+
87
+ def _next(parser):
88
+ parser["cursor"] += 1
89
+ return _current(parser)
90
+
91
+
92
+ def _current(parser) -> str:
93
+ if parser["cursor"] >= len(parser["funcs"]):
94
+ return ""
95
+ return parser["funcs"][parser["cursor"]]
96
+
97
+
98
+ def _match(parser, *expected):
99
+ expected_set = {i for e in expected for i in (e if isinstance(e, (list, tuple, str)) else [e])}
100
+
101
+ current_token = _current(parser)
102
+ if current_token in expected_set:
103
+ _next(parser)
104
+ return current_token
105
+
106
+ raise ParserError(f"Expected {expected} instead got {current_token}")
107
+
108
+
109
+ def _get_functions(expr_input: str):
110
+ funcs = expr_input.split('->')
111
+ tokens = []
112
+
113
+ valid_operators = {"+", "-", "*", "/", "%", ">", "<", '^', '!^', ">=", "<=", "=", "!=", "@", "&", "!&"}
114
+ valid_prefixes = {op[:i] for op in valid_operators for i in range(1, len(op) + 1)}
115
+
116
+ for func in funcs:
117
+ buffer = ""
118
+ for c in func:
119
+ if not c.strip():
120
+ if buffer:
121
+ tokens.append(buffer)
122
+ buffer = ""
123
+ continue
124
+
125
+ if c in punctuation:
126
+ if buffer and (buffer + c) not in valid_prefixes:
127
+ tokens.append(buffer)
128
+ buffer = ""
129
+ buffer += c
130
+ else:
131
+ if buffer:
132
+ tokens.append(buffer)
133
+ buffer = ""
134
+ tokens.append(c)
135
+
136
+ if buffer:
137
+ tokens.append(buffer)
138
+
139
+ return tokens
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: clex-py
3
+ Version: 0.1.0
4
+ Summary: An alternative to functional programming
5
+ Author-email: cfarr2019@gmail.com
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Dynamic: license-file
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/CLEX/__init__.py
5
+ src/CLEX/engine.py
6
+ src/CLEX/parser.py
7
+ src/clex_py.egg-info/PKG-INFO
8
+ src/clex_py.egg-info/SOURCES.txt
9
+ src/clex_py.egg-info/dependency_links.txt
10
+ src/clex_py.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ CLEX