Overview
Add a Lisp-format UI asset loader as a parallel format to XML, route binding evaluation through Binding objects via message dispatch, and migrate the Example application runtime flow from XML assets to Lisp assets.
Motivation
XML is verbose for deeply nested UI layouts. A Lisp S-expression format provides a more concise alternative while keeping the same object model and property system. Additionally, binding evaluation should flow through the component message system rather than direct function calls, enabling future extensibility (e.g., lazy evaluation, dirty tracking).
Changes
1. Lisp UI Asset Loader (source/filesystem/fs_lisp.c)
New 792-line file implementing an S-expression parser and object builder that produces the same in-memory Object tree as the XML loader.
Syntax overview:
; XML equivalent:
; <StackView Name="Card" Direction="Vertical" Spacing=8>
; <TextBlock Name="Title" Text="{Binding DataContext/Title}"/>
; </StackView>
; Lisp equivalent:
(StackView Name="Card" Direction="Vertical" Spacing=8
(TextBlock Name="Title" Text="{Card.Title}"))
Key syntax differences from XML:
| Feature |
XML |
Lisp |
| Element |
<Tag ...> |
(Tag ...) |
| Text content |
<TextBlock>text</TextBlock> |
(TextBlock "text") |
| Binding |
{Binding DataContext/Title} |
{Card.Title} |
| Binding directive |
<BindingExpression Target="..."> |
(:bind Target="..." expr) |
| Inline expression |
N/A |
Key=(if (step 640 (bind "x")) "a" "b") |
| Nested children |
<Parent><Child/></Parent> |
(Parent (Child)) |
| Key=value atoms |
Name="foo" (always quoted) |
Name="foo" or Name=foo (unquoted ok) |
Binding expression translation:
Lisp binding expressions are translated to the ORCA VM expression string:
(bind "Node.ActualWidth") → {Node.ActualWidth}
(if a b c) → IF(a, b, c)
(step n x) → STEP(n, x)
(vector2 x y) → Vector2(x, y)
(any-other-fn ...) → FNAME(...)
2. Binding Evaluate Message (source/core/core.cgen)
Added Binding.Evaluate message to route binding evaluation through the component message system:
<message name="Evaluate">
<summary>Evaluates the binding token program and imports the result into the target property.</summary>
<fields>
<field name="Property" type="Property" pointer="true">Target property to import evaluation result into.</field>
</fields>
</message>
The Binding class now handles Binding.Compile and Binding.Evaluate messages, enabling message-driven binding lifecycle.
3. Example App Migration
All Example prefab bindings converted from legacy {Binding Path} to non-legacy {Path} syntax:
<!-- Before (legacy) -->
<TextBlock Text="{Binding DataContext/Title}"/>
<!-- After (non-legacy) -->
<TextBlock Text="{DataContext/Title}"/>
New Lisp prefab templates created for all Example prefabs:
| XML Prefab |
Lisp Prefab |
Prefabs/SignalCard.xml |
Prefabs/SignalCard.lisp |
Prefabs/IconCard.xml |
Prefabs/IconCard.lisp |
Prefabs/GalleryCard.xml |
Prefabs/GalleryCard.lisp |
Prefabs/Quote.xml |
Prefabs/Quote.lisp |
Prefabs/Mertic.xml |
Prefabs/Metric.lisp |
Prefabs/WorkflowStep.xml |
Prefabs/WorkflowStep.lisp |
Prefabs/TabPanelHeader.xml |
Prefabs/TabPanelHeader.lisp |
Prefabs/XmlModelNode.xml |
Prefabs/XmlModelNode.lisp |
Prefabs/FeatureCard.xml |
Prefabs/FeatureCard.lisp |
Prefabs/FeatureImageCard.xml |
Prefabs/FeatureImageCard.lisp |
Prefabs/ImageCaptionCard.xml |
Prefabs/ImageCaptionCard.lisp |
Lisp prefabs use dot-notation for DataContext paths:
; XML: {Binding DataContext/Title}
; Lisp: {Card.Title}
(TextBlock Name="Title" Text="{Card.Title}")
(TextBlock Name="Value" ForegroundColor="{../Card.PrimaryColor}")
Active Example runtime now points to Lisp assets:
StartupScreen → Example/Screens/Application.lisp
PlaceholderTemplate → Example/Prefabs/*.lisp
- Modal path →
Example/Screens/GetStartedPopup.lisp
Files Changed
| File |
Change |
source/filesystem/fs_lisp.c |
New — 792-line Lisp UI loader |
source/core/core.cgen |
Added Binding.Evaluate message |
source/core/property/property_program.c |
295 lines of binding compile/evaluate changes |
source/core/property/property_core.c |
Binding evaluation routing |
samples/Example/Screens/Application.lisp |
New — 422-line Lisp screen |
samples/Example/Screens/GetStartedPopup.lisp |
New — Lisp popup |
samples/Example/Prefabs/*.lisp |
New — 11 Lisp prefab templates |
tests/test_lisp.lua |
New — 252-line test suite |
Validation
make unite — C build passes
make test-headless — All layout and interaction tests pass
tests/test_lisp.lua — 252-line Lisp loader test suite passes
References
Overview
Add a Lisp-format UI asset loader as a parallel format to XML, route binding evaluation through Binding objects via message dispatch, and migrate the Example application runtime flow from XML assets to Lisp assets.
Motivation
XML is verbose for deeply nested UI layouts. A Lisp S-expression format provides a more concise alternative while keeping the same object model and property system. Additionally, binding evaluation should flow through the component message system rather than direct function calls, enabling future extensibility (e.g., lazy evaluation, dirty tracking).
Changes
1. Lisp UI Asset Loader (
source/filesystem/fs_lisp.c)New 792-line file implementing an S-expression parser and object builder that produces the same in-memory Object tree as the XML loader.
Syntax overview:
Key syntax differences from XML:
<Tag ...>(Tag ...)<TextBlock>text</TextBlock>(TextBlock "text"){Binding DataContext/Title}{Card.Title}<BindingExpression Target="...">(:bind Target="..." expr)Key=(if (step 640 (bind "x")) "a" "b")<Parent><Child/></Parent>(Parent (Child))Name="foo"(always quoted)Name="foo"orName=foo(unquoted ok)Binding expression translation:
Lisp binding expressions are translated to the ORCA VM expression string:
2. Binding Evaluate Message (
source/core/core.cgen)Added
Binding.Evaluatemessage to route binding evaluation through the component message system:The
Bindingclass now handlesBinding.CompileandBinding.Evaluatemessages, enabling message-driven binding lifecycle.3. Example App Migration
All Example prefab bindings converted from legacy
{Binding Path}to non-legacy{Path}syntax:New Lisp prefab templates created for all Example prefabs:
Prefabs/SignalCard.xmlPrefabs/SignalCard.lispPrefabs/IconCard.xmlPrefabs/IconCard.lispPrefabs/GalleryCard.xmlPrefabs/GalleryCard.lispPrefabs/Quote.xmlPrefabs/Quote.lispPrefabs/Mertic.xmlPrefabs/Metric.lispPrefabs/WorkflowStep.xmlPrefabs/WorkflowStep.lispPrefabs/TabPanelHeader.xmlPrefabs/TabPanelHeader.lispPrefabs/XmlModelNode.xmlPrefabs/XmlModelNode.lispPrefabs/FeatureCard.xmlPrefabs/FeatureCard.lispPrefabs/FeatureImageCard.xmlPrefabs/FeatureImageCard.lispPrefabs/ImageCaptionCard.xmlPrefabs/ImageCaptionCard.lispLisp prefabs use dot-notation for DataContext paths:
Active Example runtime now points to Lisp assets:
StartupScreen→Example/Screens/Application.lispPlaceholderTemplate→Example/Prefabs/*.lispExample/Screens/GetStartedPopup.lispFiles Changed
source/filesystem/fs_lisp.csource/core/core.cgenBinding.Evaluatemessagesource/core/property/property_program.csource/core/property/property_core.csamples/Example/Screens/Application.lispsamples/Example/Screens/GetStartedPopup.lispsamples/Example/Prefabs/*.lisptests/test_lisp.luaValidation
make unite— C build passesmake test-headless— All layout and interaction tests passtests/test_lisp.lua— 252-line Lisp loader test suite passesReferences