pine2pyne 0.2.4__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 (75) hide show
  1. pine2pyne/__init__.py +84 -0
  2. pine2pyne/__main__.py +12 -0
  3. pine2pyne/ast_nodes.py +405 -0
  4. pine2pyne/ast_walk.py +93 -0
  5. pine2pyne/block_value_lowering.py +694 -0
  6. pine2pyne/bool_builtins.py +42 -0
  7. pine2pyne/bool_value_typing.py +580 -0
  8. pine2pyne/cli.py +247 -0
  9. pine2pyne/codegen.py +886 -0
  10. pine2pyne/compat/__init__.py +19 -0
  11. pine2pyne/compat/__main__.py +6 -0
  12. pine2pyne/compat/cli.py +114 -0
  13. pine2pyne/compat/comparison.py +105 -0
  14. pine2pyne/compat/corpus.py +73 -0
  15. pine2pyne/compat/report.py +95 -0
  16. pine2pyne/compat/runner.py +63 -0
  17. pine2pyne/condition_typing.py +260 -0
  18. pine2pyne/conditional_history_calls.py +96 -0
  19. pine2pyne/core/__init__.py +26 -0
  20. pine2pyne/core/contracts.py +125 -0
  21. pine2pyne/di.py +173 -0
  22. pine2pyne/emit/__init__.py +5 -0
  23. pine2pyne/emit/legacy.py +14 -0
  24. pine2pyne/emit/pynecomp/__init__.py +20 -0
  25. pine2pyne/emit/pynecomp/banner.py +30 -0
  26. pine2pyne/emit/pynecomp/blank_lines.py +69 -0
  27. pine2pyne/emit/pynecomp/emitter.py +61 -0
  28. pine2pyne/emit/pynecomp/imports.py +65 -0
  29. pine2pyne/emit/pynecomp/passes/__init__.py +84 -0
  30. pine2pyne/emit/pynecomp/passes/annotations.py +422 -0
  31. pine2pyne/emit/pynecomp/passes/base.py +69 -0
  32. pine2pyne/emit/pynecomp/passes/builtins.py +169 -0
  33. pine2pyne/emit/pynecomp/passes/hoisting.py +205 -0
  34. pine2pyne/emit/pynecomp/passes/imports.py +112 -0
  35. pine2pyne/emit/pynecomp/passes/legacy_colors.py +101 -0
  36. pine2pyne/emit/pynecomp/passes/library.py +205 -0
  37. pine2pyne/emit/pynecomp/passes/rewrites.py +215 -0
  38. pine2pyne/emit/pynecomp/printer.py +288 -0
  39. pine2pyne/emit/pynecomp/unparser.py +186 -0
  40. pine2pyne/emitted_names.py +152 -0
  41. pine2pyne/errors.py +69 -0
  42. pine2pyne/for_loop_end_bound.py +172 -0
  43. pine2pyne/grouped_history_lowering.py +310 -0
  44. pine2pyne/import_resolver.py +469 -0
  45. pine2pyne/input_hoisting.py +195 -0
  46. pine2pyne/legacy_function_names.py +74 -0
  47. pine2pyne/legacy_sessions.py +68 -0
  48. pine2pyne/lexer.py +630 -0
  49. pine2pyne/library_collision.py +434 -0
  50. pine2pyne/namespace_members.py +247 -0
  51. pine2pyne/node_ids.py +235 -0
  52. pine2pyne/once_lowering.py +75 -0
  53. pine2pyne/overload_resolution.py +475 -0
  54. pine2pyne/parse_repair.py +398 -0
  55. pine2pyne/parser.py +1873 -0
  56. pine2pyne/pine_builtins.py +394 -0
  57. pine2pyne/pine_semantics.py +604 -0
  58. pine2pyne/pipeline/__init__.py +18 -0
  59. pine2pyne/pipeline/service.py +70 -0
  60. pine2pyne/pipeline/stages.py +73 -0
  61. pine2pyne/scope_renamer.py +965 -0
  62. pine2pyne/strategy_when_lowering.py +101 -0
  63. pine2pyne/strict_boolean.py +101 -0
  64. pine2pyne/symbol_table.py +166 -0
  65. pine2pyne/tokens.py +143 -0
  66. pine2pyne/transformer.py +2510 -0
  67. pine2pyne/type_inference.py +422 -0
  68. pine2pyne/udt_field_defaults.py +149 -0
  69. pine2pyne/value_blocks.py +357 -0
  70. pine2pyne-0.2.4.dist-info/METADATA +99 -0
  71. pine2pyne-0.2.4.dist-info/RECORD +75 -0
  72. pine2pyne-0.2.4.dist-info/WHEEL +5 -0
  73. pine2pyne-0.2.4.dist-info/entry_points.txt +3 -0
  74. pine2pyne-0.2.4.dist-info/licenses/LICENSE +201 -0
  75. pine2pyne-0.2.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,394 @@
1
+ """
2
+ Mapping of Pine Script built-in functions, variables, and constants to PyneCore equivalents.
3
+ """
4
+ from typing import Dict, FrozenSet, Set, Tuple
5
+
6
+ from .namespace_members import NAMESPACE_MEMBERS
7
+
8
+
9
+ # Functions that need to be renamed
10
+ FUNCTION_RENAMES: Dict[str, str] = {
11
+ 'array.from': 'array.from_items', # 'from' is Python keyword
12
+ }
13
+
14
+ # Module renames
15
+ MODULE_RENAMES: Dict[str, str] = {
16
+ 'str': 'string', # str is Python built-in
17
+ }
18
+
19
+ # Type renames (Pine Script -> Python/PyneCore)
20
+ TYPE_RENAMES: Dict[str, str] = {
21
+ # Drawing types
22
+ 'line': 'Line',
23
+ 'label': 'Label',
24
+ 'box': 'Box',
25
+ 'table': 'Table',
26
+ 'linefill': 'LineFill',
27
+ 'polyline': 'Polyline',
28
+ 'color': 'Color',
29
+ 'chart.point': 'ChartPoint',
30
+
31
+ # Data structure types
32
+ 'matrix': 'Matrix',
33
+
34
+ # Basic types (in type hints)
35
+ 'string': 'str',
36
+ 'int': 'int',
37
+ 'float': 'float',
38
+ 'bool': 'bool',
39
+ }
40
+
41
+ # Built-in variables that are always Series
42
+ SERIES_VARIABLES: Set[str] = {
43
+ 'open', 'high', 'low', 'close', 'volume',
44
+ 'hl2', 'hlc3', 'ohlc4', 'hlcc4',
45
+ 'bar_index', 'last_bar_index', 'last_bar_time',
46
+ 'time', 'time_close', 'time_tradingday', 'timenow',
47
+ 'dayofmonth', 'dayofweek', 'hour', 'minute', 'second', 'month', 'weekofyear', 'year',
48
+ }
49
+ # The builtin series variables that are also builtin functions, `time("D")` and `hour(t)`
50
+ # (the names both the functions and the variables of pine_reference_types.json list, card #89)
51
+ CALLABLE_SERIES_VARIABLES: Set[str] = {
52
+ 'time', 'time_close',
53
+ 'dayofmonth', 'dayofweek', 'hour', 'minute', 'second', 'month', 'weekofyear', 'year',
54
+ }
55
+
56
+ # Constants that should be preserved as-is
57
+ CONSTANTS: Set[str] = {
58
+ # Strategy constants
59
+ 'strategy.long',
60
+ 'strategy.short',
61
+ 'strategy.commission.percent',
62
+ 'strategy.commission.cash_per_contract',
63
+ 'strategy.commission.cash_per_order',
64
+ 'strategy.percent',
65
+ 'strategy.fixed',
66
+ 'strategy.cash',
67
+ 'strategy.percent_of_equity',
68
+ 'strategy.closedtrades',
69
+ 'strategy.opentrades',
70
+
71
+ # Color constants
72
+ 'color.red', 'color.green', 'color.blue', 'color.white', 'color.black',
73
+ 'color.yellow', 'color.orange', 'color.purple', 'color.gray',
74
+ 'color.silver', 'color.maroon', 'color.fuchsia', 'color.lime',
75
+ 'color.olive', 'color.navy', 'color.teal', 'color.aqua',
76
+
77
+ # Plot styles (need remapping to plot_style module)
78
+ 'plot.style_line',
79
+ 'plot.style_stepline',
80
+ 'plot.style_histogram',
81
+ 'plot.style_cross',
82
+ 'plot.style_area',
83
+ 'plot.style_columns',
84
+ 'plot.style_circles',
85
+ 'plot.style_linebr',
86
+ 'plot.style_steplinebr',
87
+
88
+ # Barstate
89
+ 'barstate.isfirst',
90
+ 'barstate.islast',
91
+ 'barstate.ishistory',
92
+ 'barstate.isrealtime',
93
+ 'barstate.isnew',
94
+ 'barstate.isconfirmed',
95
+ }
96
+
97
+ # Plot style constants need special handling
98
+ PLOT_STYLE_REMAP: Dict[str, str] = {
99
+ 'plot.style_line': 'plot_style.style_line',
100
+ 'plot.style_stepline': 'plot_style.style_stepline',
101
+ 'plot.style_histogram': 'plot_style.style_histogram',
102
+ 'plot.style_cross': 'plot_style.style_cross',
103
+ 'plot.style_area': 'plot_style.style_area',
104
+ 'plot.style_columns': 'plot_style.style_columns',
105
+ 'plot.style_circles': 'plot_style.style_circles',
106
+ 'plot.style_linebr': 'plot_style.style_linebr',
107
+ 'plot.style_steplinebr': 'plot_style.style_steplinebr',
108
+ }
109
+
110
+ # Modules that should be imported from pynecore.lib
111
+ # Synced with https://github.com/PyneSys/pynecore/tree/main/src/pynecore/lib
112
+ PYNECORE_LIB_MODULES: Set[str] = {
113
+ # Core modules (submodules as .py files in pynecore/lib/)
114
+ 'script', 'input', 'plot', 'color', 'ta', 'math', 'strategy',
115
+ 'array', 'matrix', 'map', 'string',
116
+ 'timeframe', 'runtime', 'alert', 'label', 'line', 'box', 'table',
117
+ 'linefill', 'polyline', 'log', 'barstate', 'session', 'syminfo',
118
+ 'adjustment', 'barmerge', 'chart', 'dividends', 'earnings',
119
+ # Higher-timeframe / other-symbol data access. Present in upstream PyneCore
120
+ # (lib/request.py + lib/ticker.py); without them here the transpiler emits
121
+ # request.*/ticker.* calls it never imports -> NameError at runtime.
122
+ 'request', 'ticker',
123
+ # Other upstream lib modules that were likewise never imported. Found by
124
+ # diffing pynecore/lib/*.py against this set — 'splits' is used by the
125
+ # request.splits samples, 'dayofweek' by session/date logic.
126
+ 'dayofweek', 'splits', 'footprint', 'pivotpointtype', 'volume_row',
127
+ # Standalone functions (defined in pynecore/lib/__init__.py)
128
+ 'currency', 'timestamp', 'max_bars_back',
129
+ # Plotting and coloring functions
130
+ 'bgcolor', 'barcolor', 'alertcondition', 'plotarrow', 'plotbar', 'plotcandle',
131
+ 'plotchar', 'plotshape', 'fill',
132
+ # Utility functions
133
+ 'na', 'nz', 'fixnan',
134
+ # Constant namespaces
135
+ 'xloc', 'yloc', 'position', 'size', 'shape', 'location',
136
+ 'extend', 'font', 'text', 'display', 'scale', 'hline', 'format',
137
+ 'order',
138
+ }
139
+
140
+ # Drawing and structural types that should be imported from pynecore.types
141
+ DRAWING_TYPES: Set[str] = {
142
+ 'Line', 'Label', 'Box', 'Table', 'LineFill', 'Polyline', 'Color', 'Matrix',
143
+ }
144
+
145
+ # Names a generated module may read from pynecore: a user binding of one of them is renamed
146
+ # `<name>__ren__` (PyneComp renames every such binding, whether or not the script reads the
147
+ # builtin: 270 renames in assets/pyne). pynecore.lib's modules and functions, its types, and
148
+ # the decorators and namespaces the module itself uses.
149
+ PYNECOMP_RESERVED_NAMES: FrozenSet[str] = frozenset(
150
+ PYNECORE_LIB_MODULES | DRAWING_TYPES | SERIES_VARIABLES
151
+ | {'method', 'str', 'Series', 'Persistent', 'PersistentSeries', 'NA', 'ask', 'bid', 'updatetime',
152
+ 'dayofweek', 'pivotpointtype', 'volume_row'})
153
+
154
+ # Pine's type-cast functions of the drawing types (`box(na)`); each shares its name
155
+ # with a pynecore namespace module, which is not callable (card #8)
156
+ DRAWING_CAST_FUNCTIONS: Set[str] = {'box', 'label', 'line', 'linefill', 'table'}
157
+
158
+ # Chart types that should be imported from pynecore.types.chart
159
+ CHART_TYPES: Set[str] = {
160
+ 'ChartPoint',
161
+ }
162
+
163
+ # The namespaces whose functions a Pine method call can reach: pynecore's
164
+ # `method_call('m', receiver, ...)` picks one of them from the receiver's runtime
165
+ # class (core/pine_method.py). Which of them defines a method is read from the
166
+ # GENERATED namespace_members table, never from a hand-kept list (card #8).
167
+ METHOD_NAMESPACES = ('array', 'matrix', 'map', 'line', 'box', 'label', 'table', 'linefill', 'polyline')
168
+
169
+ # Members of those namespaces that are not methods of an object: the constructors
170
+ # (`new`, `new_*`, `from_items` for Pine's array.from), `X.all`, the typed na value,
171
+ # a module's `annotations` import, and the `style_*` constants.
172
+ NON_METHOD_MEMBERS: FrozenSet[str] = frozenset({'new', 'from_items', 'all', 'na_float', 'annotations'})
173
+ NON_METHOD_PREFIXES = ('new_', 'style_')
174
+
175
+
176
+ def _namespaces_by_method() -> Dict[str, Tuple[str, ...]]:
177
+ """Method name -> the METHOD_NAMESPACES that define it, in that order."""
178
+ namespaces_by_method: Dict[str, Tuple[str, ...]] = {}
179
+ for namespace in METHOD_NAMESPACES:
180
+ for member in NAMESPACE_MEMBERS.get(namespace, ()):
181
+ if member not in NON_METHOD_MEMBERS and not member.startswith(NON_METHOD_PREFIXES):
182
+ namespaces_by_method[member] = namespaces_by_method.get(member, ()) + (namespace,)
183
+ return namespaces_by_method
184
+
185
+
186
+ NAMESPACES_BY_METHOD: Dict[str, Tuple[str, ...]] = _namespaces_by_method()
187
+
188
+
189
+ def method_namespaces(method_name: str) -> Tuple[str, ...]:
190
+ """Every method namespace that defines `method_name`; () when none does."""
191
+ return NAMESPACES_BY_METHOD.get(method_name, ())
192
+
193
+
194
+ def get_function_name(pine_func: str) -> str:
195
+ """Get the PyneCore function name for a Pine Script function."""
196
+ return FUNCTION_RENAMES.get(pine_func, pine_func)
197
+
198
+
199
+ def get_module_name(pine_module: str) -> str:
200
+ """Get the PyneCore module name for a Pine Script module."""
201
+ return MODULE_RENAMES.get(pine_module, pine_module)
202
+
203
+
204
+ def get_type_name(pine_type: str) -> str:
205
+ """Get the Python/PyneCore type name for a Pine Script type."""
206
+ # Strip .new suffix from UDT types (e.g., TradeStats.new -> TradeStats)
207
+ if '.new' in pine_type and pine_type and pine_type[0].isupper():
208
+ pine_type = pine_type.replace('.new', '')
209
+ return TYPE_RENAMES.get(pine_type, pine_type)
210
+
211
+
212
+ def is_series_variable(name: str) -> bool:
213
+ """Check if a variable is a built-in Series variable."""
214
+ return name in SERIES_VARIABLES
215
+
216
+
217
+ def is_constant(name: str) -> bool:
218
+ """Check if a name is a built-in constant."""
219
+ return name in CONSTANTS
220
+
221
+
222
+ def needs_plot_style_remap(name: str) -> bool:
223
+ """Check if a name needs plot_style module remapping."""
224
+ return name in PLOT_STYLE_REMAP
225
+
226
+
227
+ def get_plot_style_remap(name: str) -> str:
228
+ """Get the remapped plot_style constant name."""
229
+ return PLOT_STYLE_REMAP.get(name, name)
230
+
231
+
232
+ # Input functions that extract to main() parameters
233
+ INPUT_FUNCTIONS: Set[str] = {
234
+ 'input.int',
235
+ 'input.float',
236
+ 'input.bool',
237
+ 'input.string',
238
+ 'input.color',
239
+ 'input.source',
240
+ 'input.timeframe',
241
+ 'input.session',
242
+ 'input.symbol',
243
+ 'input.price',
244
+ 'input.time',
245
+ 'input.enum',
246
+ }
247
+
248
+
249
+ def is_input_function(func_name: str) -> bool:
250
+ """Check if function is an input.* function."""
251
+ return func_name in INPUT_FUNCTIONS
252
+
253
+
254
+ # Pine Script input.* positional parameter names (after defval, title).
255
+ # Pine Script allows these as positional; PyneCore requires keyword-only.
256
+ INPUT_POSITIONAL_PARAMS: Dict[str, list[str]] = {
257
+ 'input.int': ['minval', 'maxval', 'step', 'tooltip', 'inline', 'group', 'confirm', 'display', 'active'],
258
+ 'input.float': ['minval', 'maxval', 'step', 'tooltip', 'inline', 'group', 'confirm', 'display', 'active'],
259
+ 'input.bool': ['tooltip', 'inline', 'group', 'confirm', 'display'],
260
+ 'input.string': ['options', 'tooltip', 'inline', 'group', 'confirm', 'display'],
261
+ 'input.color': ['tooltip', 'inline', 'group', 'confirm', 'display'],
262
+ 'input.timeframe': ['options', 'tooltip', 'inline', 'group', 'confirm', 'display'],
263
+ 'input.session': ['options', 'tooltip', 'inline', 'group', 'confirm', 'display'],
264
+ # v6 reference: input.symbol has no options; input.source puts confirm last (#89 review)
265
+ 'input.symbol': ['tooltip', 'inline', 'group', 'confirm', 'display', 'active'],
266
+ 'input.source': ['tooltip', 'inline', 'group', 'display', 'active', 'confirm'],
267
+ 'input.price': ['tooltip', 'inline', 'group', 'confirm', 'display'],
268
+ 'input.time': ['tooltip', 'inline', 'group', 'confirm', 'display'],
269
+ 'input.enum': ['options', 'tooltip', 'inline', 'group', 'confirm', 'display', 'active'], # card #97
270
+ }
271
+
272
+ # Pine v2-v4 `input(defval, title, type=input.X)`: the keyword names the kind the v5+ `input.X` functions
273
+ # carry in their names (card #115). The key is the spelling after an optional `input.` prefix.
274
+ LEGACY_INPUT_TYPE_ARGUMENT_FUNCTIONS: Dict[str, str] = {
275
+ 'integer': 'input.int', 'float': 'input.float', 'bool': 'input.bool', 'string': 'input.string',
276
+ 'symbol': 'input.symbol', 'resolution': 'input.timeframe', 'source': 'input.source',
277
+ 'session': 'input.session', 'time': 'input.time', 'color': 'input.color', 'price': 'input.price',
278
+ }
279
+
280
+ # input.int/input.float have a second positional form after `title` (v6 reference): the options form,
281
+ # selected by a list third argument (pynecore's _bind_numeric_tail, _NUMERIC_OPTIONS_TAIL; card #95).
282
+ NUMERIC_INPUT_FUNCTIONS: FrozenSet[str] = frozenset({'input.int', 'input.float'})
283
+ NUMERIC_INPUT_OPTIONS_POSITIONAL_PARAMS: list[str] = ['options', 'tooltip', 'inline', 'group', 'confirm',
284
+ 'display', 'active']
285
+
286
+
287
+ def input_positional_param_names(func_name: str, positional_args: list) -> list[str]:
288
+ """The names Pine gives the positional arguments of an input.* call after `defval` and `title`
289
+ (empty for a function this table does not list). The one place that decides which positional
290
+ form a call uses, for the transformer and the semantic pass alike."""
291
+ from .ast_nodes import ArrayLiteral
292
+ if func_name in NUMERIC_INPUT_FUNCTIONS and len(positional_args) > 2 \
293
+ and isinstance(positional_args[2], ArrayLiteral):
294
+ return NUMERIC_INPUT_OPTIONS_POSITIONAL_PARAMS
295
+ return INPUT_POSITIONAL_PARAMS.get(func_name, [])
296
+
297
+
298
+ def bind_input_positional_args(func_name: str, positional_args: list) -> Tuple[list, dict]:
299
+ """Split an input.* call's positional arguments into `defval, title` and the rest bound by
300
+ name: PyneCore takes everything after `title` as a keyword. A positional past the last name
301
+ is dropped, as before card #95."""
302
+ names = input_positional_param_names(func_name, positional_args)
303
+ if not names or len(positional_args) <= 2:
304
+ return list(positional_args), {}
305
+ return list(positional_args[:2]), dict(zip(names, positional_args[2:]))
306
+
307
+
308
+ # Pine's positional parameter order of the script declarations (Pine v6 reference; card #55).
309
+ # The parser binds positionals to these names and codegen emits keywords, so pynecore's own
310
+ # order (its strategy inserts calc_on_every_history_tick at 9) does not matter. v4's study()
311
+ # binds as 'indicator'.
312
+ SCRIPT_DECLARATION_POSITIONAL_PARAMS: Dict[str, list[str]] = {
313
+ 'indicator': ['title', 'shorttitle', 'overlay', 'format', 'precision', 'scale', 'max_bars_back',
314
+ 'timeframe', 'timeframe_gaps', 'explicit_plot_zorder', 'max_lines_count',
315
+ 'max_labels_count', 'max_boxes_count', 'calc_bars_count', 'max_polylines_count',
316
+ 'dynamic_requests', 'behind_chart'],
317
+ 'strategy': ['title', 'shorttitle', 'overlay', 'format', 'precision', 'scale', 'pyramiding',
318
+ 'calc_on_order_fills', 'calc_on_every_tick', 'max_bars_back',
319
+ 'backtest_fill_limits_assumption', 'default_qty_type', 'default_qty_value',
320
+ 'initial_capital', 'currency', 'slippage', 'commission_type', 'commission_value',
321
+ 'process_orders_on_close', 'close_entries_rule', 'margin_long', 'margin_short',
322
+ 'explicit_plot_zorder', 'max_lines_count', 'max_labels_count', 'max_boxes_count',
323
+ 'calc_bars_count', 'risk_free_rate', 'use_bar_magnifier',
324
+ 'fill_orders_on_standard_ohlc', 'max_polylines_count', 'dynamic_requests',
325
+ 'behind_chart'],
326
+ 'library': ['title', 'overlay', 'dynamic_requests'],
327
+ }
328
+
329
+ # How many leading SCRIPT_DECLARATION_POSITIONAL_PARAMS names every Pine version shares.
330
+ # A positional past them is bound only under //@version>=5 (v4 has no margin/zorder slots).
331
+ VERSION_INVARIANT_POSITIONAL_COUNT: Dict[str, int] = {'indicator': 7, 'strategy': 9, 'library': 2}
332
+
333
+ # v4 study() keywords that indicator() renamed (Pine v5 migration guide).
334
+ STUDY_KEYWORD_RENAMES: Dict[str, str] = {'resolution': 'timeframe', 'resolution_gaps': 'timeframe_gaps'}
335
+
336
+ # Pine functions whose result is a constant when their arguments are: a script declaration
337
+ # argument may be built from them (`pyramiding = BASE + int(50 / QTY)`) and still be hoisted.
338
+ DECLARATION_CONSTANT_FUNCTIONS: Set[str] = {'int', 'float', 'bool'}
339
+ DECLARATION_CONSTANT_FUNCTION_NAMESPACES = ('math.',)
340
+ # ... except the members of those namespaces that keep state between bars or draw a random number:
341
+ # hoisted, they would run once at import (card #78).
342
+ DECLARATION_STATEFUL_FUNCTIONS: Set[str] = {'math.sum', 'math.random'}
343
+ # Builtin namespaces whose members a script declaration argument may name (`strategy.fixed`,
344
+ # `format.price`); a member of any other builtin namespace (`syminfo.ticker`) is not a constant.
345
+ DECLARATION_CONSTANT_NAMESPACES = ('format.', 'scale.', 'currency.', 'strategy.', 'display.', 'math.', 'color.')
346
+
347
+
348
+ # Python reserved words that cannot be used as function/variable names.
349
+ PYTHON_RESERVED_WORDS: Set[str] = {
350
+ 'False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await',
351
+ 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except',
352
+ 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is',
353
+ 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return',
354
+ 'try', 'while', 'with', 'yield',
355
+ }
356
+
357
+
358
+ #: The suffix PyneComp gives a Pine name that would clash in Python: a reserved word
359
+ #: (`from` -> `from__ren__`) or a name pynecore defines (`color` -> `color__ren__`)
360
+ RENAMED_SUFFIX = '__ren__'
361
+ #: PyneComp's suffix for a global named like a function, a type or a namespace the script uses (`thma__global__`)
362
+ GLOBAL_SUFFIX = '__global__'
363
+
364
+
365
+ def sanitize_identifier(name: str) -> str:
366
+ """Rename Python reserved words used as identifiers, as PyneComp does (`lambda__ren__`)."""
367
+ if name in PYTHON_RESERVED_WORDS:
368
+ return f'{name}{RENAMED_SUFFIX}'
369
+ return name
370
+
371
+
372
+ # Pine Script built-in "variable-functions": used without () in Pine Script
373
+ # but require () in PyneCore because they're implemented as functions.
374
+ # Values are the argument text of the call (all empty: each bare form takes none).
375
+ TA_VARIABLE_FUNCTIONS: Dict[str, str] = {
376
+ # No argument: pynecore's bare form reads hlc3 itself, and only a call with no
377
+ # arguments is hoisted out of a condition (card #8)
378
+ 'ta.vwap': '',
379
+ 'ta.tr': '', # no args needed
380
+ 'ta.accdist': '',
381
+ 'ta.obv': '',
382
+ 'ta.iii': '',
383
+ 'ta.nvi': '',
384
+ 'ta.pvi': '',
385
+ 'ta.pvt': '',
386
+ 'ta.wad': '',
387
+ 'ta.wvad': '',
388
+ }
389
+
390
+ # Pine variables that pynecore implements as a function returning an array: as a
391
+ # method receiver (`box.all.size()`) they become `array.size(box.all)` (card #8)
392
+ DRAWING_ALL_VARIABLES: Set[str] = {
393
+ f'{namespace}.all' for namespace in ('box', 'label', 'line', 'linefill', 'polyline', 'table')
394
+ }