pulse-framework 0.1.73__py3-none-any.whl → 0.1.74__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.
- pulse/transpiler/__init__.py +5 -0
- pulse/transpiler/function.py +56 -32
- pulse/transpiler/nodes.py +17 -2
- pulse/transpiler/parse.py +70 -0
- pulse/transpiler/transpiler.py +327 -76
- {pulse_framework-0.1.73.dist-info → pulse_framework-0.1.74.dist-info}/METADATA +1 -1
- {pulse_framework-0.1.73.dist-info → pulse_framework-0.1.74.dist-info}/RECORD +9 -8
- {pulse_framework-0.1.73.dist-info → pulse_framework-0.1.74.dist-info}/WHEEL +0 -0
- {pulse_framework-0.1.73.dist-info → pulse_framework-0.1.74.dist-info}/entry_points.txt +0 -0
pulse/transpiler/__init__.py
CHANGED
|
@@ -106,6 +106,11 @@ from pulse.transpiler.nodes import While as While
|
|
|
106
106
|
# Emit
|
|
107
107
|
from pulse.transpiler.nodes import emit as emit
|
|
108
108
|
|
|
109
|
+
# Parse helpers
|
|
110
|
+
from pulse.transpiler.parse import ParsedSource as ParsedSource
|
|
111
|
+
from pulse.transpiler.parse import get_ast as get_ast
|
|
112
|
+
from pulse.transpiler.parse import get_source as get_source
|
|
113
|
+
|
|
109
114
|
# Transpiler
|
|
110
115
|
from pulse.transpiler.transpiler import Transpiler as Transpiler
|
|
111
116
|
from pulse.transpiler.transpiler import transpile as transpile
|
pulse/transpiler/function.py
CHANGED
|
@@ -7,8 +7,8 @@ and JsFunction which wraps transpiled functions with their dependencies.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import ast
|
|
10
|
+
import dis
|
|
10
11
|
import inspect
|
|
11
|
-
import textwrap
|
|
12
12
|
import types as pytypes
|
|
13
13
|
from collections.abc import Callable
|
|
14
14
|
from dataclasses import dataclass, field
|
|
@@ -25,7 +25,6 @@ from typing import (
|
|
|
25
25
|
override,
|
|
26
26
|
)
|
|
27
27
|
|
|
28
|
-
from pulse.helpers import getsourcecode
|
|
29
28
|
from pulse.transpiler.errors import TranspileError
|
|
30
29
|
from pulse.transpiler.id import next_id, reset_id_counter
|
|
31
30
|
from pulse.transpiler.imports import Import
|
|
@@ -38,6 +37,7 @@ from pulse.transpiler.nodes import (
|
|
|
38
37
|
Return,
|
|
39
38
|
to_js_identifier,
|
|
40
39
|
)
|
|
40
|
+
from pulse.transpiler.parse import clear_parse_cache, get_ast, get_source
|
|
41
41
|
from pulse.transpiler.transpiler import Transpiler
|
|
42
42
|
from pulse.transpiler.vdom import VDOMExpr
|
|
43
43
|
|
|
@@ -63,6 +63,7 @@ def clear_function_cache() -> None:
|
|
|
63
63
|
|
|
64
64
|
FUNCTION_CACHE.clear()
|
|
65
65
|
CONSTANT_REGISTRY.clear()
|
|
66
|
+
clear_parse_cache()
|
|
66
67
|
clear_import_registry()
|
|
67
68
|
clear_asset_registry()
|
|
68
69
|
reset_id_counter()
|
|
@@ -137,33 +138,17 @@ def _transpile_function_body(
|
|
|
137
138
|
deps: dict[str, Expr],
|
|
138
139
|
*,
|
|
139
140
|
jsx: bool = False,
|
|
140
|
-
) ->
|
|
141
|
+
) -> Function | Arrow:
|
|
141
142
|
"""Shared transpilation logic for JsFunction and JsxFunction.
|
|
142
143
|
|
|
143
|
-
Returns the transpiled Function/Arrow node
|
|
144
|
+
Returns the transpiled Function/Arrow node.
|
|
144
145
|
"""
|
|
145
146
|
# Get and parse source
|
|
146
|
-
|
|
147
|
-
src =
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
source_start_line = None
|
|
152
|
-
module = ast.parse(src)
|
|
153
|
-
|
|
154
|
-
# Find the function definition
|
|
155
|
-
fndefs = [
|
|
156
|
-
n for n in module.body if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))
|
|
157
|
-
]
|
|
158
|
-
if not fndefs:
|
|
159
|
-
raise TranspileError("No function definition found in source")
|
|
160
|
-
fndef = fndefs[-1]
|
|
161
|
-
|
|
162
|
-
# Get filename for error messages and source file resolution
|
|
163
|
-
try:
|
|
164
|
-
filename = inspect.getfile(fn)
|
|
165
|
-
except (TypeError, OSError):
|
|
166
|
-
filename = None
|
|
147
|
+
parsed = get_source(fn)
|
|
148
|
+
src = parsed.source
|
|
149
|
+
fndef = get_ast(fn)
|
|
150
|
+
filename = parsed.filename
|
|
151
|
+
source_start_line = parsed.source_start_line
|
|
167
152
|
|
|
168
153
|
# Transpile with source context for errors
|
|
169
154
|
try:
|
|
@@ -181,7 +166,7 @@ def _transpile_function_body(
|
|
|
181
166
|
) from None
|
|
182
167
|
raise
|
|
183
168
|
|
|
184
|
-
return result
|
|
169
|
+
return result
|
|
185
170
|
|
|
186
171
|
|
|
187
172
|
@dataclass(slots=True, init=False)
|
|
@@ -238,7 +223,7 @@ class JsFunction(Expr, Generic[*Args, R]):
|
|
|
238
223
|
if self._transpiled is not None:
|
|
239
224
|
return self._transpiled
|
|
240
225
|
|
|
241
|
-
result
|
|
226
|
+
result = _transpile_function_body(self.fn, self.deps)
|
|
242
227
|
|
|
243
228
|
# Convert Arrow to Function if needed, and set the name
|
|
244
229
|
if isinstance(result, Function):
|
|
@@ -326,7 +311,7 @@ class JsxFunction(Expr, Generic[P, R]):
|
|
|
326
311
|
if self._transpiled is not None:
|
|
327
312
|
return self._transpiled
|
|
328
313
|
|
|
329
|
-
result
|
|
314
|
+
result = _transpile_function_body(self.fn, self.deps, jsx=True)
|
|
330
315
|
|
|
331
316
|
# JSX transpilation always returns Function (never Arrow)
|
|
332
317
|
assert isinstance(result, Function), (
|
|
@@ -376,7 +361,6 @@ def analyze_code_object(
|
|
|
376
361
|
- effective_globals: dict mapping names to their values (includes closure vars)
|
|
377
362
|
- all_names: set of all names referenced in the code (including nested functions)
|
|
378
363
|
"""
|
|
379
|
-
import dis
|
|
380
364
|
|
|
381
365
|
code = fn.__code__
|
|
382
366
|
|
|
@@ -443,14 +427,54 @@ def analyze_deps(fn: Callable[..., Any]) -> dict[str, Expr]:
|
|
|
443
427
|
"""
|
|
444
428
|
# Analyze code object and resolve globals + closure vars
|
|
445
429
|
effective_globals, all_names = analyze_code_object(fn)
|
|
430
|
+
code_names = set(all_names)
|
|
431
|
+
default_names: set[str] = set()
|
|
432
|
+
default_name_values: dict[str, Any] = {}
|
|
433
|
+
|
|
434
|
+
# Include names referenced only in default expressions (not in bytecode)
|
|
435
|
+
try:
|
|
436
|
+
args = get_ast(fn).args
|
|
437
|
+
pos_defaults = list(args.defaults)
|
|
438
|
+
py_defaults = fn.__defaults__ or ()
|
|
439
|
+
num_args = len(args.args)
|
|
440
|
+
num_defaults = len(pos_defaults)
|
|
441
|
+
for i, _arg in enumerate(args.args):
|
|
442
|
+
default_idx = i - (num_args - num_defaults)
|
|
443
|
+
if default_idx < 0 or default_idx >= len(pos_defaults):
|
|
444
|
+
continue
|
|
445
|
+
default_node = pos_defaults[default_idx]
|
|
446
|
+
if isinstance(default_node, ast.Name) and default_idx < len(py_defaults):
|
|
447
|
+
default_name_values[default_node.id] = py_defaults[default_idx]
|
|
448
|
+
for node in ast.walk(default_node):
|
|
449
|
+
if isinstance(node, ast.Name):
|
|
450
|
+
default_names.add(node.id)
|
|
451
|
+
|
|
452
|
+
py_kwdefaults = fn.__kwdefaults__ or {}
|
|
453
|
+
for i, kwarg in enumerate(args.kwonlyargs):
|
|
454
|
+
default_node = args.kw_defaults[i]
|
|
455
|
+
if default_node is None:
|
|
456
|
+
continue
|
|
457
|
+
if isinstance(default_node, ast.Name) and kwarg.arg in py_kwdefaults:
|
|
458
|
+
default_name_values[default_node.id] = py_kwdefaults[kwarg.arg]
|
|
459
|
+
for node in ast.walk(default_node):
|
|
460
|
+
if isinstance(node, ast.Name):
|
|
461
|
+
default_names.add(node.id)
|
|
462
|
+
except (OSError, TypeError, SyntaxError, TranspileError):
|
|
463
|
+
pass
|
|
464
|
+
|
|
465
|
+
all_names.update(default_names)
|
|
466
|
+
default_only_names = default_names - code_names
|
|
446
467
|
|
|
447
468
|
# Build dependencies dictionary - all values are Expr
|
|
448
469
|
deps: dict[str, Expr] = {}
|
|
449
470
|
|
|
471
|
+
missing = object()
|
|
450
472
|
for name in all_names:
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
473
|
+
if name in default_only_names and name in default_name_values:
|
|
474
|
+
value = default_name_values[name]
|
|
475
|
+
else:
|
|
476
|
+
value = effective_globals.get(name, missing)
|
|
477
|
+
if value is missing:
|
|
454
478
|
# Not in globals - could be a builtin or unresolved
|
|
455
479
|
# For now, skip - builtins will be handled by the transpiler
|
|
456
480
|
# TODO: Add builtin support
|
pulse/transpiler/nodes.py
CHANGED
|
@@ -1517,7 +1517,7 @@ class If(Stmt):
|
|
|
1517
1517
|
|
|
1518
1518
|
@dataclass(slots=True)
|
|
1519
1519
|
class ForOf(Stmt):
|
|
1520
|
-
"""JS for-of loop: for (
|
|
1520
|
+
"""JS for-of loop: for (x of iter) { ... }
|
|
1521
1521
|
|
|
1522
1522
|
target can be a single name or array pattern for destructuring: [a, b]
|
|
1523
1523
|
"""
|
|
@@ -1528,7 +1528,7 @@ class ForOf(Stmt):
|
|
|
1528
1528
|
|
|
1529
1529
|
@override
|
|
1530
1530
|
def emit(self, out: list[str]) -> None:
|
|
1531
|
-
out.append("for (
|
|
1531
|
+
out.append("for (")
|
|
1532
1532
|
out.append(self.target)
|
|
1533
1533
|
out.append(" of ")
|
|
1534
1534
|
self.iter.emit(out)
|
|
@@ -1619,6 +1619,21 @@ class Assign(Stmt):
|
|
|
1619
1619
|
out.append(";")
|
|
1620
1620
|
|
|
1621
1621
|
|
|
1622
|
+
@dataclass(slots=True)
|
|
1623
|
+
class LetDecl(Stmt):
|
|
1624
|
+
"""JS let declaration: let a, b;"""
|
|
1625
|
+
|
|
1626
|
+
names: Sequence[str]
|
|
1627
|
+
|
|
1628
|
+
@override
|
|
1629
|
+
def emit(self, out: list[str]) -> None:
|
|
1630
|
+
if not self.names:
|
|
1631
|
+
return
|
|
1632
|
+
out.append("let ")
|
|
1633
|
+
out.append(", ".join(self.names))
|
|
1634
|
+
out.append(";")
|
|
1635
|
+
|
|
1636
|
+
|
|
1622
1637
|
@dataclass(slots=True)
|
|
1623
1638
|
class ExprStmt(Stmt):
|
|
1624
1639
|
"""JS expression statement: expr;"""
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Cached parsing helpers for transpiler source inspection."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ast
|
|
6
|
+
import inspect
|
|
7
|
+
import textwrap
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from pulse.helpers import getsourcecode
|
|
13
|
+
from pulse.transpiler.errors import TranspileError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(slots=True)
|
|
17
|
+
class ParsedSource:
|
|
18
|
+
source: str
|
|
19
|
+
filename: str | None
|
|
20
|
+
source_start_line: int | None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_SOURCE_CACHE: dict[Callable[..., Any], ParsedSource] = {}
|
|
24
|
+
_AST_CACHE: dict[Callable[..., Any], ast.FunctionDef | ast.AsyncFunctionDef] = {}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def clear_parse_cache() -> None:
|
|
28
|
+
_SOURCE_CACHE.clear()
|
|
29
|
+
_AST_CACHE.clear()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_source(fn: Callable[..., Any]) -> ParsedSource:
|
|
33
|
+
cached = _SOURCE_CACHE.get(fn)
|
|
34
|
+
if cached is not None:
|
|
35
|
+
return cached
|
|
36
|
+
|
|
37
|
+
src = getsourcecode(fn)
|
|
38
|
+
src = textwrap.dedent(src)
|
|
39
|
+
try:
|
|
40
|
+
source_start_line = inspect.getsourcelines(fn)[1]
|
|
41
|
+
except (OSError, TypeError):
|
|
42
|
+
source_start_line = None
|
|
43
|
+
try:
|
|
44
|
+
filename = inspect.getfile(fn)
|
|
45
|
+
except (TypeError, OSError):
|
|
46
|
+
filename = None
|
|
47
|
+
|
|
48
|
+
parsed = ParsedSource(
|
|
49
|
+
source=src,
|
|
50
|
+
filename=filename,
|
|
51
|
+
source_start_line=source_start_line,
|
|
52
|
+
)
|
|
53
|
+
_SOURCE_CACHE[fn] = parsed
|
|
54
|
+
return parsed
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_ast(fn: Callable[..., Any]) -> ast.FunctionDef | ast.AsyncFunctionDef:
|
|
58
|
+
cached = _AST_CACHE.get(fn)
|
|
59
|
+
if cached is not None:
|
|
60
|
+
return cached
|
|
61
|
+
|
|
62
|
+
module = ast.parse(get_source(fn).source)
|
|
63
|
+
fndefs = [
|
|
64
|
+
n for n in module.body if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))
|
|
65
|
+
]
|
|
66
|
+
if not fndefs:
|
|
67
|
+
raise TranspileError("No function definition found in source")
|
|
68
|
+
fndef = fndefs[-1]
|
|
69
|
+
_AST_CACHE[fn] = fndef
|
|
70
|
+
return fndef
|
pulse/transpiler/transpiler.py
CHANGED
|
@@ -10,8 +10,9 @@ from __future__ import annotations
|
|
|
10
10
|
import ast
|
|
11
11
|
import re
|
|
12
12
|
from collections.abc import Callable, Mapping
|
|
13
|
+
from dataclasses import dataclass
|
|
13
14
|
from pathlib import Path
|
|
14
|
-
from typing import Any, cast
|
|
15
|
+
from typing import Any, cast, override
|
|
15
16
|
|
|
16
17
|
from pulse.transpiler.builtins import BUILTINS, emit_method
|
|
17
18
|
from pulse.transpiler.errors import TranspileError
|
|
@@ -30,6 +31,7 @@ from pulse.transpiler.nodes import (
|
|
|
30
31
|
Function,
|
|
31
32
|
Identifier,
|
|
32
33
|
If,
|
|
34
|
+
LetDecl,
|
|
33
35
|
Literal,
|
|
34
36
|
Member,
|
|
35
37
|
New,
|
|
@@ -78,6 +80,228 @@ ALLOWED_CMPOPS: dict[type[ast.cmpop], str] = {
|
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
|
|
83
|
+
def _collect_param_names(args: ast.arguments) -> list[str]:
|
|
84
|
+
"""Collect argument names (regular, vararg, kwonly, kwarg)."""
|
|
85
|
+
names: list[str] = [arg.arg for arg in args.args]
|
|
86
|
+
if args.vararg:
|
|
87
|
+
names.append(args.vararg.arg)
|
|
88
|
+
names.extend(arg.arg for arg in args.kwonlyargs)
|
|
89
|
+
if args.kwarg:
|
|
90
|
+
names.append(args.kwarg.arg)
|
|
91
|
+
return names
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass(slots=True)
|
|
95
|
+
class Scope:
|
|
96
|
+
locals: set[str]
|
|
97
|
+
params: set[str]
|
|
98
|
+
parent: "Scope | None" = None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ScopeAnalyzer(ast.NodeVisitor):
|
|
102
|
+
"""Collect locals per scope (function/lambda/comprehension)."""
|
|
103
|
+
|
|
104
|
+
_scopes: dict[ast.AST, Scope]
|
|
105
|
+
_stack: list[Scope]
|
|
106
|
+
_global_scope: Scope
|
|
107
|
+
|
|
108
|
+
def __init__(self) -> None:
|
|
109
|
+
self._scopes = {}
|
|
110
|
+
self._stack = []
|
|
111
|
+
self._global_scope = Scope(locals=set(), params=set(), parent=None)
|
|
112
|
+
|
|
113
|
+
def analyze(
|
|
114
|
+
self, node: ast.FunctionDef | ast.AsyncFunctionDef
|
|
115
|
+
) -> dict[ast.AST, Scope]:
|
|
116
|
+
self._stack.append(self._global_scope)
|
|
117
|
+
self._visit_defaults(node.args)
|
|
118
|
+
scope = self._new_scope(
|
|
119
|
+
node, _collect_param_names(node.args), parent=self._global_scope
|
|
120
|
+
)
|
|
121
|
+
self._stack.append(scope)
|
|
122
|
+
for stmt in node.body:
|
|
123
|
+
self.visit(stmt)
|
|
124
|
+
self._stack.pop()
|
|
125
|
+
self._stack.pop()
|
|
126
|
+
return self._scopes
|
|
127
|
+
|
|
128
|
+
def _new_scope(
|
|
129
|
+
self,
|
|
130
|
+
node: ast.AST,
|
|
131
|
+
params: list[str],
|
|
132
|
+
*,
|
|
133
|
+
parent: Scope | None,
|
|
134
|
+
) -> Scope:
|
|
135
|
+
param_set = set(params)
|
|
136
|
+
scope = Scope(locals=set(param_set), params=param_set, parent=parent)
|
|
137
|
+
self._scopes[node] = scope
|
|
138
|
+
return scope
|
|
139
|
+
|
|
140
|
+
def _current_scope(self) -> Scope:
|
|
141
|
+
return self._stack[-1]
|
|
142
|
+
|
|
143
|
+
def _add_local(self, name: str) -> None:
|
|
144
|
+
self._current_scope().locals.add(name)
|
|
145
|
+
|
|
146
|
+
def _add_targets(self, target: ast.expr) -> None:
|
|
147
|
+
if isinstance(target, ast.Name):
|
|
148
|
+
self._add_local(target.id)
|
|
149
|
+
return
|
|
150
|
+
if isinstance(target, (ast.Tuple, ast.List)):
|
|
151
|
+
for elt in target.elts:
|
|
152
|
+
if isinstance(elt, ast.Name):
|
|
153
|
+
self._add_local(elt.id)
|
|
154
|
+
|
|
155
|
+
def _visit_target_expr(self, target: ast.expr) -> None:
|
|
156
|
+
if isinstance(target, (ast.Tuple, ast.List)):
|
|
157
|
+
for elt in target.elts:
|
|
158
|
+
self._visit_target_expr(elt)
|
|
159
|
+
return
|
|
160
|
+
self.visit(target)
|
|
161
|
+
|
|
162
|
+
def _analyze_function(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None:
|
|
163
|
+
self._visit_defaults(node.args)
|
|
164
|
+
scope = self._new_scope(
|
|
165
|
+
node,
|
|
166
|
+
_collect_param_names(node.args),
|
|
167
|
+
parent=self._current_scope(),
|
|
168
|
+
)
|
|
169
|
+
self._stack.append(scope)
|
|
170
|
+
for stmt in node.body:
|
|
171
|
+
self.visit(stmt)
|
|
172
|
+
self._stack.pop()
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def global_scope(self) -> Scope:
|
|
176
|
+
return self._global_scope
|
|
177
|
+
|
|
178
|
+
def _visit_defaults(self, args: ast.arguments) -> None:
|
|
179
|
+
for default in args.defaults:
|
|
180
|
+
self.visit(default)
|
|
181
|
+
for default in args.kw_defaults:
|
|
182
|
+
if default is not None:
|
|
183
|
+
self.visit(default)
|
|
184
|
+
|
|
185
|
+
@override
|
|
186
|
+
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
|
187
|
+
self._add_local(node.name)
|
|
188
|
+
self._analyze_function(node)
|
|
189
|
+
|
|
190
|
+
@override
|
|
191
|
+
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
|
|
192
|
+
self._add_local(node.name)
|
|
193
|
+
self._analyze_function(node)
|
|
194
|
+
|
|
195
|
+
@override
|
|
196
|
+
def visit_Lambda(self, node: ast.Lambda) -> None:
|
|
197
|
+
scope = self._new_scope(
|
|
198
|
+
node,
|
|
199
|
+
_collect_param_names(node.args),
|
|
200
|
+
parent=self._current_scope(),
|
|
201
|
+
)
|
|
202
|
+
self._stack.append(scope)
|
|
203
|
+
self.visit(node.body)
|
|
204
|
+
self._stack.pop()
|
|
205
|
+
|
|
206
|
+
def _visit_comprehension(
|
|
207
|
+
self,
|
|
208
|
+
node: ast.ListComp | ast.SetComp | ast.DictComp | ast.GeneratorExp,
|
|
209
|
+
*,
|
|
210
|
+
elt: ast.expr,
|
|
211
|
+
key: ast.expr | None = None,
|
|
212
|
+
) -> None:
|
|
213
|
+
scope = self._new_scope(node, [], parent=self._current_scope())
|
|
214
|
+
self._stack.append(scope)
|
|
215
|
+
if key is not None:
|
|
216
|
+
self.visit(key)
|
|
217
|
+
self.visit(elt)
|
|
218
|
+
for gen in node.generators:
|
|
219
|
+
self.visit(gen.iter)
|
|
220
|
+
for if_node in gen.ifs:
|
|
221
|
+
self.visit(if_node)
|
|
222
|
+
self._stack.pop()
|
|
223
|
+
|
|
224
|
+
@override
|
|
225
|
+
def visit_ListComp(self, node: ast.ListComp) -> None:
|
|
226
|
+
self._visit_comprehension(node, elt=node.elt)
|
|
227
|
+
|
|
228
|
+
@override
|
|
229
|
+
def visit_SetComp(self, node: ast.SetComp) -> None:
|
|
230
|
+
self._visit_comprehension(node, elt=node.elt)
|
|
231
|
+
|
|
232
|
+
@override
|
|
233
|
+
def visit_GeneratorExp(self, node: ast.GeneratorExp) -> None:
|
|
234
|
+
self._visit_comprehension(node, elt=node.elt)
|
|
235
|
+
|
|
236
|
+
@override
|
|
237
|
+
def visit_DictComp(self, node: ast.DictComp) -> None:
|
|
238
|
+
self._visit_comprehension(node, elt=node.value, key=node.key)
|
|
239
|
+
|
|
240
|
+
@override
|
|
241
|
+
def visit_Assign(self, node: ast.Assign) -> None:
|
|
242
|
+
for target in node.targets:
|
|
243
|
+
if isinstance(target, (ast.Name, ast.Tuple, ast.List)):
|
|
244
|
+
self._add_targets(target)
|
|
245
|
+
self._visit_target_expr(target)
|
|
246
|
+
self.visit(node.value)
|
|
247
|
+
|
|
248
|
+
@override
|
|
249
|
+
def visit_AnnAssign(self, node: ast.AnnAssign) -> None:
|
|
250
|
+
if isinstance(node.target, (ast.Name, ast.Tuple, ast.List)):
|
|
251
|
+
self._add_targets(node.target)
|
|
252
|
+
self._visit_target_expr(node.target)
|
|
253
|
+
if node.value is not None:
|
|
254
|
+
self.visit(node.value)
|
|
255
|
+
|
|
256
|
+
@override
|
|
257
|
+
def visit_AugAssign(self, node: ast.AugAssign) -> None:
|
|
258
|
+
if isinstance(node.target, (ast.Name, ast.Tuple, ast.List)):
|
|
259
|
+
self._add_targets(node.target)
|
|
260
|
+
self._visit_target_expr(node.target)
|
|
261
|
+
self.visit(node.value)
|
|
262
|
+
|
|
263
|
+
@override
|
|
264
|
+
def visit_For(self, node: ast.For) -> None:
|
|
265
|
+
if isinstance(node.target, (ast.Name, ast.Tuple, ast.List)):
|
|
266
|
+
self._add_targets(node.target)
|
|
267
|
+
self._visit_target_expr(node.target)
|
|
268
|
+
self.visit(node.iter)
|
|
269
|
+
for stmt in node.body:
|
|
270
|
+
self.visit(stmt)
|
|
271
|
+
for stmt in node.orelse:
|
|
272
|
+
self.visit(stmt)
|
|
273
|
+
|
|
274
|
+
@override
|
|
275
|
+
def visit_ExceptHandler(self, node: ast.ExceptHandler) -> None:
|
|
276
|
+
if node.name:
|
|
277
|
+
self._add_local(node.name)
|
|
278
|
+
if node.type is not None:
|
|
279
|
+
self.visit(node.type)
|
|
280
|
+
for stmt in node.body:
|
|
281
|
+
self.visit(stmt)
|
|
282
|
+
|
|
283
|
+
@override
|
|
284
|
+
def visit_With(self, node: ast.With) -> None:
|
|
285
|
+
for item in node.items:
|
|
286
|
+
if item.optional_vars and isinstance(
|
|
287
|
+
item.optional_vars, (ast.Name, ast.Tuple, ast.List)
|
|
288
|
+
):
|
|
289
|
+
self._add_targets(item.optional_vars)
|
|
290
|
+
if item.optional_vars is not None:
|
|
291
|
+
self._visit_target_expr(item.optional_vars)
|
|
292
|
+
self.visit(item.context_expr)
|
|
293
|
+
for stmt in node.body:
|
|
294
|
+
self.visit(stmt)
|
|
295
|
+
|
|
296
|
+
@override
|
|
297
|
+
def visit_Global(self, node: ast.Global) -> None:
|
|
298
|
+
raise TranspileError("global is not supported", node=node)
|
|
299
|
+
|
|
300
|
+
@override
|
|
301
|
+
def visit_Nonlocal(self, node: ast.Nonlocal) -> None:
|
|
302
|
+
raise TranspileError("nonlocal is not supported", node=node)
|
|
303
|
+
|
|
304
|
+
|
|
81
305
|
class Transpiler:
|
|
82
306
|
"""Transpile Python AST to v2 Expr/Stmt AST nodes.
|
|
83
307
|
|
|
@@ -93,10 +317,12 @@ class Transpiler:
|
|
|
93
317
|
fndef: ast.FunctionDef | ast.AsyncFunctionDef
|
|
94
318
|
args: list[str]
|
|
95
319
|
deps: Mapping[str, Expr]
|
|
96
|
-
locals: set[str]
|
|
97
320
|
jsx: bool
|
|
98
321
|
source_file: Path | None
|
|
99
322
|
_temp_counter: int
|
|
323
|
+
_scope_map: dict[ast.AST, Scope]
|
|
324
|
+
_scope_stack: list[Scope]
|
|
325
|
+
_global_scope: Scope
|
|
100
326
|
|
|
101
327
|
def __init__(
|
|
102
328
|
self,
|
|
@@ -109,22 +335,21 @@ class Transpiler:
|
|
|
109
335
|
self.fndef = fndef
|
|
110
336
|
self.source_file = source_file
|
|
111
337
|
# Collect all argument names (regular, vararg, kwonly, kwarg)
|
|
112
|
-
args
|
|
113
|
-
if fndef.args.vararg:
|
|
114
|
-
args.append(fndef.args.vararg.arg)
|
|
115
|
-
args.extend(arg.arg for arg in fndef.args.kwonlyargs)
|
|
116
|
-
if fndef.args.kwarg:
|
|
117
|
-
args.append(fndef.args.kwarg.arg)
|
|
118
|
-
self.args = args
|
|
338
|
+
self.args = _collect_param_names(fndef.args)
|
|
119
339
|
self.deps = deps
|
|
120
340
|
self.jsx = jsx
|
|
121
|
-
self.locals = set(self.args)
|
|
122
341
|
self._temp_counter = 0
|
|
342
|
+
analyzer = ScopeAnalyzer()
|
|
343
|
+
self._scope_map = analyzer.analyze(fndef)
|
|
344
|
+
self._global_scope = analyzer.global_scope
|
|
345
|
+
self._scope_stack = [self._scope_map[fndef]]
|
|
123
346
|
self.init_temp_counter()
|
|
124
347
|
|
|
125
348
|
def init_temp_counter(self) -> None:
|
|
126
349
|
"""Initialize temp counter to avoid collisions with args or globals."""
|
|
127
|
-
all_names = set(self.
|
|
350
|
+
all_names = set(self.deps.keys())
|
|
351
|
+
for scope in self._scope_map.values():
|
|
352
|
+
all_names.update(scope.locals)
|
|
128
353
|
counter = 0
|
|
129
354
|
while f"$tmp{counter}" in all_names:
|
|
130
355
|
counter += 1
|
|
@@ -136,6 +361,38 @@ class Transpiler:
|
|
|
136
361
|
self._temp_counter += 1
|
|
137
362
|
return name
|
|
138
363
|
|
|
364
|
+
def _current_scope(self) -> Scope:
|
|
365
|
+
return self._scope_stack[-1]
|
|
366
|
+
|
|
367
|
+
def _push_scope(self, node: ast.AST) -> None:
|
|
368
|
+
self._scope_stack.append(self._scope_map[node])
|
|
369
|
+
|
|
370
|
+
def _pop_scope(self) -> None:
|
|
371
|
+
self._scope_stack.pop()
|
|
372
|
+
|
|
373
|
+
def _is_local_here(self, name: str) -> bool:
|
|
374
|
+
return name in self._current_scope().locals
|
|
375
|
+
|
|
376
|
+
def _is_local(self, name: str) -> bool:
|
|
377
|
+
scope: Scope | None = self._current_scope()
|
|
378
|
+
while scope is not None:
|
|
379
|
+
if name in scope.locals:
|
|
380
|
+
return True
|
|
381
|
+
scope = scope.parent
|
|
382
|
+
return False
|
|
383
|
+
|
|
384
|
+
def _require_local(
|
|
385
|
+
self, name: str, *, node: ast.expr | ast.stmt | ast.excepthandler
|
|
386
|
+
) -> None:
|
|
387
|
+
if not self._is_local_here(name):
|
|
388
|
+
raise TranspileError(f"Assignment to unknown local: {name}", node=node)
|
|
389
|
+
|
|
390
|
+
def _function_prelude(self, scope: Scope) -> list[Stmt]:
|
|
391
|
+
names = sorted(scope.locals - scope.params)
|
|
392
|
+
if not names:
|
|
393
|
+
return []
|
|
394
|
+
return [LetDecl(names)]
|
|
395
|
+
|
|
139
396
|
# --- Entrypoint ---------------------------------------------------------
|
|
140
397
|
|
|
141
398
|
def transpile(self) -> Function | Arrow:
|
|
@@ -176,7 +433,8 @@ class Transpiler:
|
|
|
176
433
|
return Arrow(self.args, expr)
|
|
177
434
|
|
|
178
435
|
# General case: Function (for JSX or multi-statement)
|
|
179
|
-
|
|
436
|
+
prelude = self._function_prelude(self._current_scope())
|
|
437
|
+
stmts = prelude + [self.emit_stmt(s) for s in body]
|
|
180
438
|
is_async = isinstance(self.fndef, ast.AsyncFunctionDef)
|
|
181
439
|
args = [self._jsx_args()] if self.jsx else self.args
|
|
182
440
|
return Function(args, stmts, is_async=is_async)
|
|
@@ -202,7 +460,7 @@ class Transpiler:
|
|
|
202
460
|
if default_idx >= 0:
|
|
203
461
|
# Has a default value
|
|
204
462
|
default_node = args.defaults[default_idx]
|
|
205
|
-
default_expr = self.
|
|
463
|
+
default_expr = self._emit_default_expr(default_node)
|
|
206
464
|
default_out.clear()
|
|
207
465
|
default_expr.emit(default_out)
|
|
208
466
|
destructure_parts.append(f"{param_name} = {''.join(default_out)}")
|
|
@@ -220,7 +478,7 @@ class Transpiler:
|
|
|
220
478
|
default_node = args.kw_defaults[i]
|
|
221
479
|
if default_node is not None:
|
|
222
480
|
# Has a default value
|
|
223
|
-
default_expr = self.
|
|
481
|
+
default_expr = self._emit_default_expr(default_node)
|
|
224
482
|
default_out.clear()
|
|
225
483
|
default_expr.emit(default_out)
|
|
226
484
|
destructure_parts.append(f"{param_name} = {''.join(default_out)}")
|
|
@@ -234,6 +492,14 @@ class Transpiler:
|
|
|
234
492
|
|
|
235
493
|
return "{" + ", ".join(destructure_parts) + "}"
|
|
236
494
|
|
|
495
|
+
def _emit_default_expr(self, node: ast.expr) -> Expr:
|
|
496
|
+
"""Emit defaults in defining-scope context."""
|
|
497
|
+
self._scope_stack.append(self._global_scope)
|
|
498
|
+
try:
|
|
499
|
+
return self.emit_expr(node)
|
|
500
|
+
finally:
|
|
501
|
+
self._scope_stack.pop()
|
|
502
|
+
|
|
237
503
|
# --- Statements ----------------------------------------------------------
|
|
238
504
|
|
|
239
505
|
def emit_stmt(self, node: ast.stmt) -> Stmt:
|
|
@@ -262,6 +528,7 @@ class Transpiler:
|
|
|
262
528
|
"Only simple augmented assignments supported", node=node
|
|
263
529
|
)
|
|
264
530
|
target = node.target.id
|
|
531
|
+
self._require_local(target, node=node)
|
|
265
532
|
op_type = type(node.op)
|
|
266
533
|
if op_type not in ALLOWED_BINOPS:
|
|
267
534
|
raise TranspileError(
|
|
@@ -296,22 +563,16 @@ class Transpiler:
|
|
|
296
563
|
target = target_node.id
|
|
297
564
|
value_expr = self.emit_expr(node.value)
|
|
298
565
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
else:
|
|
302
|
-
self.locals.add(target)
|
|
303
|
-
return Assign(target, value_expr, declare="let")
|
|
566
|
+
self._require_local(target, node=node)
|
|
567
|
+
return Assign(target, value_expr)
|
|
304
568
|
|
|
305
569
|
if isinstance(node, ast.AnnAssign):
|
|
306
570
|
if not isinstance(node.target, ast.Name):
|
|
307
571
|
raise TranspileError("Only simple annotated assignments supported")
|
|
308
572
|
target = node.target.id
|
|
309
573
|
value = Literal(None) if node.value is None else self.emit_expr(node.value)
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
else:
|
|
313
|
-
self.locals.add(target)
|
|
314
|
-
return Assign(target, value, declare="let")
|
|
574
|
+
self._require_local(target, node=node)
|
|
575
|
+
return Assign(target, value)
|
|
315
576
|
|
|
316
577
|
if isinstance(node, ast.If):
|
|
317
578
|
cond = self.emit_expr(node.test)
|
|
@@ -358,11 +619,8 @@ class Transpiler:
|
|
|
358
619
|
assert isinstance(e, ast.Name)
|
|
359
620
|
name = e.id
|
|
360
621
|
sub = Subscript(Identifier(tmp_name), Literal(idx))
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
else:
|
|
364
|
-
self.locals.add(name)
|
|
365
|
-
stmts.append(Assign(name, sub, declare="let"))
|
|
622
|
+
self._require_local(name, node=target)
|
|
623
|
+
stmts.append(Assign(name, sub))
|
|
366
624
|
|
|
367
625
|
return StmtSequence(stmts)
|
|
368
626
|
|
|
@@ -453,7 +711,7 @@ class Transpiler:
|
|
|
453
711
|
"Only simple name targets supported in for-loop unpacking"
|
|
454
712
|
)
|
|
455
713
|
names.append(e.id)
|
|
456
|
-
self.
|
|
714
|
+
self._require_local(e.id, node=node)
|
|
457
715
|
iter_expr = self.emit_expr(node.iter)
|
|
458
716
|
body = [self.emit_stmt(s) for s in node.body]
|
|
459
717
|
# Use array pattern for destructuring
|
|
@@ -464,7 +722,7 @@ class Transpiler:
|
|
|
464
722
|
raise TranspileError("Only simple name targets supported in for-loops")
|
|
465
723
|
|
|
466
724
|
target = node.target.id
|
|
467
|
-
self.
|
|
725
|
+
self._require_local(target, node=node)
|
|
468
726
|
iter_expr = self.emit_expr(node.iter)
|
|
469
727
|
body = [self.emit_stmt(s) for s in node.body]
|
|
470
728
|
return ForOf(target, iter_expr, body)
|
|
@@ -474,31 +732,29 @@ class Transpiler:
|
|
|
474
732
|
) -> Stmt:
|
|
475
733
|
"""Emit a nested function definition."""
|
|
476
734
|
name = node.name
|
|
477
|
-
params =
|
|
478
|
-
|
|
479
|
-
# Save current locals and extend with params
|
|
480
|
-
saved_locals = set(self.locals)
|
|
481
|
-
self.locals.update(params)
|
|
482
|
-
|
|
483
|
-
# Skip docstrings and emit body
|
|
484
|
-
body_stmts = node.body
|
|
485
|
-
if (
|
|
486
|
-
body_stmts
|
|
487
|
-
and isinstance(body_stmts[0], ast.Expr)
|
|
488
|
-
and isinstance(body_stmts[0].value, ast.Constant)
|
|
489
|
-
and isinstance(body_stmts[0].value.value, str)
|
|
490
|
-
):
|
|
491
|
-
body_stmts = body_stmts[1:]
|
|
735
|
+
params = _collect_param_names(node.args)
|
|
736
|
+
self._require_local(name, node=node)
|
|
492
737
|
|
|
493
|
-
|
|
738
|
+
self._push_scope(node)
|
|
739
|
+
try:
|
|
740
|
+
# Skip docstrings and emit body
|
|
741
|
+
body_stmts = node.body
|
|
742
|
+
if (
|
|
743
|
+
body_stmts
|
|
744
|
+
and isinstance(body_stmts[0], ast.Expr)
|
|
745
|
+
and isinstance(body_stmts[0].value, ast.Constant)
|
|
746
|
+
and isinstance(body_stmts[0].value.value, str)
|
|
747
|
+
):
|
|
748
|
+
body_stmts = body_stmts[1:]
|
|
494
749
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
750
|
+
prelude = self._function_prelude(self._current_scope())
|
|
751
|
+
stmts: list[Stmt] = prelude + [self.emit_stmt(s) for s in body_stmts]
|
|
752
|
+
finally:
|
|
753
|
+
self._pop_scope()
|
|
498
754
|
|
|
499
755
|
is_async = isinstance(node, ast.AsyncFunctionDef)
|
|
500
756
|
fn = Function(params, stmts, is_async=is_async)
|
|
501
|
-
return Assign(name, fn
|
|
757
|
+
return Assign(name, fn)
|
|
502
758
|
|
|
503
759
|
def _emit_try(self, node: ast.Try) -> Stmt:
|
|
504
760
|
"""Emit a try/except/finally statement."""
|
|
@@ -517,7 +773,6 @@ class Transpiler:
|
|
|
517
773
|
handler = node.handlers[0]
|
|
518
774
|
if handler.name:
|
|
519
775
|
catch_param = handler.name
|
|
520
|
-
self.locals.add(catch_param)
|
|
521
776
|
catch_body = [self.emit_stmt(s) for s in handler.body]
|
|
522
777
|
|
|
523
778
|
# Handle finally
|
|
@@ -594,23 +849,21 @@ class Transpiler:
|
|
|
594
849
|
|
|
595
850
|
if isinstance(node, ast.ListComp):
|
|
596
851
|
return self._emit_comprehension_chain(
|
|
597
|
-
node
|
|
852
|
+
node, lambda: self.emit_expr(node.elt)
|
|
598
853
|
)
|
|
599
854
|
|
|
600
855
|
if isinstance(node, ast.GeneratorExp):
|
|
601
856
|
return self._emit_comprehension_chain(
|
|
602
|
-
node
|
|
857
|
+
node, lambda: self.emit_expr(node.elt)
|
|
603
858
|
)
|
|
604
859
|
|
|
605
860
|
if isinstance(node, ast.SetComp):
|
|
606
|
-
arr = self._emit_comprehension_chain(
|
|
607
|
-
node.generators, lambda: self.emit_expr(node.elt)
|
|
608
|
-
)
|
|
861
|
+
arr = self._emit_comprehension_chain(node, lambda: self.emit_expr(node.elt))
|
|
609
862
|
return New(Identifier("Set"), [arr])
|
|
610
863
|
|
|
611
864
|
if isinstance(node, ast.DictComp):
|
|
612
865
|
pairs = self._emit_comprehension_chain(
|
|
613
|
-
node
|
|
866
|
+
node,
|
|
614
867
|
lambda: Array([self.emit_expr(node.key), self.emit_expr(node.value)]),
|
|
615
868
|
)
|
|
616
869
|
return New(Identifier("Map"), [pairs])
|
|
@@ -648,14 +901,14 @@ class Transpiler:
|
|
|
648
901
|
"""Emit a name reference."""
|
|
649
902
|
name = node.id
|
|
650
903
|
|
|
651
|
-
#
|
|
904
|
+
# Local variable (current or enclosing scope)
|
|
905
|
+
if self._is_local(name):
|
|
906
|
+
return Identifier(name)
|
|
907
|
+
|
|
908
|
+
# Check deps
|
|
652
909
|
if name in self.deps:
|
|
653
910
|
return self.deps[name]
|
|
654
911
|
|
|
655
|
-
# Local variable
|
|
656
|
-
if name in self.locals:
|
|
657
|
-
return Identifier(name)
|
|
658
|
-
|
|
659
912
|
# Check builtins
|
|
660
913
|
if name in BUILTINS:
|
|
661
914
|
return BUILTINS[name]
|
|
@@ -1093,29 +1346,26 @@ class Transpiler:
|
|
|
1093
1346
|
|
|
1094
1347
|
def _emit_lambda(self, node: ast.Lambda) -> Expr:
|
|
1095
1348
|
"""Emit a lambda expression as an arrow function."""
|
|
1096
|
-
params =
|
|
1097
|
-
|
|
1098
|
-
# Add params to locals temporarily
|
|
1099
|
-
saved_locals = set(self.locals)
|
|
1100
|
-
self.locals.update(params)
|
|
1101
|
-
|
|
1102
|
-
body = self.emit_expr(node.body)
|
|
1349
|
+
params = _collect_param_names(node.args)
|
|
1103
1350
|
|
|
1104
|
-
self.
|
|
1351
|
+
self._push_scope(node)
|
|
1352
|
+
try:
|
|
1353
|
+
body = self.emit_expr(node.body)
|
|
1354
|
+
finally:
|
|
1355
|
+
self._pop_scope()
|
|
1105
1356
|
|
|
1106
1357
|
return Arrow(params, body)
|
|
1107
1358
|
|
|
1108
1359
|
def _emit_comprehension_chain(
|
|
1109
1360
|
self,
|
|
1110
|
-
|
|
1361
|
+
node: ast.ListComp | ast.SetComp | ast.DictComp | ast.GeneratorExp,
|
|
1111
1362
|
build_last: Callable[[], Expr],
|
|
1112
1363
|
) -> Expr:
|
|
1113
1364
|
"""Build a flatMap/map chain for comprehensions."""
|
|
1365
|
+
generators = node.generators
|
|
1114
1366
|
if len(generators) == 0:
|
|
1115
1367
|
raise TranspileError("Empty comprehension")
|
|
1116
1368
|
|
|
1117
|
-
saved_locals = set(self.locals)
|
|
1118
|
-
|
|
1119
1369
|
def build_chain(gen_index: int) -> Expr:
|
|
1120
1370
|
gen = generators[gen_index]
|
|
1121
1371
|
if gen.is_async:
|
|
@@ -1139,7 +1389,7 @@ class Transpiler:
|
|
|
1139
1389
|
)
|
|
1140
1390
|
|
|
1141
1391
|
for nm in names:
|
|
1142
|
-
self.locals.add(nm)
|
|
1392
|
+
self._current_scope().locals.add(nm)
|
|
1143
1393
|
|
|
1144
1394
|
base = iter_expr
|
|
1145
1395
|
|
|
@@ -1159,10 +1409,11 @@ class Transpiler:
|
|
|
1159
1409
|
inner = build_chain(gen_index + 1)
|
|
1160
1410
|
return Call(Member(base, "flatMap"), [Arrow(params, inner)])
|
|
1161
1411
|
|
|
1412
|
+
self._push_scope(node)
|
|
1162
1413
|
try:
|
|
1163
1414
|
return build_chain(0)
|
|
1164
1415
|
finally:
|
|
1165
|
-
self.
|
|
1416
|
+
self._pop_scope()
|
|
1166
1417
|
|
|
1167
1418
|
|
|
1168
1419
|
def transpile(
|
|
@@ -101,13 +101,13 @@ pulse/state/property.py,sha256=pitudvCGBNqK6lePdPrs4Inyy7szo8qcR_0BI30veek,6274
|
|
|
101
101
|
pulse/state/query_param.py,sha256=7faf244_KJ3yrsBimKTBlNLg6wP96FUuWoZl40oxpyY,14478
|
|
102
102
|
pulse/state/state.py,sha256=_4DcB-og2s5Ui01kDx9HzpNwmAVD_pdCLB0TfYbcnzM,11603
|
|
103
103
|
pulse/test_helpers.py,sha256=4iO5Ymy3SMvSjh-UaAaSdqm1I_SAJMNjdY2iYVro5f8,436
|
|
104
|
-
pulse/transpiler/__init__.py,sha256=
|
|
104
|
+
pulse/transpiler/__init__.py,sha256=0UqeEQ2dMD2tDDkzTASv-StQfKZkPuSvbYTqjkNk-3A,4895
|
|
105
105
|
pulse/transpiler/assets.py,sha256=digd5hKYPEgLOzMtDBHULX3Adj1sfngdvnx3quQmgPY,2299
|
|
106
106
|
pulse/transpiler/builtins.py,sha256=QZrow7XJ2wxGMAE-mgZmaUD03egOnXCbikOg8yMx9vQ,30807
|
|
107
107
|
pulse/transpiler/dynamic_import.py,sha256=1AmBl6agGSoTZBp_94seXH733fewLOULUix9BOBPtKI,3372
|
|
108
108
|
pulse/transpiler/emit_context.py,sha256=GyK6VdsBSTVIewQRhBagaV0hlqLTlPZ1i8EAZGi8SaY,1321
|
|
109
109
|
pulse/transpiler/errors.py,sha256=LSBjLBnMglbl2D94p9JR4y-3jDefk6iHSlUVBaBOTu4,2823
|
|
110
|
-
pulse/transpiler/function.py,sha256=
|
|
110
|
+
pulse/transpiler/function.py,sha256=8-WZC2VcPTGfFeWWQpCltOVPLyr4f1H8pWoCvSnI654,18105
|
|
111
111
|
pulse/transpiler/id.py,sha256=CdgA1NndBpZjv0Hp4XiYbKn7wi-x4zWsFSjEiViKxVk,434
|
|
112
112
|
pulse/transpiler/imports.py,sha256=gWLjRr9jakbUzBGDEepE2RI5Xn_UZwOD4TmlqjNIapM,10302
|
|
113
113
|
pulse/transpiler/js_module.py,sha256=OcIgmrfiA6Hh6aukzgkyX63KsVSHdLzx5ezdKiJFUaQ,11093
|
|
@@ -118,15 +118,16 @@ pulse/transpiler/modules/math.py,sha256=8gjvdYTMqtuOnXrvX_Lwuo0ywAdSl7cpss4TMk6m
|
|
|
118
118
|
pulse/transpiler/modules/pulse/__init__.py,sha256=TfMsiiB53ZFlxdNl7jfCAiMZs-vSRUTxUmqzkLTj-po,91
|
|
119
119
|
pulse/transpiler/modules/pulse/tags.py,sha256=FMN1mWMlnsXa2qO6VmXxUAhFn1uOfGoKPQOjH4ZPlRE,6218
|
|
120
120
|
pulse/transpiler/modules/typing.py,sha256=J9QCkXE6zzwMjiprX2q1BtK-iKLIiS21sQ78JH4RSMc,1716
|
|
121
|
-
pulse/transpiler/nodes.py,sha256=
|
|
121
|
+
pulse/transpiler/nodes.py,sha256=ObdCFIEvtKMVRO8iy1hIN4L-vC4yPqRvhPS6E344-bE,52673
|
|
122
|
+
pulse/transpiler/parse.py,sha256=uz_KDnjmjzFSjGtVKRznWg95P0NHM8CafWgvqrqJcOs,1622
|
|
122
123
|
pulse/transpiler/py_module.py,sha256=um4BYLrbs01bpgv2LEBHTbhXXh8Bs174c3ygv5tHHOg,4410
|
|
123
|
-
pulse/transpiler/transpiler.py,sha256=
|
|
124
|
+
pulse/transpiler/transpiler.py,sha256=bu33-wGNqHGheT_ZqMnQgEARyPG6xyOvuLuixjxIZnI,42761
|
|
124
125
|
pulse/transpiler/vdom.py,sha256=Bf1yw10hQl8BXa6rhr5byRa5ua3qgRsVGNgEtQneA2A,6460
|
|
125
126
|
pulse/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
126
127
|
pulse/types/event_handler.py,sha256=psQCydj-WEtBcFU5JU4mDwvyzkW8V2O0g_VFRU2EOHI,1618
|
|
127
128
|
pulse/user_session.py,sha256=nsnsMgqq2xGJZLpbHRMHUHcLrElMP8WcA4gjGMrcoBk,10208
|
|
128
129
|
pulse/version.py,sha256=711vaM1jVIQPgkisGgKZqwmw019qZIsc_QTae75K2pg,1895
|
|
129
|
-
pulse_framework-0.1.
|
|
130
|
-
pulse_framework-0.1.
|
|
131
|
-
pulse_framework-0.1.
|
|
132
|
-
pulse_framework-0.1.
|
|
130
|
+
pulse_framework-0.1.74.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
131
|
+
pulse_framework-0.1.74.dist-info/entry_points.txt,sha256=i7aohd3QaPu5IcuGKKvsQQEiMYMe5HcF56QEsaLVO64,46
|
|
132
|
+
pulse_framework-0.1.74.dist-info/METADATA,sha256=m4TuEvBPlRud4ARyJi26t3nDDk2gFPvFd4xWi8k2w3k,8299
|
|
133
|
+
pulse_framework-0.1.74.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|