feat(compiler): list literals, typed compound assignment, file:line diagnostics
- `[a, b, c]` list literals (E_LIST → emit_list); static_type learns slice-element, `new T`, list, string and literal kinds - `x op= y` lowers through the same path as `x = x op y` (emit_bin_vals): fixed `*=`/`/=` use the Q16.16 64-bit paths, string `+=` concatenates, int→long widens; unary `-` keeps a fixed operand's type (arith_ty) - one `unescape()` table for "strings", 'chars' and `interpolation`; `'\''`, `'\\'`, `'\"'` no longer read as 0; unterminated char literals and unexpected characters are errors instead of silently skipped - every diagnostic is `file:line: error: msg` (g_parse_file / g_err_file, Node.file + Node.line set by node()); tok_desc() in expectation errors; duplicate `function` names and unknown `phase` names are reported in source terms (phase_id used to default unknown phases to Overlay) - interpolation holes skip braces inside string literals - hand-IR preludes move from the user `@fn_` prefix to `@lp_` so a user `is_ws` / `str_eq` / `path_join` no longer collides at link time - `@ClearColor(expr)` accepts any constant expression; `Os.pid()` added (docs page + inventory); `str_starts()` in support/str - main.ludic: `else if` flag ladder, char literals, stale script comments - examples/lang/operators.ludic covers all of the above; os.ludic covers Os.pid; docs pages for Os.pid and the Overlay phase; ten changesets - reseeded: selfhost/ludicc.seed.ll is the new compiler's own fixpoint Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
ad548840c7
commit
647dfec334
88 changed files with 30081 additions and 29179 deletions
|
|
@ -54,38 +54,38 @@ function is_mime_ns(meth: pointer) -> bool {
|
|||
function emit_fs_ns(meth: pointer, e: Node) -> Val {
|
||||
g_uses_fsrt = true
|
||||
let a = emit_expr(e.kids[0])
|
||||
if (meth == "exists") { return val(emit_bind(`call i32 @fn_fs_exists(ptr {a.code})`), "bool") }
|
||||
if (meth == "is_dir") { return val(emit_bind(`call i32 @fn_fs_is_dir(ptr {a.code})`), "bool") }
|
||||
if (meth == "read_text") { return val(emit_bind(`call ptr @fn_fs_read_text(ptr {a.code})`), "string") }
|
||||
if (meth == "remove") { return val(emit_bind(`call i32 @fn_fs_remove(ptr {a.code})`), "bool") }
|
||||
if (meth == "size") { return val(emit_bind(`call i32 @fn_fs_size(ptr {a.code})`), "int") }
|
||||
if (meth == "mkdir") { return val(emit_bind(`call i32 @fn_fs_mkdir(ptr {a.code})`), "bool") }
|
||||
if (meth == "list") { return val(emit_bind(`call ptr @fn_fs_list(ptr {a.code})`), "[]string") }
|
||||
if (meth == "exists") { return val(emit_bind(`call i32 @lp_fs_exists(ptr {a.code})`), "bool") }
|
||||
if (meth == "is_dir") { return val(emit_bind(`call i32 @lp_fs_is_dir(ptr {a.code})`), "bool") }
|
||||
if (meth == "read_text") { return val(emit_bind(`call ptr @lp_fs_read_text(ptr {a.code})`), "string") }
|
||||
if (meth == "remove") { return val(emit_bind(`call i32 @lp_fs_remove(ptr {a.code})`), "bool") }
|
||||
if (meth == "size") { return val(emit_bind(`call i32 @lp_fs_size(ptr {a.code})`), "int") }
|
||||
if (meth == "mkdir") { return val(emit_bind(`call i32 @lp_fs_mkdir(ptr {a.code})`), "bool") }
|
||||
if (meth == "list") { return val(emit_bind(`call ptr @lp_fs_list(ptr {a.code})`), "[]string") }
|
||||
let b = emit_expr(e.kids[1])
|
||||
if (meth == "write_text") { return val(emit_bind(`call i32 @fn_fs_write_text(ptr {a.code}, ptr {b.code})`), "bool") }
|
||||
if (meth == "append_text") { return val(emit_bind(`call i32 @fn_fs_append_text(ptr {a.code}, ptr {b.code})`), "bool") }
|
||||
if (meth == "write_text") { return val(emit_bind(`call i32 @lp_fs_write_text(ptr {a.code}, ptr {b.code})`), "bool") }
|
||||
if (meth == "append_text") { return val(emit_bind(`call i32 @lp_fs_append_text(ptr {a.code}, ptr {b.code})`), "bool") }
|
||||
# copy
|
||||
return val(emit_bind(`call i32 @fn_fs_copy(ptr {a.code}, ptr {b.code})`), "bool")
|
||||
return val(emit_bind(`call i32 @lp_fs_copy(ptr {a.code}, ptr {b.code})`), "bool")
|
||||
}
|
||||
|
||||
function emit_path_ns(meth: pointer, e: Node) -> Val {
|
||||
g_uses_fsrt = true
|
||||
let a = emit_expr(e.kids[0])
|
||||
if (meth == "dir") { return val(emit_bind(`call ptr @fn_path_dir(ptr {a.code})`), "string") }
|
||||
if (meth == "base") { return val(emit_bind(`call ptr @fn_path_base(ptr {a.code})`), "string") }
|
||||
if (meth == "ext") { return val(emit_bind(`call ptr @fn_path_ext(ptr {a.code})`), "string") }
|
||||
if (meth == "stem") { return val(emit_bind(`call ptr @fn_path_stem(ptr {a.code})`), "string") }
|
||||
if (meth == "normalize") { return val(emit_bind(`call ptr @fn_path_norm(ptr {a.code})`), "string") }
|
||||
if (meth == "dir") { return val(emit_bind(`call ptr @lp_path_dir(ptr {a.code})`), "string") }
|
||||
if (meth == "base") { return val(emit_bind(`call ptr @lp_path_base(ptr {a.code})`), "string") }
|
||||
if (meth == "ext") { return val(emit_bind(`call ptr @lp_path_ext(ptr {a.code})`), "string") }
|
||||
if (meth == "stem") { return val(emit_bind(`call ptr @lp_path_stem(ptr {a.code})`), "string") }
|
||||
if (meth == "normalize") { return val(emit_bind(`call ptr @lp_path_norm(ptr {a.code})`), "string") }
|
||||
# join
|
||||
let b = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call ptr @fn_path_join(ptr {a.code}, ptr {b.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_path_join(ptr {a.code}, ptr {b.code})`), "string")
|
||||
}
|
||||
|
||||
function emit_mime_ns(meth: pointer, e: Node) -> Val {
|
||||
g_uses_fsrt = true
|
||||
let a = emit_expr(e.kids[0])
|
||||
if (meth == "sniff") { return val(emit_bind(`call ptr @fn_mime_sniff(ptr {a.code})`), "string") }
|
||||
return val(emit_bind(`call ptr @fn_mime_of(ptr {a.code})`), "string")
|
||||
if (meth == "sniff") { return val(emit_bind(`call ptr @lp_mime_sniff(ptr {a.code})`), "string") }
|
||||
return val(emit_bind(`call ptr @lp_mime_of(ptr {a.code})`), "string")
|
||||
}
|
||||
|
||||
# emit_fs_prelude — the Fs/Path/Mime runtime, emitted once per program that uses
|
||||
|
|
@ -108,15 +108,15 @@ function emit_fs_prelude() -> void {
|
|||
# ---- shared helpers --------------------------------------------------------
|
||||
function emit_fs_common() -> void {
|
||||
# duplicate %n bytes of %s into a fresh NUL-terminated buffer
|
||||
emith("define ptr @fn_fs_dup(ptr %s, i32 %n) {\n")
|
||||
emith("define ptr @lp_fs_dup(ptr %s, i32 %n) {\n")
|
||||
emith("entry:\n %nz = zext i32 %n to i64\n %t = add i64 %nz, 1\n %m = call ptr @malloc(i64 %t)\n call ptr @memcpy(ptr %m, ptr %s, i64 %nz)\n")
|
||||
emith(" %end = getelementptr i8, ptr %m, i32 %n\n store i8 0, ptr %end\n ret ptr %m\n}\n")
|
||||
|
||||
emith("define ptr @fn_fs_strdup(ptr %s) {\n")
|
||||
emith("entry:\n %l = call i64 @strlen(ptr %s)\n %li = trunc i64 %l to i32\n %r = call ptr @fn_fs_dup(ptr %s, i32 %li)\n ret ptr %r\n}\n")
|
||||
emith("define ptr @lp_fs_strdup(ptr %s) {\n")
|
||||
emith("entry:\n %l = call i64 @strlen(ptr %s)\n %li = trunc i64 %l to i32\n %r = call ptr @lp_fs_dup(ptr %s, i32 %li)\n ret ptr %r\n}\n")
|
||||
|
||||
# index of the last '/' in %s, or -1 if none
|
||||
emith("define i32 @fn_fs_lastslash(ptr %s) {\n")
|
||||
emith("define i32 @lp_fs_lastslash(ptr %s) {\n")
|
||||
emith("entry:\n %ip = alloca i32\n %rp = alloca i32\n store i32 0, ptr %ip\n store i32 -1, ptr %rp\n br label %lp\n")
|
||||
emith("lp:\n %i = load i32, ptr %ip\n %p = getelementptr i8, ptr %s, i32 %i\n %b = load i8, ptr %p\n %c = zext i8 %b to i32\n")
|
||||
emith(" %z = icmp eq i32 %c, 0\n br i1 %z, label %done, label %go\n")
|
||||
|
|
@ -130,14 +130,14 @@ function emit_fs_common() -> void {
|
|||
function emit_fs_path() -> void {
|
||||
# join(a, b): b absolute -> b; a empty -> b; b empty -> a; else a + "/" + b
|
||||
# (avoiding a doubled separator when a already ends with one).
|
||||
emith("define ptr @fn_path_join(ptr %a, ptr %b) {\n")
|
||||
emith("define ptr @lp_path_join(ptr %a, ptr %b) {\n")
|
||||
emith("entry:\n %la64 = call i64 @strlen(ptr %a)\n %la = trunc i64 %la64 to i32\n %lb64 = call i64 @strlen(ptr %b)\n %lb = trunc i64 %lb64 to i32\n")
|
||||
emith(" %b0 = load i8, ptr %b\n %b0i = zext i8 %b0 to i32\n %babs = icmp eq i32 %b0i, 47\n br i1 %babs, label %retb, label %ka\n")
|
||||
emith("retb:\n %rb = call ptr @fn_fs_strdup(ptr %b)\n ret ptr %rb\n")
|
||||
emith("retb:\n %rb = call ptr @lp_fs_strdup(ptr %b)\n ret ptr %rb\n")
|
||||
emith("ka:\n %aemp = icmp eq i32 %la, 0\n br i1 %aemp, label %retb2, label %kb\n")
|
||||
emith("retb2:\n %rb2 = call ptr @fn_fs_strdup(ptr %b)\n ret ptr %rb2\n")
|
||||
emith("retb2:\n %rb2 = call ptr @lp_fs_strdup(ptr %b)\n ret ptr %rb2\n")
|
||||
emith("kb:\n %bemp = icmp eq i32 %lb, 0\n br i1 %bemp, label %reta, label %chk\n")
|
||||
emith("reta:\n %ra = call ptr @fn_fs_strdup(ptr %a)\n ret ptr %ra\n")
|
||||
emith("reta:\n %ra = call ptr @lp_fs_strdup(ptr %a)\n ret ptr %ra\n")
|
||||
emith("chk:\n %lam1 = sub i32 %la, 1\n %pe = getelementptr i8, ptr %a, i32 %lam1\n %ae = load i8, ptr %pe\n %aei = zext i8 %ae to i32\n %ends = icmp eq i32 %aei, 47\n")
|
||||
emith(" %sep = select i1 %ends, i32 0, i32 1\n %tot = add i32 %la, %lb\n %tot2 = add i32 %tot, %sep\n %tot3 = add i32 %tot2, 1\n %totz = zext i32 %tot3 to i64\n %m = call ptr @malloc(i64 %totz)\n")
|
||||
emith(" %laz = zext i32 %la to i64\n call ptr @memcpy(ptr %m, ptr %a, i64 %laz)\n")
|
||||
|
|
@ -149,39 +149,39 @@ function emit_fs_path() -> void {
|
|||
emith(" %endoff = add i32 %boff, %lb\n %ep = getelementptr i8, ptr %m, i32 %endoff\n store i8 0, ptr %ep\n ret ptr %m\n}\n")
|
||||
|
||||
# base(p): the component after the last '/', or p itself
|
||||
emith("define ptr @fn_path_base(ptr %s) {\n")
|
||||
emith("entry:\n %ls = call i32 @fn_fs_lastslash(ptr %s)\n %none = icmp eq i32 %ls, -1\n br i1 %none, label %whole, label %tail\n")
|
||||
emith("whole:\n %r = call ptr @fn_fs_strdup(ptr %s)\n ret ptr %r\n")
|
||||
emith("tail:\n %st = add i32 %ls, 1\n %p = getelementptr i8, ptr %s, i32 %st\n %r2 = call ptr @fn_fs_strdup(ptr %p)\n ret ptr %r2\n}\n")
|
||||
emith("define ptr @lp_path_base(ptr %s) {\n")
|
||||
emith("entry:\n %ls = call i32 @lp_fs_lastslash(ptr %s)\n %none = icmp eq i32 %ls, -1\n br i1 %none, label %whole, label %tail\n")
|
||||
emith("whole:\n %r = call ptr @lp_fs_strdup(ptr %s)\n ret ptr %r\n")
|
||||
emith("tail:\n %st = add i32 %ls, 1\n %p = getelementptr i8, ptr %s, i32 %st\n %r2 = call ptr @lp_fs_strdup(ptr %p)\n ret ptr %r2\n}\n")
|
||||
|
||||
# dir(p): everything before the last '/', or "." if none; "/" stays "/"
|
||||
emith("define ptr @fn_path_dir(ptr %s) {\n")
|
||||
emith("entry:\n %ls = call i32 @fn_fs_lastslash(ptr %s)\n %none = icmp eq i32 %ls, -1\n br i1 %none, label %dot, label %chk0\n")
|
||||
emith("dot:\n %d = call ptr @fn_fs_strdup(ptr @fn_str_dot)\n ret ptr %d\n")
|
||||
emith("define ptr @lp_path_dir(ptr %s) {\n")
|
||||
emith("entry:\n %ls = call i32 @lp_fs_lastslash(ptr %s)\n %none = icmp eq i32 %ls, -1\n br i1 %none, label %dot, label %chk0\n")
|
||||
emith("dot:\n %d = call ptr @lp_fs_strdup(ptr @fn_str_dot)\n ret ptr %d\n")
|
||||
emith("chk0:\n %isroot = icmp eq i32 %ls, 0\n br i1 %isroot, label %root, label %cut\n")
|
||||
emith("root:\n %r = call ptr @fn_fs_strdup(ptr @fn_str_slash)\n ret ptr %r\n")
|
||||
emith("cut:\n %r2 = call ptr @fn_fs_dup(ptr %s, i32 %ls)\n ret ptr %r2\n}\n")
|
||||
emith("root:\n %r = call ptr @lp_fs_strdup(ptr @fn_str_slash)\n ret ptr %r\n")
|
||||
emith("cut:\n %r2 = call ptr @lp_fs_dup(ptr %s, i32 %ls)\n ret ptr %r2\n}\n")
|
||||
|
||||
# ext(p): from the last '.' in the base component to the end, incl. the dot;
|
||||
# "" when the base has no '.' or begins with '.' (a dotfile has no extension)
|
||||
emith("define ptr @fn_path_ext(ptr %s) {\n")
|
||||
emith("entry:\n %ls = call i32 @fn_fs_lastslash(ptr %s)\n %bstart = add i32 %ls, 1\n") # ls=-1 -> bstart=0
|
||||
emith("define ptr @lp_path_ext(ptr %s) {\n")
|
||||
emith("entry:\n %ls = call i32 @lp_fs_lastslash(ptr %s)\n %bstart = add i32 %ls, 1\n") # ls=-1 -> bstart=0
|
||||
emith(" %dp = alloca i32\n %ip = alloca i32\n store i32 -1, ptr %dp\n store i32 %bstart, ptr %ip\n br label %lp\n")
|
||||
emith("lp:\n %i = load i32, ptr %ip\n %p = getelementptr i8, ptr %s, i32 %i\n %b = load i8, ptr %p\n %c = zext i8 %b to i32\n %z = icmp eq i32 %c, 0\n br i1 %z, label %done, label %go\n")
|
||||
emith("go:\n %dot = icmp eq i32 %c, 46\n br i1 %dot, label %sd, label %nx\n")
|
||||
emith("sd:\n store i32 %i, ptr %dp\n br label %nx\n")
|
||||
emith("nx:\n %i1 = add i32 %i, 1\n store i32 %i1, ptr %ip\n br label %lp\n")
|
||||
emith("done:\n %d = load i32, ptr %dp\n %nod = icmp eq i32 %d, -1\n br i1 %nod, label %empty, label %chkpos\n")
|
||||
emith("empty:\n %e = call ptr @fn_fs_strdup(ptr @fn_str_empty)\n ret ptr %e\n")
|
||||
emith("empty:\n %e = call ptr @lp_fs_strdup(ptr @fn_str_empty)\n ret ptr %e\n")
|
||||
emith("chkpos:\n %atstart = icmp eq i32 %d, %bstart\n br i1 %atstart, label %empty2, label %take\n")
|
||||
emith("empty2:\n %e2 = call ptr @fn_fs_strdup(ptr @fn_str_empty)\n ret ptr %e2\n")
|
||||
emith("take:\n %pp = getelementptr i8, ptr %s, i32 %d\n %r = call ptr @fn_fs_strdup(ptr %pp)\n ret ptr %r\n}\n")
|
||||
emith("empty2:\n %e2 = call ptr @lp_fs_strdup(ptr @fn_str_empty)\n ret ptr %e2\n")
|
||||
emith("take:\n %pp = getelementptr i8, ptr %s, i32 %d\n %r = call ptr @lp_fs_strdup(ptr %pp)\n ret ptr %r\n}\n")
|
||||
|
||||
# stem(p): the base component without its extension
|
||||
emith("define ptr @fn_path_stem(ptr %s) {\n")
|
||||
emith("entry:\n %base = call ptr @fn_path_base(ptr %s)\n %ext = call ptr @fn_path_ext(ptr %s)\n")
|
||||
emith("define ptr @lp_path_stem(ptr %s) {\n")
|
||||
emith("entry:\n %base = call ptr @lp_path_base(ptr %s)\n %ext = call ptr @lp_path_ext(ptr %s)\n")
|
||||
emith(" %bl64 = call i64 @strlen(ptr %base)\n %bl = trunc i64 %bl64 to i32\n %el64 = call i64 @strlen(ptr %ext)\n %el = trunc i64 %el64 to i32\n")
|
||||
emith(" %keep = sub i32 %bl, %el\n %r = call ptr @fn_fs_dup(ptr %base, i32 %keep)\n ret ptr %r\n}\n")
|
||||
emith(" %keep = sub i32 %bl, %el\n %r = call ptr @lp_fs_dup(ptr %base, i32 %keep)\n ret ptr %r\n}\n")
|
||||
|
||||
emit_fs_normalize()
|
||||
}
|
||||
|
|
@ -190,7 +190,7 @@ function emit_fs_path() -> void {
|
|||
# popping the previous kept segment (never above an absolute root). Preserves a
|
||||
# leading '/'. An empty result becomes ".".
|
||||
function emit_fs_normalize() -> void {
|
||||
emith("define ptr @fn_path_norm(ptr %s) {\n")
|
||||
emith("define ptr @lp_path_norm(ptr %s) {\n")
|
||||
emith("entry:\n %len64 = call i64 @strlen(ptr %s)\n %len = trunc i64 %len64 to i32\n %cap = add i32 %len, 2\n %capz = zext i32 %cap to i64\n")
|
||||
emith(" %out = call ptr @malloc(i64 %capz)\n")
|
||||
# segst holds output offsets (i32) of each kept segment's start; size cap ints
|
||||
|
|
@ -240,17 +240,17 @@ function emit_fs_normalize() -> void {
|
|||
|
||||
# ---- Fs.* (libc) -----------------------------------------------------------
|
||||
function emit_fs_io() -> void {
|
||||
emith("define i32 @fn_fs_exists(ptr %p) {\n")
|
||||
emith("define i32 @lp_fs_exists(ptr %p) {\n")
|
||||
emith("entry:\n %r = call i32 @access(ptr %p, i32 0)\n %ok = icmp eq i32 %r, 0\n %z = zext i1 %ok to i32\n ret i32 %z\n}\n")
|
||||
|
||||
# is_dir: opendir succeeds iff it is a directory
|
||||
emith("define i32 @fn_fs_is_dir(ptr %p) {\n")
|
||||
emith("define i32 @lp_fs_is_dir(ptr %p) {\n")
|
||||
emith("entry:\n %d = call ptr @opendir(ptr %p)\n %nz = icmp ne ptr %d, null\n br i1 %nz, label %yes, label %no\n")
|
||||
emith("yes:\n call i32 @closedir(ptr %d)\n ret i32 1\n")
|
||||
emith("no:\n ret i32 0\n}\n")
|
||||
|
||||
# size: bytes via fseek/ftell, or -1 if it cannot be opened
|
||||
emith("define i32 @fn_fs_size(ptr %p) {\n")
|
||||
emith("define i32 @lp_fs_size(ptr %p) {\n")
|
||||
emith("entry:\n %f = call ptr @fopen(ptr %p, ptr @fn_str_rb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("bad:\n ret i32 -1\n")
|
||||
emith("ok:\n call i32 @fseek(ptr %f, i64 0, i32 2)\n %n = call i64 @ftell(ptr %f)\n call i32 @fclose(ptr %f)\n %ni = trunc i64 %n to i32\n ret i32 %ni\n}\n")
|
||||
|
|
@ -258,54 +258,54 @@ function emit_fs_io() -> void {
|
|||
# readall: whole file into a fresh buffer; store byte length to %lenout; null on
|
||||
# failure. The buffer is NUL-terminated (one past the length) so text callers
|
||||
# can use it directly while binary callers use the length.
|
||||
emith("define ptr @fn_fs_readall(ptr %p, ptr %lenout) {\n")
|
||||
emith("define ptr @lp_fs_readall(ptr %p, ptr %lenout) {\n")
|
||||
emith("entry:\n store i32 0, ptr %lenout\n %f = call ptr @fopen(ptr %p, ptr @fn_str_rb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("bad:\n ret ptr null\n")
|
||||
emith("ok:\n call i32 @fseek(ptr %f, i64 0, i32 2)\n %n64 = call i64 @ftell(ptr %f)\n call i32 @fseek(ptr %f, i64 0, i32 0)\n %n = trunc i64 %n64 to i32\n")
|
||||
emith(" %cap = add i64 %n64, 1\n %m = call ptr @malloc(i64 %cap)\n %rd = call i64 @fread(ptr %m, i64 1, i64 %n64, ptr %f)\n call i32 @fclose(ptr %f)\n")
|
||||
emith(" %rdi = trunc i64 %rd to i32\n %endp = getelementptr i8, ptr %m, i32 %rdi\n store i8 0, ptr %endp\n store i32 %rdi, ptr %lenout\n ret ptr %m\n}\n")
|
||||
|
||||
emith("define ptr @fn_fs_read_text(ptr %p) {\n")
|
||||
emith("entry:\n %lp = alloca i32\n %r = call ptr @fn_fs_readall(ptr %p, ptr %lp)\n ret ptr %r\n}\n")
|
||||
emith("define ptr @lp_fs_read_text(ptr %p) {\n")
|
||||
emith("entry:\n %lp = alloca i32\n %r = call ptr @lp_fs_readall(ptr %p, ptr %lp)\n ret ptr %r\n}\n")
|
||||
|
||||
# write_text: write to "<p>.tmp" then rename over %p, so a crash mid-write
|
||||
# never truncates the previous file. Returns 1 on success.
|
||||
emith("define i32 @fn_fs_write_text(ptr %p, ptr %s) {\n")
|
||||
emith("entry:\n %tmp = call ptr @fn_path_join_ext(ptr %p, ptr @fn_str_dottmp)\n %f = call ptr @fopen(ptr %tmp, ptr @fn_str_wb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("define i32 @lp_fs_write_text(ptr %p, ptr %s) {\n")
|
||||
emith("entry:\n %tmp = call ptr @lp_path_join_ext(ptr %p, ptr @fn_str_dottmp)\n %f = call ptr @fopen(ptr %tmp, ptr @fn_str_wb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("bad:\n ret i32 0\n")
|
||||
emith("ok:\n %n = call i64 @strlen(ptr %s)\n %w = call i64 @fwrite(ptr %s, i64 1, i64 %n, ptr %f)\n call i32 @fclose(ptr %f)\n")
|
||||
emith(" %rr = call i32 @rename(ptr %tmp, ptr %p)\n %ok2 = icmp eq i32 %rr, 0\n %z = zext i1 %ok2 to i32\n ret i32 %z\n}\n")
|
||||
|
||||
# concat two strings (used to build "<p>.tmp"); local so write_text needs no
|
||||
# dependency on the Os prelude
|
||||
emith("define ptr @fn_path_join_ext(ptr %a, ptr %b) {\n")
|
||||
emith("define ptr @lp_path_join_ext(ptr %a, ptr %b) {\n")
|
||||
emith("entry:\n %la = call i64 @strlen(ptr %a)\n %lb = call i64 @strlen(ptr %b)\n %sum = add i64 %la, %lb\n %tot = add i64 %sum, 1\n %m = call ptr @malloc(i64 %tot)\n")
|
||||
emith(" call ptr @memcpy(ptr %m, ptr %a, i64 %la)\n %m2 = getelementptr i8, ptr %m, i64 %la\n call ptr @memcpy(ptr %m2, ptr %b, i64 %lb)\n %ep = getelementptr i8, ptr %m, i64 %sum\n store i8 0, ptr %ep\n ret ptr %m\n}\n")
|
||||
|
||||
emith("define i32 @fn_fs_append_text(ptr %p, ptr %s) {\n")
|
||||
emith("define i32 @lp_fs_append_text(ptr %p, ptr %s) {\n")
|
||||
emith("entry:\n %f = call ptr @fopen(ptr %p, ptr @fn_str_ab)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("bad:\n ret i32 0\n")
|
||||
emith("ok:\n %n = call i64 @strlen(ptr %s)\n call i64 @fwrite(ptr %s, i64 1, i64 %n, ptr %f)\n call i32 @fclose(ptr %f)\n ret i32 1\n}\n")
|
||||
|
||||
emith("define i32 @fn_fs_remove(ptr %p) {\n")
|
||||
emith("define i32 @lp_fs_remove(ptr %p) {\n")
|
||||
emith("entry:\n %r = call i32 @remove(ptr %p)\n %ok = icmp eq i32 %r, 0\n %z = zext i1 %ok to i32\n ret i32 %z\n}\n")
|
||||
|
||||
# copy: byte-for-byte via readall + a sized write. Returns 1 on success.
|
||||
emith("define i32 @fn_fs_copy(ptr %src, ptr %dst) {\n")
|
||||
emith("entry:\n %lp = alloca i32\n %buf = call ptr @fn_fs_readall(ptr %src, ptr %lp)\n %nz = icmp ne ptr %buf, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("define i32 @lp_fs_copy(ptr %src, ptr %dst) {\n")
|
||||
emith("entry:\n %lp = alloca i32\n %buf = call ptr @lp_fs_readall(ptr %src, ptr %lp)\n %nz = icmp ne ptr %buf, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("bad:\n ret i32 0\n")
|
||||
emith("ok:\n %tmp = call ptr @fn_path_join_ext(ptr %dst, ptr @fn_str_dottmp)\n %f = call ptr @fopen(ptr %tmp, ptr @fn_str_wb)\n %fnz = icmp ne ptr %f, null\n br i1 %fnz, label %w, label %bad\n")
|
||||
emith("ok:\n %tmp = call ptr @lp_path_join_ext(ptr %dst, ptr @fn_str_dottmp)\n %f = call ptr @fopen(ptr %tmp, ptr @fn_str_wb)\n %fnz = icmp ne ptr %f, null\n br i1 %fnz, label %w, label %bad\n")
|
||||
emith("w:\n %n = load i32, ptr %lp\n %nz64 = zext i32 %n to i64\n call i64 @fwrite(ptr %buf, i64 1, i64 %nz64, ptr %f)\n call i32 @fclose(ptr %f)\n %rr = call i32 @rename(ptr %tmp, ptr %dst)\n %ok2 = icmp eq i32 %rr, 0\n %z = zext i1 %ok2 to i32\n ret i32 %z\n}\n")
|
||||
|
||||
# mkdir: create %p and any missing parents (mkdir -p). Returns 1 if the
|
||||
# directory exists afterwards. Intermediate EEXIST errors are ignored.
|
||||
emith("define i32 @fn_fs_mkdir(ptr %p) {\n")
|
||||
emith("entry:\n %dup = call ptr @fn_fs_strdup(ptr %p)\n %ip = alloca i32\n store i32 1, ptr %ip\n br label %lp\n")
|
||||
emith("define i32 @lp_fs_mkdir(ptr %p) {\n")
|
||||
emith("entry:\n %dup = call ptr @lp_fs_strdup(ptr %p)\n %ip = alloca i32\n store i32 1, ptr %ip\n br label %lp\n")
|
||||
emith("lp:\n %i = load i32, ptr %ip\n %pp = getelementptr i8, ptr %dup, i32 %i\n %c = load i8, ptr %pp\n %ci = zext i8 %c to i32\n %z = icmp eq i32 %ci, 0\n br i1 %z, label %fin, label %go\n")
|
||||
emith("go:\n %sl = icmp eq i32 %ci, 47\n br i1 %sl, label %cut, label %nx\n")
|
||||
emith("cut:\n store i8 0, ptr %pp\n call i32 @mkdir(ptr %dup, i32 493)\n store i8 47, ptr %pp\n br label %nx\n")
|
||||
emith("nx:\n %i1 = add i32 %i, 1\n store i32 %i1, ptr %ip\n br label %lp\n")
|
||||
emith("fin:\n call i32 @mkdir(ptr %dup, i32 493)\n %r = call i32 @fn_fs_is_dir(ptr %p)\n ret i32 %r\n}\n")
|
||||
emith("fin:\n call i32 @mkdir(ptr %dup, i32 493)\n %r = call i32 @lp_fs_is_dir(ptr %p)\n ret i32 %r\n}\n")
|
||||
}
|
||||
|
||||
# ---- Fs.list (+ sort) ------------------------------------------------------
|
||||
|
|
@ -314,7 +314,7 @@ function emit_fs_dir() -> void {
|
|||
# by byte value for a stable, reproducible order. Each name is copied out of
|
||||
# the readdir buffer before the next call. macOS/BSD dirent: d_name at offset
|
||||
# 21 (documented native layout).
|
||||
emith("define ptr @fn_fs_list(ptr %path) {\n")
|
||||
emith("define ptr @lp_fs_list(ptr %path) {\n")
|
||||
emith("entry:\n %h = call ptr @malloc(i64 16)\n %d0 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 0\n %d1 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 1\n %d2 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 2\n")
|
||||
emith(" %datap = alloca ptr\n %cntp = alloca i32\n %capp = alloca i32\n %init = call ptr @malloc(i64 128)\n store ptr %init, ptr %datap\n store i32 0, ptr %cntp\n store i32 16, ptr %capp\n")
|
||||
emith(" %dir = call ptr @opendir(ptr %path)\n %dnz = icmp ne ptr %dir, null\n br i1 %dnz, label %rd, label %empty\n")
|
||||
|
|
@ -325,7 +325,7 @@ function emit_fs_dir() -> void {
|
|||
emith("chkdots:\n %n1p = getelementptr i8, ptr %namep, i32 1\n %n1 = load i8, ptr %n1p\n %n1i = zext i8 %n1 to i32\n %n1z = icmp eq i32 %n1i, 0\n br i1 %n1z, label %rd, label %chkdd\n") # "." -> skip
|
||||
emith("chkdd:\n %isdot1 = icmp eq i32 %n1i, 46\n br i1 %isdot1, label %chkdd2, label %keep\n")
|
||||
emith("chkdd2:\n %n2p = getelementptr i8, ptr %namep, i32 2\n %n2 = load i8, ptr %n2p\n %n2i = zext i8 %n2 to i32\n %n2z = icmp eq i32 %n2i, 0\n br i1 %n2z, label %rd, label %keep\n") # ".." -> skip
|
||||
emith("keep:\n %nm = call ptr @fn_fs_strdup(ptr %namep)\n")
|
||||
emith("keep:\n %nm = call ptr @lp_fs_strdup(ptr %namep)\n")
|
||||
# grow if full
|
||||
emith(" %cnt = load i32, ptr %cntp\n %cap = load i32, ptr %capp\n %full = icmp sge i32 %cnt, %cap\n br i1 %full, label %grow, label %put\n")
|
||||
emith("grow:\n %nc = mul i32 %cap, 2\n store i32 %nc, ptr %capp\n %ncz = zext i32 %nc to i64\n %nb = mul i64 %ncz, 8\n %old = load ptr, ptr %datap\n %new = call ptr @realloc(ptr %old, i64 %nb)\n store ptr %new, ptr %datap\n br label %put\n")
|
||||
|
|
@ -376,7 +376,7 @@ function emit_fs_mime() -> void {
|
|||
# create the string constants FIRST (as module globals), then reference them
|
||||
let en = new []pointer; let tn = new []pointer
|
||||
var i = 0
|
||||
while i < len(exts) { push(en, emit_str_const(exts[i])); push(tn, emit_str_const(tys[i])); i = i + 1 }
|
||||
while i < len(exts) { push(en, emit_str_const(exts[i])); push(tn, emit_str_const(tys[i])); i += 1 }
|
||||
let k_octet = emit_str_const("application/octet-stream")
|
||||
let n = len(exts)
|
||||
|
||||
|
|
@ -386,21 +386,21 @@ function emit_fs_mime() -> void {
|
|||
while i < len(exts) {
|
||||
if i > 0 { emith(", ") }
|
||||
emith(`{{ ptr, ptr }} {{ ptr {en[i]}, ptr {tn[i]} }}`)
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
emith("]\n")
|
||||
|
||||
# of(path): lowercased extension, then a linear scan of the table
|
||||
emith("define ptr @fn_mime_of(ptr %path) {\n")
|
||||
emith("entry:\n %ext = call ptr @fn_path_ext(ptr %path)\n %lc = call ptr @fn_mime_lc(ptr %ext)\n %ip = alloca i32\n store i32 0, ptr %ip\n br label %lp\n")
|
||||
emith("define ptr @lp_mime_of(ptr %path) {\n")
|
||||
emith("entry:\n %ext = call ptr @lp_path_ext(ptr %path)\n %lc = call ptr @lp_mime_lc(ptr %ext)\n %ip = alloca i32\n store i32 0, ptr %ip\n br label %lp\n")
|
||||
emith(`lp:\n %i = load i32, ptr %ip\n %lt = icmp slt i32 %i, {itoa(n)}\n br i1 %lt, label %body, label %def\n`)
|
||||
emith(`body:\n %kp = getelementptr [{itoa(n)} x {{ ptr, ptr }}], ptr @mime_tbl, i32 0, i32 %i, i32 0\n %k = load ptr, ptr %kp\n %c = call i32 @strcmp(ptr %lc, ptr %k)\n %eq = icmp eq i32 %c, 0\n br i1 %eq, label %hit, label %nx\n`)
|
||||
emith(`hit:\n %vp = getelementptr [{itoa(n)} x {{ ptr, ptr }}], ptr @mime_tbl, i32 0, i32 %i, i32 1\n %v = load ptr, ptr %vp\n %r = call ptr @fn_fs_strdup(ptr %v)\n ret ptr %r\n`)
|
||||
emith(`hit:\n %vp = getelementptr [{itoa(n)} x {{ ptr, ptr }}], ptr @mime_tbl, i32 0, i32 %i, i32 1\n %v = load ptr, ptr %vp\n %r = call ptr @lp_fs_strdup(ptr %v)\n ret ptr %r\n`)
|
||||
emith("nx:\n %i1 = add i32 %i, 1\n store i32 %i1, ptr %ip\n br label %lp\n")
|
||||
emith(`def:\n %d = call ptr @fn_fs_strdup(ptr {k_octet})\n ret ptr %d\n}}\n`)
|
||||
emith(`def:\n %d = call ptr @lp_fs_strdup(ptr {k_octet})\n ret ptr %d\n}}\n`)
|
||||
|
||||
# lowercase an extension, dropping a leading '.' (ASCII only, for table lookup)
|
||||
emith("define ptr @fn_mime_lc(ptr %s) {\n")
|
||||
emith("define ptr @lp_mime_lc(ptr %s) {\n")
|
||||
emith("entry:\n %l64 = call i64 @strlen(ptr %s)\n %l = trunc i64 %l64 to i32\n %cap = add i64 %l64, 1\n %out = call ptr @malloc(i64 %cap)\n")
|
||||
emith(" %b0 = load i8, ptr %s\n %b0i = zext i8 %b0 to i32\n %isdot = icmp eq i32 %b0i, 46\n %start = select i1 %isdot, i32 1, i32 0\n")
|
||||
emith(" %ip = alloca i32\n %op = alloca i32\n store i32 %start, ptr %ip\n store i32 0, ptr %op\n br label %lp\n")
|
||||
|
|
@ -414,23 +414,23 @@ function emit_fs_mime() -> void {
|
|||
# sniff(path): read the first bytes and recognise a few well-known signatures,
|
||||
# otherwise fall back to the extension. Covers PNG/JPEG/GIF/PDF for now.
|
||||
function emit_fs_sniff() -> void {
|
||||
emith("define ptr @fn_mime_sniff(ptr %path) {\n")
|
||||
emith("define ptr @lp_mime_sniff(ptr %path) {\n")
|
||||
emith("entry:\n %f = call ptr @fopen(ptr %path, ptr @fn_str_rb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %fallback\n")
|
||||
emith("ok:\n %buf = call ptr @malloc(i64 16)\n %rd = call i64 @fread(ptr %buf, i64 1, i64 8, ptr %f)\n call i32 @fclose(ptr %f)\n %rdi = trunc i64 %rd to i32\n %has4 = icmp sge i32 %rdi, 4\n br i1 %has4, label %chk, label %fallback\n")
|
||||
emith("chk:\n %b0p = getelementptr i8, ptr %buf, i32 0\n %b0 = load i8, ptr %b0p\n %b0i = zext i8 %b0 to i32\n %b1p = getelementptr i8, ptr %buf, i32 1\n %b1 = load i8, ptr %b1p\n %b1i = zext i8 %b1 to i32\n %b2p = getelementptr i8, ptr %buf, i32 2\n %b2 = load i8, ptr %b2p\n %b2i = zext i8 %b2 to i32\n %b3p = getelementptr i8, ptr %buf, i32 3\n %b3 = load i8, ptr %b3p\n %b3i = zext i8 %b3 to i32\n")
|
||||
# PNG: 89 50 4E 47
|
||||
emith(" %p0 = icmp eq i32 %b0i, 137\n %p1 = icmp eq i32 %b1i, 80\n %p2 = icmp eq i32 %b2i, 78\n %p3 = icmp eq i32 %b3i, 71\n %pa = and i1 %p0, %p1\n %pb = and i1 %pa, %p2\n %pc = and i1 %pb, %p3\n br i1 %pc, label %png, label %cj\n")
|
||||
emith("png:\n %rpng = call ptr @fn_fs_strdup(ptr @fn_sig_png)\n ret ptr %rpng\n")
|
||||
emith("png:\n %rpng = call ptr @lp_fs_strdup(ptr @fn_sig_png)\n ret ptr %rpng\n")
|
||||
# JPEG: FF D8 FF
|
||||
emith("cj:\n %j0 = icmp eq i32 %b0i, 255\n %j1 = icmp eq i32 %b1i, 216\n %j2 = icmp eq i32 %b2i, 255\n %ja = and i1 %j0, %j1\n %jb = and i1 %ja, %j2\n br i1 %jb, label %jpg, label %cg\n")
|
||||
emith("jpg:\n %rjpg = call ptr @fn_fs_strdup(ptr @fn_sig_jpg)\n ret ptr %rjpg\n")
|
||||
emith("jpg:\n %rjpg = call ptr @lp_fs_strdup(ptr @fn_sig_jpg)\n ret ptr %rjpg\n")
|
||||
# GIF: 47 49 46
|
||||
emith("cg:\n %g0 = icmp eq i32 %b0i, 71\n %g1 = icmp eq i32 %b1i, 73\n %g2 = icmp eq i32 %b2i, 70\n %ga = and i1 %g0, %g1\n %gb = and i1 %ga, %g2\n br i1 %gb, label %gif, label %cp\n")
|
||||
emith("gif:\n %rgif = call ptr @fn_fs_strdup(ptr @fn_sig_gif)\n ret ptr %rgif\n")
|
||||
emith("gif:\n %rgif = call ptr @lp_fs_strdup(ptr @fn_sig_gif)\n ret ptr %rgif\n")
|
||||
# PDF: 25 50 44 46
|
||||
emith("cp:\n %q0 = icmp eq i32 %b0i, 37\n %q1 = icmp eq i32 %b1i, 80\n %q2 = icmp eq i32 %b2i, 68\n %q3 = icmp eq i32 %b3i, 70\n %qa = and i1 %q0, %q1\n %qb = and i1 %qa, %q2\n %qc = and i1 %qb, %q3\n br i1 %qc, label %pdf, label %fallback\n")
|
||||
emith("pdf:\n %rpdf = call ptr @fn_fs_strdup(ptr @fn_sig_pdf)\n ret ptr %rpdf\n")
|
||||
emith("fallback:\n %r = call ptr @fn_mime_of(ptr %path)\n ret ptr %r\n}\n")
|
||||
emith("pdf:\n %rpdf = call ptr @lp_fs_strdup(ptr @fn_sig_pdf)\n ret ptr %rpdf\n")
|
||||
emith("fallback:\n %r = call ptr @lp_mime_of(ptr %path)\n ret ptr %r\n}\n")
|
||||
|
||||
# the small string constants the Fs/Path/Mime runtime references
|
||||
emith("@fn_str_rb = private unnamed_addr constant [3 x i8] c\"rb\\00\"\n")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue