OpenUILang (openui_lang)

Copy Markdown View Source

Zero-dependency Elixir parser, streaming engine, runtime evaluator, schema validator, and prompt generator for the OpenUI 1.0 Language (openui-lang).

OpenUILang compiles a declarative UI component catalog into a fast, zero-dependency parser and evaluator capable of turning streaming LLM tokens (root = Dashboard(...)) into validated, reactive component trees at ~50% fewer tokens than equivalent JSON.

Used as the core language engine for openui_tui (OpenUI TUI Maker & Studio for Elixir).


Features

  • Zero Runtime Package Dependencies: Powered by a native Zig NIF (native/openui_nif.zig) for lexing, Pratt expression parsing, stateful stream buffering, AST materialization, and JSON Schema validation, paired with OTP 27+ :json stdlib.
  • Line-Oriented Streaming Parser (OpenUILang.stream_parser/2): Incrementally scans, parses, and materializes the UI tree on every incoming LLM token chunk (~6.9 µs/chunk) via a stateful ErlNifResourceType (ZigStreamResource) with zero-copy BEAM term caching so Terminal UIs (openui_tui) or Phoenix LiveViews can render progressive frames in real time.
  • Positional-to-Named Schema Binding (OpenUILang.compile_schema/1): Binds compact positional calls (Stat("Throughput", $live_ops, "+12%", Sparkline($history, "info"))) to JSON Schema properties in exact document key insertion order (OpenUILang.decode_json!/1).
  • Statement-Level Diff & Patch Merging (OpenUILang.merge_statements/3): Allows LLMs to emit surgical 1–3 line statement patches (kpis = Row([...])) instead of regenerating an entire UI program, automatically pruning orphaned subtrees.
  • Reactive Runtime Evaluator (OpenUILang.evaluate_result/2):
    • Reactive $state bindings ($slab = "1", @Set($slab, "2"), @Reset($slab)).
    • Collection & string built-ins (@Each, @Filter, @Sort, @Count, @Sum, @Avg, @Min, @Max, @Round, @Concat, @Join, @If, @Default).
    • Action plans (@ToAssistant, @OpenUrl, @Run, @Set, @Reset, @Sequence).
  • Stream Demultiplexer & Flat Symbol Extractor (OpenUILang.demux/1, OpenUILang.parse_symbols/1): Separates conversational Markdown prose from fenced/unfenced openui-lang code and extracts a flat symbol table with {:ref, id} and {:var, "$name"} nodes for fixed-width TUI layout engines.
  • Round-Trip Serializer & Prompt Generator:

Installation

Add openui_lang to your list of dependencies in mix.exs (requires zig in PATH to compile the native NIF):

def deps do
  [
    {:openui_lang, "~> 1.0"}
  ]
end

Quick Start

1. Compile a Component Library Schema

Positional arguments in openui-lang map to JSON Schema properties in document key order. Use OpenUILang.decode_json!/1 to preserve property insertion order before calling OpenUILang.compile_schema/1:

schema =
  File.read!("library.json")
  |> OpenUILang.decode_json!()
  |> OpenUILang.compile_schema()

2. Parse & Evaluate an openui-lang Program

code = """
$selected_env = "prod"
root = Dashboard("Cluster Health", "hel1-dc2", [banner, kpis, controls])
banner = Alert("success", "All nodes healthy")
kpis = Row([
  Stat("Requests/s", "14.2k", "+8%", Sparkline([11, 12, 13, 14], "info")),
  Stat("Error Rate", "0.01%", "-0.02%", Sparkline([2, 1, 1, 0], "ok"))
])
controls = Form([
  Select("Environment", $selected_env, ["prod", "staging", "dev"])
], [
  Button("Inspect Environment", @ToAssistant("Show node breakdown for " + $selected_env))
])
"""

parse_result = OpenUILang.parse(code, schema)

evaluated =
  OpenUILang.evaluate_result(parse_result, %{
    cat: schema,
    functions: %{
      "Upper" => fn %{"text" => text} -> String.upcase(to_string(text || "")) end
    }
  })

evaluated["root"]         # Evaluated element tree (%{"type" => "Dashboard", "props" => ...})
evaluated["state"]        # %{"$selected_env" => "prod"}
evaluated["errors"]       # []
evaluated["errorObjects"] # Structured diagnostics with source, code, component, path, and hint

3. Progressive Streaming from LLM Token Chunks

sp = OpenUILang.stream_parser(schema)

{partial_parse, sp} = OpenUILang.push_chunk(sp, "root = Stack([Card(\"Hello\", ", false)
{final_parse, _sp}  = OpenUILang.push_chunk(sp, "[Text(\"World\")])])\n", true)

evaluated = OpenUILang.evaluate_result(final_parse, %{cat: schema})

4. Incremental Statement Patching (merge_statements/3)

When evolving a UI in chat, merge new or updated statements into the existing program while automatically pruning orphaned subtrees:

base_code = """
root = Panel("Overview", [kpis], "info")
kpis = Row([Stat("CPU", "42%")])
"""

patch_code = """
kpis = Row([Stat("CPU", "38%"), Stat("RAM", "6.4 GB")])
"""

merged = OpenUILang.merge_statements(base_code, patch_code)

5. Demultiplexing Mixed Chat Prose & OpenUI Code

mixed_llm_output = """
I added a real-time memory stat card to the KPI row:

```openui
kpis = Row([Stat("CPU", "38%"), Stat("RAM", "6.4 GB")])
```
"""

%{prose: prose, code: openui_code} = OpenUILang.demux(mixed_llm_output)
symbols = OpenUILang.parse_symbols(openui_code)
# symbols["kpis"] => {"Row", [[{"Stat", ["CPU", "38%"]}, {"Stat", ["RAM", "6.4 GB"]}]]}

Architecture Overview

ModuleFileResponsibility
OpenUILanglib/openui_lang.exPublic facade for parsing, streaming, evaluating, merging, demuxing, and prompt generation
OpenUILang.ZigNIFlib/openui_lang/zig_nif.ex • native/openui_nif.zigNative Zig NIF engine for lexing, Pratt parsing, stateful stream buffering (ZigStreamResource), AST materialization, and JSON Schema validation
OpenUILang.ASTlib/openui_lang/ast.exAST node constructors and helpers
OpenUILang.Lexerlib/openui_lang/lexer.exStatement splitter, string/comment scanner, and tokenizer
OpenUILang.Statementslib/openui_lang/statements.exTop-level assignment, query, mutation, and $state declaration classifier
OpenUILang.Expressionslib/openui_lang/expressions.exPratt expression parser for literals, operators, lambdas, components, and @ builtins/actions
OpenUILang.Parserlib/openui_lang/parser.exBatch & streaming parser orchestrator and flat symbol table extractor (parse_symbols/1)
OpenUILang.Materializelib/openui_lang/materialize.exReference resolution, cycle detection, orphan tracking, and schema validation
OpenUILang.Schemalib/openui_lang/schema.exOrder-preserving JSON decoder (decode_json!/1), schema compiler, and positional-to-named prop mapper
OpenUILang.Evaluatorlib/openui_lang/evaluator.exRuntime evaluator for $state bindings, expressions, and @Action plans
OpenUILang.Builtinslib/openui_lang/builtins.exStandard library built-in functions (@Each, @Filter, @Sort, @Sum, @Avg, @If, etc.)
OpenUILang.Ruleslib/openui_lang/rules.exForm and property validation rule engine (validate_rules/2)
OpenUILang.Mergelib/openui_lang/merge.exStatement-level diff merger (merge_statements/3) with unreachable-statement garbage collection
OpenUILang.Messagelib/openui_lang/message.exOpenUI message envelope builder (build_message/1), parser (parse_message/1), and prose/code demuxer (demux/1)
OpenUILang.Promptlib/openui_lang/prompt.exGenerates OpenUI 1.0 LLM system prompts from component catalog JSON specs
OpenUILang.Serializelib/openui_lang/serialize.exRound-trip serializer (json_to_openui/3) from evaluated element trees back to openui-lang
OpenUILang.Errorlib/openui_lang/error.exStructured diagnostic and error-hint builder

Running Tests

Run the standalone test suite:

mix test

Companion Project

  • openui_tui: Conversational Terminal UI (TUI) Studio, Jido Agentic UI Builder, and 1Hz Live Telemetry Engine built on openui_lang.

License

MIT