pine2pyne 0.2.5__tar.gz → 0.2.6__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 (74) hide show
  1. {pine2pyne-0.2.5/pine2pyne.egg-info → pine2pyne-0.2.6}/PKG-INFO +3 -3
  2. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/__init__.py +3 -3
  3. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/ast_nodes.py +12 -12
  4. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/ast_walk.py +1 -1
  5. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/block_value_lowering.py +17 -17
  6. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/bool_builtins.py +1 -1
  7. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/bool_value_typing.py +6 -6
  8. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/cli.py +5 -5
  9. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/codegen.py +17 -17
  10. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/condition_typing.py +8 -8
  11. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/conditional_history_calls.py +3 -3
  12. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/blank_lines.py +2 -2
  13. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/imports.py +1 -1
  14. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/passes/annotations.py +4 -4
  15. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/passes/imports.py +1 -1
  16. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/passes/library.py +3 -3
  17. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/passes/rewrites.py +4 -4
  18. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/printer.py +1 -1
  19. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/unparser.py +3 -4
  20. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emitted_names.py +1 -2
  21. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/errors.py +4 -4
  22. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/for_loop_end_bound.py +3 -3
  23. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/grouped_history_lowering.py +6 -6
  24. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/import_resolver.py +17 -17
  25. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/legacy_function_names.py +2 -2
  26. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/legacy_sessions.py +1 -1
  27. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/lexer.py +4 -4
  28. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/library_collision.py +9 -9
  29. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/namespace_members.py +1 -1
  30. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/node_ids.py +1 -1
  31. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/once_lowering.py +1 -2
  32. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/overload_resolution.py +5 -5
  33. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/parse_repair.py +2 -2
  34. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/parser.py +34 -34
  35. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/pine_builtins.py +13 -13
  36. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/pine_semantics.py +9 -9
  37. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/pipeline/stages.py +4 -4
  38. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/scope_renamer.py +30 -31
  39. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/strategy_when_lowering.py +2 -2
  40. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/strict_boolean.py +1 -1
  41. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/tokens.py +2 -2
  42. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/transformer.py +86 -87
  43. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/type_inference.py +3 -3
  44. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/udt_field_defaults.py +7 -8
  45. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/value_blocks.py +5 -5
  46. {pine2pyne-0.2.5 → pine2pyne-0.2.6/pine2pyne.egg-info}/PKG-INFO +3 -3
  47. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pyproject.toml +11 -12
  48. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/LICENSE +0 -0
  49. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/MANIFEST.in +0 -0
  50. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/README.md +0 -0
  51. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/__main__.py +0 -0
  52. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/core/__init__.py +0 -0
  53. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/core/contracts.py +0 -0
  54. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/di.py +0 -0
  55. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/__init__.py +0 -0
  56. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/legacy.py +0 -0
  57. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/__init__.py +0 -0
  58. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/banner.py +0 -0
  59. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/emitter.py +0 -0
  60. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/passes/__init__.py +0 -0
  61. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/passes/base.py +0 -0
  62. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/passes/builtins.py +0 -0
  63. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/passes/hoisting.py +0 -0
  64. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/emit/pynecomp/passes/legacy_colors.py +0 -0
  65. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/input_hoisting.py +0 -0
  66. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/pipeline/__init__.py +0 -0
  67. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/pipeline/service.py +0 -0
  68. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne/symbol_table.py +0 -0
  69. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne.egg-info/SOURCES.txt +0 -0
  70. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne.egg-info/dependency_links.txt +0 -0
  71. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne.egg-info/entry_points.txt +0 -0
  72. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne.egg-info/requires.txt +0 -0
  73. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/pine2pyne.egg-info/top_level.txt +0 -0
  74. {pine2pyne-0.2.5 → pine2pyne-0.2.6}/setup.cfg +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pine2pyne
3
- Version: 0.2.5
4
- Summary: Pine Script v6 to PyneCore Python transpiler
5
- Author: rubycell
3
+ Version: 0.2.6
4
+ Summary: Pine Script v4, v5 and v6 to PyneCore Python transpiler
5
+ Author: tripolskypetr
6
6
  License-Expression: Apache-2.0
7
7
  Project-URL: Homepage, https://github.com/pine2pyne/pine2pyne
8
8
  Project-URL: Repository, https://github.com/pine2pyne/pine2pyne
@@ -1,11 +1,11 @@
1
1
  """
2
- pine2pyne: Pine Script v6 to PyneCore Python transpiler.
2
+ pine2pyne: Pine Script v4, v5 and v6 to PyneCore Python transpiler.
3
3
 
4
4
  A tool to convert TradingView Pine Script indicators and strategies
5
5
  to PyneCore Python code for local backtesting and analysis.
6
6
  """
7
7
 
8
- __version__ = '0.2.5'
8
+ __version__ = '0.2.6'
9
9
  __author__ = 'Pine2Pyne Transpiler'
10
10
 
11
11
  from .lexer import Lexer, tokenize
@@ -48,7 +48,7 @@ def transpile(source: str, profile: 'OutputProfile | str | None' = None) -> str:
48
48
  * ``'legacy'`` - the historical pine2pyne layout, kept as a reference.
49
49
 
50
50
  Args:
51
- source: Pine Script v6 source code
51
+ source: Pine Script v4, v5 or v6 source code
52
52
  profile: output profile (an :class:`OutputProfile` or its name)
53
53
 
54
54
  Returns:
@@ -1,5 +1,5 @@
1
1
  """
2
- AST (Abstract Syntax Tree) node definitions for Pine Script v6.
2
+ AST (Abstract Syntax Tree) node definitions for Pine Script v4, v5 and v6.
3
3
 
4
4
  All node types used by the parser to represent Pine Script syntax.
5
5
  """
@@ -127,9 +127,9 @@ class FuncDecl(ASTNode):
127
127
  return_type: Optional[str] = None
128
128
  is_method: bool = False
129
129
  is_export: bool = False
130
- is_overload: bool = False # One of several same-name declarations (card #39)
130
+ is_overload: bool = False # One of several same-name declarations
131
131
  indexed_params: set = field(default_factory=set) # Params that use [n] history access
132
- value_is_bool: bool = False # Pine v6: its value is of type bool (card #56)
132
+ value_is_bool: bool = False # Pine v6: its value is of type bool
133
133
 
134
134
 
135
135
  @dataclass
@@ -238,7 +238,7 @@ class Identifier(Expression):
238
238
  class NamedExpression(Expression):
239
239
  """Transformer output only: binds `name` to `value` and is that value
240
240
  (Python `(name := value)`). A switch subject is bound this way in the
241
- first case test, so it is evaluated once, where Pine evaluates it (card #43)."""
241
+ first case test, so it is evaluated once, where Pine evaluates it."""
242
242
  name: str = ""
243
243
  value: Expression = None
244
244
 
@@ -263,7 +263,7 @@ class IfExpression(Expression):
263
263
  condition: Expression = None
264
264
  true_expr: Union[Expression, List['Statement']] = None
265
265
  false_expr: Optional[Union[Expression, List['Statement']]] = None
266
- value_is_bool: bool = False # Pine v6: its value is of type bool (card #56)
266
+ value_is_bool: bool = False # Pine v6: its value is of type bool
267
267
  chained: bool = False # written `else if` after the if before it (not an `if` inside an `else` block)
268
268
 
269
269
 
@@ -273,7 +273,7 @@ class SwitchExpression(Expression):
273
273
  expr: Optional[Expression] = None
274
274
  cases: List[tuple[Expression, Union[Expression, List['Statement']]]] = field(default_factory=list)
275
275
  default: Optional[Union[Expression, List['Statement']]] = None
276
- value_is_bool: bool = False # Pine v6: its value is of type bool (card #56)
276
+ value_is_bool: bool = False # Pine v6: its value is of type bool
277
277
 
278
278
 
279
279
  # ============================================================================
@@ -319,12 +319,12 @@ class ForLoop(Statement):
319
319
  to_val: Expression = None
320
320
  step: Optional[Expression] = None
321
321
  body: List[Statement] = field(default_factory=list)
322
- value_is_bool: bool = False # Pine v6: its value is of type bool (card #56)
322
+ value_is_bool: bool = False # Pine v6: its value is of type bool
323
323
  # Pine v6, an end bound that can change between passes: the name of the runtime
324
- # counter (`pine_loop`) that re-reads it; empty for a fixed `pine_range` (card #82)
324
+ # counter (`pine_loop`) that re-reads it; empty for a fixed `pine_range`
325
325
  counter_name: str = ""
326
326
  counter_helper: str = "" # the name `pine_loop` is imported under (an alias when the script uses the name)
327
- range_helper: str = "" # the name `pine_range` is imported under for a fixed loop; empty is `pine_range` (card #94)
327
+ range_helper: str = "" # the name `pine_range` is imported under for a fixed loop; empty is `pine_range`
328
328
  # the value of an assignment (`x = for ...`): PyneComp writes no default step for it, even in Pine v4/v5
329
329
  is_assigned_value: bool = False
330
330
 
@@ -335,7 +335,7 @@ class ForInLoop(Statement):
335
335
  vars: List[str] = field(default_factory=list)
336
336
  iterable: Expression = None
337
337
  body: List[Statement] = field(default_factory=list)
338
- value_is_bool: bool = False # Pine v6: its value is of type bool (card #56)
338
+ value_is_bool: bool = False # Pine v6: its value is of type bool
339
339
 
340
340
 
341
341
  @dataclass
@@ -343,7 +343,7 @@ class WhileLoop(Statement):
343
343
  """While loop: while condition\n body"""
344
344
  condition: Expression = None
345
345
  body: List[Statement] = field(default_factory=list)
346
- value_is_bool: bool = False # Pine v6: its value is of type bool (card #56)
346
+ value_is_bool: bool = False # Pine v6: its value is of type bool
347
347
 
348
348
 
349
349
  @dataclass
@@ -357,7 +357,7 @@ class SwitchStatement(Statement):
357
357
  @dataclass
358
358
  class OnceStatement(Statement):
359
359
  """Pine v6 once structure: once [cond]\n body. The parser lowers it to
360
- a `var` flag and an `if` (once_lowering.py, card #59); no later stage sees it."""
360
+ a `var` flag and an `if` (once_lowering.py); no later stage sees it."""
361
361
  condition: Optional[Expression] = None
362
362
  body: List[Statement] = field(default_factory=list)
363
363
 
@@ -5,7 +5,7 @@ A Pine AST node is a dataclass; its children sit in its fields, directly or
5
5
  inside lists, tuples (the arms of an else-if, the cases of a switch) and dicts
6
6
  (the keyword arguments of a call). A walk that forgets one of these containers
7
7
  silently misses what is inside it: a history index in a keyword argument or in
8
- an else-if arm was not seen (cards #32, #44). Every generic walk goes through
8
+ an else-if arm was not seen. Every generic walk goes through
9
9
  `walk` (to find nodes) or `map_children` (to rebuild them), so the containers
10
10
  are decided in one place.
11
11
 
@@ -11,7 +11,7 @@ they run when the branch is taken, and its last expression is the value:
11
11
  else
12
12
  0.0
13
13
 
14
- A Python conditional expression cannot hold statements (card #41: the branch
14
+ A Python conditional expression cannot hold statements (the branch
15
15
  was emitted as `None`). This pre-pass rewrites every such value into the idiom
16
16
  Pine authors write by hand, which the transformer already emits correctly:
17
17
 
@@ -25,7 +25,7 @@ Pine authors write by hand, which the transformer already emits correctly:
25
25
  A block-valued switch becomes an if / else-if chain over a temporary that holds
26
26
  the subject, so the subject is evaluated once. `var`/`varip` targets run the
27
27
  lowered statements once, behind a flag. A branch that is a single binding
28
- (`k := close`) is a block too: its binding runs, and its value is k (card #50).
28
+ (`k := close`) is a block too: its binding runs, and its value is k.
29
29
 
30
30
  A loop used as a value (`s = for …`, `s = while …`, a branch or a function tail
31
31
  that ends in a loop) yields what its body's tail gave on the last iteration
@@ -40,7 +40,7 @@ written to a fresh local, so the body still reads the target's old value:
40
40
 
41
41
  An arm that does not run (an if without else) yields false for a bool value
42
42
  and na otherwise; a tuple-returning one yields a tuple of na as wide as its
43
- tuples (cards #50, #54). `s op= <block>` reads s, then runs the block into a
43
+ tuples. `s op= <block>` reads s, then runs the block into a
44
44
  fresh local (premise P1: Pine's operand order is UNVERIFIED on TradingView).
45
45
  `[a, b] = <if/switch/loop>` fills a fresh local that starts as a tuple of na,
46
46
  destructured once. A block value anywhere else (an operand of an operator, a
@@ -80,7 +80,7 @@ Write = Callable[[Expression], object] # the statement that takes a value
80
80
  def lower_block_values(script: Script) -> Script:
81
81
  """Return a copy of `script` with every block value lowered to statements.
82
82
  A script without one is returned unchanged. An untaken bool arm is false
83
- only where `stamp_bool_values` stamped its value first (card #56)."""
83
+ only where `stamp_bool_values` stamped its value first."""
84
84
  if not _needs_lowering_somewhere(script):
85
85
  return script
86
86
  return _BlockValueLowering(collect_names(script), _UserFunctions.of(script),
@@ -107,7 +107,7 @@ def _needs_lowering_somewhere(script: Script) -> bool:
107
107
  accepts a function in a block. Other such functions are left to the
108
108
  transformer, so they do not cost this pass a copy of the script. This runs
109
109
  on every node of every script, hence the dispatch on the node's type
110
- (card #50, transpile-time gate)."""
110
+ (transpile-time gate)."""
111
111
  untaken_tails = []
112
112
  for node in walk(script):
113
113
  if type(node) in CHILDLESS_NODE_TYPES:
@@ -130,7 +130,7 @@ def _needs_lowering_somewhere(script: Script) -> bool:
130
130
  def _has_loop_operand(node) -> bool:
131
131
  """True when a loop is a direct child of `node`: a bound value, an operand.
132
132
  Runs on every expression of every script, so it reads the fields once and
133
- builds no lists (card #50, transpile-time gate)."""
133
+ builds no lists (transpile-time gate)."""
134
134
  for name in child_field_names(type(node)):
135
135
  child = getattr(node, name)
136
136
  if isinstance(child, LOOPS) or (type(child) is list and any(isinstance(item, LOOPS) for item in child)):
@@ -154,7 +154,7 @@ def has_block_branch(expression) -> bool:
154
154
  tail, is a block: several statements, a sole binding, or a tail statement
155
155
  with no value. A default-only switch with a subject counts too: its
156
156
  subject runs as a statement before the default, so its side effect is
157
- kept (card #43)."""
157
+ kept."""
158
158
  if not isinstance(expression, VALUE_EXPRESSIONS):
159
159
  return False
160
160
  if isinstance(expression, SwitchExpression) and not expression.cases and expression.expr is not None:
@@ -164,7 +164,7 @@ def has_block_branch(expression) -> bool:
164
164
  return True # an inline switch case `0 => for …` keeps its loop bare
165
165
  if isinstance(branch, list) and branch and (len(branch) > 1
166
166
  or isinstance(branch[-1], VALUELESS_TAILS + BINDINGS)):
167
- # a sole binding `k := close` is a block: the binding must run (card #50)
167
+ # a sole binding `k := close` is a block: the binding must run
168
168
  return True
169
169
  tail = _tail_expression(branch)
170
170
  if has_block_branch(tail):
@@ -262,8 +262,8 @@ def _is_bool_value(value) -> bool:
262
262
  def yields_bool(value) -> bool:
263
263
  """True when an arm of `value` yields a bool by its syntax. Only the slot of
264
264
  a loop that runs no iteration reads it: Pine's guide gives false to an
265
- untaken if/switch arm, which the bool typing decides (card #56), and says
266
- nothing of a loop, so that slot stays as #50 left it (UNVERIFIED)."""
265
+ untaken if/switch arm, which the bool typing decides, and says
266
+ nothing of a loop, so that slot stays as it is (UNVERIFIED)."""
267
267
  return any(_is_bool_value(tail) for tail in _value_tails(value))
268
268
 
269
269
 
@@ -280,8 +280,8 @@ THREE_STATE_BOOL_VERSIONS = ('4', '5')
280
280
 
281
281
  def keeps_three_state_bool(script: Script) -> bool:
282
282
  """True for a Pine v4 or v5 script: its bool may be na (`bool b = na`, a bool history before
283
- warm-up), so its decorator sets the runtime's `na_bool` switch (card #99, item 5; MEASURED on
284
- TradingView 2026-10-06, probe6). A script with no version annotation is v6, as in `bool_is_never_na`."""
283
+ warm-up), so its decorator sets the runtime's `na_bool` switch (MEASURED on
284
+ TradingView 2026-10-06). A script with no version annotation is v6, as in `bool_is_never_na`."""
285
285
  return bool(script.version) and script.version.version in THREE_STATE_BOOL_VERSIONS
286
286
 
287
287
 
@@ -391,7 +391,7 @@ class _BlockValueLowering:
391
391
 
392
392
  def untaken_of(self, value, value_is_bool: bool) -> _Untaken:
393
393
  """`value_is_bool` is the stamp of the if/switch/loop, or of the
394
- function, whose value `value` is (bool_value_typing, card #56)."""
394
+ function, whose value `value` is (bool_value_typing)."""
395
395
  return _Untaken(tuple_width=_tuple_width(value, self.functions), is_bool=value_is_bool,
396
396
  never_run_is_bool=self.bool_is_never_na and yields_bool(value))
397
397
 
@@ -575,7 +575,7 @@ class _BlockValueLowering:
575
575
  def compound_statement(self, node: Reassignment) -> list:
576
576
  """`s op= <block>`: s is read first, then the block runs into a fresh
577
577
  local, then `s := <s as read> op <block value>`. Reading s first is
578
- premise P1, UNVERIFIED on TradingView (card #51)."""
578
+ premise P1, UNVERIFIED on TradingView."""
579
579
  operation = node.value
580
580
  left_operand = fresh_name('left_operand', self.used_names)
581
581
  block_value = fresh_name('block_value', self.used_names)
@@ -590,7 +590,7 @@ class _BlockValueLowering:
590
590
  def tuple_statement(self, node: Assignment) -> list:
591
591
  """`[a, b] = <if/switch/loop>`: the value goes to a fresh local that
592
592
  starts as a tuple of na as wide as the target, as does an arm that
593
- does not run; the target destructures it once (card #50)."""
593
+ does not run; the target destructures it once."""
594
594
  names = list(node.target.names)
595
595
  untaken = _Untaken(tuple_width=len(names))
596
596
  slot = fresh_name('tuple_value', self.used_names)
@@ -630,7 +630,7 @@ class _BlockValueLowering:
630
630
  line=self.line)])
631
631
  if node.body and self.tail_is_lowered(node):
632
632
  # every arm of the tail ends in the expression it yields, which the
633
- # transformer's tail walk returns (cards #53, #54)
633
+ # transformer's tail walk returns
634
634
  *leading, tail = node.body
635
635
  lowered_leading = self.statements(leading)
636
636
  enclosing_line, self.line = self.line, tail.line or self.line
@@ -645,7 +645,7 @@ class _BlockValueLowering:
645
645
  def tail_is_lowered(self, node: FuncDecl) -> bool:
646
646
  """A tail the transformer cannot return by itself: one that reaches a
647
647
  loop, or a tuple-returning if/switch with an arm that may not run,
648
- which yields a tuple of na (card #54). A function the script
648
+ which yields a tuple of na. A function the script
649
649
  destructures, whose tuple width is unknown, is refused."""
650
650
  if _tail_reaches_loop(node.body):
651
651
  return True
@@ -4,7 +4,7 @@ each builtin series, as the runtime annotates them.
4
4
 
5
5
  GENERATED by tools/collect_bool_builtins.py from opencode-pyneruntime 6.9.4; do not
6
6
  edit by hand. In Pine v6 an untaken if/switch whose value is of type bool is
7
- false, not na; bool_value_typing reads these to type a builtin (card #56).
7
+ false, not na; bool_value_typing reads these to type a builtin.
8
8
  """
9
9
  from typing import Dict, FrozenSet
10
10
 
@@ -1,5 +1,5 @@
1
1
  """
2
- Pine v6 bool typing of the values an untaken arm can default (card #56).
2
+ Pine v6 bool typing of the values an untaken arm can default.
3
3
 
4
4
  In Pine v6 an if/switch whose value is of TYPE bool gives false for an arm
5
5
  that does not run, not na (migration guide to v6). The type comes from the
@@ -67,12 +67,12 @@ def reads_na_of_bool(script: Script) -> bool:
67
67
  """Whether a named bool value is read where it may be na: with history (`b[1]`) or
68
68
  through `na(b)`. Pine v5 marks only such a script `na_bool=True`; the history of an
69
69
  expression (`(a > b)[1]`) or of an untyped parameter does not count. Measured with
70
- probe scripts compiled by PyneComp v6.0.80; 82 of the 327 v5 scripts of the reference."""
70
+ scripts compiled by PyneComp v6.0.80; 82 of the 327 v5 scripts of the reference."""
71
71
  typing = _BoolTyping(script)
72
72
  for name, scope in typing.na_calls:
73
73
  binding = scope.lookup(name)
74
74
  if binding is not None and _is_bool_collection(binding.declared_type):
75
- return True # `na(a)` of an `array<bool>`, not its history: any Pine version (probes nb6_02, nb7_*)
75
+ return True # `na(a)` of an `array<bool>`, not its history: any Pine version
76
76
  if _reads_na_of_a_bool_collection_argument(script):
77
77
  return True
78
78
  if _declared_version(script) != 5:
@@ -98,7 +98,7 @@ def _is_bool_collection(declared_type) -> bool:
98
98
 
99
99
  def _reads_na_of_a_bool_collection_argument(script: Script) -> bool:
100
100
  """`is_empty(arr) => na(arr) or ...` called with an `array<bool>`: the untyped parameter is a bool
101
- collection where it is called with one (probe bis_isEmpty, PyneComp v6.0.80)."""
101
+ collection where it is called with one (PyneComp v6.0.80)."""
102
102
  from .ast_walk import walk
103
103
  from .ast_nodes import MethodCall, Parameter
104
104
  nodes = list(walk(script))
@@ -133,7 +133,7 @@ def _declared_version(script: Script) -> int:
133
133
 
134
134
  def has_untaken_bool_switch(script: Script) -> bool:
135
135
  """Whether a `switch` without a default arm gives a bool: an untaken switch is na, so a Pine v5
136
- script that has one is `na_bool=True` (probes nb3_02..04, PyneComp v6.0.80). A switch used as
136
+ script that has one is `na_bool=True` (PyneComp v6.0.80). A switch used as
137
137
  a value or as a statement whose arms end with a bool (`x := true`) counts; an `if` does not."""
138
138
  switches = [node for node in walk_nodes(script)
139
139
  if isinstance(node, (SwitchStatement, SwitchExpression)) and node.default is None and node.cases]
@@ -254,7 +254,7 @@ class _BoolTyping:
254
254
  # ------------------------------------------------------------ the walk
255
255
  def _walk(self, script: Script) -> None:
256
256
  """Visit every node once, giving each the scope it is in. Every file is
257
- walked, so the loop makes no call per plain node (card #56 cost gate)."""
257
+ walked, so the loop makes no call per plain node."""
258
258
  pending = [(script, BlockScope(), None)]
259
259
  fields_by_type: Dict[type, tuple] = {}
260
260
  while pending:
@@ -15,7 +15,7 @@ def parse_args(args: Optional[List[str]] = None) -> argparse.Namespace:
15
15
  """Parse command-line arguments."""
16
16
  parser = argparse.ArgumentParser(
17
17
  prog='pine2pyne',
18
- description='Transpile Pine Script v6 to PyneCore Python',
18
+ description='Transpile Pine Script v4, v5 and v6 to PyneCore Python',
19
19
  formatter_class=argparse.RawDescriptionHelpFormatter,
20
20
  epilog='''
21
21
  Examples:
@@ -76,7 +76,7 @@ Examples:
76
76
 
77
77
  def reporting_semantic_warnings(input_path: Path, action: Callable[[], object]):
78
78
  """Run `action`, printing each SemanticWarning it raises as
79
- `⚠ <file>:<line>:<column>: <message> [<code>]` (card #28). The warning
79
+ `⚠ <file>:<line>:<column>: <message> [<code>]`. The warning
80
80
  filters stay the user's: a fresh catch_warnings resets Python's
81
81
  once-per-location record, so every file reports its own warnings, and
82
82
  `-W ignore::pine2pyne.errors.SemanticWarning` silences them. A filter that
@@ -112,8 +112,8 @@ def transpile_file(input_path: Path, output_path: Optional[Path], validate_only:
112
112
  source = input_path.read_text(encoding='utf-8')
113
113
 
114
114
  if validate_only:
115
- # Only lex and parse to validate syntax, with transpile()'s repairs (card #55),
116
- # and refuse what transpile() refuses (card #28)
115
+ # Only lex and parse to validate syntax, with transpile()'s repairs,
116
+ # and refuse what transpile() refuses
117
117
  from .di import get_transpiler
118
118
 
119
119
  if verbose:
@@ -161,7 +161,7 @@ def transpile_file(input_path: Path, output_path: Optional[Path], validate_only:
161
161
 
162
162
  except Pine2PyneError as e:
163
163
  print(f'✗ {input_path}: {e}', file=sys.stderr)
164
- # A repaired source that still fails: the first error above, then where it stopped (card #55)
164
+ # A repaired source that still fails: the first error above, then where it stopped
165
165
  cause = e.__cause__
166
166
  while isinstance(cause, Pine2PyneError):
167
167
  print(f' after repairing it as valid Pine, the source stops at: {cause}', file=sys.stderr)
@@ -37,11 +37,11 @@ class CodeGenerator:
37
37
  self.output: List[str] = []
38
38
  # The Pine line of the top-level statement being emitted. The parser
39
39
  # stamps only top-level statements, so a refusal of a nested node
40
- # reports this line (card #40)
40
+ # reports this line
41
41
  self.top_level_line = 0
42
- # Pine v5 and earlier evaluate both operands of and/or (card #65)
42
+ # Pine v5 and earlier evaluate both operands of and/or
43
43
  self.strict_boolean_operators = False
44
- # Pine v6 gives a UDT bool field without a default false; before v6 it is na (card #76)
44
+ # Pine v6 gives a UDT bool field without a default false; before v6 it is na
45
45
  self.bool_is_never_na = True
46
46
  self.user_function_names: frozenset = frozenset()
47
47
  # Output line index -> the Pine line its statement came from. A layout
@@ -119,7 +119,7 @@ class CodeGenerator:
119
119
  if script.imports or uses_any:
120
120
  self.emit('')
121
121
 
122
- # 3-5. Module prelude in three phases (card #78): enum classes, then the module
122
+ # 3-5. Module prelude in three phases: enum classes, then the module
123
123
  # constants (they may read an enum member), then the UDT classes (a field default may
124
124
  # read an enum member, and cannot read a constant)
125
125
  # Enum declarations
@@ -145,7 +145,7 @@ class CodeGenerator:
145
145
  self.generate_type_declaration(type_decl)
146
146
  self.emit('')
147
147
 
148
- # A library's export surface: one Exported proxy per exported name, published in __all__ (card #111).
148
+ # A library's export surface: one Exported proxy per exported name, published in __all__.
149
149
  # pynecore's `@export` resolves the proxy by name in this module's globals
150
150
  exported_names = list(dict.fromkeys(
151
151
  statement.name for statement in script.main_body if isinstance(statement, FuncDecl) and statement.is_export))
@@ -237,7 +237,7 @@ class CodeGenerator:
237
237
  prev_source_line = source_line
238
238
 
239
239
  # A script with no executable statement (a type-only or comment-only
240
- # Pine script) still needs a statement under `def main():` (card #15)
240
+ # Pine script) still needs a statement under `def main():`
241
241
  if len(self.output) == main_body_start:
242
242
  self.emit('pass')
243
243
 
@@ -271,7 +271,7 @@ class CodeGenerator:
271
271
  def generate_decorator(self, decl: Union[IndicatorDecl, StrategyDecl, LibraryDecl]) -> str:
272
272
  """Generate @script.indicator, @script.strategy or @script.library decorator."""
273
273
  name = {IndicatorDecl: 'indicator', LibraryDecl: 'library'}.get(type(decl), 'strategy')
274
- # A None title is an expression, emitted as the 'title' keyword (card #55)
274
+ # A None title is an expression, emitted as the 'title' keyword
275
275
  arguments = [] if decl.title is None else [f"'{self._escape_string(decl.title, chr(39))}'"]
276
276
  arguments += [f'{key}={self.generate_decorator_value(value)}' for key, value in decl.kwargs.items()]
277
277
  return f"@script.{name}({', '.join(arguments)})"
@@ -333,7 +333,7 @@ class CodeGenerator:
333
333
  self.indent_level += 1
334
334
 
335
335
  # Generate fields: a series built-in default is read per instance, a
336
- # field without a default is na (card #17, udt_field_defaults)
336
+ # field without a default is na (udt_field_defaults)
337
337
  lines = list(getattr(type_decl, 'field_lines', ()) or ())
338
338
  for index, (field_name, field_type, default_value) in enumerate(type_decl.fields):
339
339
  if getattr(self, '_keeps_field_gaps', False) and 0 < index < len(lines) and lines[index] - lines[index - 1] > 1:
@@ -388,10 +388,10 @@ class CodeGenerator:
388
388
  # Emit @method decorator if this is a method
389
389
  if hasattr(func, 'is_method') and func.is_method:
390
390
  self.emit('@method')
391
- # A library's exported function (card #111); pynecore's @export goes above @overload
391
+ # A library's exported function; pynecore's @export goes above @overload
392
392
  if func.is_export:
393
393
  self.emit('@export')
394
- # Same-name declarations share one pynecore @overload dispatcher (card #39)
394
+ # Same-name declarations share one pynecore @overload dispatcher
395
395
  if func.is_overload:
396
396
  self.emit('@overload')
397
397
 
@@ -480,7 +480,7 @@ class CodeGenerator:
480
480
  self.emit(line)
481
481
  else:
482
482
  # Refuse rather than drop the statement: a comment in its place
483
- # changes what the program computes, or leaves a block empty (card #40)
483
+ # changes what the program computes, or leaves a block empty
484
484
  raise CodeGenError(f'Unsupported statement type: {type(stmt).__name__}',
485
485
  line=self.refusal_line(stmt))
486
486
 
@@ -559,7 +559,7 @@ class CodeGenerator:
559
559
 
560
560
  def generate_for_loop(self, stmt: ForLoop) -> None:
561
561
  """Generate for loop with pine_range(), or with pine_loop() when the end
562
- bound is re-read on every pass (Pine v6, card #82)."""
562
+ bound is re-read on every pass (Pine v6)."""
563
563
  # Convert Pine 'for i = from to to_val [by step]' to Python pine_range()
564
564
  from_val = self.generate_expression(stmt.from_val)
565
565
  to_val = self.generate_expression(stmt.to_val)
@@ -596,7 +596,7 @@ class CodeGenerator:
596
596
  iterable = self.generate_expression(stmt.iterable)
597
597
 
598
598
  # `for [k, v] in x` reaches here with its pairs already chosen by the
599
- # transformer: enumerate(), a map's keys, or a run-time choice (card #17)
599
+ # transformer: enumerate(), a map's keys, or a run-time choice
600
600
  self.emit(f'for {vars_str} in {iterable}:')
601
601
 
602
602
  self.indent_level += 1
@@ -652,7 +652,7 @@ class CodeGenerator:
652
652
  elif isinstance(expr, NaLiteral):
653
653
  return 'na'
654
654
  else:
655
- # A comment in expression position is not Python; refuse (card #40)
655
+ # A comment in expression position is not Python; refuse
656
656
  raise CodeGenError(f'Unsupported expression type: {type(expr).__name__}',
657
657
  line=self.refusal_line(expr))
658
658
 
@@ -694,7 +694,7 @@ class CodeGenerator:
694
694
  return f'{left} {expr.op} {right}'
695
695
 
696
696
  def _generate_strict_boolean(self, operator: str, operands: List[Expression]) -> str:
697
- """`all((a, b))` / `any((a, b))`: the tuple evaluates every operand, in order (card #65)."""
697
+ """`all((a, b))` / `any((a, b))`: the tuple evaluates every operand, in order."""
698
698
  function = 'all' if operator == 'and' else 'any'
699
699
  return f'{function}(({", ".join(self.generate_expression(operand) for operand in operands)}))'
700
700
 
@@ -705,7 +705,7 @@ class CodeGenerator:
705
705
  The parser keeps no parenthesis node, so grouping exists only in the
706
706
  tree shape and every operator-precedence parenthesis between binary
707
707
  and unary operators is re-derived here (index and member access on a
708
- grouped object are not: card #32).
708
+ grouped object are not).
709
709
  """
710
710
  if isinstance(operand, UnaryOp):
711
711
  # Pine `not` binds tightest; Python `not` binds looser than any
@@ -793,7 +793,7 @@ class CodeGenerator:
793
793
 
794
794
  def generate_method_call(self, call: MethodCall) -> str:
795
795
  """Generate method call. The transformer routes every builtin method to its
796
- namespace (card #8), so what reaches here is a Python method call."""
796
+ namespace, so what reaches here is a Python method call."""
797
797
  from .ast_nodes import BinaryOp, UnaryOp, TernaryOp, IfExpression
798
798
 
799
799
  obj = self.generate_expression(call.object)
@@ -1,8 +1,8 @@
1
1
  """Which values PyneComp reads as numbers where Pine wants a condition.
2
2
 
3
3
  A number in a condition is cast: ``if x`` is ``if cast_bool(x):``. PyneComp
4
- decides it from what it knows of the value's type; probe scripts compiled by
5
- PyneComp v6.0.80 (``tmp/probes/cb4..6.pine``) and the reference show what that is.
4
+ decides it from what it knows of the value's type; scripts compiled by
5
+ PyneComp v6.0.80 and its reference output show what that is.
6
6
 
7
7
  A *name* (or its history, or a literal) is typed from its declaration, at any
8
8
  distance: a number literal, a built-in price or time series (``close``,
@@ -51,7 +51,7 @@ COMPARISONS = frozenset({'==', '!=', '<', '>', '<=', '>=', 'and', 'or'})
51
51
  _DEPTH = 32
52
52
  #: a name declared without a type PyneComp reads: a parameter or a tuple element
53
53
  _UNTYPED = object()
54
- #: a parameter declared without a type: beside it, an `and`/`or` operand is not cast (probes cb16, cb17)
54
+ #: a parameter declared without a type: beside it, an `and`/`or` operand is not cast
55
55
  _UNTYPED_PARAMETER = object()
56
56
 
57
57
 
@@ -81,7 +81,7 @@ class ConditionTyping:
81
81
  functions = [node for node in walk(script) if isinstance(node, FuncDecl)]
82
82
  inside = set()
83
83
  for function in functions:
84
- # a typed parameter is its type (`b = vx * 2` with `float vx` is a number, probe cb13)
84
+ # a typed parameter is its type (`b = vx * 2` with `float vx` is a number)
85
85
  values: Dict[str, object] = {parameter.name: ((parameter.type_hint, NaLiteral())
86
86
  if parameter.type_hint in ('int', 'float', 'bool')
87
87
  else _UNTYPED_PARAMETER if not parameter.type_hint
@@ -103,7 +103,7 @@ class ConditionTyping:
103
103
  if isinstance(node, Assignment) and isinstance(node.target, str):
104
104
  values.setdefault(node.target, (node.type_hint, node.value))
105
105
  elif isinstance(node, Assignment) and hasattr(node.target, 'names'):
106
- for name in node.target.names: # `[a, b] = f()`: untyped (probe-checked on mZigzag)
106
+ for name in node.target.names: # `[a, b] = f()`: untyped
107
107
  values.setdefault(name, _UNTYPED)
108
108
  elif isinstance(node, (VarDecl, VaripDecl)):
109
109
  values.setdefault(node.name, (node.type_hint, node.value))
@@ -139,7 +139,7 @@ class ConditionTyping:
139
139
  return False
140
140
 
141
141
  def has_a_type(self, expression) -> bool:
142
- """False for a parameter, beside which PyneComp casts no `and`/`or` operand (probes cb16, cb17)."""
142
+ """False for a parameter, beside which PyneComp casts no `and`/`or` operand."""
143
143
  return not (isinstance(expression, Identifier) and expression.name in self._parameters)
144
144
 
145
145
  def is_bool(self, expression) -> bool:
@@ -241,14 +241,14 @@ class ConditionTyping:
241
241
  return None # PyneComp does not type a user function's result in an if/while header
242
242
  if self._in_function:
243
243
  # inside a function PyneComp reads no function's result type: a call is a number when
244
- # an argument is arithmetic (`f(x, -1.0)`, `f(x, 0 - 1)`), else untyped (probes cb10..cb12)
244
+ # an argument is arithmetic (`f(x, -1.0)`, `f(x, 0 - 1)`), else untyped
245
245
  arguments = list(call.args) + list(call.kwargs.values())
246
246
  return 'number' if any(_is_arithmetic(argument) for argument in arguments) else None
247
247
  declaration = self._functions[function]
248
248
  body = declaration.body
249
249
  tail = body[-1] if isinstance(body, list) and body else body
250
250
  tail = getattr(tail, 'expr', tail)
251
- # the tail reads the callee's names: its typed parameters are their type (probe cb8, `v * 2`)
251
+ # the tail reads the callee's names: its typed parameters are their type (`v * 2`)
252
252
  outer = self._values
253
253
  self._values = {**self._global_values, **self._function_values.get(_scope_key(declaration), {}),
254
254
  **{parameter.name: (parameter.type_hint, NaLiteral()) for parameter in declaration.params
@@ -1,8 +1,8 @@
1
- """A `ta.*` call that runs on only some bars (card #96).
1
+ """A `ta.*` call that runs on only some bars.
2
2
 
3
3
  Pine keeps a call site's series history by running the call on every bar. A `ta.*` call
4
4
  reached only when a condition holds keeps a history that skips the other bars, so its value
5
- differs from the same call made on every bar (card #60 measured this: the transpiled output
5
+ differs from the same call made on every bar (measured: the transpiled output
6
6
  matches Pine's call-site semantics in every construct, and a hoisted call gives another value).
7
7
  The Pine User Manual (Execution model) says to run such functions in the global scope for
8
8
  consistent results. The transpiler never hoists: it keeps the call where the script has it and
@@ -14,7 +14,7 @@ A call is conditional when it sits in:
14
14
  - a branch of `?:`;
15
15
  - a `switch` arm or its default, or a case after the first (the first case is always read);
16
16
  - the right operand of `and` / `or` in Pine v6, which evaluates them lazily. Pine v5 and earlier
17
- evaluate both operands (card #65), so the right operand there is not conditional.
17
+ evaluate both operands, so the right operand there is not conditional.
18
18
 
19
19
  A function body starts unconditional again: whether the function is called conditionally is not
20
20
  decided here. A loop body is not conditional. `ta.vwap` and the other argument-free `ta.`
@@ -1,6 +1,6 @@
1
1
  """Where PyneComp leaves blank lines inside a function body.
2
2
 
3
- Measured on ``assets/pyne``: for a Pine v4/v5 script every blank line in
3
+ Measured on PyneComp's output: for a Pine v4/v5 script every blank line in
4
4
  ``main`` follows a nested ``def``/``class`` (619 of 622 modules); a Pine v6
5
5
  script keeps the blank lines of its source (291 of 293 modules have some).
6
6
 
@@ -9,7 +9,7 @@ a comment. The statements PyneComp moves out of ``main`` to the module (the
9
9
  constants the inputs read) keep their marks: between two statements of
10
10
  ``main``, ``n`` marks (the moved statements' and the second statement's own)
11
11
  print ``(2n + 1) // 3`` blank lines. Inputs, which become ``main``'s
12
- parameters, leave no mark. Measured with probe scripts compiled by PyneComp
12
+ parameters, leave no mark. Measured with scripts compiled by PyneComp
13
13
  v6.0.80 (``n`` = 1..6) and checked on the 71 such gaps of the reference.
14
14
  """
15
15
  from __future__ import annotations
@@ -1,6 +1,6 @@
1
1
  """The import block, laid out the way PyneComp lays it out.
2
2
 
3
- Measured on the 1005 reference modules in ``assets/pyne``:
3
+ Measured on 1005 modules compiled by PyneComp:
4
4
 
5
5
  * ``from`` imports come first, one per module, modules in alphabetical order;
6
6
  plain ``import`` statements (library imports) follow, sorted;
@@ -9,7 +9,7 @@
9
9
  * A series or persistent annotation keeps its element type (``Series[float]``)
10
10
  only then; otherwise it is bare (``Series``).
11
11
 
12
- Measured on ``assets/pyne``: 2626 ``str``, 1254 ``int``, 1048 ``float``, 1146
12
+ Measured on PyneComp's output: 2626 ``str``, 1254 ``int``, 1048 ``float``, 1146
13
13
  ``bool``, 941 ``Color`` declarations; ``Series`` 2108 bare against 859 typed.
14
14
  """
15
15
  from __future__ import annotations
@@ -62,7 +62,7 @@ def inferable_type(value: ast.expr | None, classes: frozenset = frozenset(),
62
62
  methods: frozenset = frozenset()) -> str | None:
63
63
  """The type PyneComp reads off a value without inference, or None. ``classes``:
64
64
  the module's UDT classes, whose constructor call names its type; ``methods``: the user
65
- methods, whose call on a constructor (`T.new().m()`) PyneComp types as `T` (probe mc5)."""
65
+ methods, whose call on a constructor (`T.new().m()`) PyneComp types as `T`."""
66
66
  found = literal_type(value)
67
67
  if found:
68
68
  return found
@@ -129,7 +129,7 @@ class DeclaredTypes:
129
129
 
130
130
  @staticmethod
131
131
  def _generic_constructor_type(value) -> str | None:
132
- """`map.new<string, bool>()` names its type, `map<string, bool>` (probe nb4_02); `T.copy(x)` names `T`
132
+ """`map.new<string, bool>()` names its type, `map<string, bool>`; `T.copy(x)` names `T`
133
133
  (Trendoscope/Zigzag `lPivot: Pivot = udt_copy(...)`)."""
134
134
  from ....ast_nodes import FunctionCall
135
135
  if isinstance(value, FunctionCall) and isinstance(value.func, str) and value.func.startswith('map.new<'):
@@ -232,7 +232,7 @@ class DeclarationAnnotationPass:
232
232
  self._classes = frozenset(node.name for node in tree.body if isinstance(node, ast.ClassDef))
233
233
  self._methods = frozenset(node.name for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)
234
234
  and any(isinstance(d, ast.Name) and d.id == 'method' for d in node.decorator_list))
235
- # each UDT's fields and their annotations: a field read is typed by its field (probe nodes1)
235
+ # each UDT's fields and their annotations: a field read is typed by its field
236
236
  self._fields = {node.name: {field.target.id: _unquoted(field.annotation) for field in node.body
237
237
  if isinstance(field, ast.AnnAssign) and isinstance(field.target, ast.Name)}
238
238
  for node in tree.body if isinstance(node, ast.ClassDef)}