feat(lang): 0.S - components take states in their header; migrate writes them, never inside a name, never outside the programs given, never into a package's state
- component Name (a: mut A, b: B) { ... }: every getter, default, function and event takes the
header's states before the instance; a member's call to another passes them on; the glue is
supplied them; the template never sees them; a read-only one is read-only in every member
- ludic migrate state: a component's members' needs go into its header (added to an existing one,
mut added where now changed); a field read or a member call inside a component is the compiler's,
so nothing is written inside a name and no ', )' is left; an entry point that declares states
already gets the rest after them
- a program's module named like a package gets <Name>AppState; a program's own file its own state;
a friend module's files go by directory; a package's settable var stays state
- it writes only under the programs and directories given (and runtime/ with --runtime), and
refuses the whole run naming any other file that would have to change
- a name a package already moved into its state is rewritten through it; a read of the runtime's
var through the runtime function that answers it (gl_w: gl_width())
- a state's instance supplied by the runtime is not a uses reference
- tests: state/component, rejected/state_component_ro, rendering/ui_render3d, migrate component
and foreign cases
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c167ecc714
commit
ce80e64b24
23 changed files with 67967 additions and 61444 deletions
|
|
@ -13,6 +13,7 @@ var mgx_len: []int = null
|
|||
var mgx_text: []pointer = null
|
||||
var mgx_texts: []pointer = null # the files' texts, read once
|
||||
var mgx_names: []pointer = null
|
||||
var mgx_roots: []pointer = null # where it may write: the programs and directories it was given
|
||||
|
||||
function mgx_unesc(s: pointer) -> pointer {
|
||||
var out = ""
|
||||
|
|
@ -99,32 +100,85 @@ function mgx_close(t: pointer, at: int) -> int {
|
|||
}
|
||||
return -1
|
||||
}
|
||||
# a directory as the plans spell a path (relative to here, `..` resolved), with a trailing '/'
|
||||
function mgx_dir(d: pointer) -> pointer {
|
||||
var p = mgx_norm(d)
|
||||
if len(p) == 0 or (p == ".") { return "" }
|
||||
if p[len(p) - 1] != '/' { p = p + "/" }
|
||||
return p
|
||||
}
|
||||
function mgx_norm(p0: pointer) -> pointer {
|
||||
var p = p0
|
||||
let cwd = getenv_or("PWD", "")
|
||||
if len(cwd) > 0 and s_starts(p, cwd + "/") { p = p[len(cwd) + 1 .. len(p)] }
|
||||
let abs = len(p) > 0 and p[0] == '/'
|
||||
let segs = new []pointer
|
||||
var n = 0
|
||||
var a = 0
|
||||
for i in 0 .. len(p) + 1 {
|
||||
if i == len(p) or p[i] == '/' {
|
||||
let seg = p[a .. i]
|
||||
if (seg == "..") and n > 0 and not (segs[n - 1] == "..") { n -= 1 }
|
||||
else if not (seg == ".") and not (seg == "") {
|
||||
if n < len(segs) { segs[n] = seg } else { push(segs, seg) }
|
||||
n += 1
|
||||
}
|
||||
a = i + 1
|
||||
}
|
||||
}
|
||||
var out = ""
|
||||
if abs { out = "/" }
|
||||
for i in 0 .. n {
|
||||
if i > 0 { out = out + "/" }
|
||||
out = out + segs[i]
|
||||
}
|
||||
return out
|
||||
}
|
||||
# may it write this file: it is under one of the programs' directories (a package the program only
|
||||
# imports - installed, or a neighbour's - is not the migration's to change)
|
||||
function mgx_mine(path: pointer) -> bool {
|
||||
let p = mgx_norm(path)
|
||||
for i in 0 .. len(mgx_roots) {
|
||||
if mgx_roots[i] == "" and not (p[0] == '/') and not s_starts(p, "../") { return true }
|
||||
if not (mgx_roots[i] == "") and s_starts(p, mgx_roots[i]) { return true }
|
||||
}
|
||||
return false
|
||||
}
|
||||
function cmd_migrate() -> int {
|
||||
if arg_count() < 3 or not (arg(2) == "state") {
|
||||
err("usage: ludic migrate state [file|dir...] [--runtime] [--unsafe] [--dry-run]\n")
|
||||
return 2
|
||||
}
|
||||
let srcs = new []pointer
|
||||
mgx_roots = new []pointer
|
||||
var runtime = ""
|
||||
var dry = false
|
||||
var ai = 3
|
||||
while ai < arg_count() {
|
||||
let a = arg(ai)
|
||||
if a == "--runtime" { runtime = " --migrate-runtime" }
|
||||
if a == "--runtime" {
|
||||
runtime = " --migrate-runtime"
|
||||
push(mgx_roots, "runtime/")
|
||||
}
|
||||
else if a == "--unsafe" { g_unsafe_build = true }
|
||||
else if a == "--dry-run" { dry = true }
|
||||
else if a[0] == '-' { err(`ludic migrate: unknown option {a}\n`); return 2 }
|
||||
else if shq(`test -d {sh_single(a)}`) {
|
||||
let found = test_files_in(a)
|
||||
for k in 0 .. len(found) { push(srcs, found[k]) }
|
||||
push(mgx_roots, mgx_dir(a))
|
||||
}
|
||||
else {
|
||||
push(srcs, a)
|
||||
push(mgx_roots, mgx_dir(dir_of_path(a)))
|
||||
}
|
||||
else { push(srcs, a) }
|
||||
ai += 1
|
||||
}
|
||||
if len(srcs) == 0 {
|
||||
let entry = find_entry("")
|
||||
if entry == "" { return no_entry() }
|
||||
push(srcs, entry)
|
||||
push(mgx_roots, mgx_dir(dir_of_path(entry)))
|
||||
}
|
||||
ensure_ludicc()
|
||||
# every program's plan from the source as it is, then one pass over the files: programs that
|
||||
|
|
@ -190,11 +244,21 @@ function mgx_apply(lines: []pointer, dry: bool) -> int {
|
|||
let sexp = new []int
|
||||
let smore = new []int # 1: the state is already declared, and gains fields
|
||||
let reports = new []pointer
|
||||
let foreign = new []pointer # files a plan would change that the migration may not
|
||||
let lfile = new []pointer
|
||||
let lpos = new []int
|
||||
for i in 0 .. len(lines) {
|
||||
let w = dp_words(lines[i])
|
||||
if len(w) == 0 { continue }
|
||||
var at = ""
|
||||
if (w[0] == "VAR") or (w[0] == "REF") or (w[0] == "INS") or (w[0] == "CLOSE") or (w[0] == "LET") { at = w[1] }
|
||||
if (w[0] == "STATE") and len(w) > 2 { at = w[2] }
|
||||
if len(at) > 0 and mgx_roots != null and not mgx_mine(at) {
|
||||
var seen = false
|
||||
for k in 0 .. len(foreign) { if foreign[k] == at { seen = true } }
|
||||
if not seen { push(foreign, at) }
|
||||
continue
|
||||
}
|
||||
if w[0] == "VAR" and len(w) == 6 {
|
||||
push(vfile, w[1])
|
||||
push(vpos, s_to_int(w[2]))
|
||||
|
|
@ -228,6 +292,11 @@ function mgx_apply(lines: []pointer, dry: bool) -> int {
|
|||
mgx_edit(w[1], s_to_int(w[2]), s_to_int(w[3]), mgx_unesc(w[4]))
|
||||
} else if (w[0] == "INS") and len(w) == 4 {
|
||||
mgx_edit(w[1], s_to_int(w[2]), 0, mgx_unesc(w[3]))
|
||||
} else if (w[0] == "CLOSE") and len(w) == 4 {
|
||||
let t = mgx_text_of(w[1])
|
||||
var k = s_to_int(w[2])
|
||||
while t[k] != 0 and t[k] != ')' { k += 1 }
|
||||
mgx_edit(w[1], k, 0, mgx_unesc(w[3]))
|
||||
} else if w[0] == "LET" and len(w) == 3 {
|
||||
push(lfile, w[1])
|
||||
push(lpos, s_to_int(w[2]))
|
||||
|
|
@ -249,6 +318,23 @@ function mgx_apply(lines: []pointer, dry: bool) -> int {
|
|||
}
|
||||
}
|
||||
}
|
||||
# a change the programs need in a file it may not write (a package's, a neighbour's): refused,
|
||||
# whole - that code is migrated where it lives, or given here too
|
||||
if len(foreign) > 0 {
|
||||
err("ludic migrate: these files would have to change too, and are not under a program or directory given; nothing was changed:\n")
|
||||
for f in 0 .. len(foreign) { err(` {foreign[f]}\n`) }
|
||||
err("migrate them first (a package: in its own checkout), or name their directory as well\n")
|
||||
return 1
|
||||
}
|
||||
# a var whose state would be declared in a file it may not write: refused, whole
|
||||
for v in 0 .. len(vfile) {
|
||||
var ok = false
|
||||
for k in 0 .. len(sat) { if sat[k] == vstate[v] { ok = true } }
|
||||
if not ok {
|
||||
err(`ludic migrate: {vfile[v]} has a var for the state of {vstate[v]}, which is declared where this migration may not write; nothing was changed\n`)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
# the vars: each line goes, and the first of each state's becomes the state
|
||||
let del_file = new []pointer
|
||||
let del_a = new []int
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue