feat(lang): UI components as files - component Name { ... } beside Name.xml and Name.lss
The compiler turns a component declaration into: - a record of its props and state; - a constructor, a props setter, a model and a call; - a class, registered with ludic.ui at start. Its template and styles are read from beside it and compiled in, with @import inlined, and a missing template fails the build. In the runtime: - each mounted instance keeps its own props and state, and `set` in a template writes the state; - styles are scoped to the component; - class, style and id on a component's tag land on its root, styled by the parent's sheet too; - ui_reload re-reads a component's files from disk and keeps instances. The package's own module is now ludic_ui, so a program may call a directory of its own ui. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
637d07400e
commit
20d011f8e9
40 changed files with 62224 additions and 49996 deletions
116
selfhost/frontend/component.ludic
Normal file
116
selfhost/frontend/component.ludic
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
# component.ludic — L11: a UI component is three files side by side: NAME.ludic declares it, NAME.xml
|
||||
# is its template and NAME.lss its styles (optional). The declaration says what it takes, keeps and
|
||||
# does, in Ludic:
|
||||
#
|
||||
# component ShopRow {
|
||||
# prop item: Val # given by the parent's template, as an attribute
|
||||
# prop picked: bool = false
|
||||
# state taps: int = 0 # the instance's own, kept while it is mounted
|
||||
# price: int = cost_of(item) # worked out each frame
|
||||
# function label() -> string { return `{taps}` }
|
||||
# on buy() { taps += 1 } # props and state are plain names here
|
||||
# }
|
||||
#
|
||||
# Each mounted instance is a record of its props and state. The compiler reads the template and the
|
||||
# styles into the program, so a missing template fails the build and nothing more has to ship, and
|
||||
# registers the component with ludic.ui when the program starts.
|
||||
var g_cm_name: []pointer = new []pointer
|
||||
var g_cm_file: []pointer = new []pointer
|
||||
var g_cm_line: []int = new []int
|
||||
var g_cm_vis: []int = new []int
|
||||
var g_cm_keep: [][]pointer = new [][]pointer # props and state, which are fields of the record
|
||||
var g_cm_keep_ty: [][]pointer = new [][]pointer
|
||||
var g_cm_prop: [][]bool = new [][]bool
|
||||
var g_cm_defs: [][]Node = new [][]Node # a getter per default, or null
|
||||
var g_cm_fields: [][]Node = new [][]Node
|
||||
var g_cm_fnames: [][]pointer = new [][]pointer
|
||||
var g_cm_calls: [][]Node = new [][]Node
|
||||
var g_cm_cnames: [][]pointer = new [][]pointer
|
||||
var g_cm_head: []Node = new []Node
|
||||
|
||||
function cm_prefix(name: pointer) -> pointer { return `cmp_{reg_lower(name)}_` }
|
||||
|
||||
# component NAME { members }
|
||||
function parse_ui_component() -> void {
|
||||
pi += 1
|
||||
let name = eat_id()
|
||||
let px = cm_prefix(name)
|
||||
g_uses_value = true
|
||||
let c = len(g_cm_name)
|
||||
push(g_cm_name, name)
|
||||
push(g_cm_file, g_parse_file)
|
||||
push(g_cm_line, toks[pi].line)
|
||||
push(g_cm_keep, new []pointer)
|
||||
push(g_cm_keep_ty, new []pointer)
|
||||
push(g_cm_prop, new []bool)
|
||||
push(g_cm_defs, new []Node)
|
||||
push(g_cm_fields, new []Node)
|
||||
push(g_cm_fnames, new []pointer)
|
||||
push(g_cm_calls, new []Node)
|
||||
push(g_cm_cnames, new []pointer)
|
||||
skipnl()
|
||||
eat_op("{")
|
||||
while true {
|
||||
skipnl()
|
||||
if is_op("}") { break }
|
||||
cm_member(c, px)
|
||||
}
|
||||
eat_op("}")
|
||||
let head = node(N_VAR)
|
||||
head.s = px + "reg"
|
||||
head.ty = "int"
|
||||
push(g_cm_head, head)
|
||||
push(prog, head)
|
||||
}
|
||||
function cm_member(c: int, px: pointer) -> void {
|
||||
if is_id("prop") or is_id("state") {
|
||||
let is_prop = is_id("prop")
|
||||
pi += 1
|
||||
let fname = eat_id()
|
||||
eat_op(":")
|
||||
let ty = ptype()
|
||||
push(g_cm_keep[c], fname)
|
||||
push(g_cm_keep_ty[c], ty)
|
||||
push(g_cm_prop[c], is_prop)
|
||||
var def: Node = null
|
||||
if is_op("=") {
|
||||
pi += 1
|
||||
def = cm_getter(px + "def_" + fname, ty, expr())
|
||||
}
|
||||
push(g_cm_defs[c], def)
|
||||
return
|
||||
}
|
||||
if is_id("function") or is_id("on") {
|
||||
let ev = is_id("on")
|
||||
if ev { toks[pi].text = "function" }
|
||||
let f = parse_fn()
|
||||
push(g_cm_cnames[c], f.s)
|
||||
if ev { f.s = px + "on_" + f.s } else { f.s = px + f.s }
|
||||
push(g_cm_calls[c], f)
|
||||
push(prog, f)
|
||||
return
|
||||
}
|
||||
let fname = eat_id()
|
||||
var ty: pointer = null
|
||||
if is_op(":") {
|
||||
pi += 1
|
||||
ty = ptype()
|
||||
}
|
||||
if not is_op("=") { perr(`component field {fname}: say what it is - {fname} = some_global`) }
|
||||
pi += 1
|
||||
push(g_cm_fnames[c], fname)
|
||||
push(g_cm_fields[c], cm_getter(px + "get_" + fname, ty, expr()))
|
||||
}
|
||||
# a function of the instance that answers one expression (a field, or a default)
|
||||
function cm_getter(name: pointer, ty: pointer, e: Node) -> Node {
|
||||
let g = node(N_FN)
|
||||
g.s = name
|
||||
g.ty = ty
|
||||
let r = node(S_RETURN)
|
||||
r.a = e
|
||||
let body = node(N_BLOCK)
|
||||
push(body.kids, r)
|
||||
g.a = body
|
||||
push(prog, g)
|
||||
return g
|
||||
}
|
||||
78
selfhost/frontend/component_class.ludic
Normal file
78
selfhost/frontend/component_class.ludic
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# component_class.ludic — L11: a component's class: its name, its template and styles as read from
|
||||
# beside it (and where they are, for a developer's build to read again), and its functions; the
|
||||
# registration is a global, so every component is known before the program's first frame
|
||||
function cm_class(c: int, px: pointer, head: Node) -> void {
|
||||
let name = g_cm_name[c]
|
||||
let dir = dir_of(g_cm_file[c])
|
||||
let xml_path = join_path(dir, name + ".xml")
|
||||
let lss_path = join_path(dir, name + ".lss")
|
||||
let xml = read_file(xml_path)
|
||||
if xml == null { cm_err(c, `there is no {name}.xml beside it - a component's template is NAME.xml in its directory`) }
|
||||
var lss = read_file(lss_path)
|
||||
var lss_at = lss_path
|
||||
if lss == null {
|
||||
lss = ""
|
||||
lss_at = ""
|
||||
} else { lss = cm_lss_inline(c, lss, dir, 0) }
|
||||
var src = `function {px}class() -> UiClass {{\n let k = new UiClass\n k.name = "{name}"\n`
|
||||
src = src + ` k.xml_path = "{xml_path}"\n k.xml = {px}xml_text()\n k.lss_path = "{lss_at}"\n k.lss = {px}lss_text()\n`
|
||||
src = src + ` k.make = fn {px}new\n k.props = fn {px}props\n k.model = fn {px}model\n k.call = fn {px}call\n return k\n}}\n`
|
||||
vw_parse(src, g_cm_file[c], g_cm_line[c], head.vis)
|
||||
cm_text_fn(c, px + "xml_text", xml, head)
|
||||
cm_text_fn(c, px + "lss_text", lss, head)
|
||||
let reg = node(E_CALL)
|
||||
let callee = node(E_ID)
|
||||
callee.s = "ui_register"
|
||||
reg.a = callee
|
||||
let arg = node(E_CALL)
|
||||
let make = node(E_ID)
|
||||
make.s = px + "class"
|
||||
arg.a = make
|
||||
arg.kids = new []Node
|
||||
reg.kids = new []Node
|
||||
push(reg.kids, arg)
|
||||
head.a = reg
|
||||
}
|
||||
# a function answering a file's text, carried in the program as it was read
|
||||
function cm_text_fn(c: int, name: pointer, text: pointer, head: Node) -> void {
|
||||
let at = len(prog)
|
||||
vw_parse(`function {name}() -> string {{\n return ""\n}}\n`, g_cm_file[c], g_cm_line[c], head.vis)
|
||||
prog[at].a.kids[0].a.s = text
|
||||
}
|
||||
# a stylesheet with each `@import "x.lss";` replaced by that file (beside it, or from the project's
|
||||
# root when it starts with /), so the program carries its whole look
|
||||
function cm_lss_inline(c: int, text: pointer, dir: pointer, depth: int) -> pointer {
|
||||
if depth > 8 { cm_err(c, "its stylesheets @import each other in a circle") }
|
||||
var out = ""
|
||||
var i = 0
|
||||
let n = len(text)
|
||||
while i < n {
|
||||
var j = i
|
||||
while j < n and text[j] != '\n' { j += 1 }
|
||||
let line: pointer = text[i..j]
|
||||
let path = cm_import_of(line)
|
||||
if path == null { out = out + line + "\n" } else {
|
||||
var at = join_path(dir, path)
|
||||
if path[0] == '/' { at = path[1..len(path)] }
|
||||
let inner = read_file(at)
|
||||
if inner == null { cm_err(c, `its stylesheet imports {path}, which is not there`) }
|
||||
out = out + cm_lss_inline(c, inner, dir_of(at), depth + 1) + "\n"
|
||||
}
|
||||
i = j + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
# the path in `@import "x";` (or with single quotes), or null for any other line
|
||||
function cm_import_of(line: pointer) -> pointer {
|
||||
var i = 0
|
||||
while i < len(line) and (line[i] == ' ' or line[i] == '\t') { i += 1 }
|
||||
if i + 7 > len(line) { return null }
|
||||
if not (line[i..i + 7] == "@import") { return null }
|
||||
var a = i + 7
|
||||
while a < len(line) and line[a] != '"' and line[a] != '\'' { a += 1 }
|
||||
if a >= len(line) { return null }
|
||||
let q = line[a]
|
||||
var b = a + 1
|
||||
while b < len(line) and line[b] != q { b += 1 }
|
||||
return line[a + 1..b]
|
||||
}
|
||||
91
selfhost/frontend/component_gen.ludic
Normal file
91
selfhost/frontend/component_gen.ludic
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# component_gen.ludic — L11: when the program is read, each component gets its record, its
|
||||
# constructor, a props setter, a model, a call and its class, registered with ludic.ui at start
|
||||
function components_finish() -> void {
|
||||
var c = 0
|
||||
while c < len(g_cm_name) {
|
||||
cm_finish(c)
|
||||
c += 1
|
||||
}
|
||||
}
|
||||
function cm_err(c: int, msg: pointer) -> void {
|
||||
g_err_file = g_cm_file[c]
|
||||
g_err_line = g_cm_line[c]
|
||||
g_parsing = false
|
||||
perr(`component {g_cm_name[c]}: {msg}`)
|
||||
}
|
||||
function cm_finish(c: int) -> void {
|
||||
if find_comp("UiClass") == null { cm_err(c, `a component is shown by ludic.ui - import "ludic.ui"`) }
|
||||
let name = g_cm_name[c]
|
||||
let px = cm_prefix(name)
|
||||
let rec = "Cmp" + name
|
||||
let head = g_cm_head[c]
|
||||
let file = g_cm_file[c]
|
||||
let line = g_cm_line[c]
|
||||
var src = `property {rec} {{\n`
|
||||
var i = 0
|
||||
while i < len(g_cm_keep[c]) {
|
||||
src = src + ` {g_cm_keep[c][i]}: {g_cm_keep_ty[c][i]}\n`
|
||||
i += 1
|
||||
}
|
||||
vw_parse(src + "}\n", file, line, head.vis)
|
||||
cm_bodies(c, rec)
|
||||
vw_parse(cm_new_src(c, px, rec), file, line, head.vis)
|
||||
vw_parse(cm_props_src(c, px, rec), file, line, head.vis)
|
||||
vw_parse(cm_model_src(c, px, rec, head), file, line, head.vis)
|
||||
vw_parse(cm_call_src(c, px, rec, head), file, line, head.vis)
|
||||
cm_class(c, px, head)
|
||||
}
|
||||
# every function of the component (getters, defaults, functions, events) takes the instance
|
||||
function cm_bodies(c: int, rec: pointer) -> void {
|
||||
var i = 0
|
||||
while i < len(g_cm_defs[c]) {
|
||||
if g_cm_defs[c][i] != null { cm_rewrite(c, g_cm_defs[c][i], rec) }
|
||||
i += 1
|
||||
}
|
||||
i = 0
|
||||
while i < len(g_cm_fields[c]) {
|
||||
let g = g_cm_fields[c][i]
|
||||
if g.ty == null { g.ty = cm_infer(c, g, g_cm_fnames[c][i]) }
|
||||
cm_rewrite(c, g, rec)
|
||||
i += 1
|
||||
}
|
||||
i = 0
|
||||
while i < len(g_cm_calls[c]) {
|
||||
cm_rewrite(c, g_cm_calls[c][i], rec)
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
function cm_infer(c: int, g: Node, fname: pointer) -> pointer {
|
||||
let e = g.a.kids[0].a
|
||||
if e.kind == E_ID and cm_is_keep(c, e.s) {
|
||||
var i = 0
|
||||
while i < len(g_cm_keep[c]) {
|
||||
if (g_cm_keep[c][i] == e.s) { return g_cm_keep_ty[c][i] }
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
if e.kind == E_ID {
|
||||
let d = find_global(e.s)
|
||||
if d != null and d.ty != null { return d.ty }
|
||||
}
|
||||
if e.kind == E_INT { return "int" }
|
||||
if e.kind == E_FLOAT { return "float" }
|
||||
if e.kind == E_BOOL { return "bool" }
|
||||
if e.kind == E_STR { return "string" }
|
||||
if e.kind == E_CALL and e.a != null and e.a.kind == E_ID {
|
||||
let f = find_fn(e.a.s)
|
||||
if f != null and f.ty != null and not (f.ty == "void") { return f.ty }
|
||||
}
|
||||
g_cm_line[c] = g.line
|
||||
cm_err(c, `say what type {fname} is - {fname}: int = ...`)
|
||||
return "int"
|
||||
}
|
||||
function cm_new_src(c: int, px: pointer, rec: pointer) -> pointer {
|
||||
var src = `function {px}new() -> pointer {{\n let s = new {rec}\n`
|
||||
var i = 0
|
||||
while i < len(g_cm_keep[c]) {
|
||||
if g_cm_defs[c][i] != null { src = src + ` s.{g_cm_keep[c][i]} = {px}def_{g_cm_keep[c][i]}(s)\n` }
|
||||
i += 1
|
||||
}
|
||||
return src + " return s\n}\n"
|
||||
}
|
||||
90
selfhost/frontend/component_gen2.ludic
Normal file
90
selfhost/frontend/component_gen2.ludic
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# component_gen2.ludic — L11: a component's props from the parent's attributes, its model for the
|
||||
# template (props, state and fields), and its call (functions, events, and `set` of a state)
|
||||
function cm_props_src(c: int, px: pointer, rec: pointer) -> pointer {
|
||||
var src = `function {px}props(p: pointer, v: Val) -> void {{\n let s: {rec} = p\n`
|
||||
var i = 0
|
||||
while i < len(g_cm_keep[c]) {
|
||||
if g_cm_prop[c][i] {
|
||||
let k = g_cm_keep[c][i]
|
||||
let got = cm_from(c, g_cm_keep_ty[c][i], "value_get(v, \"" + k + "\")")
|
||||
src = src + ` if value_has(v, "{k}") != 0 {{ s.{k} = {got} }}\n`
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return src + "}\n"
|
||||
}
|
||||
# a template's value as a prop or state of the component's own type
|
||||
function cm_from(c: int, ty: pointer, x: pointer) -> pointer {
|
||||
if (ty == "Val") { return x }
|
||||
if (ty == "int") { return `value_as_int({x})` }
|
||||
if (ty == "float") { return `value_as_float({x})` }
|
||||
if (ty == "bool") { return `value_as_int({x}) != 0` }
|
||||
if (ty == "string") { return `value_as_str({x})` }
|
||||
cm_err(c, `a prop or state is an int, a float, a bool, a string or a Val, not a {ty}`)
|
||||
return x
|
||||
}
|
||||
function cm_model_src(c: int, px: pointer, rec: pointer, head: Node) -> pointer {
|
||||
var src = `function {px}model(p: pointer) -> Val {{\n let s: {rec} = p\n let o = value_object()\n`
|
||||
var i = 0
|
||||
while i < len(g_cm_keep[c]) {
|
||||
let k = g_cm_keep[c][i]
|
||||
let put = cm_to(c, g_cm_keep_ty[c][i], "s." + k, head)
|
||||
src = src + ` value_put(o, "{k}", {put})\n`
|
||||
i += 1
|
||||
}
|
||||
i = 0
|
||||
while i < len(g_cm_fields[c]) {
|
||||
let g = g_cm_fields[c][i]
|
||||
let put = cm_to(c, g.ty, g.s + "(s)", head)
|
||||
src = src + ` value_put(o, "{g_cm_fnames[c][i]}", {put})\n`
|
||||
i += 1
|
||||
}
|
||||
return src + " return o\n}\n"
|
||||
}
|
||||
function cm_to(c: int, ty: pointer, x: pointer, head: Node) -> pointer {
|
||||
if (ty == "Val") { return x }
|
||||
let conv = vw_conv(0, ty, x, head)
|
||||
if conv == null { cm_err(c, `a {ty} is something a template cannot read`) }
|
||||
return conv
|
||||
}
|
||||
# call(name, args): a function or an event by name, or "set:NAME" for a state the template sets.
|
||||
# A name it does not know answers a Value of kind 99, which the runtime reports.
|
||||
function cm_call_src(c: int, px: pointer, rec: pointer, head: Node) -> pointer {
|
||||
var src = `function {px}call(p: pointer, name: string, args: Val) -> Val {{\n let s: {rec} = p\n`
|
||||
let calls = g_cm_calls[c]
|
||||
var i = 0
|
||||
while i < len(calls) {
|
||||
let f = calls[i]
|
||||
var args = "s"
|
||||
var k = 1
|
||||
while k < len(f.kids) {
|
||||
args = args + ", " + cm_arg(c, f.kids[k], k - 1)
|
||||
k += 1
|
||||
}
|
||||
src = src + ` if name == "{g_cm_cnames[c][i]}" {{\n`
|
||||
let call = f.s + "(" + args + ")"
|
||||
if f.ty == "void" { src = src + ` {call}\n return value_null()\n` } else {
|
||||
let back = cm_to(c, f.ty, call, head)
|
||||
src = src + ` return {back}\n`
|
||||
}
|
||||
src = src + " }\n"
|
||||
i += 1
|
||||
}
|
||||
i = 0
|
||||
while i < len(g_cm_keep[c]) {
|
||||
if not g_cm_prop[c][i] {
|
||||
let k = g_cm_keep[c][i]
|
||||
let got = cm_from(c, g_cm_keep_ty[c][i], "value_at(args, 0)")
|
||||
src = src + ` if name == "set:{k}" {{\n s.{k} = {got}\n return value_null()\n }}\n`
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return src + " return value_new(99)\n}\n"
|
||||
}
|
||||
# a parameter out of the template's argument list
|
||||
function cm_arg(c: int, p: Node, i: int) -> pointer {
|
||||
let at = `value_at(args, {i})`
|
||||
if (p.ty == "int") or (p.ty == "float") or (p.ty == "bool") or (p.ty == "string") or (p.ty == "Val") { return cm_from(c, p.ty, at) }
|
||||
cm_err(c, `{p.s} is a {p.ty}; a template can pass an int, a float, a bool, a string or a Val`)
|
||||
return ""
|
||||
}
|
||||
99
selfhost/frontend/component_rw.ludic
Normal file
99
selfhost/frontend/component_rw.ludic
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
# component_rw.ludic — L11: a component's code names its props, state, fields and functions plainly;
|
||||
# each becomes the instance's: `taps += 1` is `self.taps += 1`, `label()` is cmp_x_label(self), a
|
||||
# field is its getter called on self. Every function of the component takes `self` first.
|
||||
function cm_rewrite(c: int, f: Node, self_ty: pointer) -> void {
|
||||
var i = 0
|
||||
while i < len(f.kids) {
|
||||
let p = f.kids[i]
|
||||
if cm_is_keep(c, p.s) or cm_is_field(c, p.s) {
|
||||
g_err_file = f.file
|
||||
g_err_line = f.line
|
||||
g_parsing = false
|
||||
perr(`component {g_cm_name[c]}: the parameter {p.s} has the name of its prop, state or field; call it something else`)
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
let me = node(N_PARAM)
|
||||
me.s = "self"
|
||||
me.ty = self_ty
|
||||
let kids = new []Node
|
||||
push(kids, me)
|
||||
i = 0
|
||||
while i < len(f.kids) {
|
||||
push(kids, f.kids[i])
|
||||
i += 1
|
||||
}
|
||||
f.kids = kids
|
||||
cm_rw(c, f.a)
|
||||
}
|
||||
function cm_is_keep(c: int, s: pointer) -> bool {
|
||||
var i = 0
|
||||
while i < len(g_cm_keep[c]) {
|
||||
if (g_cm_keep[c][i] == s) { return true }
|
||||
i += 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
function cm_is_field(c: int, s: pointer) -> bool {
|
||||
var i = 0
|
||||
while i < len(g_cm_fnames[c]) {
|
||||
if (g_cm_fnames[c][i] == s) { return true }
|
||||
i += 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
function cm_is_call(c: int, s: pointer) -> bool {
|
||||
var i = 0
|
||||
while i < len(g_cm_cnames[c]) {
|
||||
if (g_cm_cnames[c][i] == s) { return true }
|
||||
i += 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
function cm_self() -> Node {
|
||||
let e = node(E_ID)
|
||||
e.s = "self"
|
||||
return e
|
||||
}
|
||||
function cm_rw(c: int, n: Node) -> void {
|
||||
if n == null { return }
|
||||
if (n.kind == S_LET or n.kind == S_FOR) and n.s != null and (cm_is_keep(c, n.s) or cm_is_field(c, n.s)) {
|
||||
g_err_file = n.file
|
||||
g_err_line = n.line
|
||||
g_parsing = false
|
||||
perr(`component {g_cm_name[c]}: {n.s} is its own prop, state or field; name the local something else`)
|
||||
}
|
||||
if n.kind == E_ID and cm_is_keep(c, n.s) {
|
||||
n.kind = E_MEMBER
|
||||
n.a = cm_self()
|
||||
return
|
||||
}
|
||||
if n.kind == E_ID and cm_is_field(c, n.s) {
|
||||
n.kind = E_CALL
|
||||
let callee = node(E_ID)
|
||||
callee.s = cm_prefix(g_cm_name[c]) + "get_" + n.s
|
||||
n.a = callee
|
||||
n.kids = new []Node
|
||||
push(n.kids, cm_self())
|
||||
return
|
||||
}
|
||||
if n.kind == E_CALL and n.a != null and n.a.kind == E_ID and cm_is_call(c, n.a.s) and not cm_is_keep(c, n.a.s) {
|
||||
n.a.s = cm_prefix(g_cm_name[c]) + n.a.s
|
||||
let args = new []Node
|
||||
push(args, cm_self())
|
||||
var i = 0
|
||||
while i < len(n.kids) {
|
||||
push(args, n.kids[i])
|
||||
i += 1
|
||||
}
|
||||
n.kids = args
|
||||
}
|
||||
cm_rw(c, n.a)
|
||||
cm_rw(c, n.b)
|
||||
cm_rw(c, n.c)
|
||||
var i = 0
|
||||
while i < len(n.kids) {
|
||||
cm_rw(c, n.kids[i])
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
|
@ -1017,6 +1017,7 @@ function parse_one_decl() -> void {
|
|||
if is_id("registry") and (toks[pi + 1].kind == TK_ID) and (toks[pi + 2].text == "of") { parse_registry(); return } # L8
|
||||
if is_id("def") and (toks[pi + 1].kind == TK_ID) and (toks[pi + 2].kind == TK_ID) { parse_def(); return } # L8
|
||||
if is_id("view") and (toks[pi + 1].kind == TK_ID) and (toks[pi + 2].text == "{") { parse_view(); return } # L11
|
||||
if is_id("component") and (toks[pi + 1].kind == TK_ID) and (toks[pi + 2].text == "{") { parse_ui_component(); return } # L11
|
||||
if is_id("var") { push(prog, parse_var()); return }
|
||||
if is_id("const") { push(prog, parse_const()); return }
|
||||
var is_unsafe = false
|
||||
|
|
@ -1514,4 +1515,5 @@ function parse_program() -> void {
|
|||
}
|
||||
registries_finish() # L8: each registry gets its defs
|
||||
views_finish() # L11: each view gets its model and call
|
||||
components_finish() # L11: each component gets its class
|
||||
}
|
||||
|
|
|
|||
55581
selfhost/ludicc.seed.ll
55581
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue