diff --git a/layout/src/lower.rs b/layout/src/lower.rs index 87ddcc30..e77612e1 100644 --- a/layout/src/lower.rs +++ b/layout/src/lower.rs @@ -1098,7 +1098,7 @@ fn infer_op_type(opcode: &str, reads: &[String], tm: &mut HashMap return "char/utf32".to_string(), "random.int63" => return "int64".to_string(), "random·intn" | "random.uint64" | "string·parseuint" => return "uint64".to_string(), - "pow" | "sqrt" | "exp" | "log" => return "float64".to_string(), + "pow" | "sqrt" | "exp" | "log" | "string·parsefloat" => return "float64".to_string(), "sign" => return "int64".to_string(), "abs" | "neg" | "max" | "min" => { return if !reads.is_empty() { diff --git a/runtime/src/myrwircaps.c b/runtime/src/myrwircaps.c index 8827c133..1f86ef14 100644 --- a/runtime/src/myrwircaps.c +++ b/runtime/src/myrwircaps.c @@ -149,6 +149,7 @@ static const struct { const char *op; kvlangBuiltinFn fn; } myrwircaps[] = { {"string·slice", kvlangBuiltinStringSlice}, {"string·concat", kvlangBuiltinStringConcat}, {"string·formatint", kvlangBuiltinStringFormatInt}, {"string·formatuint", kvlangBuiltinStringFormatUint}, {"string·parseint", kvlangBuiltinStringParseInt}, {"string·parseuint", kvlangBuiltinStringParseUint}, + {"string·parsefloat", kvlangBuiltinStringParseFloat}, {"time·now", kvlangBuiltinTimeNow}, {"time·sub", kvlangBuiltinTimeSub}, {"time·add", kvlangBuiltinTimeAdd}, {"time/duration·nanos", kvlangBuiltinDurFrom}, {"time/duration·millis", kvlangBuiltinDurFrom}, {"time/duration·seconds", kvlangBuiltinDurFrom}, {"time/duration·minutes", kvlangBuiltinDurFrom}, diff --git a/runtime/src/rwir_internal.h b/runtime/src/rwir_internal.h index a952d810..356ff004 100644 --- a/runtime/src/rwir_internal.h +++ b/runtime/src/rwir_internal.h @@ -74,6 +74,7 @@ int kvlangBuiltinArray(kvlangFrame_t *f), kvlangBuiltinStringFormatUint(kvlangFrame_t *f), kvlangBuiltinStringParseInt(kvlangFrame_t *f), kvlangBuiltinStringParseUint(kvlangFrame_t *f), + kvlangBuiltinStringParseFloat(kvlangFrame_t *f), kvlangBuiltinTimeNow(kvlangFrame_t *f), kvlangBuiltinTimeSub(kvlangFrame_t *f), kvlangBuiltinTimeAdd(kvlangFrame_t *f), diff --git a/runtime/src/rwir_string.c b/runtime/src/rwir_string.c index 614818ff..863698cd 100644 --- a/runtime/src/rwir_string.c +++ b/runtime/src/rwir_string.c @@ -260,3 +260,19 @@ int kvlangBuiltinStringParseUint(kvlangFrame_t *f) { int rc = kvlangBuiltinWriteResult(f, &e); kvlangXvalueFree(&e); kvlangBuiltinFreeInputs(in, n); return rc; } + +/* string·parsefloat(s) -> f:对齐 Go strconv.ParseFloat / Python float() / Rust parse::()。 + * 与 parseint 同形:整串须消费完(无尾随空白/垃圾),否则 ValueError;无 base(浮点无进制)。 */ +int kvlangBuiltinStringParseFloat(kvlangFrame_t *f) { + kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); + if (n < 1) return kvlangBuiltinSetErr(f, "TypeError: string.parsefloat requires a string"); + char *s = kvlangXvalueValueString(&in[0]); + char *end = NULL; double v = strtod(s, &end); + int bad = s[0] == '\0' || end == s || *end != '\0'; + free(s); + if (bad) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "ValueError: string.parsefloat: invalid syntax"); } + uint8_t r[8]; memcpy(r, &v, 8); + kvlangXvalue_t e; kvlangXvalueNewTlv(&e, KVSPACE_KIND_FLOAT64, r, 8, 1); + int rc = kvlangBuiltinWriteResult(f, &e); kvlangXvalueFree(&e); kvlangBuiltinFreeInputs(in, n); + return rc; +} diff --git a/stdlib/kvlang/kvlangbrief.kv b/stdlib/kvlang/kvlangbrief.kv index a2893f39..7b8f7787 100644 --- a/stdlib/kvlang/kvlangbrief.kv +++ b/stdlib/kvlang/kvlangbrief.kv @@ -200,6 +200,7 @@ rwfunc main() -> () { ### 外部进程/文件系统(networld) - `networld/proc·exec(args, envs) -> code[, out, err]`:args/envs 是 `{...}` 数组字面量,args 首元素=可执行文件;out/err 是 `@[]uint8` 捕获句柄(绑定即捕获)。 - `networld/fs·size(p)->n`、`·read(p,start,len)->raw`、`·write(p,bytes)->n`、`·append(p,bytes)->n`、`·list(p)->names`、`·del(p)->code`、`·mkdir(p)->code`、`·exists(p)->b`。 +- 文本级(rwfunc):`readtext(p)->(t,sz)`、`writetext(p,t)->n`(UTF-8 往返)、`match(name,pat)->b`(`*`/`?` 通配)、`glob(pat)->paths`、`grep(pat,path,glob)->hits`(`路径:行号:文本`)、`multi(path,olds,news)->(done,bad)`(逐条精确替换,全成才落盘)。 - 字符串↔字节:`xv·reinterpret("s","[]uint8")` 或 `xv·reinterpret(raw,"[]char/utf8")`。 ```kv @@ -216,7 +217,10 @@ networld/fs·write("/tmp/x.txt", xv·reinterpret("hi", "[]uint8")) -> n - `kvlang·abs(x)`(= `&x`,取址)、`kvspace·cp(src,dst)`、`kvspace·cpdir(src,dst)`、`kvspace·cplist(src,dst)`、`kvspace·has(path)`。 ### 字符串(string·*) -- `len / char / ord / cmp / find / slice / concat / formatint / formatuint / parseint / parseuint`。 +- `len / char / ord / cmp / find / slice / concat / set / formatint / formatuint / parseint / parseuint / parsefloat`。 +- rwfunc 判定/查找/替换:`eq / ne / empty / contains / startswith / endswith / eqfold`、`count / rfind`、`replace / replacen / cut`。 +- rwfunc 修剪/切分:`trim / trimstart / trimend / padstart / padend`、`split / splitn / lines / fields / join`。 +- rwfunc 变换/分类:`reverse / repeat / upper / lower / capitalize / title / swapcase`、`allinset / isalpha / isdigit / isalnum / isspace / isupper / islower / isxdigit / ispunct`(大小写与分类一律 ASCII)。 ### 数组(ndarray·* / xv·*) - `ndarray·numel / dim / shape`;`xv·at / set / reshape / reinterpret / langtype / bodylen`。 diff --git a/stdlib/networld/fs.kv b/stdlib/networld/fs.kv new file mode 100644 index 00000000..3320d692 --- /dev/null +++ b/stdlib/networld/fs.kv @@ -0,0 +1,208 @@ +// 欢迎加入kvspace世界 +// lib networld/fs —— 宿主文件系统的文本级工具(rwfunc,kv 源码)。 +// native rwir(字节级,runtime-rs):size / read(p,start,off) / write(p,bytes) +// / append / list / del / mkdir / exists。本库在其上补文本级组合: +// readtext / writetext 文件 ↔ []char/utf32 文本(UTF-8 解码 / 编码往返) +// match 名字段 shell 通配(* 任意串、? 单字符) +// glob 按通配展开路径 +// grep 逐行查找(文件或目录下匹配 glob 的成员) +// multi 事务式多处编辑(逐条精确替换,全部成功才落盘) +// 文本约定:文件按 UTF-8 解码为 []char/utf32 后操作(只有定宽的 char/utf32 可 +// 索引 / 切片);落盘再编码回 UTF-8。字节级 I/O 仍走 native rwir。 + +lib networld/fs { + // readtext(p) -> (t, sz):整文件读为文本;sz = 字节数(缺失 = -1,t = "") + rwfunc readtext(p:[]char/utf32) -> (t:[]char/utf32, sz:int64) { + networld/fs·size(p) -> sz + "" -> t + if (sz > 0) { + networld/fs·read(p, 0, sz) -> raw + xv·reinterpret(raw, "[]char/utf8") -> u8 + char/utf32(u8) -> t + } + } + // writetext(p, t) -> n:文本按 UTF-8 覆盖写盘,返回字节数(失败 -1) + rwfunc writetext(p:[]char/utf32, t:[]char/utf32) -> (n:int64) { + char/utf8(t) -> u8 + xv·reinterpret(u8, "[]uint8") -> bytes + networld/fs·write(p, bytes) -> n + } + // match(name, pat) -> b:名字段通配匹配(* 任意串、? 单字符,其余按字面) + rwfunc match(name:[]char/utf32, pat:[]char/utf32) -> (b:bool) { + nn = string·len(name) + pn = string·len(pat) + if (pn == 0) { + b = nn == 0 + } else { + string·slice(pat, 1, pn) -> ptail + if (string·startswith(pat, "*")) { + networld/fs·match(name, ptail) -> b + if (!b && nn > 0) { + string·slice(name, 1, nn) -> ntail + networld/fs·match(ntail, pat) -> b + } + } else { + if (nn == 0) { + b = false + } else { + string·slice(name, 1, nn) -> ntail + if (string·startswith(pat, "?")) { + networld/fs·match(ntail, ptail) -> b + } else { + string·slice(name, 0, 1) -> nc + string·slice(pat, 0, 1) -> pc + if (string·cmp(nc, pc) == 0) { + networld/fs·match(ntail, ptail) -> b + } else { + b = false + } + } + } + } + } + } + // glob(pat) -> paths:按通配展开路径(\n 连接;无匹配 = 空串)。 + // 仅最后一段可含 *?,其前目录须为字面路径,如 "/tmp/*.kv"、"src/*.c"。 + rwfunc glob(pat:[]char/utf32) -> (paths:[]char/utf32) { + "" -> paths + ln = string·len(pat) + cut = -1 + i = 0 + while (i < ln) { + string·slice(pat, i, i + 1) -> c + if (string·cmp(c, "/") == 0) { + cut = i + } + i = i + 1 + } + if (cut < 0) { + "." -> dir + pat -> np + } else { + string·slice(pat, 0, cut) -> dir + string·slice(pat, cut + 1, ln) -> np + } + dir -> scan + if (string·len(dir) == 0) { + "/" -> scan + } + networld/fs·list(scan) -> ns + kvspace·listlen(ns) -> nc + i = 0 + while (i < nc) { + kvspace·listn(ns, i) -> k + kvspace·get(ns, k) -> nm + networld/fs·match(nm, np) -> ok + if (ok) { + string·concat(dir, "/") -> d + string·concat(d, nm) -> cp + string·concat(paths, cp) -> paths + string·concat(paths, "\n") -> paths + } + i = i + 1 + } + } + // scan(text, path, pat) -> hits:逐行找含 pat 的行,输出 "path:行号:文本"\n + rwfunc scan(text:[]char/utf32, path:[]char/utf32, + pat:[]char/utf32) -> (hits:[]char/utf32) { + "" -> hits + ln = string·len(text) + pos = 0 + no = 0 + while (pos < ln) { + string·slice(text, pos, ln) -> rest + string·find(rest, "\n") -> e + no = no + 1 + if (e == -1) { + string·slice(text, pos, ln) -> line + pos = ln + } else { + string·slice(text, pos, pos + e) -> line + pos = pos + e + 1 + } + string·find(line, pat) -> h + if (h != -1) { + string·concat(hits, path) -> hits + string·concat(hits, ":") -> hits + string·formatint(no, 10) -> sn + string·concat(hits, sn) -> hits + string·concat(hits, ":") -> hits + string·concat(hits, line) -> hits + string·concat(hits, "\n") -> hits + } + } + } + // grep(pat, path, glob) -> hits:path 是文件则扫它本身;是目录则扫其下匹配 + // glob 的成员(glob 空 = 全部成员)。命中输出 "路径:行号:文本",多行 \n 连接。 + rwfunc grep(pat:[]char/utf32, path:[]char/utf32, + glob:[]char/utf32) -> (hits:[]char/utf32) { + "" -> hits + networld/fs·list(path) -> ns + kvspace·listlen(ns) -> nc + if (nc == 0) { + networld/fs·readtext(path) -> (t, sz) + if (sz > 0) { + networld/fs·scan(t, path, pat) -> h + string·concat(hits, h) -> hits + } + } else { + gl = string·len(glob) + i = 0 + while (i < nc) { + kvspace·listn(ns, i) -> k + kvspace·get(ns, k) -> nm + networld/fs·match(nm, glob) -> ok + if (gl == 0 || ok) { + string·concat(path, "/") -> d + string·concat(d, nm) -> cp + networld/fs·readtext(cp) -> (t, sz) + if (sz > 0) { + networld/fs·scan(t, cp, pat) -> h + string·concat(hits, h) -> hits + } + } + i = i + 1 + } + } + } + // multi(path, olds, news) -> (done, bad):事务式多处编辑。按序对文件文本做 + // 逐条精确替换,每条 old 须恰命中 1 处;全部成功才落盘。 + // bad = -1 全部成功,done = 条数(文件已落盘) + // bad >= 0 该下标编辑失败(未命中或多处命中),文件不变 + // bad = -2 文件不可读 bad = -3 olds / news 长度不等 + rwfunc multi(path:[]char/utf32, olds:*[int64]·[]char/utf32, + news:*[int64]·[]char/utf32) -> (done:int64, bad:int64) { + kvspace·listlen(olds) -> cnt + kvspace·listlen(news) -> m + done = 0 + bad = -1 + if (cnt != m) { + bad = -3 + } else { + networld/fs·readtext(path) -> (t, sz) + if (sz < 0) { + bad = -2 + } else { + i = 0 + while (i < cnt) { + kvspace·listn(olds, i) -> ko + kvspace·listn(news, i) -> kn + kvspace·get(olds, ko) -> o + kvspace·get(news, kn) -> w + string·count(t, o) -> c + if (c != 1) { + bad = i + break + } + string·replace(t, o, w) -> (t2, _) + t = t2 + done = done + 1 + i = i + 1 + } + if (bad < 0) { + networld/fs·writetext(path, t) -> wn + } + } + } + } +} diff --git a/stdlib/string.kv b/stdlib/string.kv index 70257b6d..7300519a 100644 --- a/stdlib/string.kv +++ b/stdlib/string.kv @@ -1,10 +1,13 @@ // 欢迎加入kvspace世界 -// lib string —— 字符串便利标准库。string native rwir 提供 len/char/ord/cmp/concat/find/slice -// 及 strconv 家族 formatint/formatuint/parseint/parseuint(base 2..36,对齐 Go strconv); -// 以下 rwfunc 封装常用组合操作(判断 / 变换)。 -// 字符串类型恒 []char/utf32(kvlang 字面量默认编码)。 +// lib string —— 字符串便利标准库。native rwir:len/char/ord/cmp/find/slice/ +// concat/set;strconv 家族 formatint/formatuint/parseint/parseuint/parsefloat +// (base 2..36,对齐 Go strconv)。 +// 以下 rwfunc 封装组合操作:命名取 C/Python/Rust/TypeScript/Go 五语言共识名, +// 分歧处按族选阵营并在注释里记明(见 kvlang#157)。 +// 字符串类型恒 []char/utf32(kvlang 字面量默认编码);大小写与分类一律 ASCII。 lib string { + // ── 判定 ────────────────────────────────────────────────────────── rwfunc eq(a:[]char/utf32, b:[]char/utf32) -> (r:bool) { string·cmp(a, b) == 0 -> r } @@ -38,6 +41,333 @@ lib string { string·cmp(tail, suf) == 0 -> r } } + // eqfold(a, b) -> r:忽略 ASCII 大小写相等(Go EqualFold / Rust + // eq_ignore_ascii_case) + rwfunc eqfold(a:[]char/utf32, b:[]char/utf32) -> (r:bool) { + string·upper(a) -> ua + string·upper(b) -> ub + string·cmp(ua, ub) == 0 -> r + } + + // ── 查找与计数 ──────────────────────────────────────────────────── + // count(s, sub) -> n:sub 不重叠出现次数;sub 空 = len+1(Py/Go/Rust/JS 一致) + rwfunc count(s:[]char/utf32, sub:[]char/utf32) -> (n:int64) { + ln = string·len(s) + m = string·len(sub) + n = 0 + if (m == 0) { + n = ln + 1 + } else { + pos = 0 + while (pos + m <= ln) { + string·slice(s, pos, ln) -> rest + string·find(rest, sub) -> i + if (i == -1) { + break + } + n = n + 1 + pos = pos + i + m + } + } + } + // rfind(s, sub) -> i:最后一次出现的下标,无则 -1(Py/Rust rfind、Go + // LastIndex、C strrchr) + rwfunc rfind(s:[]char/utf32, sub:[]char/utf32) -> (i:int64) { + ln = string·len(s) + m = string·len(sub) + i = -1 + pos = 0 + while (pos + m <= ln) { + string·slice(s, pos, ln) -> rest + string·find(rest, sub) -> k + if (k == -1) { + break + } + i = pos + k + pos = pos + k + 1 + } + } + + // ── 修剪与填充 ──────────────────────────────────────────────────── + // trim(s) -> r:去两侧空白(Go TrimSpace / Rust trim / TS trim; 空白集同 C + // isspace) + rwfunc trim(s:[]char/utf32) -> (r:[]char/utf32) { + string·trimstart(s) -> t + string·trimend(t) -> r + } + // trimstart(s) -> r:去左侧空白(Python lstrip / Go TrimLeft / Rust + // trim_start) + rwfunc trimstart(s:[]char/utf32) -> (r:[]char/utf32) { + lo = 0 + hi = string·len(s) + while (lo < hi) { + string·char(s, lo) -> c + string·find(" \t\n\r\v\f", c) -> w + if (w == -1) { + break + } + lo = lo + 1 + } + string·slice(s, lo, hi) -> r + } + // trimend(s) -> r:去右侧空白(Python rstrip / Go TrimRight / Rust trim_end) + rwfunc trimend(s:[]char/utf32) -> (r:[]char/utf32) { + lo = 0 + hi = string·len(s) + while (hi > lo) { + string·char(s, hi - 1) -> c + string·find(" \t\n\r\v\f", c) -> w + if (w == -1) { + break + } + hi = hi - 1 + } + string·slice(s, lo, hi) -> r + } + // padstart(s, n, pad) -> r:左侧补到长度 n(TS padStart / Python rjust), + // pad 超长取前段 + rwfunc padstart(s:[]char/utf32, n:int64, + pad:[]char/utf32) -> (r:[]char/utf32) { + need = n - string·len(s) + pl = string·len(pad) + "" -> pre + while (need > 0 && pl > 0 && string·len(pre) < need) { + string·concat(pre, pad) -> pre + } + string·len(pre) -> q + if (need > 0 && q > need) { + string·slice(pre, 0, need) -> pre + } + string·concat(pre, s) -> r + } + // padend(s, n, pad) -> r:右侧补到长度 n(TS padEnd / Python ljust) + rwfunc padend(s:[]char/utf32, n:int64, + pad:[]char/utf32) -> (r:[]char/utf32) { + s -> r + need = n - string·len(s) + pl = string·len(pad) + "" -> suf + while (need > 0 && pl > 0 && string·len(suf) < need) { + string·concat(suf, pad) -> suf + } + string·len(suf) -> q + if (need > 0 && q > need) { + string·slice(suf, 0, need) -> suf + } + string·concat(r, suf) -> r + } + + // ── 切分与连接 ──────────────────────────────────────────────────── + // splitn(s, sep, n) -> (parts, k) :最多 n 段,末段为剩余; n <= 0 = + // 全切(Go SplitN) + // sep 空 = 逐码点切(Go/TS 阵营; Python 此处报错)。parts + // 由调用方先声明空容器。 + rwfunc splitn(s:[]char/utf32, sep:[]char/utf32, n:int64) -> ( + parts:*[int64]·[]char/utf32, k:int64) { + k = 0 + if (n != 0) { + ln = string·len(s) + m = string·len(sep) + pos = 0 + if (m == 0) { + while (pos < ln) { + if (n > 0 && k == n - 1) { + string·slice(s, pos, ln) -> seg + array·append(parts, seg) -> parts + k = k + 1 + pos = ln + } else { + string·char(s, pos) -> c + array·append(parts, c) -> parts + k = k + 1 + pos = pos + 1 + } + } + } else { + while (pos <= ln) { + if (n > 0 && k == n - 1) { + string·slice(s, pos, ln) -> seg + array·append(parts, seg) -> parts + k = k + 1 + pos = ln + 1 + } else { + string·slice(s, pos, ln) -> rest + string·find(rest, sep) -> j + if (j == -1) { + rest -> seg + array·append(parts, seg) -> parts + k = k + 1 + pos = ln + 1 + } else { + string·slice(s, pos, pos + j) -> seg + array·append(parts, seg) -> parts + k = k + 1 + pos = pos + j + m + } + } + } + } + } + } + // split(s, sep) -> (parts, k) :全切(Go Split / Python split / TS split / + // Rust split) + rwfunc split(s:[]char/utf32, sep:[]char/utf32) -> ( + parts:*[int64]·[]char/utf32, k:int64) { + string·splitn(s, sep, -1) -> (parts, k) + } + // lines(s) -> (parts, k) :按 \n 切行,行尾 \r 剥除,末尾空行丢弃(Go Lines + // / Rust lines) + rwfunc lines(s:[]char/utf32) -> ( + parts:*[int64]·[]char/utf32, k:int64) { + k = 0 + ln = string·len(s) + pos = 0 + while (pos < ln) { + string·slice(s, pos, ln) -> rest + string·find(rest, "\n") -> j + line = rest + if (j != -1) { + string·slice(s, pos, pos + j) -> line2 + line2 -> line + pos = pos + j + 1 + } else { + pos = ln + } + string·len(line) -> llen + if (llen > 0) { + string·char(line, llen - 1) -> last + if (string·cmp(last, "\r") == 0) { + string·slice(line, 0, llen - 1) -> line3 + line3 -> line + } + } + array·append(parts, line) -> parts + k = k + 1 + } + } + // fields(s) -> (parts, k) :按空白连续段切词,跳过首尾空白(Go Fields / + // Python s.split()) + rwfunc fields(s:[]char/utf32) -> ( + parts:*[int64]·[]char/utf32, k:int64) { + k = 0 + ln = string·len(s) + i = 0 + while (i < ln) { + while (i < ln) { + string·char(s, i) -> c + string·find(" \t\n\r\v\f", c) -> w + if (w == -1) { + break + } + i = i + 1 + } + if (i < ln) { + start = i + while (i < ln) { + string·char(s, i) -> c2 + string·find(" \t\n\r\v\f", c2) -> w2 + if (w2 != -1) { + break + } + i = i + 1 + } + string·slice(s, start, i) -> word + array·append(parts, word) -> parts + k = k + 1 + } + } + } + // join(parts, sep) -> r:用 sep 连接各成员(Go Join / Rust join) + rwfunc join(parts:*[int64]·[]char/utf32, + sep:[]char/utf32) -> (r:[]char/utf32) { + "" -> r + kvspace·listlen(parts) -> c + i = 0 + while (i < c) { + if (i > 0) { + string·concat(r, sep) -> r + } + kvspace·listn(parts, i) -> key + kvspace·get(parts, key) -> v + string·concat(r, v) -> r + i = i + 1 + } + } + // cut(s, sep) -> (before, after, found) :首次出现处切成两段(Go Cut; sep + // 空同 Go 语义) + rwfunc cut(s:[]char/utf32, sep:[]char/utf32) -> ( + before:[]char/utf32, after:[]char/utf32, found:bool) { + string·find(s, sep) -> j + m = string·len(sep) + ln = string·len(s) + if (j == -1) { + s -> before + "" -> after + found = false + } else { + string·slice(s, 0, j) -> before + string·slice(s, j + m, ln) -> after + found = true + } + } + + // ── 替换 ────────────────────────────────────────────────────────── + // replacen(s, old, new, limit) -> (out, k) :替换前 limit 处(limit < 0 = + // 全部,Go Replace) + // old 空 = 在每个码点边界插入 new(Py/Go/Rust/JS 一致);k = 实际替换处数 + rwfunc replacen(s:[]char/utf32, old:[]char/utf32, new:[]char/utf32, + limit:int64) -> (out:[]char/utf32, k:int64) { + ln = string·len(s) + m = string·len(old) + "" -> out + k = 0 + if (m == 0) { + i = 0 + while (i <= ln) { + if (limit >= 0 && k >= limit) { + break + } + string·concat(out, new) -> out + k = k + 1 + if (i < ln) { + string·char(s, i) -> c + string·concat(out, c) -> out + } + i = i + 1 + } + if (i < ln) { + string·slice(s, i, ln) -> tail + string·concat(out, tail) -> out + } + } else { + pos = 0 + while (pos + m <= ln) { + if (limit >= 0 && k >= limit) { + break + } + string·slice(s, pos, ln) -> rest + string·find(rest, old) -> j + if (j == -1) { + break + } + string·slice(s, pos, pos + j) -> seg + string·concat(out, seg) -> out + string·concat(out, new) -> out + k = k + 1 + pos = pos + j + m + } + string·slice(s, pos, ln) -> tail + string·concat(out, tail) -> out + } + } + // replace(s, old, new) -> (out, k) :全部替换(Go ReplaceAll / Py replace / + // Rust replace) + rwfunc replace(s:[]char/utf32, old:[]char/utf32, + new:[]char/utf32) -> (out:[]char/utf32, k:int64) { + string·replacen(s, old, new, -1) -> (out, k) + } + + // ── 变换 ────────────────────────────────────────────────────────── rwfunc reverse(s:[]char/utf32) -> (r:[]char/utf32) { n = string·len(s) "" -> r @@ -88,4 +418,151 @@ lib string { i = i + 1 } } + // capitalize(s) -> r:首字母大写、其余小写(Python capitalize) + rwfunc capitalize(s:[]char/utf32) -> (r:[]char/utf32) { + ln = string·len(s) + if (ln == 0) { + "" -> r + } else { + string·char(s, 0) -> c0 + string·slice(s, 1, ln) -> rest + string·upper(c0) -> uc + string·lower(rest) -> lr + string·concat(uc, lr) -> r + } + } + // title(s) -> r:每个字母词首字母大写、其余小写(Python title; + // 非字母即词边界) + rwfunc title(s:[]char/utf32) -> (r:[]char/utf32) { + "" -> r + n = string·len(s) + i = 0 + prev = false + while (i < n) { + string·char(s, i) -> c + string·isalpha(c) -> a + if (a) { + if (prev) { + string·lower(c) -> x + } else { + string·upper(c) -> x + } + prev = true + } else { + c -> x + prev = false + } + string·concat(r, x) -> r + i = i + 1 + } + } + // swapcase(s) -> r:字母大小写互换(Python swapcase) + rwfunc swapcase(s:[]char/utf32) -> (r:[]char/utf32) { + "" -> r + n = string·len(s) + i = 0 + while (i < n) { + string·char(s, i) -> c + string·upper(c) -> u + string·lower(c) -> l + if (string·cmp(u, c) == 0) { + string·concat(r, l) -> r + } else { + string·concat(r, u) -> r + } + i = i + 1 + } + } + + // ── 字符分类(ASCII)───────────────────────────────────────────── + // allinset(s, set) -> r:s 非空且每个码点都在 set 中(各 is* 的分类原语) + rwfunc allinset(s:[]char/utf32, set:[]char/utf32) -> (r:bool) { + n = string·len(s) + r = n > 0 + i = 0 + while (i < n) { + string·char(s, i) -> c + string·find(set, c) -> k + if (k == -1) { + r = false + break + } + i = i + 1 + } + } + rwfunc isalpha(s:[]char/utf32) -> (r:bool) { + set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + string·allinset(s, set) -> r + } + rwfunc isdigit(s:[]char/utf32) -> (r:bool) { + string·allinset(s, "0123456789") -> r + } + rwfunc isalnum(s:[]char/utf32) -> (r:bool) { + set = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + string·allinset(s, set) -> r + } + rwfunc isxdigit(s:[]char/utf32) -> (r:bool) { + string·allinset(s, "0123456789abcdefABCDEF") -> r + } + rwfunc isspace(s:[]char/utf32) -> (r:bool) { + string·allinset(s, " \t\n\r\v\f") -> r + } + // isupper(s) -> r:至少一个字母且无小写(Python isupper:数字/符号不影响判定) + rwfunc isupper(s:[]char/utf32) -> (r:bool) { + up = false + bad = false + n = string·len(s) + i = 0 + while (i < n) { + string·char(s, i) -> c + string·find("ABCDEFGHIJKLMNOPQRSTUVWXYZ", c) -> u + string·find("abcdefghijklmnopqrstuvwxyz", c) -> l + if (u != -1) { + up = true + } + if (l != -1) { + bad = true + } + i = i + 1 + } + r = up && !bad + } + // islower(s) -> r:至少一个字母且无大写(Python islower) + rwfunc islower(s:[]char/utf32) -> (r:bool) { + lo = false + bad = false + n = string·len(s) + i = 0 + while (i < n) { + string·char(s, i) -> c + string·find("abcdefghijklmnopqrstuvwxyz", c) -> l + string·find("ABCDEFGHIJKLMNOPQRSTUVWXYZ", c) -> u + if (l != -1) { + lo = true + } + if (u != -1) { + bad = true + } + i = i + 1 + } + r = lo && !bad + } + // ispunct(s) -> r:非空且全为可见 ASCII 标点(C ispunct:33..126 + // 中非字母数字) + rwfunc ispunct(s:[]char/utf32) -> (r:bool) { + al = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + n = string·len(s) + r = n > 0 + i = 0 + while (i < n) { + string·char(s, i) -> c + string·ord(c) -> o + string·find(al, c) -> a + if (o < 33 || o > 126 || a != -1) { + r = false + break + } + i = i + 1 + } + } } diff --git a/tutorial/11-string/08-strconv.kv b/tutorial/11-string/08-strconv.kv index 21056aab..612fae1f 100644 --- a/tutorial/11-string/08-strconv.kv +++ b/tutorial/11-string/08-strconv.kv @@ -1,11 +1,14 @@ -// strconv:整数 ↔ 字符串(对齐 Go strconv,base 2..36) -// formatint(有符号)/formatuint(无符号) -> []char/utf32;parseint -> int64、parseuint -> uint64 +// strconv:整数/浮点 ↔ 字符串(对齐 Go strconv,base 2..36) +// formatint/formatuint -> []char/utf32;parseint -> int64、parseuint -> uint64 +// parsefloat -> float64(整串须消费完,无 base) // 期望输出: // ff // 11111111 // -42 // 255 // 5 +// f= 3.5 +// g= -1000 rwfunc test() -> () { string·formatint(255, 16) -> hex println(hex) @@ -17,4 +20,8 @@ rwfunc test() -> () { println(n) string·parseuint("101", 2) -> u println(u) + string·parsefloat("3.5") -> f + println("f=", f) + string·parsefloat("-1e3") -> g + println("g=", g) } diff --git a/tutorial/13-stdlib/string/03-search.kv b/tutorial/13-stdlib/string/03-search.kv new file mode 100644 index 00000000..004a2cc9 --- /dev/null +++ b/tutorial/13-stdlib/string/03-search.kv @@ -0,0 +1,29 @@ +// string 查找与替换:count / rfind / replace / replacen / cut +// 期望输出: +// l x 3 +// aa x 1 +// empty x 4 +// rfind 4 +// rfind-miss -1 +// rep a+b+c 2 +// insert xaxbxcx 4 +// replacen a-bXc 1 +// cut a b=c true +// cut-miss a=b=c false +rwfunc test() -> () { + println("l x", string·count("hello world", "l")) + println("aa x", string·count("aaa", "aa")) + println("empty x", string·count("abc", "")) + println("rfind", string·rfind("abcabc", "bc")) + println("rfind-miss", string·rfind("abc", "zz")) + string·replace("a-b-c", "-", "+") -> (r, n) + println("rep", r, n) + string·replace("abc", "", "x") -> (r2, n2) + println("insert", r2, n2) + string·replacen("aXbXc", "X", "-", 1) -> (r3, n3) + println("replacen", r3, n3) + string·cut("a=b=c", "=") -> (before, after, found) + println("cut", before, after, found) + string·cut("a=b=c", "z") -> (b2, a2, f2) + println("cut-miss", b2, f2) +} diff --git a/tutorial/13-stdlib/string/04-trim-pad.kv b/tutorial/13-stdlib/string/04-trim-pad.kv new file mode 100644 index 00000000..844cd5c2 --- /dev/null +++ b/tutorial/13-stdlib/string/04-trim-pad.kv @@ -0,0 +1,16 @@ +// string 修剪与填充:trim / trimstart / trimend / padstart / padend +// 期望输出: +// trim=[hi] +// tstart=[hi ] +// tend=[ hi] +// padstart= 121abc +// padend= abc121 +// padshort= abcdef +rwfunc test() -> () { + println("trim=[" + string·trim(" hi\t\n") + "]") + println("tstart=[" + string·trimstart(" hi ") + "]") + println("tend=[" + string·trimend(" hi ") + "]") + println("padstart=", string·padstart("abc", 6, "12")) + println("padend=", string·padend("abc", 6, "12")) + println("padshort=", string·padstart("abcdef", 3, "0")) +} diff --git a/tutorial/13-stdlib/string/05-split-join.kv b/tutorial/13-stdlib/string/05-split-join.kv new file mode 100644 index 00000000..49d1d2eb --- /dev/null +++ b/tutorial/13-stdlib/string/05-split-join.kv @@ -0,0 +1,25 @@ +// string 切分与连接:split / splitn / lines / fields / join +// 散 key 容器由调用方先声明,rwfunc 往写参里填成员(array·append) +// 期望输出: +// split a-b-c +// split-empty a|b|c +// splitn a/b,c +// lines a/b/c +// fields a/b/c +rwfunc test() -> () { + p:[int64]·[]char/utf32 = {} + string·split("a,b,c", ",") -> (p, np) + println("split", string·join(p, "-")) + q:[int64]·[]char/utf32 = {} + string·split("abc", "") -> (q, nq) + println("split-empty", string·join(q, "|")) + s2:[int64]·[]char/utf32 = {} + string·splitn("a,b,c", ",", 2) -> (s2, ns) + println("splitn", string·join(s2, "/")) + l:[int64]·[]char/utf32 = {} + string·lines("a\nb\r\nc\n") -> (l, nl) + println("lines", string·join(l, "/")) + f:[int64]·[]char/utf32 = {} + string·fields(" a b\tc ") -> (f, nf) + println("fields", string·join(f, "/")) +} diff --git a/tutorial/13-stdlib/string/06-case.kv b/tutorial/13-stdlib/string/06-case.kv new file mode 100644 index 00000000..4bc51a08 --- /dev/null +++ b/tutorial/13-stdlib/string/06-case.kv @@ -0,0 +1,17 @@ +// string 大小写:upper / lower / capitalize / title / swapcase / eqfold +// (一律 ASCII,见 stdlib/string.kv) +// 期望输出: +// upper HELLO +// lower hello +// cap Hello +// title Hello World Foo-Bar +// swap hELLO wORLD +// eqfold true false +rwfunc test() -> () { + println("upper", string·upper("Hello")) + println("lower", string·lower("Hello")) + println("cap", string·capitalize("hELLO")) + println("title", string·title("hello wORLD foo-bar")) + println("swap", string·swapcase("Hello World")) + println("eqfold", string·eqfold("AbC", "aBc"), string·eqfold("ab", "ac")) +} diff --git a/tutorial/13-stdlib/string/07-classify.kv b/tutorial/13-stdlib/string/07-classify.kv new file mode 100644 index 00000000..2e46127e --- /dev/null +++ b/tutorial/13-stdlib/string/07-classify.kv @@ -0,0 +1,28 @@ +// string 字符分类:isalpha / isdigit / isalnum / isspace / isupper / islower / +// isxdigit / ispunct(一律 ASCII;空串一律 false; +// 至少一个字母且无相反大小写,数字与符号不影响判定) +// 期望输出: +// isalpha true false false +// isdigit true false +// isalnum true false +// isspace true false +// isupper true false false +// islower true false +// isxdigit true false +// ispunct true false +rwfunc test() -> () { + a1 = string·isalpha("ab") + a2 = string·isalpha("ab1") + a3 = string·isalpha("") + println("isalpha", a1, a2, a3) + println("isdigit", string·isdigit("123"), string·isdigit("12a")) + println("isalnum", string·isalnum("ab1"), string·isalnum("a b")) + println("isspace", string·isspace(" \t"), string·isspace("")) + u1 = string·isupper("AB1") + u2 = string·isupper("Ab") + u3 = string·isupper("1") + println("isupper", u1, u2, u3) + println("islower", string·islower("ab1"), string·islower("aB")) + println("isxdigit", string·isxdigit("1aF"), string·isxdigit("1g")) + println("ispunct", string·ispunct("!?,."), string·ispunct("a!")) +} diff --git a/tutorial/14-networld/11-fs-text.kv b/tutorial/14-networld/11-fs-text.kv new file mode 100644 index 00000000..a18fa608 --- /dev/null +++ b/tutorial/14-networld/11-fs-text.kv @@ -0,0 +1,52 @@ +// networld/fs 文本级 rwfunc:readtext / writetext / match / glob / grep / multi +// 文件 ↔ 文本按 UTF-8 往返(字节级 read/write 仍是 native rwir) +// match:名字段通配(* 任意串、? 单字符) glob:按通配展开路径(\n 连接) +// grep:文件或目录下匹配 glob 的成员逐行查找 multi:事务式多处编辑 +// 期望输出: +// size= 12 +// text= hello world +// utf8= 7 héllo +// match= true false +// glob= /tmp/kvlang_fs_text/a.txt +// grep= /tmp/kvlang_fs_text/a.txt:1:hello world +// multi= 1 -1 +// after= HELLO world +// bad= 0 +// keep= HELLO world +rwfunc test() -> () { + d = "/tmp/kvlang_fs_text" + a = "/tmp/kvlang_fs_text/a.txt" + b = "/tmp/kvlang_fs_text/b.md" + c = "/tmp/kvlang_fs_text/c.txt" + noenv:[int64]·[]char/utf32 = {} + clean:[int64]·[]char/utf32 = {"rm", "-rf", "/tmp/kvlang_fs_text"} + networld/proc·exec(clean, noenv) -> rc + networld/fs·mkdir(d) -> mc + networld/fs·writetext(a, "hello world\n") -> w1 + networld/fs·writetext(b, "bye\n") -> w2 + networld/fs·writetext(c, "héllo\n") -> w3 + networld/fs·readtext(a) -> (t, sz) + println("size=", sz) + println("text=", t) + networld/fs·readtext(c) -> (t4, sz4) + println("utf8=", sz4, t4) + networld/fs·match("a.txt", "*.txt") -> m1 + networld/fs·match("a.txt", "?.md") -> m2 + println("match=", m1, m2) + networld/fs·glob("/tmp/kvlang_fs_text/*.txt") -> g + println("glob=", g) + networld/fs·grep("world", d, "*.txt") -> hits + println("grep=", hits) + olds:[int64]·[]char/utf32 = {"hello"} + news:[int64]·[]char/utf32 = {"HELLO"} + networld/fs·multi(a, olds, news) -> (done, bad) + println("multi=", done, bad) + networld/fs·readtext(a) -> (t2, sz2) + println("after=", t2) + old2:[int64]·[]char/utf32 = {"L"} + new2:[int64]·[]char/utf32 = {"X"} + networld/fs·multi(a, old2, new2) -> (d2, bad2) + println("bad=", bad2) + networld/fs·readtext(a) -> (t3, sz3) + println("keep=", t3) +}