Module

Tramaj.Halogen

Package
purescript-tramaj-halogen
Repository
lucasdicioccio/purescript-tramaj-halogen

Folds the normative Tramaj.Node AST into real Halogen output. The only module in this repository that depends on halogen — Tramaj.Ast/Parser/Eval/Node have no Halogen dependency at all, so the same evaluated tree can be folded to something else entirely.

Three v2 changes shape this module. A node is no longer guaranteed to be a single element, because fragments are real nodes, so the fold returns an Array. An element may carry any number of actions rather than at most one. And attribute values, text values and the element value slot are arbitrary JSON rather than pre-stringified text — evaluation deliberately stops short of deciding how a number renders, which makes it this module's decision (renderScalar).

v3-symbols support: a value anywhere in the tree may be the §5.3 wire tag {"$sym": <id>, "path": [...]} rather than an ordinary scalar, and renderScalar recognizes it unconditionally rather than behind a mode flag — that shape cannot arise honestly from concrete-mode output, since "$sym" as a literal object key is a parse error, so seeing the shape already tells the whole story. renderSymbolTable and renderConstraintTable render the envelope's "symbols"/ "constraints" arrays the same way; renderTypesTable and renderTypeConstraintTable do the same for v4-types \S8's "types"/ "type-constraints" arrays. This module is an example host, not a normative one, so treat the table layout and origin formatting below as a starting point, not a contract.

#foldToHalogen Source

foldToHalogen :: forall action slots m. (String -> String -> Json -> Maybe action) -> Node -> Array (ComponentHTML action slots m)

dispatch maps an action — its event type, its key, and its JSON payload — onto a real Halogen action; a host that wants purely read-only rendering supplies \_ _ _ -> Nothing. An action that maps to Nothing attaches no handler at all.

Every action dispatch accepts is wired to HE.onClick, without this module looking at the event type: evaluation does not restrict that string to any vocabulary, so dispatch is the only place that can tell "on-click" from anything else and must return Nothing for event types it does not want turned into a click. Continuous-input handling is out of scope.

Returns an Array because a Tramaj.Node need not be a single element: a fragment contributes its children with no wrapper, which is the whole point of it. A host rendering a document root splices the result into whatever container it already has.

foldToHalogen trusts that attribute names are legal DOM attribute names and passes them to HP.attr. The language does not restrict them, so a template can produce a name the DOM rejects, throwing mid-render. Call validateAttrNames first and handle a non-empty result rather than folding straight through.

An svg element and its descendants are built in the SVG namespace so they actually paint (see foldIn).

#renderScalar Source

renderScalar :: Json -> String

How this host renders a JSON value as DOM text. A symbol (v3-symbols §5.3's {"$sym": ..., "path": [...]} tag) renders as its id with its path dotted on, e.g. #0:"d".replicas. Otherwise: a string is itself, a whole number drops its trailing .0, null renders as nothing, and anything structured falls back to compact JSON.

Deliberately this module's decision rather than the evaluator's: a different host targeting YAML or a UI model would render the same values differently, and v2's Node keeps them unconverted precisely so that it can.

#isValidAttrName Source

isValidAttrName :: String -> Boolean

A DOM attribute name this module is willing to set: alphanumeric plus -/_ only (no spaces, colons, quotes) — deliberately stricter than HTML itself permits, since the only names templates should need are kebab-case/snake_case identifiers like data-count.

#validateAttrNames Source

validateAttrNames :: Node -> Array String

Every invalid attribute name found anywhere in the tree (deduplicated, tree order), or [] if node is safe to fold with foldToHalogen.

#renderSymbolTable Source

renderSymbolTable :: forall action slots m. Array Json -> ComponentHTML action slots m

Renders a v3-symbols §5.2 "symbols" array (the envelope's symbol table) as an HTML table: id, origin (an allocation's site and key, or a demand's path), and the binding it was read into, if any. An entry that doesn't match the expected shape renders as a "?" row rather than being dropped or throwing — this is reading a JSON contract, not typed data.

#renderConstraintTable Source

renderConstraintTable :: forall action slots m. Array Json -> ComponentHTML action slots m

Renders a v3-symbols §5.2 "constraints" array as an HTML table: name and arguments, each argument shown with renderScalar — so a symbol argument renders the same placeholder text renderSymbolTable's rows and the document tree use.

#renderTypesTable Source

renderTypesTable :: forall action slots m. Array Json -> ComponentHTML action slots m

Renders a v4-types \S8 "types" array (the envelope's type table) as an HTML table: canonical id, and its definition rendered to short text by resolvedTypeText. Declaration boundaries stay boundaries here too — a ref definition shows only the id it points at, never the pointee's own definition inlined, since that entry is its own row.

#renderTypeConstraintTable Source

renderTypeConstraintTable :: forall action slots m. Array Json -> ComponentHTML action slots m

Renders a v4-types \S8 "type-constraints" array — same shape as renderConstraintTable's "constraints" (name/arguments), except a type argument is the erased {"$type": ...} tag \S7 reserves rather than a plain scalar, so it needs its own argument formatter.

#renderCard Source

renderCard :: forall action slots m. Card -> ComponentHTML action slots m

Renders a Tramaj.Analysis.Card — the same five-field summary tramaj-cli analyze card prints as JSON — as a definition list: what the program produces, what its own $ctx must supply, every library it reaches, every action key it can emit, and which import sites still leave one of their library's own parameters unsupplied.

Unlike the symbol/constraint/type tables above, this doesn't decode a JSON envelope — a host that already has the Program and the LibraryTable (as the playground does, from its own tabs) calls Tramaj.Analysis.Card.programCard directly and hands the typed result here.