Skip to content

array2d/kvlang

Repository files navigation

kvlang

CI Go Version License: MIT Tutorial Examples

The VM of deepx (formerly dxlang) — an agent-native, train-inference-unified, self-iterating AI compute architecture. kvspace tree paths form a single unified address space; one syntax simultaneously serves as VM instructions, high-level language, compiler IR, and human-readable source.

中文文档: README_CN.md | Design: deep-dive — root design doc; README is the teaching derivative. All behavior norms (p0–p7), instruction model (§2), Link call mechanism (§6), type system (§9), diagnostics (§12) live there.


Core Model in One Screen

No IR layers — source IS the IR. The program counter is a kvspace path string; call-stack depth equals path depth:

PC    = "/vthread/tid/[0,0]/.fn/[1,0]"    the program counter is a KV path
fetch = kv.Get(PC)                         instruction fetch is one KV read
call  = create subtree; return = clean it  crash? restart and resume from PC

Every instruction occupies a 2-D coordinate [s0, s1]: [s0,0] is always the opcode, [s0,-j] read params, [s0,+j] write params.

def add(A: int, B: int) -> (C: int) { A + B -> C }
/lib/main/add/[0,0]  = "+"     /lib/main/add/[0,-1] = "A"
/lib/main/add/[0,-2] = "B"     /lib/main/add/[0,1]  = "C"

Four address-space domains: /lib (function library) /vthread (runtime frames) /sys (infrastructure) /dev (I/O).


Quick Start

# Requirements: Go 1.24+, Redis
make build

./kvlang tutorial/01-basics/hello.kv         # run a file
./kvlang -c 'print("hello, world")'          # inline mode
echo '40 + 2 -> x; print(x)' | ./kvlang      # pipe mode (; separates statements on one line)
./kvlang vet my.kv                           # syntax check
./kvlang format my.kv                        # format

Language Guide

Program Structure (read this first)

Top level: lib name { } def, and single instructions. Bare if / while / for at the top level are auto-wrapped into an implicit def init() { … }. The convention is to define main and call it:

def main() -> () {
    total = 0  # = is equivalent to <-
    1 -> i
    while (i <= 5) {
        total <- total + i
        i + 1 -> i
    }
    print(total)
}

main()

rwir(Read-Write IR):Three Assignment Forms

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 * y -> z            # right arrow: write slot on the right
f(a, b) -> r          # write-param mapping for calls; multiple: -> x, y; discard: -> _
divmod(17, 5) -> _, r # multi-write-param: discard quotient with _, keep remainder

A write slot must be a location: a bare name (frame-local), /abs/path (global key), or base.name (member). Literals are not locations.

def func(ra,rb) -> (wa,wb) { … } = composite rwir, the named form. Single-line rwir like A + B -> C is atomic (one opcode + reads + writes); def 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. Must match all write params — discard unwanted with ._ (Go-style _, engine-level: never written to kvspace).

-> (C: int) in a def 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.

# ❌ wrong:数组作读参,a[i] <- v 写读参槽 → parser 拒绝
def bad(a) -> () { 99 -> a[0] }

# ✅ correct:数组作写参,函数内读写自由
def good() -> (a:int64) { a:int64 = [10, 20]; 99 -> a[0]; a }

Decide the role first — an accumulator is an output, so declare it as a write param (write params start at zero, are readable and writable in the body — like Go named return values): def sum(arr:int64) -> (acc:int64) { acc + arr[i] -> acc }. A pure working variable is copied to a local first (A -> a, then use a):

def add(A: int, B: int) -> (C: int) {
    A + B -> C
}

def main() -> () {
    add(3, 4) -> s
    print(s)          # 7
}

main()

dict, Member Access, and Linked Lists

d = { name="kv"; ver=1 }    # dict literal: members are the flat key-family d.name, d.ver
print(d.name)               # member read
d.ver = 2                   # member write
k = "name"; d.*k -> v       # dynamic key: reads d.name (k's value becomes the key)

Pointer via path string: store an absolute path in a variable, then use .member to read/write at that path — the variable's string value becomes the path prefix.

/node = { val=42 }       # dict at absolute path
"/node" -> p             # p holds the path string
p.val -> v               # reads /node.val → 42

Data structures shared across functions (e.g. linked lists) create nodes at absolute paths (frame-locals die when the frame returns):

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

def main() -> () {
    build()
    "/n1" -> p                   # p holds a path string (a pointer)
    while (p != "") {
        p.val -> v               # pointer deref: reads /n1.val
        print(v)
        p.next -> p
    }
}

main()

Numeric Types (optional precision declaration)

f = float32(3)        # ten operators int8/16/32/64 uint8/16/32/64 float32/64 — they construct AND convert
w = int8(300)         # 44: narrowing wraps (two's complement); float→int truncates toward zero; arithmetic domain is int64/float64

Control Flow (inside def bodies only)

i = 1; sum = 0
while (i <= 10) { sum + i -> sum; i + 1 -> i }
if (sum > 50) { print("big") } else { print("small") }   # sum=55 → big
for (x in [7, 2, 9, 4]) { print(x) }

Conditions may be compound expressions: if (7 % 2 != 0) and while (i < strlen(s)) both work (auto-flattened to temp slots at compile time).

Operators

Category Symbols
Arithmetic + - * / %
Comparison == != < > <= >=
Logic && || !
Bitwise & | ^ << >>

/: both ints → integer division (C-style, 7/2=3, -9/2=-4); either side float → float division (7.0/2=3.5).

Builtins

abs neg sign pow sqrt exp log min max (variadic, e.g. max(a,b,c)) print cerr input
int float bool plus the ten precision operators · char ord strlen strcmp strstr slice concat · array len at set has sort dict kvat kvhas

a:int64 = [7, 2, 9, 4]     # typed 1D array, = ≡ <-
len(a) -> n              # 4
at(a, 2) -> e            # 9 (0-indexed)
s = "hello"
s[1] = "a"             # replaces char at index 1 → "hallo"
s + " world" -> t      # concatenation → "hallo world"

Strings support indexing and concatenation: s[i] reads the i-th char, s[i] = "X" replaces one char, a + b concatenates. C-style API: strlen; strcmp returns -1/0/1; strstr(hay, needle) returns the first index (-1 if absent); ord(c) returns the byte code.


Tutorial

100 self-contained examples (99 with expected output, fully CI-verified), organized by topic:

01-basics/        hello, arith, precision, numtypes, strings  (6 files)
02-func/          def, call, accumulator                      (2 files)
03-control/       if, while, for, guess game                  (5 files)
04-algo/          fibonacci, gcd, collatz, ...                (13 files)
07-leetcode/      LeetCode solutions                          (69 files)
06-lib/        lib block, nested, cross-lib, anon          (8 files)
./kvlang tutorial/01-basics/hello.kv         # hello kvlang
./kvlang tutorial/04-algo/fibonacci.kv       # fib = 55
./kvlang tutorial/07-leetcode/001_two_sum.kv # LeetCode

python3 tutorial/test.py                     # all 99 examples — CI verification

Error Cases

Negative tests verifying diagnostic accuracy: each .kv file is annotated with expected error/warning messages, and error_test.py checks that the compiler and runtime produce those diagnostics exactly.

error_cases/
  type_error/       e.g. char(1) → TypeError
  index_error/      e.g. at([1,2], 5) → IndexError
  zero_division/    e.g. 1/0 → ZeroDivisionError
  key_error/        e.g. kvat("/x","y") → KeyError
  value_error/      e.g. log(-5) → ValueError
  name_error/       e.g. nosuch() → NameError
  read_only/        e.g. reading a read param → compiler rejection
  recursion_error/  e.g. unbounded recurse → RecursionError
  runtime_error/    e.g. Bootstrap failure → RuntimeError
python3 tutorial/error_test.py              # all 10 negative tests
python3 tutorial/test.py                    # all 94 positive tests — CI verification

Diagnostic categories follow the diagnostic-style specification aligned to Python naming conventions (fix-028).

License

MIT — see LICENSE

About

Agent-native 训推一体自迭代强人工智能计算架构 — kvspace 树形路径统一地址空间,单层 IR(源码即指令),读写码显式数据流

Resources

License

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors