expr-codegen 0.8.6__tar.gz → 0.8.7__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.
Files changed (28) hide show
  1. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/PKG-INFO +4 -4
  2. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/README.md +3 -3
  3. expr_codegen-0.8.7/expr_codegen/_version.py +1 -0
  4. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/codes.py +115 -70
  5. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen.egg-info/PKG-INFO +4 -4
  6. expr_codegen-0.8.6/expr_codegen/_version.py +0 -1
  7. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/LICENSE +0 -0
  8. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/__init__.py +0 -0
  9. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/dag.py +0 -0
  10. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/expr.py +0 -0
  11. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/latex/__init__.py +0 -0
  12. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/latex/printer.py +0 -0
  13. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/model.py +0 -0
  14. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/pandas/__init__.py +0 -0
  15. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/pandas/code.py +0 -0
  16. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/pandas/printer.py +0 -0
  17. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/pandas/template.py.j2 +0 -0
  18. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/polars/__init__.py +0 -0
  19. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/polars/code.py +0 -0
  20. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/polars/printer.py +0 -0
  21. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/polars/template.py.j2 +0 -0
  22. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen/tool.py +0 -0
  23. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen.egg-info/SOURCES.txt +0 -0
  24. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen.egg-info/dependency_links.txt +0 -0
  25. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen.egg-info/requires.txt +0 -0
  26. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/expr_codegen.egg-info/top_level.txt +0 -0
  27. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/pyproject.toml +0 -0
  28. {expr_codegen-0.8.6 → expr_codegen-0.8.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: expr_codegen
3
- Version: 0.8.6
3
+ Version: 0.8.7
4
4
  Summary: symbol expression to polars expression tool
5
5
  Author-email: wukan <wu-kan@163.com>
6
6
  License: BSD 3-Clause License
@@ -198,11 +198,11 @@ df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代
198
198
 
199
199
  ## 特别语法
200
200
 
201
- 1. `(A<B)*-1`,底层将转换成`int_(A<B)*-1`
202
- 2. 支持`C?T:F`三元表达式(只能在字符串中使用),Python只支持`T if C else F`, 而在本项目中会自动转成`if_else(C,T,F)`
201
+ 1. 支持`C?T:F`三元表达式(仅可字符串中使用),底层会先转成`C or True if( T )else F`,然后修正成`T if C else F`,最后转成`if_else(C,T,F)`。支持与`if else`混用
202
+ 2. `(A<B)*-1`,底层将转换成`int_(A<B)*-1`
203
203
  3. 为防止`A==B`被`sympy`替换成`False`,底层会换成`Eq(A,B)`
204
204
  4. `A^B`的含义与`convert_xor`参数有关,`convert_xor=True`底层会转换成`Pow(A,B)`,反之为`Xor(A,B)`。默认为`False`,用`**`表示乘方
205
- 5. 支持`A&B&C`,但不支持`A==B==C`。如果C是布尔,AB是数值,可以替换成`(A==B)==C`。如果ABC是数值需手工替换成`(A==B)&(B==C)`
205
+ 5. 支持`A&B&C`,但不支持`A==B==C`。如果C是布尔,AB是数值,可手工替换成`(A==B)==C`。如果ABC是数值需手工替换成`(A==B)&(B==C)`
206
206
  6. 不支持`A<=B<=C`,需手工替换成`(A<=B)&(B<=C)`
207
207
  7. 支持`A[0]+B[1]+C[2]`,底层会转成`A+ts_delay(B,1)+ts_delay(C,2)`
208
208
  8. 支持`~A`,底层会转换成`Not(A)`
@@ -148,11 +148,11 @@ df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代
148
148
 
149
149
  ## 特别语法
150
150
 
151
- 1. `(A<B)*-1`,底层将转换成`int_(A<B)*-1`
152
- 2. 支持`C?T:F`三元表达式(只能在字符串中使用),Python只支持`T if C else F`, 而在本项目中会自动转成`if_else(C,T,F)`
151
+ 1. 支持`C?T:F`三元表达式(仅可字符串中使用),底层会先转成`C or True if( T )else F`,然后修正成`T if C else F`,最后转成`if_else(C,T,F)`。支持与`if else`混用
152
+ 2. `(A<B)*-1`,底层将转换成`int_(A<B)*-1`
153
153
  3. 为防止`A==B`被`sympy`替换成`False`,底层会换成`Eq(A,B)`
154
154
  4. `A^B`的含义与`convert_xor`参数有关,`convert_xor=True`底层会转换成`Pow(A,B)`,反之为`Xor(A,B)`。默认为`False`,用`**`表示乘方
155
- 5. 支持`A&B&C`,但不支持`A==B==C`。如果C是布尔,AB是数值,可以替换成`(A==B)==C`。如果ABC是数值需手工替换成`(A==B)&(B==C)`
155
+ 5. 支持`A&B&C`,但不支持`A==B==C`。如果C是布尔,AB是数值,可手工替换成`(A==B)==C`。如果ABC是数值需手工替换成`(A==B)&(B==C)`
156
156
  6. 不支持`A<=B<=C`,需手工替换成`(A<=B)&(B<=C)`
157
157
  7. 支持`A[0]+B[1]+C[2]`,底层会转成`A+ts_delay(B,1)+ts_delay(C,2)`
158
158
  8. 支持`~A`,底层会转换成`Not(A)`
@@ -0,0 +1 @@
1
+ __version__ = "0.8.7"
@@ -7,8 +7,100 @@ from sympy import Add, Mul, Pow, Eq
7
7
  from expr_codegen.expr import register_symbols, dict_to_exprs
8
8
 
9
9
 
10
- class SympyTransformer(ast.NodeTransformer):
11
- """将ast转换成Sympy要求的格式"""
10
+ class SyntaxTransformer(ast.NodeTransformer):
11
+ """修改语法。注意:一定要修改语法后才能改名"""
12
+
13
+ # ^ 是异或还是乘方呢?
14
+ convert_xor: bool = True
15
+
16
+ def visit_Compare(self, node):
17
+ assert len(node.comparators) == 1, f"不支持连续等号,请手工添加括号, {ast.unparse(node)}"
18
+
19
+ self.generic_visit(node)
20
+ return node
21
+
22
+ def visit_IfExp(self, node):
23
+ # 三元表达式。需要在外部提前替换成or True if else
24
+ # 只要body区域,出现了or True,就认为是特殊处理过的
25
+ if isinstance(node.body, ast.BoolOp) and isinstance(node.body.op, ast.Or):
26
+ if isinstance(node.body.values[-1], ast.Constant):
27
+ if node.body.values[-1].value == True:
28
+ node.test, node.body = node.body.values[0], node.test
29
+
30
+ node = ast.Call(
31
+ func=ast.Name(id='if_else', ctx=ast.Load()),
32
+ args=[node.test, node.body, node.orelse],
33
+ keywords=[],
34
+ )
35
+
36
+ self.generic_visit(node)
37
+ return node
38
+
39
+ def visit_BinOp(self, node):
40
+ # TypeError: unsupported operand type(s) for *: 'StrictLessThan' and 'int'
41
+ if isinstance(node.op, (ast.Mult, ast.Add, ast.Div, ast.Sub)):
42
+ # (OPEN < CLOSE) * -1
43
+ if isinstance(node.left, ast.Compare):
44
+ node.left = ast.Call(
45
+ func=ast.Name(id='int_', ctx=ast.Load()),
46
+ args=[node.left],
47
+ keywords=[],
48
+ )
49
+ # -1*(OPEN < CLOSE)
50
+ if isinstance(node.right, ast.Compare):
51
+ node.right = ast.Call(
52
+ func=ast.Name(id='int_', ctx=ast.Load()),
53
+ args=[node.right],
54
+ keywords=[],
55
+ )
56
+ # 这种情况,已经包含
57
+ # (OPEN < CLOSE)*(OPEN < CLOSE)
58
+
59
+ if isinstance(node.op, ast.BitXor):
60
+ # ^ 运算符,转换为pow还是xor
61
+ if self.convert_xor:
62
+ node = ast.Call(
63
+ func=ast.Name(id='Pow', ctx=ast.Load()),
64
+ args=[node.left, node.right],
65
+ keywords=[],
66
+ )
67
+ else:
68
+ node = ast.Call(
69
+ func=ast.Name(id='Xor', ctx=ast.Load()),
70
+ args=[node.left, node.right],
71
+ keywords=[],
72
+ )
73
+
74
+ self.generic_visit(node)
75
+ return node
76
+
77
+ def visit_UnaryOp(self, node):
78
+ # ~ts_delay 报错,替换成Not(ts_delay)
79
+ if isinstance(node.op, ast.Invert):
80
+ node = ast.Call(
81
+ func=ast.Name(id='Not', ctx=ast.Load()),
82
+ args=[node.operand],
83
+ keywords=[],
84
+ )
85
+
86
+ self.generic_visit(node)
87
+ return node
88
+
89
+ def visit_Subscript(self, node):
90
+ if node.slice.value == 0:
91
+ node = node.value
92
+ else:
93
+ node = ast.Call(
94
+ func=ast.Name(id='ts_delay', ctx=ast.Load()),
95
+ args=[node.value, node.slice],
96
+ keywords=[],
97
+ )
98
+ self.generic_visit(node)
99
+ return node
100
+
101
+
102
+ class RenameTransformer(ast.NodeTransformer):
103
+ """改名处理。改名前需要语法规范"""
12
104
 
13
105
  # 旧记录
14
106
  funcs_old = set()
@@ -25,8 +117,6 @@ class SympyTransformer(ast.NodeTransformer):
25
117
  # !!!一定要在drop_symbols时排除
26
118
  args_map = {'True': "_TRUE_", 'False': "_FALSE_", 'None': "_NONE_"}
27
119
  targets_map = {} # 只对非下划线开头的生效
28
- # ^ 是异或还是乘方呢?
29
- convert_xor: bool = True
30
120
 
31
121
  def config_map(self, funcs_map, args_map, targets_map):
32
122
  self.funcs_map = funcs_map
@@ -115,6 +205,7 @@ class SympyTransformer(ast.NodeTransformer):
115
205
  self.args_old.add(node.left.id)
116
206
  node.left.id = self.args_map.get(node.left.id, node.left.id)
117
207
  self.args_new.add(node.left.id)
208
+
118
209
  for i, com in enumerate(node.comparators):
119
210
  if isinstance(com, ast.Name):
120
211
  self.args_old.add(com.id)
@@ -128,44 +219,23 @@ class SympyTransformer(ast.NodeTransformer):
128
219
  node.comparators[i] = ast.Name(new_com_value, ctx=ast.Load())
129
220
  self.args_new.add(new_com_value)
130
221
 
131
- assert len(node.comparators) == 1, f"不支持连续等号,请手工添加括号, {ast.unparse(node)}"
132
-
133
222
  self.generic_visit(node)
134
223
  return node
135
224
 
136
225
  def visit_IfExp(self, node):
137
- # 三元表达式。需要在外部提前替换成if else
138
- # OPEN>=CLOSE?1:0
139
- # OPEN>CLOSE?A==B?3:DE>FG?5:6:0
140
- node = ast.Call(
141
- func=ast.Name(id='if_else', ctx=ast.Load()),
142
- args=[node.body, node.test, node.orelse],
143
- keywords=[],
144
- )
226
+ if isinstance(node.body, ast.Name):
227
+ self.args_old.add(node.body.id)
228
+ node.body.id = self.args_map.get(node.body.id, node.body.id)
229
+ self.args_new.add(node.body.id)
230
+ if isinstance(node.orelse, ast.Name):
231
+ self.args_old.add(node.orelse.id)
232
+ node.orelse.id = self.args_map.get(node.orelse.id, node.orelse.id)
233
+ self.args_new.add(node.orelse.id)
145
234
 
146
235
  self.generic_visit(node)
147
236
  return node
148
237
 
149
238
  def visit_BinOp(self, node):
150
- # TypeError: unsupported operand type(s) for *: 'StrictLessThan' and 'int'
151
- if isinstance(node.op, (ast.Mult, ast.Add, ast.Div, ast.Sub)):
152
- # (OPEN < CLOSE) * -1
153
- if isinstance(node.left, ast.Compare):
154
- node.left = ast.Call(
155
- func=ast.Name(id='int_', ctx=ast.Load()),
156
- args=[node.left],
157
- keywords=[],
158
- )
159
- # -1*(OPEN < CLOSE)
160
- if isinstance(node.right, ast.Compare):
161
- node.right = ast.Call(
162
- func=ast.Name(id='int_', ctx=ast.Load()),
163
- args=[node.right],
164
- keywords=[],
165
- )
166
- # 这种情况,已经包含
167
- # (OPEN < CLOSE)*(OPEN < CLOSE)
168
-
169
239
  if isinstance(node.left, ast.Name):
170
240
  self.args_old.add(node.left.id)
171
241
  node.left.id = self.args_map.get(node.left.id, node.left.id)
@@ -189,21 +259,6 @@ class SympyTransformer(ast.NodeTransformer):
189
259
  node.right = ast.Name(new_node_value, ctx=ast.Load())
190
260
  self.args_new.add(new_node_value)
191
261
 
192
- if isinstance(node.op, ast.BitXor):
193
- # ^ 运算符,转换为pow还是xor
194
- if self.convert_xor:
195
- node = ast.Call(
196
- func=ast.Name(id='Pow', ctx=ast.Load()),
197
- args=[node.left, node.right],
198
- keywords=[],
199
- )
200
- else:
201
- node = ast.Call(
202
- func=ast.Name(id='Xor', ctx=ast.Load()),
203
- args=[node.left, node.right],
204
- keywords=[],
205
- )
206
-
207
262
  self.generic_visit(node)
208
263
  return node
209
264
 
@@ -221,26 +276,14 @@ class SympyTransformer(ast.NodeTransformer):
221
276
  node.operand = ast.Name(new_operand_value, ctx=ast.Load())
222
277
  self.args_new.add(new_operand_value)
223
278
 
224
- # ~ts_delay 报错,替换成Not(ts_delay)
225
- if isinstance(node.op, ast.Invert):
226
- node = ast.Call(
227
- func=ast.Name(id='Not', ctx=ast.Load()),
228
- args=[node.operand],
229
- keywords=[],
230
- )
231
-
232
279
  self.generic_visit(node)
233
280
  return node
234
281
 
235
282
  def visit_Subscript(self, node):
236
- if node.slice.value == 0:
237
- node = node.value
238
- else:
239
- node = ast.Call(
240
- func=ast.Name(id='ts_delay', ctx=ast.Load()),
241
- args=[node.value, node.slice],
242
- keywords=[],
243
- )
283
+ self.args_old.add(node.value.id)
284
+ node.value.id = self.args_map.get(node.value.id, node.value.id)
285
+ self.args_new.add(node.value.id)
286
+
244
287
  self.generic_visit(node)
245
288
  return node
246
289
 
@@ -260,13 +303,13 @@ def sources_to_asts(*sources, convert_xor: bool):
260
303
  return '\n'.join(raw), assigns, funcs_new, args_new, targets_new
261
304
 
262
305
 
263
- def source_replace(source):
306
+ def source_replace(source: str) -> str:
264
307
  # 三元表达式转换成 错误版if( )else,一定得在Transformer中修正
265
308
  num = 1
266
309
  while num > 0:
267
- # A == B?D == E?1: 2:0 + 0
268
- # 其实会导致?与:错配,但无所谓,只要多执行几次即可
269
- source, num = re.subn(r'\?(.+?):(.+?)', r' if( \1 )else \2', source, flags=re.S)
310
+ # 利用or 的优先级最低,构造特殊的if else,只要出现,就认为位置要替换
311
+ # C?T:F --> C or True if( T )else F
312
+ source, num = re.subn(r'\?(.+?):(.+?)', r' or True if( \1 )else \2', source, flags=re.S)
270
313
  # break
271
314
  # 或、与
272
315
  source = source.replace('||', '|').replace('&&', '&')
@@ -276,8 +319,10 @@ def source_replace(source):
276
319
  def _source_to_asts(source, convert_xor: bool):
277
320
  """源代码"""
278
321
  tree = ast.parse(source_replace(source))
279
- t = SympyTransformer()
280
- t.convert_xor = convert_xor
322
+ t1 = SyntaxTransformer()
323
+ t1.convert_xor = convert_xor
324
+ t1.visit(tree)
325
+ t = RenameTransformer()
281
326
  t.visit(tree)
282
327
 
283
328
  raw = []
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: expr_codegen
3
- Version: 0.8.6
3
+ Version: 0.8.7
4
4
  Summary: symbol expression to polars expression tool
5
5
  Author-email: wukan <wu-kan@163.com>
6
6
  License: BSD 3-Clause License
@@ -198,11 +198,11 @@ df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代
198
198
 
199
199
  ## 特别语法
200
200
 
201
- 1. `(A<B)*-1`,底层将转换成`int_(A<B)*-1`
202
- 2. 支持`C?T:F`三元表达式(只能在字符串中使用),Python只支持`T if C else F`, 而在本项目中会自动转成`if_else(C,T,F)`
201
+ 1. 支持`C?T:F`三元表达式(仅可字符串中使用),底层会先转成`C or True if( T )else F`,然后修正成`T if C else F`,最后转成`if_else(C,T,F)`。支持与`if else`混用
202
+ 2. `(A<B)*-1`,底层将转换成`int_(A<B)*-1`
203
203
  3. 为防止`A==B`被`sympy`替换成`False`,底层会换成`Eq(A,B)`
204
204
  4. `A^B`的含义与`convert_xor`参数有关,`convert_xor=True`底层会转换成`Pow(A,B)`,反之为`Xor(A,B)`。默认为`False`,用`**`表示乘方
205
- 5. 支持`A&B&C`,但不支持`A==B==C`。如果C是布尔,AB是数值,可以替换成`(A==B)==C`。如果ABC是数值需手工替换成`(A==B)&(B==C)`
205
+ 5. 支持`A&B&C`,但不支持`A==B==C`。如果C是布尔,AB是数值,可手工替换成`(A==B)==C`。如果ABC是数值需手工替换成`(A==B)&(B==C)`
206
206
  6. 不支持`A<=B<=C`,需手工替换成`(A<=B)&(B<=C)`
207
207
  7. 支持`A[0]+B[1]+C[2]`,底层会转成`A+ts_delay(B,1)+ts_delay(C,2)`
208
208
  8. 支持`~A`,底层会转换成`Not(A)`
@@ -1 +0,0 @@
1
- __version__ = "0.8.6"
File without changes
File without changes