Sign in

    openui

    openui
    parser
    lexer
    wasm
    Download zip
    Author
    Version
    0.2.0
    License
    MIT
    Last updated
    7 days ago
    Downloads
    26

    #heyq02/openui

    MoonBit 写的 OpenUI Lang 解析器,编译成 Wasm-GC 后从 npm 使用。

    npm install @heyq02/openui.mbt @openuidev/lang-core@1.0.1 npx oui.mbt patch

    oui.mbt patch 把 @openuidev/lang-core@1.0.1 里的 createParser().parse 和 createStreamingParser() 换成 Wasm 解析和物化。Renderer 仍是官方代码。版本不是 1.0.1,或解析调用点和补丁对不上时,命令退出,已安装的文件保持原样。

    import { parseProgram } from "@heyq02/openui.mbt/parser"; const program = parseProgram('root = TextContent("Hello", "large")');

    其余入口见 npm/README.md。

    #基准测试

    2026-10-16,Linux,Node 22.23.3。同一份组件 Schema,官方 @openuidev/lang-core@1.0.1 对 oui.mbt patch 之后。复用 parser,取中位数。这些文档的整棵 ParseResult 一致。流式最后一次 push 的结果和同侧的一次 parse 也一致。

    一次 parse:

    文档官方patch 后
    注册表单,516 字节0.0745 ms0.0626 ms
    50 个标量字段,2635 字节0.1385 ms0.1800 ms
    50 个嵌套字段,5055 字节0.2354 ms0.3022 ms
    400 个标量字段,22085 字节0.9913 ms1.2910 ms
    4000 个标量字段,232685 字节11.7253 ms16.3780 ms

    流式按行 push,最后一行带结束标志:

    文档官方patch 后
    注册表单0.3378 ms0.2626 ms
    50 个标量字段2.1797 ms2.7088 ms
    50 个嵌套字段7.7688 ms13.2447 ms
    400 个标量字段145.9486 ms140.7504 ms
    4000 个标量字段16488.2482 ms16950.8052 ms

    标量字段是 root = Stack([field…]) 再加 TextContent 叶子,已关闭的叶子只编码一次。嵌套字段是 section = FormControl(…, field),引用会离开这条路径,每次 push 都完整物化。

    注册表单的一次解析和流式都更快。400 个标量字段的流式略快。50 与 4000 个标量字段的一次解析更慢,4000 个字段的流式中位数接近。嵌套文档的流式更慢。

    Wasm 二进制 94565 字节,gzip 37965。demo 页面代码 gzip 从 1341307 到 1388455。注册表单整页一次解析的中位数是 254.6 ms 和 259.7 ms。同一页按行流式再绘制的中位数是 260.7 ms 和 254.8 ms。

    npm ci --prefix web pnpm run benchmark

    --skip-demo 只测体积和 Node 解析。页面时间含两条:整份文档一次解析,以及按行 push 到结束后再绘制。

    #License

    Published under the MIT license. Made by @heyq02 and community 💛


    🤖 auto updated with automd

    CallDef

    pub struct CallDef {
    name : String
    kind : CallKind
    params : Array[ParamDef]
    }

    CallKind

    pub enum CallKind {
    Function
    Action
    }

    ComponentDef

    pub struct ComponentDef {
    name : String
    params : Array[ParamDef]
    }

    Expr

    pub enum Expr {
    Null
    Bool(Bool)
    Num(Double, String)
    Str(String)
    Ref(String)
    StateRef(String)
    Comp(String, Bool, Array[Expr])
    Arr(Array[Expr])
    Obj(Array[(String, Expr)])
    UnaryOp(String, Expr)
    BinOp(String, Expr, Expr)
    Ternary(Expr, Expr, Expr)
    Member(Expr, String)
    Index(Expr, Expr)
    Assign(String, Expr)
    }

    Expression produced by the phase-0 parser. Component calls use Comp. @Custom(...) sets is_function.

    Expr::dump

    fn Expr::dump(self : Expr) -> String

    Expr::to_json

    fn Expr::to_json(self : Expr) -> Json

    ParamDef

    pub struct ParamDef {
    name : String
    required : Bool
    shape : SchemaShape
    }

    ParseError

    pub struct ParseError {
    line : Int
    column : Int
    kind : SyntaxKind
    }

    ParseError::message

    fn ParseError::message(self : ParseError) -> String

    Program

    pub struct Program {
    statements : Array[Statement]
    errors : Array[ParseError]
    }

    Program::compact_json

    fn Program::compact_json(self : Program) -> String

    Statement JSON without the intermediate Json map. The Wasm binding parses this back in JavaScript.

    Program::to_json

    fn Program::to_json(self : Program) -> Json

    SchemaCatalog

    pub struct SchemaCatalog {
    components : Array[ComponentDef]
    calls : Array[CallDef]
    errors : Array[SchemaError]
    }

    Component and call tables compiled from a library JSON Schema document.

    SchemaCatalog::dump

    fn SchemaCatalog::dump(self : SchemaCatalog) -> String

    SchemaError

    pub struct SchemaError {
    path : String
    kind : SchemaKind
    }

    SchemaError::message

    fn SchemaError::message(self : SchemaError) -> String

    SchemaKind

    pub enum SchemaKind {
    InvalidJson(Int)
    InvalidJsonString(Int)
    InvalidJsonNumber(Int)
    Expected(String)
    } derive(Eq)

    Schema compilation failures. JSON syntax keeps the byte index.

    SchemaKind::equal

    fn SchemaKind::equal(SchemaKind, SchemaKind) -> Bool

    SchemaKind::message

    fn SchemaKind::message(self : SchemaKind) -> String

    SchemaKind::not_equal

    fn SchemaKind::not_equal(x : SchemaKind, y : SchemaKind) -> Bool

    SchemaShape

    pub enum SchemaShape {
    Any
    Str
    Num
    Boolean
    Obj
    Arr
    Null
    Ref(String)
    Enum(Array[String])
    Union(Array[SchemaShape])
    Unknown(String)
    }

    Type information kept for one component or call parameter.

    Statement

    pub struct Statement {
    id : String
    expr : Expr
    line : Int
    }

    SyntaxKind

    pub enum SyntaxKind {
    Expected(String)
    UnclosedInput
    StreamClosed
    } derive(Eq)

    Syntax and stream failures. The official JSON still carries message.

    SyntaxKind::equal

    fn SyntaxKind::equal(SyntaxKind, SyntaxKind) -> Bool

    SyntaxKind::message

    fn SyntaxKind::message(self : SyntaxKind) -> String

    SyntaxKind::not_equal

    fn SyntaxKind::not_equal(x : SyntaxKind, y : SyntaxKind) -> Bool

    Token

    pub struct Token {
    kind : TokenKind
    text : String
    line : Int
    column : Int
    }

    A lexer token. text is the decoded string, raw number lexeme, or name. Punctuation leaves text empty. State variables keep the leading $. Builtin calls store the name without @.

    Token::to_json

    fn Token::to_json(self : Token) -> Json

    TokenKind

    pub enum TokenKind {
    Newline
    LParen
    RParen
    LBrack
    RBrack
    LBrace
    RBrace
    Comma
    Colon
    Equals
    True
    False
    Null
    EOF
    Str
    Num
    Ident
    Type
    StateVar
    Dot
    Plus
    Minus
    Star
    Slash
    Percent
    EqEq
    NotEq
    Greater
    Less
    GreaterEq
    LessEq
    And
    Or
    Not
    Question
    BuiltinCall
    } derive(Eq)

    Token kinds aligned with the OpenUI Lang lexer.

    TokenKind::equal

    fn TokenKind::equal(TokenKind, TokenKind) -> Bool

    TokenKind::name

    fn TokenKind::name(self : TokenKind) -> String

    TokenKind::not_equal

    fn TokenKind::not_equal(x : TokenKind, y : TokenKind) -> Bool

    compile_schema

    fn compile_schema(src : String) -> SchemaCatalog

    Compile the component definition table from a library JSON Schema document. Property order is the positional order of OpenUI Lang arguments.

    materialize_count

    fn materialize_count(src : String, schema : String, root_name : String) -> Int

    Statement count after materialize, without encoding the tree.

    materialize_dump

    fn materialize_dump(src : String, schema : String, root_name : String) -> String

    Materialize a program against a library JSON Schema. root_name is used only when the program has no root statement.

    materialize_json

    fn materialize_json(src : String, schema : String, root_name : String) -> String

    JSON result: element tree, errors, unresolved, orphaned, and incomplete.

    materialize_result

    fn materialize_result(src : String, schema : String, root_name : String) -> String

    Official ParseResult shape, written without building a Json map.

    parse

    fn parse(src : String) -> Program

    Parse assignment statements. A bad line is reported and skipped.

    parse_json

    fn parse_json(src : String) -> String

    source_incomplete

    fn source_incomplete(src : String) -> Bool

    Unclosed strings or brackets mean the trailing construct is still incomplete.

    stream_append

    fn stream_append(id : Int, chunk : String, complete : Int) -> String

    Append text. complete 1 runs the final check in the same render.

    stream_close

    fn stream_close(id : Int) -> Int

    Drop the session. Later calls report unknown-stream.

    stream_dump

    fn stream_dump(id : Int) -> String

    stream_finish

    fn stream_finish(id : Int) -> String

    Mark the stream finished and run the final check on the whole buffer.

    stream_open

    fn stream_open(schema : String, root_name : String) -> Int

    Open a streaming parser. root_name is used only when there is no root.

    stream_push

    fn stream_push(id : Int, chunk : String) -> String

    Append text and return the latest ParseResult JSON.

    stream_replace

    fn stream_replace(id : Int, text : String, complete : Int) -> String

    Replace the buffer. A longer text that still starts with the old buffer keeps closed lines.

    stream_set

    fn stream_set(id : Int, text : String) -> String

    Replace the buffer and return the latest ParseResult JSON.

    stream_view

    fn stream_view(id : Int, complete : Int) -> String

    Read the current result. complete 1 checks the whole buffer without keeping that check.

    summarize

    fn summarize(src : String) -> String

    tokenize

    fn tokenize(src : String) -> Array[Token]

    Tokenize OpenUI Lang source. Newlines are tokens. Horizontal whitespace is not.

    tokens_json

    fn tokens_json(src : String) -> String