AST Nodes
JavaScript AST node types used by the transpiler. All nodes implement emit(out: list[str]) to generate JavaScript code.
Base Classes
Expr
class Expr(ABC):
def emit(self, out: list[str]) -> None: ...
def precedence(self) -> int: ...
def render(self) -> VDOMNode: ...Abstract base class for expression nodes.
Transpilation Hooks (override to customize behavior):
def transpile_call(
self,
args: list[ast.expr],
keywords: list[ast.keyword],
ctx: Transpiler,
) -> ExprCalled when expression is used as function: expr(args). Default emits Call.
def transpile_getattr(self, attr: str, ctx: Transpiler) -> ExprCalled for attribute access: expr.attr. Default returns Member(self, attr).
def transpile_subscript(self, key: ast.expr, ctx: Transpiler) -> ExprCalled for subscript: expr[key]. Default returns Subscript.
Python Operators:
Expr supports Python operators that build AST nodes:
+,-,*,/,%->Binary&->Binary("&&"),|->Binary("||")- Unary
-,+,~->Unary ()->Call[]->Subscript.attr->Member
Static Methods:
@staticmethod
def of(value: Any) -> ExprConvert Python value to Expr. Handles primitives, lists, dicts, sets.
@staticmethod
def register(value: Any, expr: Expr | Callable[..., Expr]) -> NoneRegister Python value for conversion via Expr.of().
Instance Methods:
def as_(self, typ_: T | type[T]) -> TCast expression to type or use as decorator.
def jsx(self) -> JsxWrap as JSX component.
Stmt
class Stmt(ABC):
def emit(self, out: list[str]) -> None: ...Abstract base class for statement nodes.
Expression Nodes
Identifier
@dataclass(slots=True)
class Identifier(Expr):
name: strJS identifier: x, foo, myFunc.
Literal
@dataclass(slots=True)
class Literal(Expr):
value: int | float | str | bool | NoneJS literal: 42, "hello", true, null.
Undefined
class Undefined(Expr): ...
UNDEFINED = Undefined() # SingletonJS undefined literal. Use Literal(None) for null.
Array
@dataclass(slots=True)
class Array(Expr):
elements: Sequence[Expr]JS array: [a, b, c].
Object
@dataclass(slots=True)
class Object(Expr):
props: Sequence[tuple[str, Expr] | Spread]JS object: { key: value, ...spread }.
Member
@dataclass(slots=True)
class Member(Expr):
obj: Expr
prop: strJS member access: obj.prop.
Subscript
@dataclass(slots=True)
class Subscript(Expr):
obj: Expr
key: ExprJS subscript: obj[key].
Call
@dataclass(slots=True)
class Call(Expr):
callee: Expr
args: Sequence[Expr]JS function call: fn(args).
Unary
@dataclass(slots=True)
class Unary(Expr):
op: str # "-", "+", "!", "typeof", "await", "void", "delete"
operand: ExprJS unary expression: -x, !x, typeof x.
Binary
@dataclass(slots=True)
class Binary(Expr):
left: Expr
op: str # "+", "-", "*", "/", "%", "**", "&&", "||", "??", "===", etc.
right: ExprJS binary expression: x + y, a && b.
Ternary
@dataclass(slots=True)
class Ternary(Expr):
cond: Expr
then: Expr
else_: ExprJS ternary: cond ? a : b.
Arrow
@dataclass(slots=True)
class Arrow(Expr):
params: Sequence[str]
body: Expr | Sequence[Stmt]JS arrow function: (x) => expr or (x) => { ... }.
Function
@dataclass(slots=True)
class Function(Expr):
params: Sequence[str]
body: Sequence[Stmt]
name: str | None = None
is_async: bool = FalseJS function: function name(params) { ... } or async function ....
Template
@dataclass(slots=True)
class Template(Expr):
parts: Sequence[str | Expr] # Alternating, starting with strJS template literal: `hello ${name}`.
Spread
@dataclass(slots=True)
class Spread(Expr):
expr: ExprJS spread: ...expr.
New
@dataclass(slots=True)
class New(Expr):
ctor: Expr
args: Sequence[Expr]JS new expression: new Ctor(args).
Statement Nodes
Return
@dataclass(slots=True)
class Return(Stmt):
value: Expr | None = NoneJS return: return expr;.
If
@dataclass(slots=True)
class If(Stmt):
cond: Expr
then: Sequence[Stmt]
else_: Sequence[Stmt] = ()JS if statement: if (cond) { ... } else { ... }.
ForOf
@dataclass(slots=True)
class ForOf(Stmt):
target: str # Can be array pattern: "[a, b]"
iter: Expr
body: Sequence[Stmt]JS for-of loop: for (const x of iter) { ... }.
While
@dataclass(slots=True)
class While(Stmt):
cond: Expr
body: Sequence[Stmt]JS while loop: while (cond) { ... }.
Break
@dataclass(slots=True)
class Break(Stmt): ...JS break;.
Continue
@dataclass(slots=True)
class Continue(Stmt): ...JS continue;.
Assign
@dataclass(slots=True)
class Assign(Stmt):
target: str
value: Expr
declare: Literal["let", "const"] | None = None
op: str | None = None # For augmented: +=, -=, etc.JS assignment: let x = expr;, x = expr;, or x += expr;.
ExprStmt
@dataclass(slots=True)
class ExprStmt(Stmt):
expr: ExprJS expression statement: expr;.
Block
@dataclass(slots=True)
class Block(Stmt):
body: Sequence[Stmt]JS block: { ... }.
Throw
@dataclass(slots=True)
class Throw(Stmt):
value: ExprJS throw: throw expr;.
JSX / Element Nodes
Element
class Element(Expr):
tag: str | Expr
props: Sequence[tuple[str, Prop] | Spread] | dict[str, Any] | None
children: Sequence[Node] | None
key: str | Expr | NoneReact element. Tag conventions:
""(empty): Fragment"div","span": HTML element"$$ComponentId": Client componentExpr: Direct component reference
Methods:
def with_children(self, children: Sequence[Node]) -> Element
def props_dict(self) -> dict[str, Any]Jsx
@dataclass(slots=True, init=False)
class Jsx(ExprWrapper):
expr: Expr
id: strJSX wrapper that makes any Expr callable as a component. When called, produces Element(tag=expr, ...).
from pulse.transpiler import Import, Jsx
app_shell = Import("AppShell", "@mantine/core")
Header = Jsx(app_shell.Header)
# In @javascript:
# Header(height=60) -> <AppShell_1.Header height={60} />PulseNode
@dataclass(slots=True)
class PulseNode:
fn: Any # Callable[..., Node]
args: tuple[Any, ...] = ()
kwargs: dict[str, Any] = field(default_factory=dict)
key: str | None = None
name: str | None = NoneServer-side Pulse component instance. During rendering, called and replaced by its returned tree. Cannot be transpiled.
Wrapper Classes
Value
@dataclass(slots=True)
class Value(Expr):
value: AnyWraps non-primitive Python value for pass-through serialization (e.g., complex props).
Transformer
@dataclass(slots=True)
class Transformer(Expr, Generic[_F]):
fn: _F
name: str = ""Expr wrapping a function that transforms args to Expr output. Used for Python->JS transpilation of builtins.
@transformer("len")
def emit_len(x, *, ctx):
return Member(ctx.emit_expr(x), "length")Utility Functions
emit
def emit(node: Expr | Stmt) -> strEmit an expression or statement as JavaScript/JSX code.
from pulse.transpiler import emit, Literal, Binary
code = emit(Binary(Literal(1), "+", Literal(2)))
# "1 + 2"to_js_identifier
def to_js_identifier(name: str) -> strNormalize a string to a JS-compatible identifier.
Type Aliases
Node: TypeAlias = Primitive | Expr | PulseNode
Child: TypeAlias = Node | Iterable[Node]
Children: TypeAlias = Sequence[Child]
Prop: TypeAlias = Primitive | Expr
Primitive: TypeAlias = bool | int | float | str | datetime | NoneGlobal Registry
EXPR_REGISTRY
EXPR_REGISTRY: dict[int, Expr]Global registry mapping id(value) to Expr. Used by Expr.of() to resolve registered Python values (functions, modules, etc.).