dsgrid-toolkit 0.3.3__cp313-cp313-win_amd64.whl

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 (157) hide show
  1. build_backend.py +93 -0
  2. dsgrid/__init__.py +22 -0
  3. dsgrid/api/__init__.py +0 -0
  4. dsgrid/api/api_manager.py +179 -0
  5. dsgrid/api/app.py +419 -0
  6. dsgrid/api/models.py +60 -0
  7. dsgrid/api/response_models.py +116 -0
  8. dsgrid/apps/__init__.py +0 -0
  9. dsgrid/apps/project_viewer/app.py +216 -0
  10. dsgrid/apps/registration_gui.py +444 -0
  11. dsgrid/chronify.py +32 -0
  12. dsgrid/cli/__init__.py +0 -0
  13. dsgrid/cli/common.py +120 -0
  14. dsgrid/cli/config.py +176 -0
  15. dsgrid/cli/download.py +13 -0
  16. dsgrid/cli/dsgrid.py +157 -0
  17. dsgrid/cli/dsgrid_admin.py +92 -0
  18. dsgrid/cli/install_notebooks.py +62 -0
  19. dsgrid/cli/query.py +729 -0
  20. dsgrid/cli/registry.py +1862 -0
  21. dsgrid/cloud/__init__.py +0 -0
  22. dsgrid/cloud/cloud_storage_interface.py +140 -0
  23. dsgrid/cloud/factory.py +31 -0
  24. dsgrid/cloud/fake_storage_interface.py +37 -0
  25. dsgrid/cloud/s3_storage_interface.py +156 -0
  26. dsgrid/common.py +36 -0
  27. dsgrid/config/__init__.py +0 -0
  28. dsgrid/config/annual_time_dimension_config.py +194 -0
  29. dsgrid/config/common.py +142 -0
  30. dsgrid/config/config_base.py +148 -0
  31. dsgrid/config/dataset_config.py +907 -0
  32. dsgrid/config/dataset_schema_handler_factory.py +46 -0
  33. dsgrid/config/date_time_dimension_config.py +136 -0
  34. dsgrid/config/dimension_config.py +54 -0
  35. dsgrid/config/dimension_config_factory.py +65 -0
  36. dsgrid/config/dimension_mapping_base.py +350 -0
  37. dsgrid/config/dimension_mappings_config.py +48 -0
  38. dsgrid/config/dimensions.py +1025 -0
  39. dsgrid/config/dimensions_config.py +71 -0
  40. dsgrid/config/file_schema.py +190 -0
  41. dsgrid/config/index_time_dimension_config.py +80 -0
  42. dsgrid/config/input_dataset_requirements.py +31 -0
  43. dsgrid/config/mapping_tables.py +209 -0
  44. dsgrid/config/noop_time_dimension_config.py +42 -0
  45. dsgrid/config/project_config.py +1462 -0
  46. dsgrid/config/registration_models.py +188 -0
  47. dsgrid/config/representative_period_time_dimension_config.py +194 -0
  48. dsgrid/config/simple_models.py +49 -0
  49. dsgrid/config/supplemental_dimension.py +29 -0
  50. dsgrid/config/time_dimension_base_config.py +192 -0
  51. dsgrid/data_models.py +155 -0
  52. dsgrid/dataset/__init__.py +0 -0
  53. dsgrid/dataset/dataset.py +123 -0
  54. dsgrid/dataset/dataset_expression_handler.py +86 -0
  55. dsgrid/dataset/dataset_mapping_manager.py +121 -0
  56. dsgrid/dataset/dataset_schema_handler_base.py +945 -0
  57. dsgrid/dataset/dataset_schema_handler_one_table.py +209 -0
  58. dsgrid/dataset/dataset_schema_handler_two_table.py +322 -0
  59. dsgrid/dataset/growth_rates.py +162 -0
  60. dsgrid/dataset/models.py +51 -0
  61. dsgrid/dataset/table_format_handler_base.py +257 -0
  62. dsgrid/dataset/table_format_handler_factory.py +17 -0
  63. dsgrid/dataset/unpivoted_table.py +121 -0
  64. dsgrid/dimension/__init__.py +0 -0
  65. dsgrid/dimension/base_models.py +230 -0
  66. dsgrid/dimension/dimension_filters.py +308 -0
  67. dsgrid/dimension/standard.py +252 -0
  68. dsgrid/dimension/time.py +352 -0
  69. dsgrid/dimension/time_utils.py +103 -0
  70. dsgrid/dsgrid_rc.py +88 -0
  71. dsgrid/exceptions.py +105 -0
  72. dsgrid/filesystem/__init__.py +0 -0
  73. dsgrid/filesystem/cloud_filesystem.py +32 -0
  74. dsgrid/filesystem/factory.py +32 -0
  75. dsgrid/filesystem/filesystem_interface.py +136 -0
  76. dsgrid/filesystem/local_filesystem.py +74 -0
  77. dsgrid/filesystem/s3_filesystem.py +118 -0
  78. dsgrid/loggers.py +132 -0
  79. dsgrid/minimal_patterns.cp313-win_amd64.pyd +0 -0
  80. dsgrid/notebooks/connect_to_dsgrid_registry.ipynb +949 -0
  81. dsgrid/notebooks/registration.ipynb +48 -0
  82. dsgrid/notebooks/start_notebook.sh +11 -0
  83. dsgrid/project.py +451 -0
  84. dsgrid/query/__init__.py +0 -0
  85. dsgrid/query/dataset_mapping_plan.py +142 -0
  86. dsgrid/query/derived_dataset.py +388 -0
  87. dsgrid/query/models.py +728 -0
  88. dsgrid/query/query_context.py +287 -0
  89. dsgrid/query/query_submitter.py +994 -0
  90. dsgrid/query/report_factory.py +19 -0
  91. dsgrid/query/report_peak_load.py +70 -0
  92. dsgrid/query/reports_base.py +20 -0
  93. dsgrid/registry/__init__.py +0 -0
  94. dsgrid/registry/bulk_register.py +165 -0
  95. dsgrid/registry/common.py +287 -0
  96. dsgrid/registry/config_update_checker_base.py +63 -0
  97. dsgrid/registry/data_store_factory.py +34 -0
  98. dsgrid/registry/data_store_interface.py +74 -0
  99. dsgrid/registry/dataset_config_generator.py +158 -0
  100. dsgrid/registry/dataset_registry_manager.py +950 -0
  101. dsgrid/registry/dataset_update_checker.py +16 -0
  102. dsgrid/registry/dimension_mapping_registry_manager.py +575 -0
  103. dsgrid/registry/dimension_mapping_update_checker.py +16 -0
  104. dsgrid/registry/dimension_registry_manager.py +413 -0
  105. dsgrid/registry/dimension_update_checker.py +16 -0
  106. dsgrid/registry/duckdb_data_store.py +207 -0
  107. dsgrid/registry/filesystem_data_store.py +150 -0
  108. dsgrid/registry/filter_registry_manager.py +123 -0
  109. dsgrid/registry/project_config_generator.py +57 -0
  110. dsgrid/registry/project_registry_manager.py +1623 -0
  111. dsgrid/registry/project_update_checker.py +48 -0
  112. dsgrid/registry/registration_context.py +223 -0
  113. dsgrid/registry/registry_auto_updater.py +316 -0
  114. dsgrid/registry/registry_database.py +667 -0
  115. dsgrid/registry/registry_interface.py +446 -0
  116. dsgrid/registry/registry_manager.py +558 -0
  117. dsgrid/registry/registry_manager_base.py +367 -0
  118. dsgrid/registry/versioning.py +92 -0
  119. dsgrid/rust_ext/__init__.py +14 -0
  120. dsgrid/rust_ext/find_minimal_patterns.py +129 -0
  121. dsgrid/spark/__init__.py +0 -0
  122. dsgrid/spark/functions.py +589 -0
  123. dsgrid/spark/types.py +110 -0
  124. dsgrid/tests/__init__.py +0 -0
  125. dsgrid/tests/common.py +140 -0
  126. dsgrid/tests/make_us_data_registry.py +265 -0
  127. dsgrid/tests/register_derived_datasets.py +103 -0
  128. dsgrid/tests/utils.py +25 -0
  129. dsgrid/time/__init__.py +0 -0
  130. dsgrid/time/time_conversions.py +80 -0
  131. dsgrid/time/types.py +67 -0
  132. dsgrid/units/__init__.py +0 -0
  133. dsgrid/units/constants.py +113 -0
  134. dsgrid/units/convert.py +71 -0
  135. dsgrid/units/energy.py +145 -0
  136. dsgrid/units/power.py +87 -0
  137. dsgrid/utils/__init__.py +0 -0
  138. dsgrid/utils/dataset.py +830 -0
  139. dsgrid/utils/files.py +179 -0
  140. dsgrid/utils/filters.py +125 -0
  141. dsgrid/utils/id_remappings.py +100 -0
  142. dsgrid/utils/py_expression_eval/LICENSE +19 -0
  143. dsgrid/utils/py_expression_eval/README.md +8 -0
  144. dsgrid/utils/py_expression_eval/__init__.py +847 -0
  145. dsgrid/utils/py_expression_eval/tests.py +283 -0
  146. dsgrid/utils/run_command.py +70 -0
  147. dsgrid/utils/scratch_dir_context.py +65 -0
  148. dsgrid/utils/spark.py +918 -0
  149. dsgrid/utils/spark_partition.py +98 -0
  150. dsgrid/utils/timing.py +239 -0
  151. dsgrid/utils/utilities.py +221 -0
  152. dsgrid/utils/versioning.py +36 -0
  153. dsgrid_toolkit-0.3.3.dist-info/METADATA +193 -0
  154. dsgrid_toolkit-0.3.3.dist-info/RECORD +157 -0
  155. dsgrid_toolkit-0.3.3.dist-info/WHEEL +4 -0
  156. dsgrid_toolkit-0.3.3.dist-info/entry_points.txt +4 -0
  157. dsgrid_toolkit-0.3.3.dist-info/licenses/LICENSE +29 -0
@@ -0,0 +1,847 @@
1
+ #! /usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Author: AxiaCore S.A.S. http://axiacore.com
4
+ #
5
+ # Based on js-expression-eval, by Matthew Crumley (email@matthewcrumley.com, http://silentmatt.com/)
6
+ # https://github.com/silentmatt/js-expression-eval
7
+ #
8
+ # Ported to Python and modified by Vera Mazhuga (ctrl-alt-delete@live.com, http://vero4ka.info/)
9
+ #
10
+ # You are free to use and modify this code in anyway you find useful. Please leave this comment in the code
11
+ # to acknowledge its original source. If you feel like it, I enjoy hearing about projects that use my code,
12
+ # but don't feel like you have to let me know or ask permission.
13
+ from __future__ import division
14
+
15
+ import math
16
+ import random
17
+ import re
18
+
19
+ TNUMBER = 0
20
+ TOP1 = 1
21
+ TOP2 = 2
22
+ TVAR = 3
23
+ TFUNCALL = 4
24
+
25
+
26
+ class Token():
27
+
28
+ def __init__(self, type_, index_, prio_, number_):
29
+ self.type_ = type_
30
+ self.index_ = index_ or 0
31
+ self.prio_ = prio_ or 0
32
+ self.number_ = number_ if number_ != None else 0
33
+
34
+ def toString(self):
35
+ if self.type_ == TNUMBER:
36
+ return self.number_
37
+ if self.type_ == TOP1 or self.type_ == TOP2 or self.type_ == TVAR:
38
+ return self.index_
39
+ elif self.type_ == TFUNCALL:
40
+ return 'CALL'
41
+ else:
42
+ return 'Invalid Token'
43
+
44
+
45
+ class Expression():
46
+
47
+ def __init__(self, tokens, ops1, ops2, functions):
48
+ self.tokens = tokens
49
+ self.ops1 = ops1
50
+ self.ops2 = ops2
51
+ self.functions = functions
52
+
53
+ def simplify(self, values):
54
+ values = values or {}
55
+ nstack = []
56
+ newexpression = []
57
+ L = len(self.tokens)
58
+ for i in range(0, L):
59
+ item = self.tokens[i]
60
+ type_ = item.type_
61
+ if type_ == TNUMBER:
62
+ nstack.append(item)
63
+ elif type_ == TVAR and item.index_ in values:
64
+ item = Token(TNUMBER, 0, 0, values[item.index_])
65
+ nstack.append(item)
66
+ elif type_ == TOP2 and len(nstack) > 1:
67
+ n2 = nstack.pop()
68
+ n1 = nstack.pop()
69
+ f = self.ops2[item.index_]
70
+ item = Token(TNUMBER, 0, 0, f(n1.number_, n2.number_))
71
+ nstack.append(item)
72
+ elif type_ == TOP1 and nstack:
73
+ n1 = nstack.pop()
74
+ f = self.ops1[item.index_]
75
+ item = Token(TNUMBER, 0, 0, f(n1.number_))
76
+ nstack.append(item)
77
+ else:
78
+ while len(nstack) > 0:
79
+ newexpression.append(nstack.pop(0))
80
+ newexpression.append(item)
81
+ while nstack:
82
+ newexpression.append(nstack.pop(0))
83
+
84
+ return Expression(newexpression, self.ops1, self.ops2, self.functions)
85
+
86
+ def substitute(self, variable, expr):
87
+ if not isinstance(expr, Expression):
88
+ expr = Parser().parse(str(expr))
89
+ newexpression = []
90
+ L = len(self.tokens)
91
+ for i in range(0, L):
92
+ item = self.tokens[i]
93
+ type_ = item.type_
94
+ if type_ == TVAR and item.index_ == variable:
95
+ for j in range(0, len(expr.tokens)):
96
+ expritem = expr.tokens[j]
97
+ replitem = Token(
98
+ expritem.type_,
99
+ expritem.index_,
100
+ expritem.prio_,
101
+ expritem.number_,
102
+ )
103
+ newexpression.append(replitem)
104
+ else:
105
+ newexpression.append(item)
106
+
107
+ ret = Expression(newexpression, self.ops1, self.ops2, self.functions)
108
+ return ret
109
+
110
+ def evaluate(self, values):
111
+ values = values or {}
112
+ nstack = []
113
+ L = len(self.tokens)
114
+ for item in self.tokens:
115
+ type_ = item.type_
116
+ if type_ == TNUMBER:
117
+ nstack.append(item.number_)
118
+ elif type_ == TOP2:
119
+ n2 = nstack.pop()
120
+ n1 = nstack.pop()
121
+ f = self.ops2[item.index_]
122
+ nstack.append(f(n1, n2))
123
+ elif type_ == TVAR:
124
+ if item.index_ in values:
125
+ nstack.append(values[item.index_])
126
+ elif item.index_ in self.functions:
127
+ nstack.append(self.functions[item.index_])
128
+ else:
129
+ raise Exception('undefined variable: ' + item.index_)
130
+ elif type_ == TOP1:
131
+ n1 = nstack.pop()
132
+ f = self.ops1[item.index_]
133
+ nstack.append(f(n1))
134
+ elif type_ == TFUNCALL:
135
+ n1 = nstack.pop()
136
+ f = nstack.pop()
137
+ if callable(f):
138
+ if type(n1) is list:
139
+ nstack.append(f(*n1))
140
+ else:
141
+ nstack.append(f(n1))
142
+ else:
143
+ raise Exception(f + ' is not a function')
144
+ else:
145
+ raise Exception('invalid Expression')
146
+ if len(nstack) > 1:
147
+ raise Exception('invalid Expression (parity)')
148
+ return nstack[0]
149
+
150
+ def toString(self, toJS=False):
151
+ nstack = []
152
+ L = len(self.tokens)
153
+ for i in range(0, L):
154
+ item = self.tokens[i]
155
+ type_ = item.type_
156
+ if type_ == TNUMBER:
157
+ if type(item.number_) == str:
158
+ nstack.append("'"+item.number_+"'")
159
+ else:
160
+ nstack.append( item.number_)
161
+ elif type_ == TOP2:
162
+ n2 = nstack.pop()
163
+ n1 = nstack.pop()
164
+ f = item.index_
165
+ if toJS and f == '^':
166
+ nstack.append('math.pow(' + n1 + ',' + n2 + ')')
167
+ else:
168
+ frm='({n1}{f}{n2})'
169
+ if f == ',':
170
+ frm = '{n1}{f}{n2}'
171
+
172
+ nstack.append(frm.format(
173
+ n1=n1,
174
+ n2=n2,
175
+ f=f,
176
+ ))
177
+
178
+
179
+ elif type_ == TVAR:
180
+ nstack.append(item.index_)
181
+ elif type_ == TOP1:
182
+ n1 = nstack.pop()
183
+ f = item.index_
184
+ if f == '-':
185
+ nstack.append('(' + f + str(n1) + ')')
186
+ else:
187
+ nstack.append(f + '(' + str(n1) + ')')
188
+ elif type_ == TFUNCALL:
189
+ n1 = nstack.pop()
190
+ f = nstack.pop()
191
+ nstack.append(f + '(' + n1 + ')')
192
+ else:
193
+ raise Exception('invalid Expression')
194
+ if len(nstack) > 1:
195
+ raise Exception('invalid Expression (parity)')
196
+ return nstack[0]
197
+
198
+ def __str__(self):
199
+ return self.toString()
200
+
201
+ def symbols(self):
202
+ vars = []
203
+ for i in range(0, len(self.tokens)):
204
+ item = self.tokens[i]
205
+ if item.type_ == TVAR and not item.index_ in vars:
206
+ vars.append(item.index_)
207
+ return vars
208
+
209
+ def variables(self):
210
+ return [
211
+ sym for sym in self.symbols()
212
+ if sym not in self.functions]
213
+
214
+
215
+ class Parser:
216
+
217
+ class Expression(Expression):
218
+ pass
219
+
220
+ PRIMARY = 1
221
+ OPERATOR = 2
222
+ FUNCTION = 4
223
+ LPAREN = 8
224
+ RPAREN = 16
225
+ COMMA = 32
226
+ SIGN = 64
227
+ CALL = 128
228
+ NULLARY_CALL = 256
229
+
230
+ def add(self, a, b):
231
+ return a + b
232
+
233
+ def sub(self, a, b):
234
+ return a - b
235
+
236
+ def mul(self, a, b):
237
+ return a * b
238
+
239
+ def div(self, a, b):
240
+ return a / b
241
+
242
+ def pow(self, a, b):
243
+ return a ** b
244
+
245
+ def mod(self, a, b):
246
+ return a % b
247
+
248
+ def concat(self, a, b,*args):
249
+ result=u'{0}{1}'.format(a, b)
250
+ for arg in args:
251
+ result=u'{0}{1}'.format(result, arg)
252
+ return result
253
+
254
+ def equal (self, a, b ):
255
+ return a == b
256
+
257
+ def notEqual (self, a, b ):
258
+ return a != b
259
+
260
+ def greaterThan (self, a, b ):
261
+ return a > b
262
+
263
+ def lessThan (self, a, b ):
264
+ return a < b
265
+
266
+ def greaterThanEqual (self, a, b ):
267
+ return a >= b
268
+
269
+ def lessThanEqual (self, a, b ):
270
+ return a <= b
271
+
272
+ def andOperator (self, a, b ):
273
+ return ( a and b )
274
+
275
+ def orOperator (self, a, b ):
276
+ return ( a or b )
277
+
278
+ def xorOperator (self, a, b ):
279
+ return ( a ^ b )
280
+
281
+ def inOperator(self, a, b):
282
+ return a in b
283
+
284
+ def notOperator(self, a):
285
+ return not a
286
+
287
+ def neg(self, a):
288
+ return -a
289
+
290
+ def random(self, a):
291
+ return random.random() * (a or 1)
292
+
293
+ def fac(self, a): # a!
294
+ return math.factorial(a)
295
+
296
+ def pyt(self, a, b):
297
+ return math.sqrt(a * a + b * b)
298
+
299
+ def sind(self, a):
300
+ return math.sin(math.radians(a))
301
+
302
+ def cosd(self, a):
303
+ return math.cos(math.radians(a))
304
+
305
+ def tand(self, a):
306
+ return math.tan(math.radians(a))
307
+
308
+ def asind(self, a):
309
+ return math.degrees(math.asin(a))
310
+
311
+ def acosd(self, a):
312
+ return math.degrees(math.acos(a))
313
+
314
+ def atand(self, a):
315
+ return math.degrees(math.atan(a))
316
+
317
+ def roll(self, a, b):
318
+ rolls = []
319
+ roll = 0
320
+ final = 0
321
+ for c in range(1, a):
322
+ roll = random.randint(1, b)
323
+ rolls.append(roll)
324
+ return rolls
325
+
326
+ def ifFunction(self,a,b,c):
327
+ return b if a else c
328
+
329
+ def append(self, a, b):
330
+ if type(a) != list:
331
+ return [a, b]
332
+ a.append(b)
333
+ return a
334
+
335
+ def union(self, a, b):
336
+ return a | b
337
+
338
+ def __init__(self, string_literal_quotes = ("'", "\"")):
339
+ self.string_literal_quotes = string_literal_quotes
340
+
341
+ self.success = False
342
+ self.errormsg = ''
343
+ self.expression = ''
344
+
345
+ self.pos = 0
346
+
347
+ self.tokennumber = 0
348
+ self.tokenprio = 0
349
+ self.tokenindex = 0
350
+ self.tmpprio = 0
351
+
352
+ self.ops1 = {
353
+ 'sin': math.sin,
354
+ 'cos': math.cos,
355
+ 'tan': math.tan,
356
+ 'asin': math.asin,
357
+ 'acos': math.acos,
358
+ 'atan': math.atan,
359
+
360
+ 'sind': self.sind,
361
+ 'cosd': self.cosd,
362
+ 'tand': self.tand,
363
+ 'asind': self.asind,
364
+ 'acosd': self.acosd,
365
+ 'atand': self.atand,
366
+
367
+ 'sqrt': math.sqrt,
368
+ 'abs': abs,
369
+ 'ceil': math.ceil,
370
+ 'floor': math.floor,
371
+ 'round': round,
372
+ '-': self.neg,
373
+ 'not': self.notOperator,
374
+ 'exp': math.exp,
375
+ }
376
+
377
+ self.ops2 = {
378
+ '+': self.add,
379
+ '-': self.sub,
380
+ '*': self.mul,
381
+ '/': self.div,
382
+ '%': self.mod,
383
+ '^': self.pow,
384
+ '**': self.pow,
385
+ ',': self.append,
386
+ '||': self.concat,
387
+ "==": self.equal,
388
+ "!=": self.notEqual,
389
+ ">": self.greaterThan,
390
+ "<": self.lessThan,
391
+ ">=": self.greaterThanEqual,
392
+ "<=": self.lessThanEqual,
393
+ "and": self.andOperator,
394
+ "or": self.orOperator,
395
+ "xor": self.xorOperator,
396
+ "in": self.inOperator,
397
+ "D": self.roll,
398
+ "|": self.union,
399
+ }
400
+
401
+ self.functions = {
402
+ 'random': self.random,
403
+ 'fac': self.fac,
404
+ 'log': math.log,
405
+ 'min': min,
406
+ 'max': max,
407
+ 'pyt': self.pyt,
408
+ 'pow': math.pow,
409
+ 'atan2': math.atan2,
410
+ 'concat':self.concat,
411
+ 'if': self.ifFunction
412
+ }
413
+
414
+ self.consts = {
415
+ 'E': math.e,
416
+ 'PI': math.pi,
417
+ }
418
+
419
+ self.values = {
420
+ 'sin': math.sin,
421
+ 'cos': math.cos,
422
+ 'tan': math.tan,
423
+ 'asin': math.asin,
424
+ 'acos': math.acos,
425
+ 'atan': math.atan,
426
+ 'sqrt': math.sqrt,
427
+ 'log': math.log,
428
+ 'abs': abs,
429
+ 'ceil': math.ceil,
430
+ 'floor': math.floor,
431
+ 'round': round,
432
+ 'random': self.random,
433
+ 'fac': self.fac,
434
+ 'exp': math.exp,
435
+ 'min': min,
436
+ 'max': max,
437
+ 'pyt': self.pyt,
438
+ 'pow': math.pow,
439
+ 'atan2': math.atan2,
440
+ 'E': math.e,
441
+ 'PI': math.pi
442
+ }
443
+
444
+ def parse(self, expr):
445
+ self.errormsg = ''
446
+ self.success = True
447
+ operstack = []
448
+ tokenstack = []
449
+ self.tmpprio = 0
450
+ expected = self.PRIMARY | self.LPAREN | self.FUNCTION | self.SIGN
451
+ noperators = 0
452
+ self.expression = expr
453
+ self.pos = 0
454
+
455
+ while self.pos < len(self.expression):
456
+ if self.isOperator():
457
+ if self.isSign() and expected & self.SIGN:
458
+ if self.isNegativeSign():
459
+ self.tokenprio = 5
460
+ self.tokenindex = '-'
461
+ noperators += 1
462
+ self.addfunc(tokenstack, operstack, TOP1)
463
+ expected = \
464
+ self.PRIMARY | self.LPAREN | self.FUNCTION | self.SIGN
465
+ elif self.isLogicalNot() and expected & self.SIGN:
466
+ self.tokenprio = 2
467
+ self.tokenindex = 'not'
468
+ noperators += 1
469
+ self.addfunc(tokenstack, operstack, TOP1)
470
+ expected = \
471
+ self.PRIMARY | self.LPAREN | self.FUNCTION | self.SIGN
472
+ elif self.isComment():
473
+ pass
474
+ else:
475
+ if expected and self.OPERATOR == 0:
476
+ self.error_parsing(self.pos, 'unexpected operator')
477
+ noperators += 2
478
+ self.addfunc(tokenstack, operstack, TOP2)
479
+ expected = \
480
+ self.PRIMARY | self.LPAREN | self.FUNCTION | self.SIGN
481
+ elif self.isNumber():
482
+ if expected and self.PRIMARY == 0:
483
+ self.error_parsing(self.pos, 'unexpected number')
484
+ token = Token(TNUMBER, 0, 0, self.tokennumber)
485
+ tokenstack.append(token)
486
+ expected = self.OPERATOR | self.RPAREN | self.COMMA
487
+ elif self.isString():
488
+ if (expected & self.PRIMARY) == 0:
489
+ self.error_parsing(self.pos, 'unexpected string')
490
+ token = Token(TNUMBER, 0, 0, self.tokennumber)
491
+ tokenstack.append(token)
492
+ expected = self.OPERATOR | self.RPAREN | self.COMMA
493
+ elif self.isLeftParenth():
494
+ if (expected & self.LPAREN) == 0:
495
+ self.error_parsing(self.pos, 'unexpected \"(\"')
496
+ if expected & self.CALL:
497
+ noperators += 2
498
+ self.tokenprio = -2
499
+ self.tokenindex = -1
500
+ self.addfunc(tokenstack, operstack, TFUNCALL)
501
+ expected = \
502
+ self.PRIMARY | self.LPAREN | self.FUNCTION | \
503
+ self.SIGN | self.NULLARY_CALL
504
+ elif self.isRightParenth():
505
+ if expected & self.NULLARY_CALL:
506
+ token = Token(TNUMBER, 0, 0, [])
507
+ tokenstack.append(token)
508
+ elif (expected & self.RPAREN) == 0:
509
+ self.error_parsing(self.pos, 'unexpected \")\"')
510
+ expected = \
511
+ self.OPERATOR | self.RPAREN | self.COMMA | \
512
+ self.LPAREN | self.CALL
513
+ elif self.isComma():
514
+ if (expected & self.COMMA) == 0:
515
+ self.error_parsing(self.pos, 'unexpected \",\"')
516
+ self.addfunc(tokenstack, operstack, TOP2)
517
+ noperators += 2
518
+ expected = \
519
+ self.PRIMARY | self.LPAREN | self.FUNCTION | self.SIGN
520
+ elif self.isConst():
521
+ if (expected & self.PRIMARY) == 0:
522
+ self.error_parsing(self.pos, 'unexpected constant')
523
+ consttoken = Token(TNUMBER, 0, 0, self.tokennumber)
524
+ tokenstack.append(consttoken)
525
+ expected = self.OPERATOR | self.RPAREN | self.COMMA
526
+ elif self.isOp2():
527
+ if (expected & self.FUNCTION) == 0:
528
+ self.error_parsing(self.pos, 'unexpected function')
529
+ self.addfunc(tokenstack, operstack, TOP2)
530
+ noperators += 2
531
+ expected = self.LPAREN
532
+ elif self.isOp1():
533
+ if (expected & self.FUNCTION) == 0:
534
+ self.error_parsing(self.pos, 'unexpected function')
535
+ self.addfunc(tokenstack, operstack, TOP1)
536
+ noperators += 1
537
+ expected = self.LPAREN
538
+ elif self.isVar():
539
+ if (expected & self.PRIMARY) == 0:
540
+ self.error_parsing(self.pos, 'unexpected variable')
541
+ vartoken = Token(TVAR, self.tokenindex, 0, 0)
542
+ tokenstack.append(vartoken)
543
+ expected = \
544
+ self.OPERATOR | self.RPAREN | \
545
+ self.COMMA | self.LPAREN | self.CALL
546
+ elif self.isWhite():
547
+ pass
548
+ else:
549
+ if self.errormsg == '':
550
+ self.error_parsing(self.pos, 'unknown character')
551
+ else:
552
+ self.error_parsing(self.pos, self.errormsg)
553
+ if self.tmpprio < 0 or self.tmpprio >= 10:
554
+ self.error_parsing(self.pos, 'unmatched \"()\"')
555
+ while len(operstack) > 0:
556
+ tmp = operstack.pop()
557
+ tokenstack.append(tmp)
558
+ if (noperators + 1) != len(tokenstack):
559
+ self.error_parsing(self.pos, 'parity')
560
+
561
+ return Expression(tokenstack, self.ops1, self.ops2, self.functions)
562
+
563
+ def evaluate(self, expr, variables):
564
+ return self.parse(expr).evaluate(variables)
565
+
566
+ def error_parsing(self, column, msg):
567
+ self.success = False
568
+ self.errormsg = 'parse error [column ' + str(column) + ']: ' + msg + ', expression: ' + self.expression
569
+ raise Exception(self.errormsg)
570
+
571
+ def addfunc(self, tokenstack, operstack, type_):
572
+ operator = Token(
573
+ type_,
574
+ self.tokenindex,
575
+ self.tokenprio + self.tmpprio,
576
+ 0,
577
+ )
578
+ while len(operstack) > 0:
579
+ if operator.prio_ <= operstack[len(operstack) - 1].prio_:
580
+ tokenstack.append(operstack.pop())
581
+ else:
582
+ break
583
+ operstack.append(operator)
584
+
585
+ def isNumber(self):
586
+ r = False
587
+
588
+ if self.expression[self.pos] == 'E':
589
+ return False
590
+
591
+ # number in scientific notation
592
+ pattern = r'([-+]?([0-9]*\.?[0-9]*)[eE][-+]?[0-9]+).*'
593
+ match = re.match(pattern, self.expression[self.pos: ])
594
+ if match:
595
+ self.pos += len(match.group(1))
596
+ self.tokennumber = float(match.group(1))
597
+ return True
598
+
599
+ hex_pattern = r'(0x[0-9a-fA-F]+)'
600
+ match = re.match(hex_pattern, self.expression[self.pos: ])
601
+ if match:
602
+ self.pos += len(match.group(1))
603
+ self.tokennumber = int(match.group(1), base=16)
604
+ return True
605
+
606
+ # number in decimal
607
+ str = ''
608
+ while self.pos < len(self.expression):
609
+ code = self.expression[self.pos]
610
+ if (code >= '0' and code <= '9') or code == '.':
611
+ if (len(str) == 0 and code == '.' ):
612
+ str = '0'
613
+ str += code
614
+ self.pos += 1
615
+ try:
616
+ self.tokennumber = int(str)
617
+ except ValueError:
618
+ self.tokennumber = float(str)
619
+ r = True
620
+ else:
621
+ break
622
+ return r
623
+
624
+ def unescape(self, v, pos):
625
+ buffer = []
626
+ escaping = False
627
+
628
+ for i in range(0, len(v)):
629
+ c = v[i]
630
+
631
+ if escaping:
632
+ if c == "'":
633
+ buffer.append("'")
634
+ break
635
+ elif c == '\\':
636
+ buffer.append('\\')
637
+ break
638
+ elif c == '/':
639
+ buffer.append('/')
640
+ break
641
+ elif c == 'b':
642
+ buffer.append('\b')
643
+ break
644
+ elif c == 'f':
645
+ buffer.append('\f')
646
+ break
647
+ elif c == 'n':
648
+ buffer.append('\n')
649
+ break
650
+ elif c == 'r':
651
+ buffer.append('\r')
652
+ break
653
+ elif c == 't':
654
+ buffer.append('\t')
655
+ break
656
+ elif c == 'u':
657
+ # interpret the following 4 characters
658
+ # as the hex of the unicode code point
659
+ codePoint = int(v[i + 1, i + 5], 16)
660
+ buffer.append(unichr(codePoint))
661
+ i += 4
662
+ break
663
+ else:
664
+ raise self.error_parsing(
665
+ pos + i,
666
+ 'Illegal escape sequence: \'\\' + c + '\'',
667
+ )
668
+ escaping = False
669
+ else:
670
+ if c == '\\':
671
+ escaping = True
672
+ else:
673
+ buffer.append(c)
674
+
675
+ return ''.join(buffer)
676
+
677
+ def isString(self):
678
+ r = False
679
+ str = ''
680
+ startpos = self.pos
681
+ if self.pos < len(self.expression) and self.expression[self.pos] in self.string_literal_quotes:
682
+ quote_type = self.expression[self.pos]
683
+ self.pos += 1
684
+ while self.pos < len(self.expression):
685
+ code = self.expression[self.pos]
686
+ if code != quote_type or (str != '' and str[-1] == '\\'):
687
+ str += self.expression[self.pos]
688
+ self.pos += 1
689
+ else:
690
+ self.pos += 1
691
+ self.tokennumber = self.unescape(str, startpos)
692
+ r = True
693
+ break
694
+ return r
695
+
696
+ def isConst(self):
697
+ for i in self.consts:
698
+ L = len(i)
699
+ str = self.expression[self.pos:self.pos+L]
700
+ if i == str:
701
+ if len(self.expression) <= self.pos + L:
702
+ self.tokennumber = self.consts[i]
703
+ self.pos += L
704
+ return True
705
+ if not self.expression[self.pos + L].isalnum() and self.expression[self.pos + L] != "_":
706
+ self.tokennumber = self.consts[i]
707
+ self.pos += L
708
+ return True
709
+ return False
710
+
711
+ def isOperator(self):
712
+ ops = (
713
+ ('**', 8, '**'),
714
+ ('^', 8, '^'),
715
+ ('%', 6, '%'),
716
+ ('/', 6, '/'),
717
+ (u'\u2219', 5, '*'), # bullet operator
718
+ (u'\u2022', 5, '*'), # black small circle
719
+ ('*', 5, '*'),
720
+ ('+', 4, '+'),
721
+ ('-', 4, '-'),
722
+ ('||', 3, '||'),
723
+ ('==', 3, '=='),
724
+ ('!=', 3, '!='),
725
+ ('<=', 3, '<='),
726
+ ('>=', 3, '>='),
727
+ ('<', 3, '<'),
728
+ ('>', 3, '>'),
729
+ ('in ', 3, 'in'),
730
+ ('not ', 2, 'not'),
731
+ ('and ', 1, 'and'),
732
+ ('xor ', 0, 'xor'),
733
+ ('or ', 0, 'or'),
734
+ ('|', 3, '|'),
735
+ )
736
+ for token, priority, index in ops:
737
+ if self.expression.startswith(token, self.pos):
738
+ self.tokenprio = priority
739
+ self.tokenindex = index
740
+ self.pos += len(token)
741
+ return True
742
+ return False
743
+
744
+ def isSign(self):
745
+ code = self.expression[self.pos - 1]
746
+ return (code == '+') or (code == '-')
747
+
748
+ def isPositiveSign(self):
749
+ code = self.expression[self.pos - 1]
750
+ return code == '+'
751
+
752
+ def isNegativeSign(self):
753
+ code = self.expression[self.pos - 1]
754
+ return code == '-'
755
+
756
+ def isLogicalNot(self):
757
+ code = self.expression[self.pos - 4: self.pos]
758
+ return code == 'not '
759
+
760
+ def isLeftParenth(self):
761
+ code = self.expression[self.pos]
762
+ if code == '(':
763
+ self.pos += 1
764
+ self.tmpprio += 10
765
+ return True
766
+ return False
767
+
768
+ def isRightParenth(self):
769
+ code = self.expression[self.pos]
770
+ if code == ')':
771
+ self.pos += 1
772
+ self.tmpprio -= 10
773
+ return True
774
+ return False
775
+
776
+ def isComma(self):
777
+ code = self.expression[self.pos]
778
+ if code==',':
779
+ self.pos+=1
780
+ self.tokenprio=-1
781
+ self.tokenindex=","
782
+ return True
783
+ return False
784
+
785
+ def isWhite(self):
786
+ code = self.expression[self.pos]
787
+ if code.isspace():
788
+ self.pos += 1
789
+ return True
790
+ return False
791
+
792
+ def isOp1(self):
793
+ str = ''
794
+ for i in range(self.pos, len(self.expression)):
795
+ c = self.expression[i]
796
+ if c.upper() == c.lower():
797
+ if i == self.pos or (c != '_' and (c < '0' or c > '9')):
798
+ break
799
+ str += c
800
+ if len(str) > 0 and str in self.ops1:
801
+ self.tokenindex = str
802
+ self.tokenprio = 9
803
+ self.pos += len(str)
804
+ return True
805
+ return False
806
+
807
+ def isOp2(self):
808
+ str = ''
809
+ for i in range(self.pos, len(self.expression)):
810
+ c = self.expression[i]
811
+ if c.upper() == c.lower():
812
+ if i == self.pos or (c != '_' and (c < '0' or c > '9')):
813
+ break
814
+ str += c
815
+ if len(str) > 0 and (str in self.ops2):
816
+ self.tokenindex = str
817
+ self.tokenprio = 9
818
+ self.pos += len(str)
819
+ return True
820
+ return False
821
+
822
+ def isVar(self):
823
+ str = ''
824
+ inQuotes = False
825
+ for i in range(self.pos, len(self.expression)):
826
+ c = self.expression[i]
827
+ if c.lower() == c.upper():
828
+ if ((i == self.pos and c != '"') or (not (c in '_."') and (c < '0' or c > '9'))) and not inQuotes :
829
+ break
830
+ if c == '"':
831
+ inQuotes = not inQuotes
832
+ str += c
833
+ if str:
834
+ self.tokenindex = str
835
+ self.tokenprio = 6
836
+ self.pos += len(str)
837
+ return True
838
+ return False
839
+
840
+ def isComment(self):
841
+ code = self.expression[self.pos - 1]
842
+ if code == '/' and self.expression[self.pos] == '*':
843
+ self.pos = self.expression.index('*/', self.pos) + 2
844
+ if self.pos == 1:
845
+ self.pos = len(self.expression)
846
+ return True
847
+ return False