Files
el/ui/vessels/el-ui-compiler/src/main.el

143 lines
6.0 KiB
EmacsLisp

// el-ui-compiler Elbrowser-target component compiler (STUB).
//
// The Rust crate transforms `.el` component files into JavaScript using a
// real lexer/parser/codegen pipeline backed by the el-ui runtime
// (`el-ui.js`). This El surface is a placeholder.
//
// RUNTIME GAP load-bearing:
// `elc` (the bootstrap El compiler) currently emits C only. There is no
// JavaScript or WebAssembly backend yet, so the El surface cannot
// actually compile a component to JS today.
//
// What this vessel provides:
// - The shape of the public API (Compiler with runtime_path,
// compile_component, compile_app).
// - A `CompileResult` record consumers can branch on.
// - Stub bodies that return a deterministic "not yet implemented"
// module string so downstream tooling can integration-test against
// a stable contract.
//
// When elc gains a JS/Wasm backend, swap the bodies of `lex`, `parse`,
// `semantic_check`, and `codegen` for real implementations the public
// surface should not need to change.
// Errors
fn err_lex() -> String { "elc.lex" }
fn err_parse() -> String { "elc.parse" }
fn err_semantic() -> String { "elc.semantic" }
fn err_codegen() -> String { "elc.codegen" }
fn err_backend_missing() -> String { "elc.backend_missing" }
// Compile result
//
// Modeled as JSON since the runtime's product types and field accessors
// don't yet support the Bool/String mix we want here cleanly.
fn result_ok(code: String) -> String {
"{\"ok\":true,\"code\":" + json_quote(code) + ",\"error\":\"\"}"
}
fn result_err(error: String) -> String {
"{\"ok\":false,\"code\":\"\",\"error\":\"" + error + "\"}"
}
// Minimal JSON string quoter (escape backslash + double-quote only).
// elc still doesn't expose a json_string_quote builtin.
fn json_quote(s: String) -> String {
let escaped: String = str_replace(s, "\\", "\\\\")
let escaped = str_replace(escaped, "\"", "\\\"")
"\"" + escaped + "\""
}
fn result_ok_p(result_json: String) -> Bool {
json_get_bool(result_json, "ok")
}
// Compiler config
fn compiler_default_runtime_path() -> String { "./el-ui.js" }
fn compiler_default_target() -> String { "js" }
fn compiler_new() -> String {
"{\"runtime_path\":\"./el-ui.js\",\"target\":\"js\"}"
}
fn compiler_with_runtime(c_json: String, path: String) -> String {
json_set(c_json, "runtime_path", json_quote(path))
}
fn compiler_with_target(c_json: String, target: String) -> String {
json_set(c_json, "target", json_quote(target))
}
fn compiler_runtime_path(c_json: String) -> String {
json_get_string(c_json, "runtime_path")
}
fn compiler_target(c_json: String) -> String {
json_get_string(c_json, "target")
}
// Pipeline stages (stubs)
//
// Real implementations live in the Rust crate (lexer.rs, parser.rs,
// semantic.rs, codegen.rs). These El stubs only validate inputs and
// produce a marker payload so callers can wire the contract.
fn lex(source: String) -> String {
if str_eq(source, "") { return "" }
"[]" // would be tokens JSON
}
fn parse(tokens_json: String) -> String {
if str_eq(tokens_json, "") { return "" }
"[]" // would be Component AST JSON
}
fn semantic_check(ast_json: String) -> String {
ast_json // pass-through stub
}
fn codegen(c_json: String, ast_json: String) -> String {
let runtime: String = compiler_runtime_path(c_json)
let preamble: String = "// AUTOGENERATED by el-ui-compiler (STUB)\n"
let import_line: String = "import { register, h } from \"" + runtime + "\";\n"
let body: String = "throw new Error(\"" + err_backend_missing()
+ ": el-ui-compiler has no JS backend yet; emit C with elc instead\");\n"
preamble + import_line + body
}
// Public entry points
fn compile_component(c_json: String, source: String) -> String {
let tokens: String = lex(source)
if str_eq(tokens, "") { return result_err(err_lex()) }
let ast: String = parse(tokens)
if str_eq(ast, "") { return result_err(err_parse()) }
let checked: String = semantic_check(ast)
if str_eq(checked, "") { return result_err(err_semantic()) }
let js: String = codegen(c_json, checked)
result_ok(js)
}
// `entry_source` is the app entry; `components_json` is a JSON object map of
// name -> source text. We compile each component first, then the entry.
fn compile_app(c_json: String, entry_source: String, components_json: String) -> String {
// RUNTIME GAP: no json_object_keys() yet, so we cannot iterate the map
// generically here. Real implementation will iterate and short-circuit
// on first compile error. For the stub we just compile the entry.
compile_component(c_json, entry_source)
}
// Entry smoke test
let c: String = compiler_new()
let c = compiler_with_runtime(c, "./el-ui.js")
let r: String = compile_component(c, "component Hello { template { <p>hi</p> } }")
if result_ok_p(r) {
println("[el-ui-compiler] STUB compiled (target=" + compiler_target(c) + ")")
} else {
println("[el-ui-compiler] error: " + json_get_string(r, "error"))
}