deepfos 1.1.60__py3-none-any.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 (175) hide show
  1. deepfos/__init__.py +6 -0
  2. deepfos/_version.py +21 -0
  3. deepfos/algo/__init__.py +0 -0
  4. deepfos/algo/graph.py +171 -0
  5. deepfos/algo/segtree.py +31 -0
  6. deepfos/api/V1_1/__init__.py +0 -0
  7. deepfos/api/V1_1/business_model.py +119 -0
  8. deepfos/api/V1_1/dimension.py +599 -0
  9. deepfos/api/V1_1/models/__init__.py +0 -0
  10. deepfos/api/V1_1/models/business_model.py +1033 -0
  11. deepfos/api/V1_1/models/dimension.py +2768 -0
  12. deepfos/api/V1_2/__init__.py +0 -0
  13. deepfos/api/V1_2/dimension.py +285 -0
  14. deepfos/api/V1_2/models/__init__.py +0 -0
  15. deepfos/api/V1_2/models/dimension.py +2923 -0
  16. deepfos/api/__init__.py +0 -0
  17. deepfos/api/account.py +167 -0
  18. deepfos/api/accounting_engines.py +147 -0
  19. deepfos/api/app.py +626 -0
  20. deepfos/api/approval_process.py +198 -0
  21. deepfos/api/base.py +983 -0
  22. deepfos/api/business_model.py +160 -0
  23. deepfos/api/consolidation.py +129 -0
  24. deepfos/api/consolidation_process.py +106 -0
  25. deepfos/api/datatable.py +341 -0
  26. deepfos/api/deep_pipeline.py +61 -0
  27. deepfos/api/deepconnector.py +36 -0
  28. deepfos/api/deepfos_task.py +92 -0
  29. deepfos/api/deepmodel.py +188 -0
  30. deepfos/api/dimension.py +486 -0
  31. deepfos/api/financial_model.py +319 -0
  32. deepfos/api/journal_model.py +119 -0
  33. deepfos/api/journal_template.py +132 -0
  34. deepfos/api/memory_financial_model.py +98 -0
  35. deepfos/api/models/__init__.py +3 -0
  36. deepfos/api/models/account.py +483 -0
  37. deepfos/api/models/accounting_engines.py +756 -0
  38. deepfos/api/models/app.py +1338 -0
  39. deepfos/api/models/approval_process.py +1043 -0
  40. deepfos/api/models/base.py +234 -0
  41. deepfos/api/models/business_model.py +805 -0
  42. deepfos/api/models/consolidation.py +711 -0
  43. deepfos/api/models/consolidation_process.py +248 -0
  44. deepfos/api/models/datatable_mysql.py +427 -0
  45. deepfos/api/models/deep_pipeline.py +55 -0
  46. deepfos/api/models/deepconnector.py +28 -0
  47. deepfos/api/models/deepfos_task.py +386 -0
  48. deepfos/api/models/deepmodel.py +308 -0
  49. deepfos/api/models/dimension.py +1576 -0
  50. deepfos/api/models/financial_model.py +1796 -0
  51. deepfos/api/models/journal_model.py +341 -0
  52. deepfos/api/models/journal_template.py +854 -0
  53. deepfos/api/models/memory_financial_model.py +478 -0
  54. deepfos/api/models/platform.py +178 -0
  55. deepfos/api/models/python.py +221 -0
  56. deepfos/api/models/reconciliation_engine.py +411 -0
  57. deepfos/api/models/reconciliation_report.py +161 -0
  58. deepfos/api/models/role_strategy.py +884 -0
  59. deepfos/api/models/smartlist.py +237 -0
  60. deepfos/api/models/space.py +1137 -0
  61. deepfos/api/models/system.py +1065 -0
  62. deepfos/api/models/variable.py +463 -0
  63. deepfos/api/models/workflow.py +946 -0
  64. deepfos/api/platform.py +199 -0
  65. deepfos/api/python.py +90 -0
  66. deepfos/api/reconciliation_engine.py +181 -0
  67. deepfos/api/reconciliation_report.py +64 -0
  68. deepfos/api/role_strategy.py +234 -0
  69. deepfos/api/smartlist.py +69 -0
  70. deepfos/api/space.py +582 -0
  71. deepfos/api/system.py +372 -0
  72. deepfos/api/variable.py +154 -0
  73. deepfos/api/workflow.py +264 -0
  74. deepfos/boost/__init__.py +6 -0
  75. deepfos/boost/py_jstream.py +89 -0
  76. deepfos/boost/py_pandas.py +20 -0
  77. deepfos/cache.py +121 -0
  78. deepfos/config.py +6 -0
  79. deepfos/core/__init__.py +27 -0
  80. deepfos/core/cube/__init__.py +10 -0
  81. deepfos/core/cube/_base.py +462 -0
  82. deepfos/core/cube/constants.py +21 -0
  83. deepfos/core/cube/cube.py +408 -0
  84. deepfos/core/cube/formula.py +707 -0
  85. deepfos/core/cube/syscube.py +532 -0
  86. deepfos/core/cube/typing.py +7 -0
  87. deepfos/core/cube/utils.py +238 -0
  88. deepfos/core/dimension/__init__.py +11 -0
  89. deepfos/core/dimension/_base.py +506 -0
  90. deepfos/core/dimension/dimcreator.py +184 -0
  91. deepfos/core/dimension/dimension.py +472 -0
  92. deepfos/core/dimension/dimexpr.py +271 -0
  93. deepfos/core/dimension/dimmember.py +155 -0
  94. deepfos/core/dimension/eledimension.py +22 -0
  95. deepfos/core/dimension/filters.py +99 -0
  96. deepfos/core/dimension/sysdimension.py +168 -0
  97. deepfos/core/logictable/__init__.py +5 -0
  98. deepfos/core/logictable/_cache.py +141 -0
  99. deepfos/core/logictable/_operator.py +663 -0
  100. deepfos/core/logictable/nodemixin.py +673 -0
  101. deepfos/core/logictable/sqlcondition.py +609 -0
  102. deepfos/core/logictable/tablemodel.py +497 -0
  103. deepfos/db/__init__.py +36 -0
  104. deepfos/db/cipher.py +660 -0
  105. deepfos/db/clickhouse.py +191 -0
  106. deepfos/db/connector.py +195 -0
  107. deepfos/db/daclickhouse.py +171 -0
  108. deepfos/db/dameng.py +101 -0
  109. deepfos/db/damysql.py +189 -0
  110. deepfos/db/dbkits.py +358 -0
  111. deepfos/db/deepengine.py +99 -0
  112. deepfos/db/deepmodel.py +82 -0
  113. deepfos/db/deepmodel_kingbase.py +83 -0
  114. deepfos/db/edb.py +214 -0
  115. deepfos/db/gauss.py +83 -0
  116. deepfos/db/kingbase.py +83 -0
  117. deepfos/db/mysql.py +184 -0
  118. deepfos/db/oracle.py +131 -0
  119. deepfos/db/postgresql.py +192 -0
  120. deepfos/db/sqlserver.py +99 -0
  121. deepfos/db/utils.py +135 -0
  122. deepfos/element/__init__.py +89 -0
  123. deepfos/element/accounting.py +348 -0
  124. deepfos/element/apvlprocess.py +215 -0
  125. deepfos/element/base.py +398 -0
  126. deepfos/element/bizmodel.py +1269 -0
  127. deepfos/element/datatable.py +2467 -0
  128. deepfos/element/deep_pipeline.py +186 -0
  129. deepfos/element/deepconnector.py +59 -0
  130. deepfos/element/deepmodel.py +1806 -0
  131. deepfos/element/dimension.py +1254 -0
  132. deepfos/element/fact_table.py +427 -0
  133. deepfos/element/finmodel.py +1485 -0
  134. deepfos/element/journal.py +840 -0
  135. deepfos/element/journal_template.py +943 -0
  136. deepfos/element/pyscript.py +412 -0
  137. deepfos/element/reconciliation.py +553 -0
  138. deepfos/element/rolestrategy.py +243 -0
  139. deepfos/element/smartlist.py +457 -0
  140. deepfos/element/variable.py +756 -0
  141. deepfos/element/workflow.py +560 -0
  142. deepfos/exceptions/__init__.py +239 -0
  143. deepfos/exceptions/hook.py +86 -0
  144. deepfos/lazy.py +104 -0
  145. deepfos/lazy_import.py +84 -0
  146. deepfos/lib/__init__.py +0 -0
  147. deepfos/lib/_javaobj.py +366 -0
  148. deepfos/lib/asynchronous.py +879 -0
  149. deepfos/lib/concurrency.py +107 -0
  150. deepfos/lib/constant.py +39 -0
  151. deepfos/lib/decorator.py +310 -0
  152. deepfos/lib/deepchart.py +778 -0
  153. deepfos/lib/deepux.py +477 -0
  154. deepfos/lib/discovery.py +273 -0
  155. deepfos/lib/edb_lexer.py +789 -0
  156. deepfos/lib/eureka.py +156 -0
  157. deepfos/lib/filterparser.py +751 -0
  158. deepfos/lib/httpcli.py +106 -0
  159. deepfos/lib/jsonstreamer.py +80 -0
  160. deepfos/lib/msg.py +394 -0
  161. deepfos/lib/nacos.py +225 -0
  162. deepfos/lib/patch.py +92 -0
  163. deepfos/lib/redis.py +241 -0
  164. deepfos/lib/serutils.py +181 -0
  165. deepfos/lib/stopwatch.py +99 -0
  166. deepfos/lib/subtask.py +572 -0
  167. deepfos/lib/sysutils.py +703 -0
  168. deepfos/lib/utils.py +1003 -0
  169. deepfos/local.py +160 -0
  170. deepfos/options.py +670 -0
  171. deepfos/translation.py +237 -0
  172. deepfos-1.1.60.dist-info/METADATA +33 -0
  173. deepfos-1.1.60.dist-info/RECORD +175 -0
  174. deepfos-1.1.60.dist-info/WHEEL +5 -0
  175. deepfos-1.1.60.dist-info/top_level.txt +1 -0
@@ -0,0 +1,789 @@
1
+ import collections
2
+ import re
3
+
4
+ import types
5
+ from typing import Optional, Dict, AbstractSet
6
+
7
+ __all__ = ('EdgeQLLexer', 'UnknownTokenError')
8
+
9
+ keyword_types = range(1, 4)
10
+ UNRESERVED_KEYWORD, RESERVED_KEYWORD, TYPE_FUNC_NAME_KEYWORD = keyword_types
11
+ reserved_keywords = (
12
+ "__edgedbsys__",
13
+ "__edgedbtpl__",
14
+ "__source__",
15
+ "__std__",
16
+ "__subject__",
17
+ "__type__",
18
+ "alter",
19
+ "analyze",
20
+ "and",
21
+ "anyarray",
22
+ "anytuple",
23
+ "anytype",
24
+ "begin",
25
+ "by",
26
+ "case",
27
+ "check",
28
+ "commit",
29
+ "configure",
30
+ "create",
31
+ "deallocate",
32
+ "delete",
33
+ "describe",
34
+ "detached",
35
+ "discard",
36
+ "distinct",
37
+ "do",
38
+ "drop",
39
+ "else",
40
+ "end",
41
+ "execute",
42
+ "exists",
43
+ "explain",
44
+ "extending",
45
+ "fetch",
46
+ "filter",
47
+ "for",
48
+ "get",
49
+ "global",
50
+ "grant",
51
+ "group",
52
+ "if",
53
+ "ilike",
54
+ "import",
55
+ "in",
56
+ "insert",
57
+ "introspect",
58
+ "is",
59
+ "like",
60
+ "limit",
61
+ "listen",
62
+ "load",
63
+ "lock",
64
+ "match",
65
+ "module",
66
+ "move",
67
+ "never",
68
+ "not",
69
+ "notify",
70
+ "offset",
71
+ "on",
72
+ "optional",
73
+ "or",
74
+ "over",
75
+ "partition",
76
+ "prepare",
77
+ "raise",
78
+ "refresh",
79
+ "reindex",
80
+ "revoke",
81
+ "rollback",
82
+ "select",
83
+ "set",
84
+ "single",
85
+ "start",
86
+ "typeof",
87
+ "union",
88
+ "update",
89
+ "variadic",
90
+ "when",
91
+ "window",
92
+ "with",
93
+ )
94
+ unreserved_keywords = (
95
+ "abort",
96
+ "abstract",
97
+ "access",
98
+ "after",
99
+ "alias",
100
+ "all",
101
+ "allow",
102
+ "annotation",
103
+ "applied",
104
+ "as",
105
+ "asc",
106
+ "assignment",
107
+ "before",
108
+ "cardinality",
109
+ "cast",
110
+ "config",
111
+ "conflict",
112
+ "constraint",
113
+ "cube",
114
+ "current",
115
+ "database",
116
+ "ddl",
117
+ "declare",
118
+ "default",
119
+ "deferrable",
120
+ "deferred",
121
+ "delegated",
122
+ "deny",
123
+ "desc",
124
+ "empty",
125
+ "except",
126
+ "expression",
127
+ "extension",
128
+ "final",
129
+ "first",
130
+ "from",
131
+ "function",
132
+ "implicit",
133
+ "index",
134
+ "infix",
135
+ "inheritable",
136
+ "instance",
137
+ "into",
138
+ "isolation",
139
+ "json",
140
+ "last",
141
+ "link",
142
+ "migration",
143
+ "multi",
144
+ "named",
145
+ "object",
146
+ "of",
147
+ "only",
148
+ "onto",
149
+ "operator",
150
+ "optionality",
151
+ "order",
152
+ "orphan",
153
+ "overloaded",
154
+ "owned",
155
+ "package",
156
+ "policy",
157
+ "populate",
158
+ "postfix",
159
+ "prefix",
160
+ "property",
161
+ "proposed",
162
+ "pseudo",
163
+ "read",
164
+ "reject",
165
+ "release",
166
+ "rename",
167
+ "required",
168
+ "reset",
169
+ "restrict",
170
+ "role",
171
+ "roles",
172
+ "rollup",
173
+ "savepoint",
174
+ "scalar",
175
+ "schema",
176
+ "sdl",
177
+ "serializable",
178
+ "session",
179
+ "source",
180
+ "superuser",
181
+ "system",
182
+ "target",
183
+ "ternary",
184
+ "text",
185
+ "then",
186
+ "to",
187
+ "transaction",
188
+ "type",
189
+ "unless",
190
+ "using",
191
+ "verbose",
192
+ "version",
193
+ "view",
194
+ "write",
195
+ )
196
+ _dunder_re = re.compile(r'(?i)^__[a-z]+__$')
197
+
198
+
199
+ def tok_name(keyword):
200
+ '''Convert a literal keyword into a token name.'''
201
+ if _dunder_re.match(keyword):
202
+ return f'DUNDER{keyword[2:-2].upper()}'
203
+ else:
204
+ return keyword.upper()
205
+
206
+
207
+ edgeql_keywords = {k: (tok_name(k), UNRESERVED_KEYWORD)
208
+ for k in unreserved_keywords}
209
+ edgeql_keywords.update({k: (tok_name(k), RESERVED_KEYWORD)
210
+ for k in reserved_keywords})
211
+
212
+
213
+ STATE_KEEP = 0
214
+ STATE_BASE = 1
215
+
216
+
217
+ re_dquote = r'\$(?:[A-Za-z_][A-Za-z_0-9]*)?\$'
218
+
219
+
220
+ class LexError(Exception):
221
+ def __init__(
222
+ self, msg, *, line=None, col=None, filename=None, format=True):
223
+ if format and '{' in msg:
224
+ position = self._format_position(line, col, filename)
225
+ msg = msg.format(
226
+ line=line, col=col, filename=filename, position=position)
227
+
228
+ super().__init__(msg)
229
+ self.line = line
230
+ self.col = col
231
+ self.filename = filename
232
+
233
+ @classmethod
234
+ def _format_position(cls, line, col, filename):
235
+ position = 'at {}:{}'.format(line, col)
236
+ if filename:
237
+ position += ' of ' + str(filename)
238
+ return position
239
+
240
+
241
+ Token = collections.namedtuple(
242
+ 'Token',
243
+ ['value', 'type', 'text', 'start', 'end', 'filename']
244
+ )
245
+
246
+
247
+ class UnknownTokenError(LexError):
248
+ pass
249
+
250
+
251
+ class Rule:
252
+ _idx = 0
253
+ _map: Dict[str, "Rule"] = {}
254
+
255
+ def __init__(self, *, token, next_state, regexp):
256
+ cls = self.__class__
257
+ cls._idx += 1
258
+ self.id = 'rule{}'.format(cls._idx)
259
+ cls._map[self.id] = self
260
+
261
+ self.token = token
262
+ self.next_state = next_state
263
+ self.regexp = regexp
264
+
265
+ def __repr__(self):
266
+ return '<{} {} {!r}>'.format(self.id, self.token, self.regexp)
267
+
268
+
269
+ def group(*literals, _re_alpha=re.compile(r'^\w+$'), asbytes=False):
270
+ rx = []
271
+ for lit in literals:
272
+ if r'\b' not in lit:
273
+ lit = re.escape(lit)
274
+ if _re_alpha.match(lit):
275
+ lit = r'\b' + lit + r'\b'
276
+ rx.append(lit)
277
+ result = ' | '.join(rx)
278
+ if asbytes:
279
+ result = result.encode()
280
+
281
+ return result
282
+
283
+
284
+ class Lexer:
285
+
286
+ NL: Optional[str] = None
287
+ MULTILINE_TOKENS: AbstractSet[str] = frozenset()
288
+ RE_FLAGS = re.X | re.M
289
+ asbytes = False
290
+ _NL = '\n'
291
+
292
+ def __init_subclass__(cls):
293
+ if not hasattr(cls, 'states'):
294
+ return
295
+
296
+ re_states = {}
297
+ for state, rules in cls.states.items():
298
+ res = []
299
+ for rule in rules:
300
+ if cls.asbytes:
301
+ res.append(b'(?P<%b>%b)' % (rule.id.encode(), rule.regexp))
302
+ else:
303
+ res.append('(?P<{}>{})'.format(rule.id, rule.regexp))
304
+
305
+ if cls.asbytes:
306
+ res.append(b'(?P<err>.)')
307
+ else:
308
+ res.append('(?P<err>.)')
309
+
310
+ if cls.asbytes:
311
+ full_re = b' | '.join(res)
312
+ else:
313
+ full_re = ' | '.join(res)
314
+ re_states[state] = re.compile(full_re, cls.RE_FLAGS)
315
+
316
+ cls.re_states = types.MappingProxyType(re_states)
317
+
318
+ def __init__(self):
319
+ self.reset()
320
+ if self.asbytes:
321
+ self._NL = b'\n'
322
+
323
+ def reset(self):
324
+ self.lineno = 1
325
+ self.column = 1
326
+ self._state = self.start_state
327
+ self._states = []
328
+
329
+ def setinputstr(self, inputstr, filename=None):
330
+ self.inputstr = inputstr
331
+ self.filename = filename
332
+ self.start = 0
333
+ self.end = len(inputstr)
334
+ self.reset()
335
+ self._token_stream = None
336
+
337
+ def get_start_token(self):
338
+ """Return a start token or None if no start token is wanted."""
339
+ return None
340
+
341
+ def get_eof_token(self):
342
+ """Return an EOF token or None if no EOF token is wanted."""
343
+ return None
344
+
345
+ def token_from_text(self, rule_token, txt):
346
+ """Given the rule_token with txt create a token.
347
+
348
+ Update the lexer lineno, column, and start.
349
+ """
350
+ start_pos = self.start
351
+ len_txt = len(txt)
352
+
353
+ if rule_token is self.NL:
354
+ # Newline -- increase line number & set col to 1
355
+ self.lineno += 1
356
+ self.column = 1
357
+
358
+ elif rule_token in self.MULTILINE_TOKENS and self._NL in txt:
359
+ # Advance line & col according to how many new lines
360
+ # are in comments/strings/etc.
361
+ self.lineno += txt.count(self._NL)
362
+ self.column = len(txt.rsplit(self._NL, 1)[1]) + 1
363
+ else:
364
+ self.column += len_txt
365
+
366
+ self.start += len_txt
367
+ end_pos = self.start
368
+
369
+ return Token(txt, type=rule_token, text=txt,
370
+ start=start_pos, end=end_pos,
371
+ filename=self.filename)
372
+
373
+ def lex(self):
374
+ """Tokenize the src.
375
+
376
+ Generator. Yields tokens (as defined by the rules).
377
+
378
+ May yield special start and EOF tokens.
379
+ May raise UnknownTokenError exception.
380
+ """
381
+ src = self.inputstr
382
+
383
+ start_tok = self.get_start_token()
384
+ if start_tok is not None:
385
+ yield start_tok
386
+
387
+ while self.start < self.end:
388
+ for match in self.re_states[self._state].finditer(src, self.start):
389
+ rule_id = match.lastgroup
390
+
391
+ txt = match.group(rule_id)
392
+
393
+ if rule_id == 'err':
394
+ # Error group -- no rule has been matched
395
+ self.handle_error(txt)
396
+
397
+ rule = Rule._map[rule_id]
398
+ rule_token = rule.token
399
+
400
+ token = self.token_from_text(rule_token, txt)
401
+
402
+ yield token
403
+
404
+ if rule.next_state and rule.next_state != self._state:
405
+ # Rule dictates that the lexer state should be
406
+ # switched
407
+ self._state = rule.next_state
408
+ break
409
+
410
+ # End of file
411
+ eof_tok = self.get_eof_token()
412
+ if eof_tok is not None:
413
+ yield eof_tok
414
+
415
+ def handle_error(self, txt, *,
416
+ exact_message=False, exc_type=UnknownTokenError):
417
+ if exact_message:
418
+ msg = txt
419
+ else:
420
+ msg = f"Unexpected '{txt}'"
421
+
422
+ raise exc_type(
423
+ msg, line=self.lineno, col=self.column, filename=self.filename
424
+ )
425
+
426
+ def token(self):
427
+ """Return the next token produced by the
428
+
429
+ The token is an xvalue with the following attributes: type,
430
+ text, start, end, and filename.
431
+ """
432
+ if self._token_stream is None:
433
+ self._token_stream = self.lex()
434
+
435
+ try:
436
+ return next(self._token_stream)
437
+ except StopIteration:
438
+ return None
439
+
440
+
441
+ class UnterminatedStringError(UnknownTokenError):
442
+ pass
443
+
444
+
445
+ class PseudoRule(Rule):
446
+ def __init__(self, *, token, regexp, rule_id, next_state=STATE_KEEP):
447
+ self.id = rule_id
448
+ Rule._map[rule_id] = self
449
+ self.token = token
450
+ self.next_state = next_state
451
+ self.regexp = regexp
452
+
453
+
454
+ class EdgeQLLexer(Lexer):
455
+
456
+ start_state = STATE_BASE
457
+
458
+ MERGE_TOKENS = {
459
+ ('NAMED', 'ONLY'),
460
+ ('SET', 'ANNOTATION'),
461
+ ('SET', 'TYPE'),
462
+ ('EXTENSION', 'PACKAGE'),
463
+ }
464
+
465
+ NL = 'NL'
466
+ MULTILINE_TOKENS = frozenset(('SCONST', 'BCONST', 'RSCONST'))
467
+ RE_FLAGS = re.X | re.M | re.I
468
+
469
+ # Basic keywords
470
+ keyword_rules = [Rule(token=tok[0],
471
+ next_state=STATE_KEEP,
472
+ regexp=group(val))
473
+ for val, tok in edgeql_keywords.items()]
474
+
475
+ common_rules = keyword_rules + [
476
+ Rule(token='WS',
477
+ next_state=STATE_KEEP,
478
+ regexp=r'[^\S\n]+'),
479
+
480
+ Rule(token='NL',
481
+ next_state=STATE_KEEP,
482
+ regexp=r'\n'),
483
+
484
+ Rule(token='COMMENT',
485
+ next_state=STATE_KEEP,
486
+ regexp=r'''\#.*?$'''),
487
+
488
+ Rule(token='ASSIGN',
489
+ next_state=STATE_KEEP,
490
+ regexp=r':='),
491
+
492
+ Rule(token='REMASSIGN',
493
+ next_state=STATE_KEEP,
494
+ regexp=r'-='),
495
+
496
+ Rule(token='ADDASSIGN',
497
+ next_state=STATE_KEEP,
498
+ regexp=r'\+='),
499
+
500
+ Rule(token='ARROW',
501
+ next_state=STATE_KEEP,
502
+ regexp=r'->'),
503
+
504
+ Rule(token='??',
505
+ next_state=STATE_KEEP,
506
+ regexp=r'\?\?'),
507
+
508
+ Rule(token='::',
509
+ next_state=STATE_KEEP,
510
+ regexp=r'::'),
511
+
512
+ # special path operators
513
+ Rule(token='.<',
514
+ next_state=STATE_KEEP,
515
+ regexp=r'\.<'),
516
+
517
+ Rule(token='//',
518
+ next_state=STATE_KEEP,
519
+ regexp=r'//'),
520
+
521
+ Rule(token='++',
522
+ next_state=STATE_KEEP,
523
+ regexp=r'\+\+'),
524
+
525
+ Rule(token='OP',
526
+ next_state=STATE_KEEP,
527
+ regexp=r'''
528
+ (?: >= | <= | != | \?= | \?!=)
529
+ '''),
530
+
531
+ Rule(token='self',
532
+ next_state=STATE_KEEP,
533
+ regexp=r'[,()\[\].@;:+\-*/%^<>=&|]'),
534
+
535
+ Rule(token='NFCONST',
536
+ next_state=STATE_KEEP,
537
+ regexp=r"""
538
+ (?:
539
+ (?: \d+ (?:\.\d+)?
540
+ (?:[eE](?:[+\-])?[0-9]+)
541
+ )
542
+ |
543
+ (?: \d+\.\d+)
544
+ )n
545
+ """),
546
+
547
+ Rule(token='NICONST',
548
+ next_state=STATE_KEEP,
549
+ regexp=r'((?:[1-9]\d* | 0)n)'),
550
+
551
+ Rule(token='FCONST',
552
+ next_state=STATE_KEEP,
553
+ regexp=r"""
554
+ (?: \d+ (?:\.\d+)?
555
+ (?:[eE](?:[+\-])?[0-9]+)
556
+ )
557
+ |
558
+ (?: \d+\.\d+)
559
+ """),
560
+
561
+ Rule(token='ICONST',
562
+ next_state=STATE_KEEP,
563
+ regexp=r'([1-9]\d* | 0)(?![0-9])'),
564
+
565
+ Rule(token='BCONST',
566
+ next_state=STATE_KEEP,
567
+ regexp=rf'''
568
+ (?:
569
+ b
570
+ )
571
+ (?P<BQ>
572
+ ' | "
573
+ )
574
+ (?:
575
+ (
576
+ \\\\ | \\['"] | \n | .
577
+ # we'll validate escape codes in the parser
578
+ )*?
579
+ )
580
+ (?P=BQ)
581
+ '''),
582
+
583
+ Rule(token='RSCONST',
584
+ next_state=STATE_KEEP,
585
+ regexp=rf'''
586
+ (?:
587
+ r
588
+ )?
589
+ (?P<RQ>
590
+ (?:
591
+ (?<=r) (?: ' | ")
592
+ ) | (?:
593
+ (?<!r) (?: {re_dquote})
594
+ )
595
+ )
596
+ (?:
597
+ (
598
+ \n | .
599
+ # we'll validate escape codes in the parser
600
+ )*?
601
+ )
602
+ (?P=RQ)
603
+ '''),
604
+
605
+ Rule(token='LSCONST',
606
+ next_state=STATE_KEEP,
607
+ regexp=rf'''
608
+ (?:
609
+ L
610
+ )?
611
+ (?P<LQ>
612
+ (?:
613
+ (?<=L) (?: ' | ")
614
+ ) | (?:
615
+ (?<!L) (?: {re_dquote})
616
+ )
617
+ )
618
+ (?:
619
+ (
620
+ \n | .
621
+ # we'll validate escape codes in the parser
622
+ )*?
623
+ )
624
+ (?P=LQ)
625
+ '''),
626
+
627
+ Rule(token='SCONST',
628
+ next_state=STATE_KEEP,
629
+ regexp=rf'''
630
+ (?P<Q>
631
+ ' | "
632
+ )
633
+ (?:
634
+ (
635
+ \\\\ | \\['"] | \n | .
636
+ # we'll validate escape codes in the parser
637
+ )*?
638
+ )
639
+ (?P=Q)
640
+ '''),
641
+
642
+ # this rule will capture malformed strings and allow us to
643
+ # provide better error messages
644
+ Rule(token='BADSCONST',
645
+ next_state=STATE_KEEP,
646
+ regexp=rf'''
647
+ [rb]?
648
+ (['"] | (?: {re_dquote}))
649
+ [^\n]*
650
+ '''),
651
+
652
+ Rule(token='BADIDENT',
653
+ next_state=STATE_KEEP,
654
+ regexp=r'''
655
+ __[^\W\d]\w*__
656
+ |
657
+ `__([^`]|``)*__`(?!`)
658
+ '''),
659
+
660
+ Rule(token='IDENT',
661
+ next_state=STATE_KEEP,
662
+ regexp=r'[^\W\d]\w*'),
663
+
664
+ Rule(token='QIDENT',
665
+ next_state=STATE_KEEP,
666
+ regexp=r'`([^`]|``)*`'),
667
+
668
+ Rule(token='self',
669
+ next_state=STATE_KEEP,
670
+ regexp=r'[\{\}]'),
671
+
672
+ Rule(token='ARGUMENT',
673
+ next_state=STATE_KEEP,
674
+ regexp=r'\$(?:[0-9]+|[^\W\d]\w*|`(?:[^`]|``)*`)'),
675
+
676
+ Rule(token='BADARGUMENT',
677
+ next_state=STATE_KEEP,
678
+ regexp=r'\$[0-9]+[^\W\d]\w*'),
679
+ ]
680
+
681
+ states = {
682
+ STATE_BASE:
683
+ common_rules,
684
+ }
685
+
686
+ # add capacity to handle a few tokens composed of 2 elements
687
+ _possible_long_token = {x[0] for x in MERGE_TOKENS}
688
+ _long_token_match = {x[1]: x[0] for x in MERGE_TOKENS}
689
+
690
+ special_rules = [
691
+ PseudoRule(token='UNKNOWN',
692
+ next_state=STATE_KEEP,
693
+ regexp=r'.',
694
+ rule_id='err')
695
+ ]
696
+
697
+ def __init__(self, *, strip_whitespace=True, raise_lexerror=True):
698
+ super().__init__()
699
+ self.strip_whitespace = strip_whitespace
700
+ self.raise_lexerror = raise_lexerror
701
+
702
+ def get_eof_token(self):
703
+ """Return an EOF token or None if no EOF token is wanted."""
704
+ return self.token_from_text('EOF', '')
705
+
706
+ def token_from_text(self, rule_token, txt):
707
+ if rule_token == 'BADSCONST':
708
+ self.handle_error(f"Unterminated string {txt}",
709
+ exact_message=True,
710
+ exc_type=UnterminatedStringError)
711
+ elif rule_token == 'BADIDENT':
712
+ self.handle_error(txt)
713
+
714
+ elif rule_token == 'QIDENT':
715
+ if txt == '``':
716
+ self.handle_error(f'Identifiers cannot be empty',
717
+ exact_message=True)
718
+ elif txt[1] == '@':
719
+ self.handle_error(f'Identifiers cannot start with "@"',
720
+ exact_message=True)
721
+ elif '::' in txt:
722
+ self.handle_error(f'Identifiers cannot contain "::"',
723
+ exact_message=True)
724
+ elif rule_token == 'ARGUMENT':
725
+ if txt == '$``':
726
+ self.handle_error(f'Backtick-quoted variable names '
727
+ f'cannot be empty',
728
+ exact_message=True)
729
+ elif rule_token == 'BADARGUMENT':
730
+ self.handle_error(f"Invalid argument name {txt!r} "
731
+ f"should be either all digits or "
732
+ f"start with letter",
733
+ exact_message=True,
734
+ exc_type=UnterminatedStringError)
735
+
736
+ tok = super().token_from_text(rule_token, txt)
737
+
738
+ if rule_token == 'self':
739
+ tok = tok._replace(type=txt)
740
+
741
+ elif rule_token == 'QIDENT':
742
+ # Drop the quotes and replace the "``" inside with a "`"
743
+ val = txt[1:-1].replace('``', '`')
744
+ tok = tok._replace(type='IDENT', value=val)
745
+
746
+ return tok
747
+
748
+ def lex(self):
749
+ buffer = []
750
+
751
+ for tok in super().lex():
752
+ tok_type = tok.type
753
+
754
+ if self.strip_whitespace and tok_type in {'WS', 'NL', 'COMMENT'}:
755
+ # Strip out whitespace and comments
756
+ continue
757
+
758
+ elif tok_type in self._possible_long_token:
759
+ # Buffer in case this is a merged token
760
+ if not buffer:
761
+ buffer.append(tok)
762
+ else:
763
+ yield from iter(buffer)
764
+ buffer[:] = [tok]
765
+
766
+ elif tok_type in self._long_token_match:
767
+ prev_token = buffer[-1] if buffer else None
768
+ if (prev_token and
769
+ prev_token.type == self._long_token_match[tok_type]):
770
+ tok = prev_token._replace(
771
+ value=prev_token.value + ' ' + tok.value,
772
+ type=prev_token.type + tok_type)
773
+ buffer.pop()
774
+ yield tok
775
+
776
+ else:
777
+ if buffer:
778
+ yield from iter(buffer)
779
+ buffer[:] = []
780
+ yield tok
781
+
782
+ def lex_highlight(self):
783
+ return super().lex()
784
+
785
+ def handle_error(self, txt, *,
786
+ exact_message=False, exc_type=UnknownTokenError):
787
+ if self.raise_lexerror:
788
+ super().handle_error(
789
+ txt, exact_message=exact_message, exc_type=exc_type)