# 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) } g_cm_xml_path[c] = xml_path g_cm_lss_path[c] = lss_at g_cm_xml[c] = xml 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.renew = fn {px}renew\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) vw_parse(cm_renew_src(c, px, "Cmp" + name), 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] } # a component is used by its name as a tag, so two of one name (or of names that differ only in case, # which would share their functions' names) are an error naming both files function cm_unique(name: pointer) -> void { var i = 0 while i < len(g_cm_name) { if (cm_prefix(g_cm_name[i]) == cm_prefix(name)) { perr(`component {name} is declared twice: here and in {g_cm_file[i]}:{g_cm_line[i]} - a component is used by its name as a tag, so each needs a name of its own`) } i += 1 } } # an instance's record put back as `new` made it, for ludic.ui to mount again rather than make another: # each field its default, or its type's zero function cm_renew_src(c: int, px: pointer, rec: pointer) -> pointer { var src = `function {px}renew(p: pointer) -> void {{\n let s: {rec} = p\n` var i = 0 while i < len(g_cm_keep[c]) { let f = g_cm_keep[c][i] if g_cm_defs[c][i] != null { src = src + ` s.{f} = {px}def_{f}(s)\n` } else { src = src + ` s.{f} = {cm_zero(g_cm_keep_ty[c][i])}\n` } i += 1 } return src + "}\n" } function cm_zero(ty: pointer) -> pointer { if ty == "int" or ty == "long" or ty == "byte" { return "0" } if ty == "float" { return "0.0" } if ty == "fixed" { return "fixed(0)" } if ty == "bool" { return "false" } return "null" }