Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ echo '40 + 2 -> x; print(x)' | ./kvlang # pipe mode (; separates statements

```kv
rwfunc main() -> () {
total = 0 # = is equivalent to <-
total = 0
1 -> i
while (i <= 5) {
total <- total + i
total = total + i
i + 1 -> i
}
println(total)
Expand All @@ -95,11 +95,10 @@ rwfunc main() -> () {
main()
```

### rwir(Read-Write IR):Three Assignment Forms
### rwir(Read-Write IR):Two Write Forms

```kv
x = 40 + 2 # = : write slot on the left (≡ <-); = is NOT an expression, cannot nest in conditions
y <- x # left arrow: write slot on the left
x = 40 + 2 # = : write slot on the left; = is NOT an expression, cannot nest in conditions
x × y -> z # right arrow: write slot on the right
f(a, b) -> r # write-param mapping for calls; multiple: -> x, y; discard: -> _
```
Expand All @@ -109,9 +108,9 @@ A write slot must be a **location**: a bare name (frame-local), `/abs/path` (glo
**`rwfunc func(ra,rb) -> (wa,wb) { … }` = composite rwir**, the named form. Single-line rwir like `A + B -> C` is atomic (one opcode + reads + writes); `rwfunc` packs multiple rwir into a named unit with the same arrow interface — `(ra,rb)` declare read params, `-> (wa,wb)` declare write params. Calling `add(3,4) -> s` binds arguments to read slots, maps write slots back to the caller frame. No return values, only write-param mapping.

`-> (C:int64)` in a `rwfunc` signature is a **write-param declaration**. The function writes results into its write-param slots; the caller maps them with `-> r`.
**Read params are read-only**: the body may not place a read param in a write slot (e.g. `A = A + 1`). This includes array element writes — `a[i] <- v` writes through `a`, so `a` must be a write param if you need to modify it. **Array/dict to mutate → write param; array/dict to read only → read param.**
**Read params are read-only**: the body may not place a read param in a write slot (e.g. `A = A + 1`). This includes array element writes — `a[i] = v` writes through `a`, so `a` must be a write param if you need to modify it. **Array/dict to mutate → write param; array/dict to read only → read param.**
```kv
# ❌ wrong: array as read param, a[i] <- v writes through read-param slot → parser rejects
# ❌ wrong: array as read param, a[i] = v writes through read-param slot → parser rejects
rwfunc bad(a:int64) -> () { 99 -> a[0] }

# ✅ correct: array as write param, readable and writable inside the body
Expand Down Expand Up @@ -157,8 +156,8 @@ Data structures shared across functions (e.g. linked lists) create nodes at **ab

```kv
rwfunc build() -> () {
/n1 = { val=1; next="/n2" } # = is equivalent to <-
/n2 <- { val=2; next="/n3" }
/n1 = { val=1; next="/n2" }
/n2 = { val=2; next="/n3" }
{ val=3; next="" } -> /n3
}

Expand Down Expand Up @@ -224,7 +223,7 @@ Conditions may be compound expressions: `if (7 % 2 != 0)` and `while (i < string
**`print` / `println` / `cerr` are NOT builtins.** In the KV world there is no terminal — only keys and values — so I/O is not a core-language primitive. They are **extension rwir**: the `term` extension runtime registers them at `/lib/<opcode>` (kind `rwir`) and writes to the host process's `stdout`/`stderr`. The core runtime recognizes any `/lib/<opcode>` that carries an `rwir` signature and is not a builtin as an extension rwir, and hands it off to its extension runtime. Same mechanism as `json.to` / `json.from` (the `json` extension) and tensor ops (the numpy / GPU extensions).

```kv
a:int64 = [7, 2, 9, 4] # typed 1D array, = ≡ <-
a:int64 = [7, 2, 9, 4] # typed 1D array
ndarray.numel(a) -> n # 4
at(a, 2) -> e # 9 (0-indexed)
set(a, 1, 99) -> a # modify element: a becomes [7, 99, 9, 4]
Expand Down
19 changes: 9 additions & 10 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ echo '40 + 2 -> x; print(x)' | ./kvlang # pipe 模式(; 分隔同行语

```kv
rwfunc main() -> () {
total = 0 # = 等价于 <-
total = 0
1 -> i
while (i <= 5) {
total <- total + i
total = total + i
i + 1 -> i
}
println(total)
Expand All @@ -95,11 +95,10 @@ rwfunc main() -> () {
main()
```

### rwir(读写码):赋值三形态
### rwir(读写码):写入两形态

```kv
x = 40 + 2 # = :写槽在左(≡ <-);= 不是表达式,不能嵌进条件里
y <- x # 左箭头:写槽在左
x = 40 + 2 # = :写槽在左;= 不是表达式,不能嵌进条件里
x × y -> z # 右箭头:写槽在右
f(a, b) -> r # 函数写参映射;多写参 -> x, y;丢弃用 -> _
```
Expand All @@ -109,9 +108,9 @@ f(a, b) -> r # 函数写参映射;多写参 -> x, y;丢弃用 -> _
**`rwfunc func(ra,rb) -> (wa,wb) { … }` = 自定义复合 rwir**,单条 rwir 如 `A + B -> C` 是原子 rwir(一个操作码 + 读参 + 写参);`rwfunc` 把多条 rwir 打包成命名单元,对外暴露相同的箭头接口——`(ra,rb)` 是读参声明,`-> (wa,wb)` 是写参声明。调用 `add(3,4) -> s` 即把实参绑入读槽、写槽映射回调用方帧。没有返回值,只有写参映射。

`rwfunc` 签名中 `-> (C:int64)` 是**写参声明**。函数把结果写进写参槽,调用方用 `-> r` 把写参映射到自己的位置。
**读参只读**:函数体内不可把读参放进写槽(如 `A = A + 1`)。数组元素写同理——`a[i] <- v` 写穿 `a`,要修改的数组/字典必须放写参位置。
**读参只读**:函数体内不可把读参放进写槽(如 `A = A + 1`)。数组元素写同理——`a[i] = v` 写穿 `a`,要修改的数组/字典必须放写参位置。
```kv
# ❌ 错误:数组作读参,a[i] <- v 写读参槽 → parser 拒绝
# ❌ 错误:数组作读参,a[i] = v 写读参槽 → parser 拒绝
rwfunc bad(a:int64) -> () { 99 -> a[0] }

# ✅ 正确:数组作写参,函数内读写自由
Expand Down Expand Up @@ -154,8 +153,8 @@ p.val -> v # 读 /node.val → 42

```kv
rwfunc build() -> () {
/n1 = { val=1; next="/n2" } # = 等价于 <-
/n2 <- { val=2; next="/n3" }
/n1 = { val=1; next="/n2" }
/n2 = { val=2; next="/n3" }
{ val=3; next="" } -> /n3
}

Expand Down Expand Up @@ -221,7 +220,7 @@ for (x in [7, 2, 9, 4]) { println(x) }
**`print` / `println` / `cerr` 不是内建。** KV 世界里没有终端,只有 key 和 value——I/O 不是核心语言原语。它们是**扩展 rwir**:由 `term` 扩展运行时把签名注册到 `/lib/<opcode>`(kind=`rwir`),并写宿主进程的 `stdout`/`stderr`。核心 runtime 把任何"`/lib/<opcode>` 上带 `rwir` 签名、且不在 builtin 表里"的 opcode 识别为扩展 rwir,交给其扩展运行时执行。与 `json.to` / `json.from`(json 扩展)、tensor 算子(numpy / GPU 扩展)同一套机制。

```kv
a:int64 = [7, 2, 9, 4] # 带类型 1D 数组,= ≡ <-
a:int64 = [7, 2, 9, 4] # 带类型 1D 数组
ndarray.numel(a) -> n # 4
at(a, 2) -> e # 9
set(a, 1, 99) -> a # 修改元素:a 变为 [7, 99, 9, 4]
Expand Down
2 changes: 1 addition & 1 deletion benchmark/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ kvlang 是被测对象,**分别在三个 kvspace 后端上各跑一遍,占
| `hash_table` | 哈希表增删查 | Knuth 乘法散列,插入 + 查找求和 |
| `matmul` | 浮点运算 + 循环优化 | 稠密方阵乘三重循环,float64 校验和 ×1e6 精确对齐 |
| `k_nucleotide` | 字符串 + 哈希表 | 逐字符 `ord` 入哈希表统计碱基频次 |
| `iops` | 最小寻址单元往返地板价 | 单 key 读-改-写 `a<-a+1`,per-op 延迟(对齐 #204,参考基线) |
| `iops` | 最小寻址单元往返地板价 | 单 key 读-改-写 `a=a+1`,per-op 延迟(对齐 #204,参考基线) |
| `prime_sieve` | 计算 / 控制流密集 | 嵌套 `while` + 取模,O(n²) 内层迭代(参考基线) |

kvlang 的性能瓶颈是「PC/帧/局部全落 KV 树、每步一次往返」的架构本质(见 kvlang#194 #204 #116),
Expand Down
8 changes: 4 additions & 4 deletions benchmark/cases/binary_search/binary_search.kv
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ rwfunc test() -> () {
i -> arr·*i
i + 1 -> i
}
t0 <- time·now()
t0 = time·now()
0 -> sum
0 -> found
0 -> q
Expand All @@ -41,10 +41,10 @@ rwfunc test() -> () {
}
q + 1 -> q
}
t1 <- time·now()
t1 = time·now()
println("bsearch: found =", found, "sum =", sum)
delta <- time·sub(t1, t0)
ns <- time/duration·as_nanos(delta)
delta = time·sub(t1, t0)
ns = time/duration·as_nanos(delta)
println("__bench_ns:", ns)
println("__bench_input: N=__SCALE__")
}
8 changes: 4 additions & 4 deletions benchmark/cases/binary_trees/binary_trees.kv
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ rwfunc test() -> () {
bid:[int64]·int64 = {}
bdep:[int64]·int64 = {}
0 -> nid
t0 <- time·now()
t0 = time·now()
nid + 1 -> nid
nid -> root
0 -> L·*root
Expand Down Expand Up @@ -63,10 +63,10 @@ rwfunc test() -> () {
tp + 1 -> tp
}
}
t1 <- time·now()
t1 = time·now()
println("bintree: nodes =", count)
delta <- time·sub(t1, t0)
ns <- time/duration·as_nanos(delta)
delta = time·sub(t1, t0)
ns = time/duration·as_nanos(delta)
println("__bench_ns:", ns)
println("__bench_input: depth=__SCALE__")
}
14 changes: 7 additions & 7 deletions benchmark/cases/fib/fib.kv
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ rwfunc fib(n:int64) -> (r:int64) {
if (n <= 1) {
n -> r
} else {
a <- n - 1
b <- n - 2
a = n - 1
b = n - 2
fib(a) -> x
fib(b) -> y
r <- x + y
r = x + y
}
}

rwfunc test() -> () {
t0 <- time·now()
t0 = time·now()
fib(__SCALE__) -> ans
t1 <- time·now()
t1 = time·now()
println("fib =", ans)
delta <- time·sub(t1, t0)
ns <- time/duration·as_nanos(delta)
delta = time·sub(t1, t0)
ns = time/duration·as_nanos(delta)
println("__bench_ns:", ns)
println("__bench_input: n=__SCALE__")
}
8 changes: 4 additions & 4 deletions benchmark/cases/hash_table/hash_table.kv
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
rwfunc test() -> () {
__SCALE__ -> n
h:[int64]·int64 = {}
t0 <- time·now()
t0 = time·now()
0 -> i
while (i < n) {
i × 2654435761 -> k0
Expand All @@ -27,10 +27,10 @@ rwfunc test() -> () {
}
i + 1 -> i
}
t1 <- time·now()
t1 = time·now()
println("hash: hits =", hits, "sum =", sum)
delta <- time·sub(t1, t0)
ns <- time/duration·as_nanos(delta)
delta = time·sub(t1, t0)
ns = time/duration·as_nanos(delta)
println("__bench_ns:", ns)
println("__bench_input: N=__SCALE__")
}
12 changes: 6 additions & 6 deletions benchmark/cases/iops/iops.kv
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
// iops benchmark — 单变量反复读改写 a<-a+1(最小寻址单元的每操作 KV 往返地板价,对齐 #204)
// iops benchmark — 单变量反复读改写 a=a+1(最小寻址单元的每操作 KV 往返地板价,对齐 #204)
// 循环次数 N 由 __SCALE__ 占位(勿改逻辑)
// 期望输出(N=2000 时):
// iops a = 2000
rwfunc iops(n:int64) -> (a:int64) {
a = 0
1 -> i
while (i <= n) {
a <- a + 1
a = a + 1
i = i + 1
}
}

rwfunc test() -> () {
t0 <- time·now()
t0 = time·now()
iops(__SCALE__) -> ans
t1 <- time·now()
t1 = time·now()
println("iops a =", ans)
delta <- time·sub(t1, t0)
ns <- time/duration·as_nanos(delta)
delta = time·sub(t1, t0)
ns = time/duration·as_nanos(delta)
println("__bench_ns:", ns)
println("__bench_input: N=__SCALE__")
}
8 changes: 4 additions & 4 deletions benchmark/cases/k_nucleotide/k_nucleotide.kv
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ rwfunc test() -> () {
}
string·len(s) -> n
h:[int64]·int64 = {}
t0 <- time·now()
t0 = time·now()
0 -> i
while (i < n) {
string·char(s, i) -> ch
Expand All @@ -26,14 +26,14 @@ rwfunc test() -> () {
cnt -> h·*c
i + 1 -> i
}
t1 <- time·now()
t1 = time·now()
kv·get(h, 65) -> a
kv·get(h, 67) -> cc
kv·get(h, 71) -> g
kv·get(h, 84) -> t
println("knuc: A =", a, "C =", cc, "G =", g, "T =", t)
delta <- time·sub(t1, t0)
ns <- time/duration·as_nanos(delta)
delta = time·sub(t1, t0)
ns = time/duration·as_nanos(delta)
println("__bench_ns:", ns)
println("__bench_input: rep=__SCALE__")
}
8 changes: 4 additions & 4 deletions benchmark/cases/matmul/matmul.kv
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ rwfunc test() -> () {
}
i + 1 -> i
}
t0 <- time·now()
t0 = time·now()
0.0 -> checksum
0 -> i
while (i < n) {
Expand All @@ -54,12 +54,12 @@ rwfunc test() -> () {
}
i + 1 -> i
}
t1 <- time·now()
t1 = time·now()
checksum × 1000000.0 -> scaled
int64(scaled) -> out
println("matmul: check =", out)
delta <- time·sub(t1, t0)
ns <- time/duration·as_nanos(delta)
delta = time·sub(t1, t0)
ns = time/duration·as_nanos(delta)
println("__bench_ns:", ns)
println("__bench_input: N=__SCALE__")
}
10 changes: 5 additions & 5 deletions benchmark/cases/nqueens/nqueens.kv
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@ rwfunc nq(cols:int64, d1:int64, d2:int64, all:int64) -> (cnt:int64) {
d2 | p -> u2
u2 >> 1 -> nd2
nq(nc, nd1, nd2, all) -> sub
cnt <- cnt + sub
cnt = cnt + sub
}
}
}

rwfunc test() -> () {
t0 <- time·now()
t0 = time·now()
1 << __SCALE__ -> sh
sh - 1 -> all
nq(0, 0, 0, all) -> ans
t1 <- time·now()
t1 = time·now()
println("queens =", ans)
delta <- time·sub(t1, t0)
ns <- time/duration·as_nanos(delta)
delta = time·sub(t1, t0)
ns = time/duration·as_nanos(delta)
println("__bench_ns:", ns)
println("__bench_input: N=__SCALE__")
}
14 changes: 7 additions & 7 deletions benchmark/cases/prime_sieve/prime_sieve.kv
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ rwfunc prime_sieve(limit:int64) -> () {
count = 0
2 -> n
while (n <= limit) {
is_prime <- true
is_prime = true
d = 2
while (d < n) {
n % d -> rem
divisible <- rem == 0
divisible = rem == 0
if (divisible) {
is_prime = false
break
} else {
d <- d + 1
d = d + 1
}
}
if (is_prime) {
Expand All @@ -31,11 +31,11 @@ rwfunc prime_sieve(limit:int64) -> () {
}

rwfunc test() -> () {
t0 <- time·now()
t0 = time·now()
prime_sieve(__SCALE__)
t1 <- time·now()
delta <- time·sub(t1, t0)
ns <- time/duration·as_nanos(delta)
t1 = time·now()
delta = time·sub(t1, t0)
ns = time/duration·as_nanos(delta)
println("__bench_ns:", ns)
println("__bench_input: N=__SCALE__")
}
8 changes: 4 additions & 4 deletions benchmark/cases/quicksort/quicksort.kv
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ rwfunc test() -> () {
v -> arr·*i
i + 1 -> i
}
t0 <- time·now()
t0 = time·now()
st_lo:[int64]·int64 = {}
st_hi:[int64]·int64 = {}
0 -> top
Expand Down Expand Up @@ -52,15 +52,15 @@ rwfunc test() -> () {
top + 1 -> top
}
}
t1 <- time·now()
t1 = time·now()
n ÷ 2 -> mid
n - 1 -> last
kv·get(arr, 0) -> a0
kv·get(arr, mid) -> am
kv·get(arr, last) -> al
println("qsort: a0 =", a0, "amid =", am, "alast =", al)
delta <- time·sub(t1, t0)
ns <- time/duration·as_nanos(delta)
delta = time·sub(t1, t0)
ns = time/duration·as_nanos(delta)
println("__bench_ns:", ns)
println("__bench_input: N=__SCALE__,seed=1")
}
Loading
Loading