L
ling-miao
↓ 0 downloadsmiao: a Ling-native version control system — temples, souls, rites and talismans instead of repos, branches, commits and tags; adaptive whole/delta/chunk storage; SSH temple fairs for sharing; a LingStationX-themed GUI; ROYGBIV everywhere
Keywords vcs, version-control, git, miao, ling
lingfu install ling-miaoCode / Files (268)
ling/util.ling
# ━━━ util.ling — miao helpers: lists, string compare, sort, paths, time ━━━
# Part of ling-miao (庙), the Ling version control system.
# All fns prefixed ut_ (flat namespace).
fn ut_sel(c, a, b) {
if c { return a }
return b
}
fn ut_is_bad(r) {
match r { bad(e) => { return true } ok(v) => { return false } }
}
fn ut_val(r) { return r.unwrap() }
# ── list helpers ──────────────────────────────────────────────────────
fn ut_contains(l, v) {
bind i = 0.0
while i < len(l) {
if list_get(l, i) == v { return true }
bind i = i + 1.0
}
return false
}
fn ut_index_of(l, v) {
bind i = 0.0
while i < len(l) {
if list_get(l, i) == v { return i }
bind i = i + 1.0
}
return 0.0 - 1.0
}
# slice of a list [from, to)
fn ut_lslice(l, from, to) {
bind out = list_new()
bind i = from
while i < to {
bind out = list_push(out, list_get(l, i))
bind i = i + 1.0
}
return out
}
# ── lexicographic string compare (UTF-8 byte order) ───────────────────
# strings only support == in Ling; compare via hex bytes.
fn ut_str_lt(a, b) {
bind ha = text_hex(a)
bind hb = text_hex(b)
bind la = len(ha)
bind lb = len(hb)
bind n = min(la, lb)
bind i = 0.0
while i < n {
bind ca = hex_to_number(str_slice(ha, i, 2.0))
bind cb = hex_to_number(str_slice(hb, i, 2.0))
if ca < cb { return true }
if ca > cb { return false }
bind i = i + 2.0
}
return la < lb
}
# iterative bottom-up merge sort of a list of strings
fn ut_sort(l) {
bind n = len(l)
if n < 2.0 { return l }
bind cur = l
bind width = 1.0
while width < n {
bind nxt = list_new()
bind pos = 0.0
while pos < n {
bind mid = min(pos + width, n)
bind hi = min(pos + width * 2.0, n)
# merge cur[pos..mid] with cur[mid..hi]
bind i = pos
bind j = mid
while i < mid || j < hi {
if i >= mid {
bind nxt = list_push(nxt, list_get(cur, j))
bind j = j + 1.0
} else if j >= hi {
bind nxt = list_push(nxt, list_get(cur, i))
bind i = i + 1.0
} else if ut_str_lt(list_get(cur, j), list_get(cur, i)) {
bind nxt = list_push(nxt, list_get(cur, j))
bind j = j + 1.0
} else {
bind nxt = list_push(nxt, list_get(cur, i))
bind i = i + 1.0
}
}
bind pos = hi
}
bind cur = nxt
bind width = width * 2.0
}
return cur
}
# sort keys and keep a parallel vals list aligned (bottom-up merge sort).
# returns [sorted_keys, sorted_vals]
fn ut_sort_pair(keys, vals) {
bind n = len(keys)
if n < 2.0 { return [keys, vals] }
bind ck = keys
bind cv = vals
bind width = 1.0
while width < n {
bind nk = list_new()
bind nv = list_new()
bind pos = 0.0
while pos < n {
bind mid = min(pos + width, n)
bind hi = min(pos + width * 2.0, n)
bind i = pos
bind j = mid
while i < mid || j < hi {
if i >= mid {
bind nk = list_push(nk, list_get(ck, j))
bind nv = list_push(nv, list_get(cv, j))
bind j = j + 1.0
} else if j >= hi {
bind nk = list_push(nk, list_get(ck, i))
bind nv = list_push(nv, list_get(cv, i))
bind i = i + 1.0
} else if ut_str_lt(list_get(ck, j), list_get(ck, i)) {
bind nk = list_push(nk, list_get(ck, j))
bind nv = list_push(nv, list_get(cv, j))
bind j = j + 1.0
} else {
bind nk = list_push(nk, list_get(ck, i))
bind nv = list_push(nv, list_get(cv, i))
bind i = i + 1.0
}
}
bind pos = hi
}
bind ck = nk
bind cv = nv
bind width = width * 2.0
}
return [ck, cv]
}
# ── paths ─────────────────────────────────────────────────────────────
fn ut_norm(p) {
bind q = str_replace(p, "\\", "/")
# strip trailing "/." and trailing "/"
if str_ends_with(q, "/.") { bind q = str_slice(q, 0.0, len(q) - 2.0) }
if len(q) > 1.0 {
if str_ends_with(q, "/") { bind q = str_slice(q, 0.0, len(q) - 1.0) }
}
return q
}
fn ut_last_index(s, needle) {
bind best = 0.0 - 1.0
bind off = 0.0
bind go = true
while go {
bind rest = str_slice(s, off, len(s) - off)
bind f = str_find(rest, needle)
if f < 0.0 {
bind go = false
} else {
bind best = off + f
bind off = off + f + 1.0
}
}
return best
}
fn ut_parent(p) {
bind q = ut_norm(p)
bind i = ut_last_index(q, "/")
if i < 0.0 { return q }
if i < 1.0 { return "/" }
bind r = str_slice(q, 0.0, i)
# "C:" root guard
if str_ends_with(r, ":") { return r + "/" }
return r
}
fn ut_basename(p) { return path_name(p) }
# ── time: unix seconds -> "YYYY-MM-DD HH:MM" (UTC) ────────────────────
fn ut_pad2(n) {
if n < 10.0 { return "0" + n }
return "" + n
}
fn ut_fmt_time(unix) {
bind t = floor(unix)
bind days = floor(t / 86400.0)
bind secs = t - days * 86400.0
bind z = days + 719468.0
bind era = floor(z / 146097.0)
bind doe = z - era * 146097.0
bind yoe = floor((doe - floor(doe / 1460.0) + floor(doe / 36524.0) - floor(doe / 146096.0)) / 365.0)
bind y = yoe + era * 400.0
bind doy = doe - (365.0 * yoe + floor(yoe / 4.0) - floor(yoe / 100.0))
bind mp = floor((5.0 * doy + 2.0) / 153.0)
bind d = doy - floor((153.0 * mp + 2.0) / 5.0) + 1.0
bind m = mp + 3.0
if mp >= 10.0 { bind m = mp - 9.0 }
if m <= 2.0 { bind y = y + 1.0 }
bind hh = floor(secs / 3600.0)
bind mm = floor((secs - hh * 3600.0) / 60.0)
return y + "-" + ut_pad2(m) + "-" + ut_pad2(d) + " " + ut_pad2(hh) + ":" + ut_pad2(mm)
}
# ── padding ───────────────────────────────────────────────────────────
fn ut_pad(s, n) {
bind out = s
while len(out) < n {
bind out = out + " "
}
return out
}
# ── text files ────────────────────────────────────────────────────────
fn ut_read_text(path) {
if file_exists(path) { return read_file(path) }
return ""
}
# non-empty lines of a file
fn ut_read_lines(path) {
bind t = ut_read_text(path)
bind out = list_new()
if t == "" { return out }
bind parts = split(t, "\n")
bind i = 0.0
while i < len(parts) {
bind ln = list_get(parts, i)
if ln != "" { bind out = list_push(out, ln) }
bind i = i + 1.0
}
return out
}
fn ut_short(id) {
if len(id) < 7.0 { return id }
return str_slice(id, 0.0, 7.0)
}
# first line of a text
fn ut_first_line(t) {
bind f = str_find(t, "\n")
if f < 0.0 { return t }
return str_slice(t, 0.0, f)
}