AST Overview
Sugar parses templates into an abstract syntax tree (AST), runs compiler passes, and then generates PHP output. Understanding the AST makes it easier to debug, extend, and build custom passes.
Pipeline At A Glance
- Parser builds the AST.
- Compiler passes transform and enrich the tree.
- Code generator emits PHP.
Common passes include directive extraction, component expansion, and context analysis for escaping.
Node Catalog
DocumentNode
The root of a template. It holds the top-level children nodes and is the entry point for traversal.
Example:
<h1>Hello</h1>DocumentNode
ElementNode(tag="h1")
TextNode("Hello")ElementNode
Represents an HTML element with tag, attributes, children, and selfClosing. When dynamicTag is set, the tag name is rendered from a runtime expression.
dynamicTag is set by the s:tag directive and lets the compiler emit a runtime tag name instead of a literal one. This keeps the AST stable (still an ElementNode) while allowing the opening and closing tags to be generated from an expression.
Example:
<div s:tag="$tagName">Content</div>ElementNode(tag="div", dynamicTag="$tagName")
TextNode("Content")Example:
<a href="/profile">Profile</a>ElementNode(tag="a", attributes=[href="/profile"])
TextNode("Profile")FragmentNode
Wrapperless node used for <s-template> blocks. It renders only its children, accepts only s:* attributes, and can be self-closing for directive-only markup.
Example:
<s-template s:if="$show">
<p>Visible</p>
</s-template>FragmentNode(attributes=[s:if])
ElementNode(tag="p")
TextNode("Visible")TextNode
Static text content. No escaping logic lives here; it is emitted directly by the code generator.
Example:
Welcome backTextNode("Welcome back")OutputNode
Dynamic output expression. It stores:
expression: PHP expression to evaluateescape: whether escaping is enabledcontext: output context used by the escaperpipes: optional pipe transformations
Example:
<?= $userName ?>OutputNode(expression="$userName", escape=true, context=HTML)RawPhpNode
Raw PHP code captured from the template. It is passed through without modification.
Example:
<?php $count = count($items); ?>RawPhpNode("$count = count($items);")RawBodyNode
Verbatim content preserved from s:raw regions. The parser does not interpret the contents, and the compiler emits them as-is.
Example:
<div s:raw>
<?php echo $notParsed; ?>
<span><?= $stillRaw ?></span>
</div>RawBodyNode("<?php echo $notParsed; ?>\n <span><?= $stillRaw ?></span>")DirectiveNode
Structural directives like s:if, s:foreach, or s:while. These nodes wrap child nodes and may carry paired siblings (for directives like forelse).
Example:
<p s:if="$show">Hello</p>DirectiveNode(name="if", expression="$show")
ElementNode(tag="p")
TextNode("Hello")ComponentNode
Represents a component invocation, such as <s-button>. It holds component attributes and slot children until the component expansion pass replaces it with the component template AST.
Example:
<s-button class="primary">Save</s-button>ComponentNode(name="button", attributes=[class="primary"])
TextNode("Save")RuntimeCallNode
A runtime call that returns output. Used when a template needs a dynamic runtime call (for example, dynamic component rendering).
Example:
RuntimeCallNode(callableExpression="$__sugar->renderComponent", arguments=["$name", "$bindings"])AttributeNode
Represents a single attribute name and its AttributeValue. The value can be boolean, static, output, or mixed parts.
Example:
<input disabled>AttributeNode(name="disabled", value=boolean)AttributeValue Shapes
AttributeValue normalizes attribute values into one of four shapes:
- Boolean: presence-only attributes like
disabled - Static: literal strings from markup
- Output: a single
OutputNode - Parts: interleaved strings and
OutputNodevalues
Examples:
<input disabled>
<a href="/profile">
<a href="<?= $url ?>">
<div class="btn <?= $state ?>">disabled -> boolean
href -> static("/profile")
href -> output(OutputNode("$url"))
class -> parts(["btn ", OutputNode("$state")])Use AttributeValue::from() to normalize legacy shapes (including null for boolean attributes). toParts() returns a parts list for rendering, or null for boolean attributes.
Output Context And Escaping
OutputNode carries an OutputContext enum that tells the escaper how to render the value. The context analysis pass sets the correct context for output nodes in element bodies and attributes.
Traversal Notes
All nodes carry source line and column, and the base Node tracks the originating template path for better diagnostics.
Some nodes implement sibling navigation helpers (for example DocumentNode, ElementNode, ComponentNode, and DirectiveNode). Use them when writing passes that depend on node order or need adjacent context.