diff --git a/assets/badges.svg b/assets/badges.svg
deleted file mode 100644
index 1d5e7ef..0000000
--- a/assets/badges.svg
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
- version
-
- v0.4.0
-
- license
-
- MIT
-
- language
-
- C
-
- compiler
-
- LLVM
-
- build
-
- zig
-
- docs
-
- 7 langs
-
-
diff --git a/assets/social-preview.png b/assets/social-preview.png
deleted file mode 100644
index a7cbb50..0000000
Binary files a/assets/social-preview.png and /dev/null differ
diff --git a/examples/01-hello.bio b/examples/01-hello.bio
deleted file mode 100644
index a0b591e..0000000
--- a/examples/01-hello.bio
+++ /dev/null
@@ -1,24 +0,0 @@
-// 01-hello.bio
-// Teaches: the basic skeleton of every BioLang program.
-//
-// A program starts with `program main;` and defines the main program stream
-// `Main`. Its `exec()` method is the entry point that runs when you execute
-// the file.
-//
-// `IO` is the abstract IOStream parent; `CIO` is its console implementation.
-// `CIO::println(...)` writes its arguments (space-separated) + newline.
-
-program main;
-
-Main {
- void exec() {
- CIO::println("Hello, BioLang!");
- CIO::println("Every call in BioLang is a request.");
- CIO::println("This one was just a request to print a line.");
- }
-}
-
-// Expected output:
-// Hello, BioLang!
-// Every call in BioLang is a request.
-// This one was just a request to print a line.
diff --git a/examples/02-requests.bio b/examples/02-requests.bio
deleted file mode 100644
index f13c28e..0000000
--- a/examples/02-requests.bio
+++ /dev/null
@@ -1,61 +0,0 @@
-// 02-requests.bio
-// Teaches: the request / response model — the heart of BioLang.
-//
-// Every operation is a *request*. A request is answered with
-// res ; (respond)
-// or refused with
-// ref ; (refuse)
-//
-// Calling a method returns an `ALL` value that carries both halves:
-// get result — the responded value (if not refused)
-// cause result — the refusal reason (if refused)
-//
-// A method that never responds nor refuses is refused with the default
-// reason "nothing" (which counts as falsy in `if`).
-
-program main;
-
-// A signature stream declares *what* a stream can do.
-Stream Calc {
- int add(a int, b int);
- int div(a int, b int);
-}
-
-// A fork supplies the actual implementation.
-Calc MyCalc {
- int add(a int, b int) { res a + b; } // respond
- int div(a int, b int) { ref "division by zero"; } // refuse
-}
-
-Main {
- void exec() {
- // A successful request carries its value; get takes it.
- ALL r = MyCalc::add(3, 4);
- CIO::println("3 + 4 =", get r);
-
- // A refused request carries its reason; cause takes it.
- ALL bad = MyCalc::div(1, 0);
- CIO::println("div cause:", cause bad);
-
- // Methods that were never declared are refused too.
- ALL missing = MyCalc::sqrt(9);
- CIO::println("missing method cause:", cause missing);
-
- // No explicit res/ref → default ref(nothing), which is falsy.
- ALL d = noReturn();
- CIO::println("default return cause:", cause d);
- if (d) { CIO::println("if is true"); }
- else { CIO::println("if is false (default ref nothing)"); }
- }
-
- // This method never responds or refuses.
- int noReturn() { CIO::println("(noReturn runs)"); }
-}
-
-// Expected output:
-// (noReturn runs)
-// 3 + 4 = 7
-// div cause: division by zero
-// missing method cause: stream MyCalc refuses: no method sqrt
-// default return cause: nothing
-// if is false (default ref nothing)
diff --git a/examples/03-control-flow.bio b/examples/03-control-flow.bio
deleted file mode 100644
index df12b0a..0000000
--- a/examples/03-control-flow.bio
+++ /dev/null
@@ -1,56 +0,0 @@
-// 03-control-flow.bio
-// Teaches: control flow — if / else if / else, while, for, break, continue.
-//
-// Truthiness: the number 0, the empty string "", and a refused request are
-// all false; anything else is true. Loops follow the same rule.
-
-program main;
-
-Main {
- void exec() {
- // while: sum 1..10
- ALL sum = 0;
- ALL i = 1;
- while (i <= 10) {
- sum = sum + i;
- i = i + 1;
- }
- CIO::println("1..10 sum (while) =", sum);
-
- // for: factorial 5!
- ALL fac = 1;
- for (ALL k = 1; k <= 5; k = k + 1;) {
- fac = fac * k;
- }
- CIO::println("5! (for) =", fac);
-
- // break skips out, continue skips the rest of this iteration
- for (ALL j = 1; j <= 10; j = j + 1;) {
- if (j == 4) { continue; } // never print 4
- if (j > 7) { break; } // stop printing after 7
- CIO::print(j, " ");
- }
- CIO::println();
-
- // if / else if / else
- ALL n = 0;
- if (n > 0) { CIO::println("n is positive"); }
- else if (n < 0) { CIO::println("n is negative"); }
- else { CIO::println("n is zero"); }
-
- // for(;;) is an infinite loop; break leaves it
- ALL total = 0;
- for (;;) {
- total = total + 1;
- if (total >= 3) { break; }
- }
- CIO::println("for(;;) counted:", total);
- }
-}
-
-// Expected output:
-// 1..10 sum (while) = 55
-// 5! (for) = 120
-// 1 2 3 5 6 7
-// n is zero
-// for(;;) counted: 3
diff --git a/examples/04-streams-fork.bio b/examples/04-streams-fork.bio
deleted file mode 100644
index b83ec75..0000000
--- a/examples/04-streams-fork.bio
+++ /dev/null
@@ -1,67 +0,0 @@
-// 04-streams-fork.bio
-// Teaches: signature streams, forked implementations, fields, bare calls,
-// and passing a stream as an argument.
-//
-// A `Stream` declares a contract (method signatures + optional fields).
-// A fork ` { ... }` provides the real implementation.
-// A *bare call* `add(a, b)` searches all streams for a method named `add`.
-
-program main;
-
-// 1. Signature: `Calc` can add and divide.
-Stream Calc {
- int add(a int, b int);
- int div(a int, b int);
-}
-
-// 2. Fork: `MyCalc` implements `Calc`.
-Calc MyCalc {
- int add(a int, b int) { res a + b; }
- int div(a int, b int) { ref "division by zero"; }
-}
-
-// 3. A fork can carry its own fields (state), read/written via this::.
-Stream Counter {
- int count; // a field, materialized with a default (0)
-}
-Counter C {
- void reset() { this::count = 0; } // write a field
- void bump() { this::count = count + 1; } // bare read of the field
- int get() { res count; }
- int doubleGet() { res get() + get(); } // bare call inside the stream
-}
-
-// 4. Streams are first-class: pass one as a parameter and call it.
-Stream Greeter {
- void greet(cio CIO);
-}
-Greeter G {
- void greet(cio CIO) { cio::println("hello from a passed stream!"); }
-}
-
-Main {
- void exec() {
- // Calling a forked stream by its signature name falls back to the
- // forked implementation.
- ALL r = Calc::add(2, 3);
- CIO::println("Calc::add(2,3) =", get r);
-
- // Bare calls are resolved globally.
- ALL b = add(10, 20);
- CIO::println("bare add(10,20) =", get b);
-
- // Field state with this::
- C::reset();
- C::bump(); C::bump(); C::bump();
- CIO::println("count =", get C::get(), " doubleGet =", get C::doubleGet());
-
- // Passing a stream reference (CIO) as an argument.
- G::greet(CIO);
- }
-}
-
-// Expected output:
-// Calc::add(2,3) = 5
-// bare add(10,20) = 30
-// count = 3 doubleGet = 6
-// hello from a passed stream!
diff --git a/examples/05-io-substreams.bio b/examples/05-io-substreams.bio
deleted file mode 100644
index 5865628..0000000
--- a/examples/05-io-substreams.bio
+++ /dev/null
@@ -1,47 +0,0 @@
-// 05-io-substreams.bio
-// Teaches: the IO family — CIO (console), FIO (file), SIO (string), and IO.
-//
-// CIO Console : println/print/get/getln/readInt/readNumber/error
-// FIO File : open/close/readFile/writeFile/appendFile/exists ...
-// SIO String : format/upper/lower/trim/contains/substring/replace ...
-// IO Aggregate : dispatches to CIO/FIO/SIO in order
-//
-// Stream methods come in two kinds:
-// text streams: println/print (write text), get/getln (read text)
-// byte streams: write (raw bytes), read (raw byte 0-255, -1 on EOF)
-
-program main;
-
-Main {
- void exec() {
- // CIO: console output.
- CIO::println("CIO console output");
- CIO::println("IO is abstract; CIO implements");
-
- // SIO: an in-memory string buffer acts as a "file".
- ALL s = SIO::format("%d + %d = %d", 2, 3, 5);
- CIO::println("SIO::format →", get s);
- ALL up = SIO::upper("hello");
- CIO::println("SIO::upper →", get up);
- SIO::println("line one");
- ALL line = SIO::getln(); // read the buffer back
- CIO::println("SIO::getln →", get line);
-
- // FIO: write and read back a real file.
- FIO::writeFile("/tmp/bio_example.txt", "Hello from BioLang!");
- FIO::appendFile("/tmp/bio_example.txt", " Appended line");
- ALL content = FIO::readFile("/tmp/bio_example.txt");
- CIO::println("FIO read back:", get content);
- ALL ok = FIO::exists("/tmp/bio_example.txt");
- CIO::println("FIO::exists →", get ok);
- }
-}
-
-// Expected output:
-// CIO console output
-// IO is abstract; CIO implements
-// SIO::format → 2 + 3 = 5
-// SIO::upper → HELLO
-// SIO::getln → line one
-// FIO read back: Hello from BioLang! Appended line
-// FIO::exists → 1
diff --git a/examples/06-classes-objects.bio b/examples/06-classes-objects.bio
deleted file mode 100644
index 5ece1f7..0000000
--- a/examples/06-classes-objects.bio
+++ /dev/null
@@ -1,53 +0,0 @@
-// 06-classes-objects.bio
-// Teaches: classes and objects.
-//
-// A `Class` is essentially a stream. `new Class(args...)` forks the class
-// stream, auto-calls `__init__(args...)`, and returns an object. Inside
-// object methods `this` is the object itself; declared fields are read and
-// written via `obj.field`, `obj::field`, or `this::field`.
-
-program main;
-
-Class Hero {
- // Constructor: called automatically by `new Hero(...)`.
- void __init__(name string, hp int) {
- Obj::set(this, "name", name); // store arbitrary attributes
- Obj::set(this, "hp", hp);
- CIO::println("hero appears:", name);
- }
-
- // A method returning a value.
- int getHp() { res 100; }
-
- string getName() { res get Obj::get(this, "name"); }
-}
-
-Main {
- void exec() {
- // new → fork class stream + run __init__, returning an object.
- ALL h = new Hero("TAK", 88);
- CIO::println("object:", h);
-
- // Attributes are directly accessible.
- CIO::println("name =", h.name, " hp =", h.hp);
-
- // Call methods on the object with obj::method().
- ALL hp = Obj::call(h, "getHp");
- CIO::println("Obj::call getHp() =", get hp);
- ALL nm = h::getName();
- CIO::println("h::getName() =", get nm);
-
- // Each instance is independent.
- ALL h2 = new Hero("Lily", 66);
- CIO::println("h2.hp =", h2.hp, " (h.hp still =", h.hp, ")");
- }
-}
-
-// Expected output:
-// hero appears: TAK
-// object:
-// name = TAK hp = 88
-// Obj::call getHp() = 100
-// h::getName() = TAK
-// hero appears: Lily
-// h2.hp = 66 (h.hp still = 88 )
diff --git a/examples/07-arrays.bio b/examples/07-arrays.bio
deleted file mode 100644
index c3b4536..0000000
--- a/examples/07-arrays.bio
+++ /dev/null
@@ -1,54 +0,0 @@
-// 07-arrays.bio
-// Teaches: arrays and vectors.
-//
-// Array and Vector are classes *written in Bio code* on top of the Solid
-// contiguous stream. `new Array(n)` creates an array of n zeroes;
-// `Arrays::vector()` (or `new Vector()`) creates a growable vector. Every
-// array registers itself in the `Arrays` collection.
-
-program main;
-
-Main {
- void exec() {
- // Fixed-size array of 3 zeroes.
- ALL a = new Array(3);
- a::set(0, 10); a::set(1, 20); a::set(2, 30);
- CIO::println("array:", a);
-
- // Arrays can grow too.
- a::push(40);
- CIO::println("after push:", a, " len:", get a::len());
- CIO::println("join(-):", get a::join("-"));
-
- // Indexed read and write with a[i].
- CIO::println("a[1] =", a[1]);
- a[1] = 99;
- CIO::println("after a[1] = 99:", a);
-
- // Array literal: new type[n] works for base types and classes.
- int[] b = new int[4];
- for (ALL i = 0; i < 4; i = i + 1;) { b[i] = i * i; }
- CIO::println("new int[4] squares:", b);
-
- // Vector: grows freely.
- ALL v = Arrays::vector();
- v::push(10); v::push(20); v::push(30);
- CIO::println("vector:", v, " len:", get v::len());
-
- // The Arrays collection tracks all live instances.
- CIO::println("Arrays count:", get Arrays::count());
- Arrays::forget(v);
- CIO::println("after forget:", get Arrays::count());
- }
-}
-
-// Expected output:
-// array: [10, 20, 30]
-// after push: [10, 20, 30, 40] len: 4
-// join(-): 10-20-30-40
-// a[1] = 20
-// after a[1] = 99: [10, 99, 30, 40]
-// new int[4] squares: [0, 1, 4, 9]
-// vector: [10, 20, 30] len: 3
-// Arrays count: 3 (a, b, and v all register in the Arrays collection)
-// after forget: 2
diff --git a/examples/08-multi-return.bio b/examples/08-multi-return.bio
deleted file mode 100644
index e776a77..0000000
--- a/examples/08-multi-return.bio
+++ /dev/null
@@ -1,58 +0,0 @@
-// 08-multi-return.bio
-// Teaches: multiple return types and multi-value responses.
-//
-// Methods may declare void/int/float/double/string/char, optionally with a
-// [] array suffix, uniformly across signature streams, forks, classes and
-// Main. `res a, b, c;` responds with several values at once, which arrive as
-// an array.
-//
-// Note: a raw array from `res a, b, c;` can be printed directly, but indexed
-// access and methods need an Array *object* — build one with `new Array(n)`.
-
-program main;
-
-Stream Math {
- int add(a int, b int);
- string greet(name string);
- int[] triple(a int);
-}
-Math M {
- int add(a int, b int) { res a + b; }
- string greet(name string) { res SIO::format("Hello, %s", name); }
- int[] triple(a int) { res a, a * 2, a * 3; } // multi-value → array
-}
-
-Class Hero {
- string[] titles() { res "brave", "dragon slayer", "legend"; }
-}
-
-Main {
- void exec() {
- ALL r = M::add(3, 4);
- CIO::println("add(3,4) =", get r);
-
- ALL g = M::greet("TAK");
- CIO::println("greet =", get g);
-
- // `res a, b, c;` responds with several values at once; they arrive
- // as a raw array, which prints as [ ... ].
- ALL t = get M::triple(10);
- CIO::println("triple(10) =", t);
-
- // For element access and methods, use an Array object:
- ALL arr = new Array(3);
- arr::set(0, 10); arr::set(1, 20); arr::set(2, 30);
- CIO::println("arr =", arr, " arr[1] =", arr[1], " len =", get arr::len());
-
- // A method can also return a string[].
- ALL ts = get Hero::titles();
- CIO::println("titles =", ts);
- }
-}
-
-// Expected output:
-// add(3,4) = 7
-// greet = Hello, TAK
-// triple(10) = [10, 20, 30]
-// arr = [10, 20, 30] arr[1] = 20 len = 3
-// titles = [brave, dragon slayer, legend]
diff --git a/examples/09-threads.bio b/examples/09-threads.bio
deleted file mode 100644
index 158259c..0000000
--- a/examples/09-threads.bio
+++ /dev/null
@@ -1,44 +0,0 @@
-// 09-threads.bio
-// Teaches: cooperative threads with Threads.
-//
-// Threads are cooperative user threads (ucontext). `Threads::spawn("name",
-// args...)` runs a *bare method* (found globally by name) in a new thread
-// and returns a thread id; `Threads::join(id)` waits for it and returns its
-// result. Inside a thread, `Threads::yield()` voluntarily gives up the CPU
-// so other threads can run (there is no preemption).
-
-program main;
-
-Calc Worker {
- void factorial(n int) {
- ALL f = 1;
- ALL i = 1;
- while (i <= n) { f = f * i; i = i + 1; }
- res f;
- }
- void countUp(n int) {
- ALL i = 0;
- while (i < n) { i = i + 1; Threads::yield(); }
- res i;
- }
-}
-
-Main {
- void exec() {
- // Spawn two threads running the bare methods "factorial" and "countUp".
- ALL t1 = get Threads::spawn("factorial", 10);
- ALL t2 = get Threads::spawn("countUp", 5);
- CIO::println("live threads:", get Threads::active());
-
- // Join waits for each thread and fetches its res/ref.
- ALL r1 = Threads::join(t1);
- CIO::println("thread", t1, " 10! =", get r1);
- ALL r2 = Threads::join(t2);
- CIO::println("thread", t2, " countUp =", get r2);
- }
-}
-
-// Expected output:
-// live threads: 2
-// thread 1 10! = 3628800
-// thread 2 countUp = 5
diff --git a/examples/10-taskm.bio b/examples/10-taskm.bio
deleted file mode 100644
index 8bab7d7..0000000
--- a/examples/10-taskm.bio
+++ /dev/null
@@ -1,47 +0,0 @@
-// 10-taskm.bio
-// Teaches: the Taskm task manager.
-//
-// Taskm is a scheduling stream that round-robins threads until all are done.
-// Taskm::add("method", args...) register a task (reusing Threads)
-// Taskm::interval(ms) set the rotation interval (default 0)
-// Taskm::run() drive the scheduling loop to completion
-// Taskm::stop() stop the loop
-// Taskm::active() number of unfinished tasks
-
-program main;
-
-Calc Worker {
- void jobA(n int) { // sum 1..n, yielding every step
- ALL s = 0;
- ALL i = 1;
- while (i <= n) { s = s + i; i = i + 1; Threads::yield(); }
- res s;
- }
- void jobB(n int) { // 2^n, yielding every step
- ALL p = 1;
- ALL i = 1;
- while (i <= n) { p = p * 2; i = i + 1; Threads::yield(); }
- res p;
- }
-}
-
-Main {
- void exec() {
- Taskm::interval(1); // rotate roughly every 1 ms
- ALL t1 = get Taskm::add("jobA", 5);
- ALL t2 = get Taskm::add("jobB", 4);
- CIO::println("tasks:", get Taskm::active());
-
- Taskm::run(); // run until both finish
- CIO::println("tasks done:", get Taskm::active());
-
- CIO::println("jobA sum =", get Threads::join(t1));
- CIO::println("jobB 2^4 =", get Threads::join(t2));
- }
-}
-
-// Expected output:
-// tasks: 2
-// tasks done: 0
-// jobA sum = 15
-// jobB 2^4 = 16
diff --git a/examples/11-smart-refs.bio b/examples/11-smart-refs.bio
deleted file mode 100644
index 0f6366d..0000000
--- a/examples/11-smart-refs.bio
+++ /dev/null
@@ -1,80 +0,0 @@
-// 11-smart-refs.bio
-// Teaches: smart references — a reference is a typed value:
-//
-// & = &;
-//
-// permission: r (read) / w (write) / rw (read-write) / m (move pointer)
-// follow : u (program level, Unistream) / f (method level) / a (scope level)
-// 7 permission stacks × 4 follows = 28 reference types; the base type is generic
-// (int / double / float / string / char / arrays / classes / ...).
-//
-// Read through `get p`, write through `p = v`, move the pointer with `p++`
-// (m permission — like C's a++).
-
-program main;
-
-Calc Worker {
- void threadJob(n int) {
- // a layer = the current thread's own scope, isolated per thread.
- thread int local_note = 0;
- &w a int wa = &local_note; // writable a-layer ref
- Ref::write(wa, n * 2);
- &r a int ra = &local_note; // read-only a-layer ref
- CIO::println("thread", get Threads::self(), " a-layer =", get Ref::read(ra));
- res get Ref::read(ra);
- }
-}
-
-Main {
- void exec() {
- // u layer = program level, shared by every thread.
- int counter = 0;
- &w u int wr = &counter;
- Ref::write(wr, 10);
- &r u int rr = &counter;
- CIO::println("u read counter =", get Ref::read(rr));
-
- // A read-only reference refuses writes (shown via cause).
- CIO::println("read-only write →", cause Ref::write(rr, 99));
-
- // Read/write through the reference directly.
- &rw u int rw = &counter;
- CIO::println("rw read =", get rw);
- rw = 5;
- CIO::println("counter after rw = 5 →", counter);
-
- // a-layer isolation: each thread sees its own local_note.
- ALL t1 = get Threads::spawn("threadJob", 21);
- ALL t2 = get Threads::spawn("threadJob", 22);
- CIO::println("t1 =", get Threads::join(t1), " t2 =", get Threads::join(t2));
-
- // Move permission m: the reference is a moving pointer (like C's a++).
- ALL a = new int[3];
- a[0] = 10; a[1] = 20; a[2] = 30;
- &m u int mp = &a[1];
- CIO::println("at [1] =", get mp);
- mp++;
- CIO::println("after mp++ =", get mp);
- mp = 99;
- CIO::println("a[2] =", a[2], " mp target =", get mp);
- CIO::println("move again →", cause Ref::move(mp)); // out of bounds
-
- // const declares a read-only program-level constant.
- const int SPEED = 9;
- CIO::println("const SPEED =", SPEED);
- }
-}
-
-// Expected output (the two thread lines may appear in either order):
-// u read counter = 10
-// read-only write → Ref refused: reference is read-only, cannot write
-// rw read = 10
-// counter after rw = 5 → 5
-// thread 1 a-layer = 42 (thread ids/order may vary)
-// thread 2 a-layer = 44
-// t1 = 42 t2 = 44
-// at [1] = 20
-// after mp++ = 30
-// a[2] = 99 mp target = 99
-// move again → Ref refused: reference pointer moved out of bounds
-// const SPEED = 9
diff --git a/examples/12-computation.bio b/examples/12-computation.bio
deleted file mode 100644
index b2d6acb..0000000
--- a/examples/12-computation.bio
+++ /dev/null
@@ -1,47 +0,0 @@
-// 12-computation.bio
-// Teaches: the Com computation stream and Time timers.
-//
-// Com handles instant math: abs/min/max/pow/sqrt/floor/ceil/round/sign/
-// sin/cos/tan/log/exp. Time owns several timers per thread: the first timer
-// belongs to the thread and cannot be reset; timers from Time::fork may be
-// reset.
-
-program main;
-
-Main {
- void exec() {
- // Com computation stream.
- CIO::println("Com::abs(0-5) =", get Com::abs(0 - 5),
- " Com::sqrt(9) =", get Com::sqrt(9));
- CIO::println("Com::pow(2,10) =", get Com::pow(2, 10));
- CIO::println("Com::min(3,5) =", get Com::min(3, 5),
- " Com::max(3,5) =", get Com::max(3, 5));
- CIO::println("Com::sign(0-3) =", get Com::sign(0 - 3));
- CIO::println("Com::floor(2.7) =", get Com::floor(2.7),
- " Com::ceil(2.7) =", get Com::ceil(2.7),
- " Com::round(2.5) =", get Com::round(2.5));
- CIO::println("Com::log(1) =", get Com::log(1));
-
- // Time::start starts the thread's first timer.
- Time::start();
- Time::sleep(50); // sleep 50 ms
- CIO::println("elapsed after ~50ms =", get Time::elapsed());
-
- // Time::fork creates a resettable timer; the first timer refuses reset.
- ALL t2 = Time::fork();
- CIO::println("reset first timer →", cause Time::reset());
- Time::reset(get t2); // forked timer resets fine
- CIO::println("Time::reset(forked) ok");
- }
-}
-
-// Expected output (elapsed value varies slightly):
-// Com::abs(0-5) = 5 Com::sqrt(9) = 3
-// Com::pow(2,10) = 1024
-// Com::min(3,5) = 3 Com::max(3,5) = 5
-// Com::sign(0-3) = -1
-// Com::floor(2.7) = 2 Com::ceil(2.7) = 3 Com::round(2.5) = 3
-// Com::log(1) = 0
-// elapsed after ~50ms = 0.050...
-// reset first timer → Time refused: first timer (thread default) cannot be reset; use Time::fork()
-// Time::reset(forked) ok
diff --git a/examples/13-need.bio b/examples/13-need.bio
deleted file mode 100644
index dc0ad67..0000000
--- a/examples/13-need.bio
+++ /dev/null
@@ -1,70 +0,0 @@
-// 13-need.bio
-// Teaches: assumptions with `need`.
-//
-// `need` declares that a program relies on a value, function, stream, or
-// class being available. At run time every need must be satisfied, otherwise
-// the WHOLE program refuses to run. This example satisfies all four kinds.
-//
-// need value PI; ← satisfied by the top-level const PI
-// need function greet; ← satisfied by the method greet() in Greeter G
-// need stream Writer; ← satisfied by the signature stream Writer
-// need Class Hero; ← satisfied by the class Hero
-
-program main;
-
-need value PI;
-need function greet;
-need stream Writer;
-need Class Hero;
-
-// A top-level const → Constantstream (read-only), satisfies `need value PI`.
-const int PI = 3;
-
-// A signature stream satisfies `need stream Writer`.
-Stream Writer {
- void write(msg string);
-}
-Writer ConsoleWriter {
- void write(msg string) { CIO::println(msg); }
-}
-
-// A stream whose method is named greet satisfies `need function greet`.
-Stream Greeter {
- void greet(name string);
-}
-Greeter G {
- void greet(name string) { CIO::println("hello,", name); }
-}
-
-// A class satisfies `need Class Hero`.
-Class Hero {
- int hp;
-}
-
-Main {
- void exec() {
- // The const is visible as a bare name (via the scope chain).
- CIO::println("PI =", PI);
-
- // Bare call → the method that satisfied `need function greet`.
- greet("TAK");
-
- // Use the stream that satisfied `need stream Writer`.
- ConsoleWriter::write("writing via a needed stream");
-
- // Instantiate the class that satisfied `need Class Hero`.
- ALL h = new Hero();
- CIO::println("hero created, hp =", h.hp);
- }
-}
-
-// Expected output:
-// PI = 3
-// hello, TAK
-// writing via a needed stream
-// hero created, hp = 0
-//
-// Try breaking one of the needs (e.g. delete the `Class Hero` block) and the
-// whole program refuses to run, printing something like:
-// ⛔ assumption unmet: Class Hero
-// ⛔ refusing to run: unmet assumptions exist
diff --git a/examples/14-binary-lib.bio b/examples/14-binary-lib.bio
deleted file mode 100644
index bd735ad..0000000
--- a/examples/14-binary-lib.bio
+++ /dev/null
@@ -1,36 +0,0 @@
-// 14-binary-lib.bio
-// Teaches: calling native C libraries.
-//
-// `Stream Name & "lib.so" { ... }` dlopens a shared library and exposes its
-// exported functions as stream methods. The body is a normal stream body: it
-// may also declare Bio methods and fields (implemented methods run as Bio;
-// everything else dispatches to the library). Functions follow the C
-// convention double(*)(double,...) — numeric arguments only, at most 6.
-//
-// NOTE: we use "libm.so.6" because on many systems (Fedora, Debian, ...)
-// "libm.so" is just a linker script and cannot be dlopen'd directly.
-
-program main;
-
-Stream m & "libm.so.6" {
- int doubleIt(x int) { res x * 2; } // a normal Bio method in the body
-}
-
-Main {
- void exec() {
- // Functions exported by the library become stream methods.
- ALL s = m::sin(0);
- CIO::println("m::sin(0) =", get s);
- CIO::println("m::cos(0) =", get m::cos(0));
- CIO::println("m::pow(2,10) =", get m::pow(2, 10));
-
- // Bio methods declared in the body run as normal stream methods.
- CIO::println("m::doubleIt(21) =", get m::doubleIt(21));
- }
-}
-
-// Expected output:
-// m::sin(0) = 0
-// m::cos(0) = 1
-// m::pow(2,10) = 1024
-// m::doubleIt(21) = 42
diff --git a/examples/15-annotations.bio b/examples/15-annotations.bio
deleted file mode 100644
index 3b1b8d2..0000000
--- a/examples/15-annotations.bio
+++ /dev/null
@@ -1,93 +0,0 @@
-// 15-annotations.bio
-// Teaches: stream and method annotations.
-//
-// @unfork refuses any later fork of the stream/class.
-// @onlyread refuses user calls to write methods.
-// @read/@write override the automatic write detection on methods.
-
-program main;
-
-// 1. @unfork on a signature stream: the fork below is refused at startup.
-Stream Sealed {
- void ping();
-} @unfork
-Sealed BadFork {} // startup prints the refusal and skips this fork
-
-// 2. @unfork on a class: `new` is the class fork path, so it is refused too.
-Class Frozen {
- int n;
-} @unfork
-
-// 3. @onlyread: users may not call write methods. bump() is detected as a
-// write from its AST (it assigns a field), so the call is refused.
-Stream ReadOnly {
- int count;
- void bump();
- int get();
-}
-ReadOnly RO {
- void bump() { this::count = count + 1; } @write
- int get() { res count; } @read
-} @onlyread
-
-// 4. @onlyread on another stream: same rule applies.
-Stream ReadOnlyAlias {
- int n;
- void touch();
- int get();
-}
-ReadOnlyAlias RA {
- void touch() { this::n = n + 1; } @write
- int get() { res n; } @read
-} @onlyread
-
-// 5. @read/@write override automatic detection on @onlyread streams.
-Stream Guarded {
- int state;
- void hiddenWrite();
- int markedWrite();
- int markedRead();
- int safeRead();
-}
-Guarded G {
- void hiddenWrite() { this::state = 1; res ""; }
- int markedWrite() { res 7; } @write
- int markedRead() { hiddenWrite(); res state; } @read
- int safeRead() { res state; } @read
-} @onlyread
-
-Main {
- void exec() {
- // The @unfork signature refusal was printed during startup; here the
- // class fork path (new) refuses as well.
- ALL f = new Frozen();
- CIO::println("new @unfork class →", cause f);
-
- // @onlyread: AST-based write detection refuses the write method.
- CIO::println("onlyread get =", get RO::get());
- CIO::println("onlyread bump →", cause RO::bump());
-
- // @onlyread applies the same rule here too.
- CIO::println("alias get =", get RA::get());
- CIO::println("alias touch →", cause RA::touch());
-
- // @write makes a body-looking-read method a write method.
- CIO::println("marked write →", cause G::markedWrite());
-
- // @read overrides the AST: hiddenWrite() looks like a write, but the
- // outer markedRead() is explicitly read-only (internal writes are ok).
- CIO::println("marked read =", get G::markedRead());
- CIO::println("safe read =", get G::safeRead());
- }
-}
-
-// Expected output:
-// refused: stream Sealed is @unfork, cannot fork
-// new @unfork class → Obj refused: class Frozen is @unfork, cannot fork
-// onlyread get = 0
-// onlyread bump → refused: stream RO is @onlyread — bump() is a write method
-// alias get = 0
-// alias touch → refused: stream RA is @onlyread — touch() is a write method
-// marked write → refused: stream G is @onlyread — markedWrite() is a write method
-// marked read = 1
-// safe read = 1
diff --git a/examples/16-phonebooth.bio b/examples/16-phonebooth.bio
deleted file mode 100644
index 65e54b4..0000000
--- a/examples/16-phonebooth.bio
+++ /dev/null
@@ -1,97 +0,0 @@
-// 16-phonebooth.bio
-// Teaches: phone-booth methods — @call (one booth per thread) and @ucall
-// (one global booth).
-//
-// A phone-booth method reuses a fixed memory region: the region is cleared
-// at the start of every call (zero allocation, zero fragmentation) and kept
-// for the next call. A booth fits ONE caller at a time — recursion into a
-// booth that is already in use is REFUSED (directly or indirectly).
-//
-// @call — every thread has its own booth for the method (no interference)
-// @ucall — a single booth shared by all threads (single instance)
-
-program main;
-
-// 1. @call: each thread gets its own private booth, so concurrent calls do
-// not interfere — thread A's locals never see thread B's.
-Calc Worker {
- void threadJob(n int) {
- int sum = 0;
- for (int i = 1; i <= n; i = i + 1;) { sum = sum + i; }
- res sum; // 1 + 2 + ... + n
- } @call
-}
-
-// 2. @ucall: one global booth for the whole program.
-Calc Global {
- void globalJob(n int) {
- int prod = 1;
- for (int i = 2; i <= n; i = i + 1;) { prod = prod * i; }
- res prod; // n!
- } @ucall
-}
-
-// 3. Recursion is refused: the booth is occupied while its method runs.
-Calc Rec {
- void down(n int) {
- if (n <= 0) { res 0; }
- else { res get down(n - 1) + 1; } // direct recursion → refused
- } @call
-
- void hop(n int) { res get down2(n); } // bridge for indirect recursion
- void down2(n int) {
- if (n <= 0) { res 0; }
- else { res get hop(n - 1) + 1; } // down2 → hop → down2: indirect
- } @call
-
- void uDown(n int) {
- if (n <= 0) { res 0; }
- else { res get uDown(n - 1) + 1; } // @ucall recursion → refused too
- } @ucall
-}
-
-// 4. Ordinary methods still recurse freely (no booth involved).
-Calc Plain {
- void fact(n int) {
- if (n <= 1) { res 1; }
- else { res n * get fact(n - 1); }
- }
-}
-
-Main {
- void exec() {
- // @call booth isolation: two threads run threadJob at the same time,
- // each inside its own booth — results are independent.
- ALL t1 = get Threads::spawn("threadJob", 100);
- ALL t2 = get Threads::spawn("threadJob", 200);
- ALL r1 = Threads::join(t1);
- ALL r2 = Threads::join(t2);
- CIO::println("t1 sum =", get r1, " t2 sum =", get r2);
-
- // @ucall: the global booth works from anywhere (and is a singleton —
- // repeated calls reuse the same region).
- CIO::println("global 5! =", get Global::globalJob(5));
- CIO::println("global 6! =", get Global::globalJob(6));
-
- // Direct recursion into an occupied @call booth is refused.
- CIO::println("direct recursion →", cause Rec::down(3));
-
- // Indirect recursion (down2 → hop → down2) is caught too.
- CIO::println("indirect recursion →", cause Rec::down2(3));
-
- // @ucall recursion is refused as well.
- CIO::println("ucall recursion →", cause Rec::uDown(3));
-
- // Ordinary methods keep their full recursion — no booth involved.
- CIO::println("plain fact(10) =", get Plain::fact(10));
- }
-}
-
-// Expected output:
-// t1 sum = 5050 t2 sum = 20100
-// global 5! = 120
-// global 6! = 720
-// direct recursion → refused: phone-booth method down does not support recursion
-// indirect recursion → refused: phone-booth method down2 does not support recursion
-// ucall recursion → refused: phone-booth method uDown does not support recursion
-// plain fact(10) = 3628800
diff --git a/examples/17-llvm.bio b/examples/17-llvm.bio
deleted file mode 100644
index fff6682..0000000
--- a/examples/17-llvm.bio
+++ /dev/null
@@ -1,22 +0,0 @@
-// 17-llvm.bio
-// Stage-1 LLVM backend example: loops, a user method, recursion-free calls,
-// int locals, and CIO output all compile to native code with `bio llvm`.
-
-program main;
-
-Main {
- int square(x int) {
- res x * x;
- }
-
- void exec() {
- int sum = 0;
- for (int i = 1; i <= 10; i = i + 1;) {
- sum = sum + square(i);
- }
- CIO::println("sum of squares 1..10 =", sum);
- }
-}
-
-// Expected output:
-// sum of squares 1..10 = 385
diff --git a/examples/README.md b/examples/README.md
deleted file mode 100644
index f272832..0000000
--- a/examples/README.md
+++ /dev/null
@@ -1,60 +0,0 @@
-# BiuBiuBiu examples
-
-Runnable, heavily-commented programs that walk through BiuBiuBiu feature by feature.
-Each file is self-contained — run any of them with:
-
-```bash
-bio examples/01-hello.bio # interpret
-bio shell build examples/01-hello.bio -o hello # compile to a standalone executable
-```
-
-## Reading order
-
-| # | File | Teaches |
-|---|------|---------|
-| 01 | [01-hello.bio](01-hello.bio) | The basic skeleton: `program main`, `Main { void exec() }`, `CIO::println` |
-| 02 | [02-requests.bio](02-requests.bio) | The request model: `res` / `ref` / `get` / `cause`, `ALL`, prefix unwrapping |
-| 03 | [03-control-flow.bio](03-control-flow.bio) | `if` / `else if` / `else`, `while`, `for`, `break` / `continue` |
-| 04 | [04-streams-fork.bio](04-streams-fork.bio) | Signature streams, forked implementations, fields, bare calls, streams as arguments |
-| 05 | [05-io-substreams.bio](05-io-substreams.bio) | CIO / FIO / SIO / IO: text streams vs byte streams |
-| 06 | [06-classes-objects.bio](06-classes-objects.bio) | `Class`, `new`, `__init__`, `this`, object methods, `Obj::set/get` |
-| 07 | [07-arrays.bio](07-arrays.bio) | Array/Vector (Bio classes), Solid streams, the `Arrays` collection, array literals |
-| 08 | [08-multi-return.bio](08-multi-return.bio) | Multiple return types, `res a, b, c` → array |
-| 09 | [09-threads.bio](09-threads.bio) | Cooperative threads: `spawn` / `yield` / `join` / `active` / `self` |
-| 10 | [10-taskm.bio](10-taskm.bio) | Task manager: round-robin scheduling of tasks |
-| 11 | [11-smart-refs.bio](11-smart-refs.bio) | Typed smart references `&perm follow base`, `get p` / `p = v` / `p++` moving pointer, const/thread variables |
-| 12 | [12-computation.bio](12-computation.bio) | `Com` computation stream + `Time` timers |
-| 13 | [13-need.bio](13-need.bio) | Assumptions: `need value/function/stream/Class` |
-| 14 | [14-binary-lib.bio](14-binary-lib.bio) | Calling native C libraries (`Stream m & "libm.so.6"`) |
-| 15 | [15-annotations.bio](15-annotations.bio) | Annotations: `@unfork`, `@onlyread`, `@read`/`@write` |
-| [16-phonebooth.bio](16-phonebooth.bio) | Phone-booth methods: `@call` (per-thread booth), `@ucall` (global booth), recursion refused |
-| proj | [project/](project/) | A complete project: `package.toml` + `src/` + `utils/` |
-
-## Suggested learning path
-
-1. Start with **01** and **02** — they cover the two ideas everything else builds on:
- the program skeleton and the request/response model.
-2. **03–05** give you control flow, streams, and IO — enough to write real programs.
-3. **06–08** introduce classes, objects, and arrays.
-4. **09–12** are the more advanced builtin systems: threads, scheduling,
- references, math, and time.
-5. **13** shows how a program declares what it depends on, and **14** shows how
- to call into native C code.
-6. Finish with the **[project/](project/)** example to see how a multi-file
- project is organized and built:
-
-```bash
-cd examples/project
-bio run # interpret the bundled project
-bio build # compile it into a standalone ./app executable
-```
-
-## Conventions
-
-- Every file starts with a comment block explaining what it teaches.
-- Inline comments explain each construct.
-- The trailing comment block shows the **expected output**.
-
-> The exact "refused" messages shown by the interpreter may vary slightly
-> between versions; the structural output (numbers, strings, control flow) is
-> what matters.
diff --git a/examples/project/package.toml b/examples/project/package.toml
deleted file mode 100644
index ba704a0..0000000
--- a/examples/project/package.toml
+++ /dev/null
@@ -1,8 +0,0 @@
-# BioLang demo project manifest.
-# Standard fields: name / version / repo (optional) + [dependencies].
-name = "biolang-demo-project"
-version = "0.1.0"
-
-[dependencies]
-# libfoo = { version = "1.0.0" }
-# libbar = { version = "0.2.0", repo = "https://..." }
diff --git a/examples/project/src/main.bio b/examples/project/src/main.bio
deleted file mode 100644
index 86d5d91..0000000
--- a/examples/project/src/main.bio
+++ /dev/null
@@ -1,20 +0,0 @@
-// project/src/main.bio — the entry point of the demo project.
-//
-// A project bundles its source with `need`-based, on-demand bundling:
-// starting from this entry, every `need` is satisfied by providers found in
-// src/ + utils/ + .biolang/deps/. Unmet needs abort the build/run.
-
-program main;
-
-need value GREETING; // provided by utils/config.bio
-need function greet; // provided by utils/greeter.bio
-
-Main {
- void exec() {
- // GREETING is a const declared in utils/config.bio.
- CIO::println("main entry —", GREETING);
-
- // greet() is provided by utils/greeter.bio.
- greet("project");
- }
-}
diff --git a/examples/project/utils/config.bio b/examples/project/utils/config.bio
deleted file mode 100644
index 913e533..0000000
--- a/examples/project/utils/config.bio
+++ /dev/null
@@ -1,8 +0,0 @@
-// project/utils/config.bio — provides the value GREETING.
-//
-// A top-level `const` creates a program-level constant, which satisfies a
-// `need value GREETING;` declared in another file.
-
-program utils;
-
-const string GREETING = "Hello from utils/";
diff --git a/examples/project/utils/greeter.bio b/examples/project/utils/greeter.bio
deleted file mode 100644
index b105543..0000000
--- a/examples/project/utils/greeter.bio
+++ /dev/null
@@ -1,15 +0,0 @@
-// project/utils/greeter.bio — provides the method greet().
-//
-// Any method named `greet` satisfies the `need function greet;` in main.bio.
-// It can then be called as a bare call.
-
-program utils;
-
-Stream Greeter {
- void greet(name string);
-}
-Greeter G {
- void greet(name string) {
- CIO::println("hello,", name);
- }
-}
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
deleted file mode 100644
index 0f325a9..0000000
--- a/rust/Cargo.lock
+++ /dev/null
@@ -1,43 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 4
-
-[[package]]
-name = "bbb-cli"
-version = "1.0.0"
-dependencies = [
- "bbb-core",
- "bbb-llvm",
- "bbb-syntax",
- "bbb-vm",
-]
-
-[[package]]
-name = "bbb-core"
-version = "1.0.0"
-
-[[package]]
-name = "bbb-llvm"
-version = "1.0.0"
-dependencies = [
- "bbb-syntax",
-]
-
-[[package]]
-name = "bbb-syntax"
-version = "1.0.0"
-
-[[package]]
-name = "bbb-vm"
-version = "1.0.0"
-dependencies = [
- "bbb-core",
- "bbb-syntax",
-]
-
-[[package]]
-name = "bbb-wasm"
-version = "1.0.0"
-dependencies = [
- "bbb-syntax",
-]
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
deleted file mode 100644
index 7155f5d..0000000
--- a/rust/Cargo.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[workspace]
-resolver = "2"
-members = ["crates/bbb-syntax", "crates/bbb-core", "crates/bbb-vm", "crates/bbb-llvm", "crates/bbb-wasm", "crates/bbb-cli"]
-
-[workspace.package]
-version = "1.0.0"
-edition = "2021"
-license = "MIT"
-
-[profile.release]
-opt-level = 3
-lto = true
-codegen-units = 1
diff --git a/rust/crates/bbb-cli/Cargo.toml b/rust/crates/bbb-cli/Cargo.toml
deleted file mode 100644
index 1f64d18..0000000
--- a/rust/crates/bbb-cli/Cargo.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[package]
-name = "bbb-cli"
-version.workspace = true
-edition.workspace = true
-license.workspace = true
-description = "BioLang Rust 实现 CLI(Rust+LLVM 重写阶段 M1)"
-
-[[bin]]
-name = "bbb"
-path = "src/main.rs"
-
-[dependencies]
-bbb-syntax = { path = "../bbb-syntax" }
-bbb-core = { path = "../bbb-core" }
-bbb-vm = { path = "../bbb-vm" }
-bbb-llvm = { path = "../bbb-llvm" }
diff --git a/rust/crates/bbb-cli/src/main.rs b/rust/crates/bbb-cli/src/main.rs
deleted file mode 100644
index 1114ad6..0000000
--- a/rust/crates/bbb-cli/src/main.rs
+++ /dev/null
@@ -1,861 +0,0 @@
-//! bbb — BiuBiuBiu 语言 CLI(Rust + LLVM 实现)。
-//!
-//! 标准 CLI(README 定义,与旧 C 实现一致):
-//! bbb 解释运行脚本
-//! bbb shell run 解释运行脚本(显式)
-//! bbb shell build [-o out] 编译脚本 → 原生可执行
-//! bbb init 创建项目骨架(package.toml + src/ + utils/)
-//! bbb build [dir] [-s|-m] [-o out] 构建项目
-//! bbb run [dir] 运行项目(解释)
-//! bbb install [dir] 安装依赖
-//! bbb destroy [dir] 清理构建产物
-//! bbb pack [--entry NAME]
-//! bbb unpack [dir]
-//! bbb --tokens dump tokens(调试)
-//! bbb -e 解释器内存限制(0 = unlimited)
-//! bbb -h | --help 帮助
-
-use std::io::Read;
-use std::path::{Path, PathBuf};
-use std::process::ExitCode;
-
-use bbb_core::arena::{BumpArena, StrArena};
-use bbb_core::value::Value;
-use bbb_syntax::lexer::{self, TokenKind};
-use bbb_syntax::parser::parse_source;
-use bbb_vm::interp::Interp;
-
-const USAGE: &str = "\
-🧬 BiuBiuBiu (bbb) — interpret or compile .bio/.bl programs
-
-Usage:
- bbb run (interpret) a script
- bbb shell run run a script (interpret)
- bbb shell build [-o out] compile a script → standalone executable
- bbb -e set interpreter memory limit (0 = unlimited, default 256M)
- bbb --tokens dump tokens (debug)
- bbb init create a project skeleton (src/ utils/ package.toml)
- bbb build [dir] [-o out] build a project (bundle needs, compile)
- bbb build [dir] -s build → standalone executable (default)
- bbb build [dir] -m [out.img|out.zip] build → .img/.zip package
- bbb run [dir] run a project (bundle needs, interpret)
- bbb install [dir] install deps from package.toml
- bbb destroy [dir] remove build artifacts
- bbb pack [--entry NAME]
- package compiled products (raw .img / .zip)
- bbb unpack [dir] unpack a .img / .zip package
- bbb run built-in demos
- bbb -h | --help show this help
-
- env: BIOLANG_CONFIG → global config file (TOML, may contain repo=)
-";
-
-fn read_source(path: &str) -> Result {
- let mut buf = Vec::new();
- std::fs::File::open(path)
- .map_err(|e| format!("cannot open file: {path} ({e})"))?
- .read_to_end(&mut buf)
- .map_err(|e| format!("read failed: {e}"))?;
- String::from_utf8(buf).map_err(|_| "source is not UTF-8".to_string())
-}
-
-fn parse_or_err(src: &str) -> Result {
- let (prog, errs) = parse_source(src);
- if !errs.is_empty() {
- return Err(errs.iter().map(|e| e.to_string()).collect::>().join("\n"));
- }
- Ok(prog)
-}
-
-/// 运行单个脚本文件(解释)。
-fn run_script_file(path: &str) -> Result<(), String> {
- let src = read_source(path)?;
- let prog = parse_or_err(&src)?;
- let mut interp = Interp::new();
- let unmet = interp.reg.register(&prog);
- let unmet = filter_value_needs(unmet, &prog);
- if !unmet.is_empty() {
- return Err(unmet
- .iter()
- .map(|(k, n)| format!("need {k} {n} has no provider"))
- .collect::>()
- .join("\n"));
- }
- let out = interp.run(&prog);
- print!("{}", out.stdout);
- Ok(())
-}
-
-/// 运行项目目录(解释):合并 src/ + utils/。
-fn run_project_dir(root: &Path) -> Result<(), String> {
- let prog = bbb_vm::load_project_sources(&root.to_path_buf())?;
- let mut interp = Interp::new();
- let unmet = interp.reg.register(&prog);
- let unmet = filter_value_needs(unmet, &prog);
- if !unmet.is_empty() {
- return Err(unmet
- .iter()
- .map(|(k, n)| format!("need {k} {n} has no provider"))
- .collect::>()
- .join("\n"));
- }
- let out = interp.run(&prog);
- print!("{}", out.stdout);
- Ok(())
-}
-
-fn filter_value_needs(
- unmet: Vec<(String, String)>,
- prog: &bbb_syntax::ast::Program,
-) -> Vec<(String, String)> {
- unmet
- .into_iter()
- .filter(|(k, n)| {
- !(k == "value"
- && prog
- .decls
- .iter()
- .any(|d| matches!(d, bbb_syntax::Decl::Const { name, .. } if name == n)))
- })
- .collect()
-}
-
-/// LLVM 编译:AST → IR → clang → 可执行文件(不运行)。
-fn compile_to_executable(prog: &bbb_syntax::ast::Program, out: &str) -> Result<(), String> {
- let ir = bbb_llvm::compile(prog).map_err(|e| format!("IR generation failed: {e}"))?;
- let dir = std::env::temp_dir().join(format!("bbb-build-{}", std::process::id()));
- std::fs::create_dir_all(&dir).map_err(|e| e.to_string())?;
- let ir_path = dir.join("out.ll");
- std::fs::write(&ir_path, &ir).map_err(|e| e.to_string())?;
- // 确保输出目录存在
- if let Some(parent) = Path::new(out).parent() {
- if !parent.as_os_str().is_empty() {
- std::fs::create_dir_all(parent).map_err(|e| format!("cannot create {}: {e}", parent.display()))?;
- }
- }
- let status = std::process::Command::new("clang")
- .arg(&ir_path)
- .arg("-o")
- .arg(out)
- .status()
- .map_err(|e| format!("clang invocation failed: {e}"))?;
- if !status.success() {
- return Err(format!("clang failed (IR kept at {})", ir_path.display()));
- }
- Ok(())
-}
-
-// ───────────────────────── 命令实现 ─────────────────────────
-
-fn cmd_tokens(path: &str) -> Result<(), String> {
- let src = read_source(path)?;
- let mut toks = Vec::new();
- lexer::tokenize(&src, &mut toks).map_err(|e| format!("lex error: {e}"))?;
- for t in &toks {
- let kind = match t.kind {
- TokenKind::Ident => "ident",
- TokenKind::Keyword => "keyword",
- TokenKind::Int => "int",
- TokenKind::Float => "float",
- TokenKind::Str => "string",
- TokenKind::Char => "char",
- TokenKind::Op => "op",
- TokenKind::Eof => "eof",
- };
- println!("{:>5}:{:<3} {:<8} {:?}", t.span.line, t.span.col, kind, t.text);
- }
- Ok(())
-}
-
-fn cmd_shell_run(file: &str) -> Result<(), String> {
- run_script_file(file)
-}
-
-fn cmd_shell_build(file: &str, out: Option<&str>) -> Result<(), String> {
- let src = read_source(file)?;
- let prog = parse_or_err(&src)?;
- // 默认输出:bin/
- let out_path = match out {
- Some(o) => o.to_string(),
- None => {
- let base = Path::new(file)
- .file_name()
- .map(|s| s.to_string_lossy().to_string())
- .unwrap_or_else(|| "a.out".to_string());
- let base = base
- .strip_suffix(".bio")
- .or_else(|| base.strip_suffix(".bl"))
- .unwrap_or(&base)
- .to_string();
- format!("bin/{base}")
- }
- };
- println!("compiling {file} → {out_path}");
- compile_to_executable(&prog, &out_path)?;
- println!("✔ compiled: {out_path}");
- Ok(())
-}
-
-fn cmd_run(target: &str) -> Result<(), String> {
- let path = PathBuf::from(target);
- if path.is_dir() {
- run_project_dir(&path)
- } else {
- run_script_file(target)
- }
-}
-
-fn cmd_init(name: &str) -> Result<(), String> {
- let root = PathBuf::from(name);
- if root.exists() {
- return Err(format!("{name}: already exists"));
- }
- std::fs::create_dir_all(root.join("src")).map_err(|e| e.to_string())?;
- std::fs::create_dir_all(root.join("utils")).map_err(|e| e.to_string())?;
- let toml = format!(
- "# {name} — BiuBiuBiu project manifest\n\
- # standard fields: name / version / repo (optional) + [dependencies]\n\
- name = \"{name}\"\n\
- version = \"0.1.0\"\n\
- \n\
- [dependencies]\n\
- # libfoo = {{ version = \"1.0.0\" }}\n\
- # libbar = {{ version = \"0.2.0\", repo = \"https://...\" }}\n"
- );
- std::fs::write(root.join("package.toml"), toml).map_err(|e| e.to_string())?;
- let main = format!(
- "program main;\n\
- \n\
- Main {{\n\
- void exec() {{\n\
- CIO::println(\"Hello from {name}!\");\n\
- }}\n\
- }}\n"
- );
- std::fs::write(root.join("src").join("main.bio"), main).map_err(|e| e.to_string())?;
- println!("✔ project created: {name}");
- println!(" package.toml — manifest (name/version/repo + deps)");
- println!(" src/main.bio — entry");
- println!(" utils/ — libraries (need providers)");
- Ok(())
-}
-
-/// 项目构建:need bundling(load_project_sources 合并)+ LLVM 编译 → 可执行。
-/// -s standalone(默认);-m 打包 .img/.zip(v1 简化:先做 standalone 产物,包格式后续)。
-fn cmd_build(dir: &str, _mode: &str, out: Option<&str>) -> Result<(), String> {
- let root = PathBuf::from(dir);
- let prog = bbb_vm::load_project_sources(&root)?;
- // 项目名:package.toml 的 name 字段(简单解析),默认目录名
- let pname = parse_package_name(&root).unwrap_or_else(|| {
- root.file_name()
- .map(|s| s.to_string_lossy().to_string())
- .unwrap_or_else(|| "app".to_string())
- });
- let out_path = match out {
- Some(o) => o.to_string(),
- None => {
- let dir_trim = dir.trim_end_matches('/');
- if dir_trim.is_empty() || dir_trim == "." {
- format!("bin/{pname}")
- } else {
- format!("{dir_trim}/bin/{pname}")
- }
- }
- };
- println!("building {dir} → {out_path}");
- compile_to_executable(&prog, &out_path)?;
- println!("✔ build ok: {out_path}");
- Ok(())
-}
-
-/// 简单解析 package.toml 的 name = "..."。
-fn parse_package_name(root: &Path) -> Option {
- let toml = std::fs::read_to_string(root.join("package.toml")).ok()?;
- for line in toml.lines() {
- let line = line.trim();
- if let Some(rest) = line.strip_prefix("name") {
- let rest = rest.trim_start();
- if let Some(rest) = rest.strip_prefix('=') {
- let rest = rest.trim().trim_matches('"');
- if !rest.is_empty() {
- return Some(rest.to_string());
- }
- }
- }
- }
- None
-}
-
-fn cmd_install(dir: &str) -> Result<(), String> {
- let root = PathBuf::from(dir);
- let toml_path = root.join("package.toml");
- let toml = match std::fs::read_to_string(&toml_path) {
- Ok(t) => t,
- Err(_) => return Err(format!("no package.toml in {dir}")),
- };
- // 解析 [dependencies] 下的 name = { version, repo } 或 name = "version"
- let mut found = false;
- let mut installed = 0;
- let mut in_deps = false;
- for line in toml.lines() {
- let line = line.trim();
- if line.is_empty() || line.starts_with('#') {
- continue;
- }
- if line.starts_with('[') {
- in_deps = line.starts_with("[dependencies]");
- continue;
- }
- if !in_deps {
- continue;
- }
- let Some(eq) = line.find('=') else { continue };
- let dep = line[..eq].trim().to_string();
- let spec = line[eq + 1..].trim();
- if dep.is_empty() {
- continue;
- }
- found = true;
- // repo:spec 里的 repo = "...";否则从 BIOLANG_CONFIG 全局配置读
- let repo = extract_repo(spec).or_else(global_repo);
- match repo {
- Some(r) => {
- let dest = root.join(".biolang").join("deps").join(&dep);
- let rc = fetch_dep(&r, &dest);
- if rc == 0 {
- println!("✔ installed {dep}");
- installed += 1;
- } else {
- eprintln!("⛔ dep {dep}: fetch failed from {r}");
- }
- }
- None => {
- eprintln!("⛔ dep {dep}: no repo (set repo= or BIOLANG_CONFIG global repo)");
- }
- }
- }
- if !found {
- println!("ℹ️ no dependencies in {}", toml_path.display());
- }
- if installed > 0 {
- println!("✔ {installed} dependency(ies) installed → {}/.biolang/deps", root.display());
- }
- Ok(())
-}
-
-fn extract_repo(spec: &str) -> Option {
- // 形如 { version = "1.0.0", repo = "https://..." } 或 "1.0.0"
- if let Some(inner) = spec.strip_prefix('{') {
- let inner = inner.trim_end_matches('}');
- for part in inner.split(',') {
- let part = part.trim();
- if let Some(rest) = part.strip_prefix("repo") {
- let rest = rest.trim_start();
- if let Some(rest) = rest.strip_prefix('=') {
- return Some(rest.trim().trim_matches('"').to_string());
- }
- }
- }
- None
- } else {
- None
- }
-}
-
-fn global_repo() -> Option {
- let path = std::env::var("BIOLANG_CONFIG")
- .map(PathBuf::from)
- .unwrap_or_else(|_| {
- let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
- PathBuf::from(home).join(".biolang").join("config.toml")
- });
- let text = std::fs::read_to_string(path).ok()?;
- for line in text.lines() {
- let line = line.trim();
- if let Some(rest) = line.strip_prefix("repo") {
- let rest = rest.trim_start();
- if let Some(rest) = rest.strip_prefix('=') {
- return Some(rest.trim().trim_matches('"').to_string());
- }
- }
- }
- None
-}
-
-/// 拉取依赖:git 仓库 → git clone;http → curl 下载 package.toml;本地路径 → 复制。
-fn fetch_dep(repo: &str, dest: &Path) -> i32 {
- if let Some(parent) = dest.parent() {
- let _ = std::fs::create_dir_all(parent);
- }
- if repo.starts_with("http") && repo.contains(".git") {
- let _ = std::fs::remove_dir_all(dest);
- std::process::Command::new("git")
- .args(["clone", "--depth", "1", repo])
- .arg(dest)
- .status()
- .map(|s| if s.success() { 0 } else { 1 })
- .unwrap_or(1)
- } else if repo.starts_with("http") {
- let _ = std::fs::create_dir_all(dest);
- let outfile = dest.join("package.toml");
- std::process::Command::new("curl")
- .args(["-fsSL", repo, "-o"])
- .arg(&outfile)
- .status()
- .map(|s| if s.success() { 0 } else { 1 })
- .unwrap_or(1)
- } else {
- copy_tree(Path::new(repo), dest)
- }
-}
-
-fn copy_tree(src: &Path, dest: &Path) -> i32 {
- if !src.is_dir() {
- return 1;
- }
- let _ = std::fs::remove_dir_all(dest);
- fn rec(src: &Path, dest: &Path) -> std::io::Result<()> {
- std::fs::create_dir_all(dest)?;
- for entry in std::fs::read_dir(src)? {
- let entry = entry?;
- let from = entry.path();
- let to = dest.join(entry.file_name());
- if from.is_dir() {
- rec(&from, &to)?;
- } else {
- std::fs::copy(&from, &to)?;
- }
- }
- Ok(())
- }
- match rec(src, dest) {
- Ok(()) => 0,
- Err(_) => 1,
- }
-}
-
-fn cmd_destroy(dir: &str) -> Result<(), String> {
- let root = PathBuf::from(dir);
- let biolang = root.join(".biolang");
- if biolang.exists() {
- std::fs::remove_dir_all(&biolang).map_err(|e| e.to_string())?;
- }
- let app = root.join("app");
- if app.exists() {
- let _ = std::fs::remove_dir_all(&app);
- }
- let bin_cache = root.join("bin").join(".cache");
- if bin_cache.exists() {
- let _ = std::fs::remove_dir_all(&bin_cache);
- }
- println!("✔ destroyed build artifacts: {}/.biolang, {}/app", dir, dir);
- Ok(())
-}
-
-// ── .img / .zip 打包(v1:基于系统 zip/unzip 的 .zip + 原始 .img) ──
-
-const IMG_MAGIC: &[u8; 7] = b"BIOIMG1";
-const IMG_VERSION: u32 = 2;
-
-fn cmd_pack(out: &str, entry: Option<&str>, files: &[String]) -> Result<(), String> {
- if files.is_empty() {
- return Err("pack: no files".to_string());
- }
- if out.ends_with(".img") {
- img_create(out, entry, files)
- } else {
- zip_create(out, files)
- }
-}
-
-fn zip_create(out: &str, files: &[String]) -> Result<(), String> {
- // 用系统 zip(STORE 无压缩);不存在则报错
- let status = std::process::Command::new("zip")
- .arg("-0")
- .arg("-j")
- .arg(out)
- .args(files)
- .status()
- .map_err(|e| format!("zip invocation failed: {e}"))?;
- if !status.success() {
- return Err(format!("pack failed: {out}"));
- }
- println!("packed {} file(s) → {out}", files.len());
- Ok(())
-}
-
-fn img_create(out: &str, entry: Option<&str>, files: &[String]) -> Result<(), String> {
- use std::io::Write;
- let mut data: Vec = Vec::new();
- // header 占位
- let entry_name = entry.unwrap_or(&files[0]);
- data.extend_from_slice(IMG_MAGIC);
- data.extend_from_slice(&IMG_VERSION.to_le_bytes());
- data.extend_from_slice(&0u32.to_le_bytes()); // flags
- data.extend_from_slice(&(entry_name.len() as u32).to_le_bytes());
- data.extend_from_slice(entry_name.as_bytes());
- data.extend_from_slice(&(files.len() as u32).to_le_bytes());
- // 目录记录区:先写 records(offset 暂填 0),再写 payload
- let header_size = 7 + 4 + 4 + 4 + entry_name.len() + 4; // magic(7) + ver + flags + entry_len + name + count
- // 每条记录 = name_len(4) + mode(4) + name(nl) + offset(8) + size(8)
- let mut offset = header_size as u64;
- for f in files {
- offset += (4 + 4 + f.len() + 8 + 8) as u64;
- }
- let mut records: Vec<(String, u32, u64, u64)> = Vec::new();
- for f in files {
- let bytes = std::fs::read(f).map_err(|e| format!("cannot read {f}: {e}"))?;
- let mode = file_mode(f);
- records.push((f.clone(), mode, offset, bytes.len() as u64));
- offset += bytes.len() as u64;
- }
- for (name, mode, off, size) in &records {
- data.extend_from_slice(&(name.len() as u32).to_le_bytes());
- data.extend_from_slice(&mode.to_le_bytes());
- data.extend_from_slice(name.as_bytes());
- data.extend_from_slice(&off.to_le_bytes());
- data.extend_from_slice(&size.to_le_bytes());
- }
- for f in files {
- let bytes = std::fs::read(f).map_err(|e| format!("cannot read {f}: {e}"))?;
- data.extend_from_slice(&bytes);
- }
- let mut f = std::fs::File::create(out).map_err(|e| e.to_string())?;
- f.write_all(&data).map_err(|e| e.to_string())?;
- println!("packed {} file(s) → {out}", files.len());
- Ok(())
-}
-
-fn file_mode(path: &str) -> u32 {
- use std::os::unix::fs::PermissionsExt;
- std::fs::metadata(path)
- .map(|m| m.permissions().mode() & 0o777)
- .unwrap_or(0o644)
-}
-
-fn cmd_unpack(pkg: &str, dir: &str) -> Result<(), String> {
- if pkg.ends_with(".img") {
- img_unpack(pkg, dir)
- } else {
- std::fs::create_dir_all(dir).map_err(|e| e.to_string())?;
- let status = std::process::Command::new("unzip")
- .arg("-o")
- .arg(pkg)
- .arg("-d")
- .arg(dir)
- .status()
- .map_err(|e| format!("unzip invocation failed: {e}"))?;
- if !status.success() {
- return Err(format!("unpack failed: {pkg}"));
- }
- println!("unpacked {pkg} → {dir}");
- Ok(())
- }
-}
-
-fn img_unpack(pkg: &str, dir: &str) -> Result<(), String> {
- let data = std::fs::read(pkg).map_err(|e| format!("cannot read {pkg}: {e}"))?;
- if data.len() < 24 || &data[..7] != IMG_MAGIC {
- return Err(format!("{pkg}: not a BIOIMG1 image"));
- }
- let mut pos = 7usize;
- let _version = rd_u32(&data, &mut pos);
- let _flags = rd_u32(&data, &mut pos);
- let entry_len = rd_u32(&data, &mut pos) as usize;
- pos += entry_len; // entry name
- let count = rd_u32(&data, &mut pos) as usize;
- std::fs::create_dir_all(dir).map_err(|e| e.to_string())?;
- for _ in 0..count {
- let name_len = rd_u32(&data, &mut pos) as usize;
- let mode = rd_u32(&data, &mut pos);
- let name = String::from_utf8_lossy(&data[pos..pos + name_len]).to_string();
- pos += name_len;
- let off = rd_u64(&data, &mut pos);
- let size = rd_u64(&data, &mut pos);
- let bytes = &data[off as usize..(off + size) as usize];
- let out_path = Path::new(dir).join(Path::new(&name).file_name().unwrap_or_default());
- std::fs::write(&out_path, bytes).map_err(|e| e.to_string())?;
- #[cfg(unix)]
- {
- use std::os::unix::fs::PermissionsExt;
- let _ = std::fs::set_permissions(&out_path, std::fs::Permissions::from_mode(mode));
- }
- }
- println!("unpacked {pkg} → {dir}");
- Ok(())
-}
-
-fn rd_u32(data: &[u8], pos: &mut usize) -> u32 {
- let v = u32::from_le_bytes(data[*pos..*pos + 4].try_into().unwrap());
- *pos += 4;
- v
-}
-
-fn rd_u64(data: &[u8], pos: &mut usize) -> u64 {
- let v = u64::from_le_bytes(data[*pos..*pos + 8].try_into().unwrap());
- *pos += 8;
- v
-}
-
-// ───────────────────────── 内部调试命令(保留) ─────────────────────────
-
-fn cmd_parse(path: &str) -> Result<(), String> {
- let src = read_source(path)?;
- let (prog, errs) = parse_source(&src);
- if !errs.is_empty() {
- for e in &errs {
- eprintln!("parse error: {e}");
- }
- return Err(format!("{} parse errors", errs.len()));
- }
- println!("kind: {}", if prog.kind.is_empty() { "" } else { &prog.kind });
- println!("decls: {} | main: {} | methods: {}",
- prog.decls.len(),
- if prog.main.is_some() { "yes" } else { "no" },
- prog.main.as_ref().map(|m| m.methods.len()).unwrap_or(0));
- for d in &prog.decls {
- let name = match d {
- bbb_syntax::Decl::Const { name, .. } => format!("const {name}"),
- bbb_syntax::Decl::Need { kind, name } => format!("need {kind} {name}"),
- bbb_syntax::Decl::StreamSig { name, members, .. } =>
- format!("stream {name} ({} members)", members.len()),
- bbb_syntax::Decl::StreamBin { name, file, .. } =>
- format!("bin-stream {name} <- {file}"),
- bbb_syntax::Decl::Class { name, members, .. } =>
- format!("class {name} ({} members)", members.len()),
- bbb_syntax::Decl::Fork { sig, name, members, .. } =>
- format!("fork {sig} {name} ({} members)", members.len()),
- bbb_syntax::Decl::Interface { name, members, .. } =>
- format!("interface {name} ({} members)", members.len()),
- };
- println!(" {name}");
- }
- Ok(())
-}
-
-fn cmd_arena(n: u32) {
- let mut arena = BumpArena::::new();
- let mut last = 0;
- for i in 1..=n {
- last = arena.alloc(i as u64);
- }
- assert_eq!(*arena.get(last), n as u64);
- println!("BumpArena: {} slots, {} pages, handle {last} valid", n, arena.pages());
-
- let mut strs = StrArena::new();
- let mut handle = None;
- for i in 0..n {
- let s = format!("value-{i}-{}", "x".repeat((i % 97) as usize));
- handle = Some(strs.push(&s));
- }
- let h = handle.unwrap();
- assert!(strs.get(h).starts_with("value-"));
- println!("StrArena: {} writes, last len {} (cross-page intact)", n, strs.get(h).len());
-
- let v = Value::int(42).with_refused();
- println!("Value: size={}B, refused int displays as {v}", std::mem::size_of::());
-}
-
-// ───────────────────────── 入口 ─────────────────────────
-
-fn main() -> ExitCode {
- let args: Vec = std::env::args().collect();
- let argc = args.len();
-
- // 无参数:内置演示(v1:打印 usage 提示)
- if argc == 1 {
- print!("{USAGE}");
- return ExitCode::SUCCESS;
- }
-
- let a1 = args[1].as_str();
- // 全局选项
- if a1 == "-h" || a1 == "--help" {
- print!("{USAGE}");
- return ExitCode::SUCCESS;
- }
- if a1 == "--version" || a1 == "-V" {
- println!("bbb {}", env!("CARGO_PKG_VERSION"));
- return ExitCode::SUCCESS;
- }
- if a1 == "-e" {
- // 内存限制:v1 接受参数(0 = unlimited),解释器当前无硬限制
- if argc < 3 {
- eprintln!("usage: bbb -e ");
- return ExitCode::FAILURE;
- }
- let _limit = parse_size(&args[2]);
- // 剩余参数按普通命令处理(-e 仅设置限制)
- let rest: Vec = args[2..].to_vec();
- if rest.is_empty() {
- print!("{USAGE}");
- return ExitCode::SUCCESS;
- }
- return dispatch(&rest[0], &rest[1..]);
- }
- if a1 == "--tokens" {
- if argc < 3 {
- eprintln!("usage: bbb --tokens ");
- return ExitCode::FAILURE;
- }
- return finish(cmd_tokens(&args[2]));
- }
-
- dispatch(a1, &args[2..])
-}
-
-fn parse_size(s: &str) -> u64 {
- let (num, mult) = match s.chars().last() {
- Some('K' | 'k') => (&s[..s.len() - 1], 1024u64),
- Some('M' | 'm') => (&s[..s.len() - 1], 1024u64 * 1024),
- Some('G' | 'g') => (&s[..s.len() - 1], 1024u64 * 1024 * 1024),
- _ => (s, 1),
- };
- num.parse::().unwrap_or(0) * mult
-}
-
-fn dispatch(cmd: &str, rest: &[String]) -> ExitCode {
- match cmd {
- "shell" => {
- match rest.first().map(|s| s.as_str()) {
- Some("run") => {
- if rest.len() < 2 {
- eprintln!("usage: bbb shell run ");
- return ExitCode::FAILURE;
- }
- finish(cmd_shell_run(&rest[1]))
- }
- Some("build") => {
- if rest.len() < 2 {
- eprintln!("usage: bbb shell build [-o out]");
- return ExitCode::FAILURE;
- }
- let file = &rest[1];
- let mut out = None;
- let mut i = 2;
- while i < rest.len() {
- if rest[i] == "-o" && i + 1 < rest.len() {
- out = Some(rest[i + 1].clone());
- i += 2;
- } else {
- eprintln!("usage: bbb shell build [-o out]");
- return ExitCode::FAILURE;
- }
- }
- finish(cmd_shell_build(file, out.as_deref()))
- }
- _ => {
- eprintln!("usage: bbb shell run | bbb shell build [-o out]");
- ExitCode::FAILURE
- }
- }
- }
- "init" => {
- if rest.is_empty() {
- eprintln!("usage: bbb init ");
- return ExitCode::FAILURE;
- }
- finish(cmd_init(&rest[0]))
- }
- "build" => {
- // bbb build [dir] [-s|-m [out]] [-o out]
- let mut dir = ".".to_string();
- let mut mode = "s";
- let mut out = None;
- let mut i = 0;
- while i < rest.len() {
- match rest[i].as_str() {
- "-s" => mode = "s",
- "-m" => {
- mode = "m";
- if i + 1 < rest.len() && !rest[i + 1].starts_with('-') {
- out = Some(rest[i + 1].clone());
- i += 1;
- }
- }
- "-o" => {
- if i + 1 < rest.len() {
- out = Some(rest[i + 1].clone());
- i += 1;
- }
- }
- other => dir = other.to_string(),
- }
- i += 1;
- }
- finish(cmd_build(&dir, mode, out.as_deref()))
- }
- "run" => {
- let target = rest.first().map(|s| s.as_str()).unwrap_or(".");
- finish(cmd_run(target))
- }
- "install" => {
- let dir = rest.first().map(|s| s.as_str()).unwrap_or(".");
- finish(cmd_install(dir))
- }
- "destroy" => {
- let dir = rest.first().map(|s| s.as_str()).unwrap_or(".");
- finish(cmd_destroy(dir))
- }
- "pack" => {
- if rest.len() < 2 {
- eprintln!("usage: bbb pack [--entry NAME] ");
- return ExitCode::FAILURE;
- }
- let out = &rest[0];
- let mut entry = None;
- let mut files = Vec::new();
- let mut i = 1;
- while i < rest.len() {
- if rest[i] == "--entry" && i + 1 < rest.len() {
- entry = Some(rest[i + 1].clone());
- i += 2;
- } else {
- files.push(rest[i].clone());
- i += 1;
- }
- }
- finish(cmd_pack(out, entry.as_deref(), &files))
- }
- "unpack" => {
- if rest.is_empty() {
- eprintln!("usage: bbb unpack [dir]");
- return ExitCode::FAILURE;
- }
- let dir = rest.get(1).map(|s| s.as_str()).unwrap_or(".");
- finish(cmd_unpack(&rest[0], dir))
- }
- // 内部调试命令(保留)
- "lexer" => {
- if rest.is_empty() {
- eprintln!("usage: bbb lexer ");
- return ExitCode::FAILURE;
- }
- finish(cmd_tokens(&rest[0]))
- }
- "parse" => {
- if rest.is_empty() {
- eprintln!("usage: bbb parse ");
- return ExitCode::FAILURE;
- }
- finish(cmd_parse(&rest[0]))
- }
- "arena" => {
- let n: u32 = rest.first().and_then(|s| s.parse().ok()).unwrap_or(10_000);
- cmd_arena(n);
- ExitCode::SUCCESS
- }
- // 默认:`bbb ` 解释运行
- _ => finish(cmd_run(cmd)),
- }
-}
-
-fn finish(r: Result<(), String>) -> ExitCode {
- match r {
- Ok(()) => ExitCode::SUCCESS,
- Err(e) => {
- eprintln!("{e}");
- ExitCode::FAILURE
- }
- }
-}
diff --git a/rust/crates/bbb-core/Cargo.toml b/rust/crates/bbb-core/Cargo.toml
deleted file mode 100644
index 44e7029..0000000
--- a/rust/crates/bbb-core/Cargo.toml
+++ /dev/null
@@ -1,8 +0,0 @@
-[package]
-name = "bbb-core"
-version.workspace = true
-edition.workspace = true
-license.workspace = true
-description = "BioLang 运行时核心:arena 内存规划 + Value/请求模型"
-
-[dependencies]
diff --git a/rust/crates/bbb-core/src/arena.rs b/rust/crates/bbb-core/src/arena.rs
deleted file mode 100644
index 88c7a77..0000000
--- a/rust/crates/bbb-core/src/arena.rs
+++ /dev/null
@@ -1,210 +0,0 @@
-//! Arena 内存规划(bbb-core)。
-//!
-//! # BumpArena — 类型化 bump 分配器
-//!
-//! - 分页:每页固定 `PAGE_SLOTS` 槽;页表 `Vec>` 只增不减;
-//! - `alloc(v) -> u32`:句柄 = `(page << SHIFT) | slot`,0 保留为 null;
-//! - `get(h) -> &T` / `get_mut`:O(1);
-//! - 永不释放单个槽(程序级生命周期,与旧 C `aalloc` 语义一致);
-//! - 句柄优点:扩容搬迁安全、可序列化、对齐无空洞、u32 省内存。
-//!
-//! # StrArena — 字符串字节池
-//!
-//! 分页字节缓冲(页 4 KiB 起步,几何增长),`push(&str) -> StrRef{off,len}`
-//! 只拷贝一次;之后取用零拷贝。`StrRef` 8 字节,可安全穿越线程边界
-//! (字节池只增,读不竞争——协作式调度下无并发写)。
-
-use std::marker::PhantomData;
-
-/// 字符串引用:(offset, len) 指向 StrArena 字节池。
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct StrRef {
- pub off: u32,
- pub len: u32,
-}
-
-impl StrRef {
- pub const NULL: StrRef = StrRef { off: 0, len: 0 };
- pub fn is_null(self) -> bool {
- self.off == 0 && self.len == 0
- }
-}
-
-/// 类型化 bump arena。`T: Copy` 约束保证句柄读取无别名问题。
-pub struct BumpArena {
- pages: Vec>,
- next: u32, // 当前页已用槽数
- _marker: PhantomData,
-}
-
-const PAGE_SLOTS: u32 = 256; // 每页槽数(2^8)
-const SHIFT: u32 = 8;
-const SLOT_MASK: u32 = PAGE_SLOTS - 1;
-const MAX_HANDLE: u32 = u32::MAX >> 1; // 最高位留给 null 标志扩展
-
-impl BumpArena {
- pub fn new() -> Self {
- BumpArena { pages: Vec::new(), next: 0, _marker: PhantomData }
- }
-
- fn ensure_page(&mut self) {
- if self.next == PAGE_SLOTS || self.pages.is_empty() {
- let page: Box<[T]> = (0..PAGE_SLOTS).map(|_| unsafe { std::mem::zeroed() }).collect();
- self.pages.push(page);
- self.next = 1; // 槽 0 保留为 null 哨兵,句柄永不等于 0
- }
- }
-
- /// 分配一个槽,返回 u32 句柄。0 永不返回(保留为 null)。
- #[inline]
- pub fn alloc(&mut self, v: T) -> u32 {
- self.ensure_page();
- let page = self.pages.len() as u32 - 1;
- let slot = self.next;
- self.pages[page as usize][slot as usize] = v;
- self.next += 1;
- (page << SHIFT) | slot
- }
-
- /// 句柄 → 不可变引用。
- #[inline]
- pub fn get(&self, h: u32) -> &T {
- debug_assert!(h != 0 && h <= MAX_HANDLE);
- let page = (h >> SHIFT) as usize;
- let slot = (h & SLOT_MASK) as usize;
- &self.pages[page][slot]
- }
-
- /// 句柄 → 可变引用(bump 语义下互斥由外部保证)。
- #[inline]
- pub fn get_mut(&mut self, h: u32) -> &mut T {
- debug_assert!(h != 0 && h <= MAX_HANDLE);
- let page = (h >> SHIFT) as usize;
- let slot = (h & SLOT_MASK) as usize;
- &mut self.pages[page][slot]
- }
-
- pub fn pages(&self) -> usize {
- self.pages.len()
- }
-
- pub fn capacity(&self) -> u32 {
- self.pages.len() as u32 * PAGE_SLOTS
- }
-}
-
-impl Default for BumpArena {
- fn default() -> Self {
- Self::new()
- }
-}
-
-/// 字符串字节池。
-pub struct StrArena {
- pages: Vec>,
- cur: Vec,
-}
-
-impl StrArena {
- pub fn new() -> Self {
- StrArena { pages: Vec::new(), cur: Vec::with_capacity(4096) }
- }
-
- /// 写入一个字符串,返回 (offset, len)。数据拷贝一次后永驻。
- #[inline]
- pub fn push(&mut self, s: &str) -> StrRef {
- let bytes = s.as_bytes();
- if self.cur.len() + bytes.len() > self.cur.capacity() {
- // 当前页放不下:封页,开新页(容量几何增长)
- if !self.cur.is_empty() {
- self.pages.push(std::mem::take(&mut self.cur));
- }
- let cap = (4096usize).max(bytes.len().next_power_of_two());
- self.cur = Vec::with_capacity(cap);
- }
- let off = self.total_len() as u32;
- self.cur.extend_from_slice(bytes);
- StrRef { off, len: bytes.len() as u32 }
- }
-
- /// 按 StrRef 取回字符串视图(零拷贝)。
- #[inline]
- pub fn get<'a>(&'a self, r: StrRef) -> &'a str {
- if r.is_null() {
- return "";
- }
- let start = r.off as usize;
- let end = start + r.len as usize;
- let mut acc = 0usize;
- for page in &self.pages {
- let page_len = page.len();
- if start < acc + page_len && end <= acc + page_len {
- return std::str::from_utf8(&page[start - acc..end - acc]).unwrap_or("");
- }
- acc += page_len;
- }
- std::str::from_utf8(&self.cur[start - acc..end - acc]).unwrap_or("")
- }
-
- fn total_len(&self) -> usize {
- self.pages.iter().map(|p| p.len()).sum::() + self.cur.len()
- }
-}
-
-impl Default for StrArena {
- fn default() -> Self {
- Self::new()
- }
-}
-
-/// 通用别名:对象/数组/线程等句柄表都用 BumpArena。
-pub type Arena = BumpArena;
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn bump_arena_alloc_get() {
- let mut a = BumpArena::::new();
- let h1 = a.alloc(42);
- let h2 = a.alloc(7);
- assert_ne!(h1, h2);
- assert_eq!(*a.get(h1), 42);
- assert_eq!(*a.get(h2), 7);
- *a.get_mut(h1) = 99;
- assert_eq!(*a.get(h1), 99);
- }
-
- #[test]
- fn bump_arena_multi_page() {
- let mut a = BumpArena::::new();
- let mut last = 0;
- for i in 1..1000u32 {
- last = a.alloc(i);
- }
- assert_eq!(*a.get(last), 999);
- assert!(a.pages() >= 3);
- }
-
- #[test]
- fn str_arena_roundtrip() {
- let mut s = StrArena::new();
- let a = s.push("hello");
- let b = s.push("世界");
- let c = s.push("x".repeat(5000).as_str());
- assert_eq!(s.get(a), "hello");
- assert_eq!(s.get(b), "世界");
- assert_eq!(s.get(c).len(), 5000);
- assert!(s.get(StrRef::NULL).is_empty());
- }
-
- #[test]
- fn str_arena_cross_page() {
- // 跨页边界的长串必须完整可读
- let mut s = StrArena::new();
- let long = "abc".repeat(2000);
- let r = s.push(&long);
- assert_eq!(s.get(r), long);
- }
-}
diff --git a/rust/crates/bbb-core/src/lib.rs b/rust/crates/bbb-core/src/lib.rs
deleted file mode 100644
index d8fe6eb..0000000
--- a/rust/crates/bbb-core/src/lib.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-//! bbb-core — BioLang 运行时核心。
-//!
-//! 内存规划(手写掌控版,对标旧 C 的 arena 设计并强化):
-//!
-//! 1. **一切值进 arena,引用用 u32 句柄而非指针**
-//! - 句柄 = (page, slot) 打包,arena 扩容/搬迁不需要修指针;
-//! - 句柄天然可序列化(.img 打包、跨线程传递);8 字节对齐无空洞;
-//! - 与旧 C 的 `aalloc`(裸指针 + 永不释放)相比,句柄方案在保持
-//! "程序级一次性生命周期" 的同时,获得可搬迁 + 可序列化两个能力。
-//! 2. **字符串 = 全局字节池中的 (offset, len)**,写入即 intern,
-//! 零拷贝复用;`StrRef` 8 字节。
-//! 3. **Value 16 字节**:u32 tag(含 REFUSED 标志位)+ u64 负载 + 4B pad;
-//! 所有标量(int/float/double/bool/char)内联,字符串/对象/数组走句柄。
-//! (v2 候选:NaN-boxing 压到 8 字节,代价是 int 精度受限——旧 LLVM
-//! 后端统一 double 语义,解释器保留 i64,故 v1 不采用。)
-//! 4. **请求模型 = Value 的 tag 位**:bit31 = refused,负载为 cause 的
-//! 字符串句柄;`res`/`ref` 不产生堆分配,随值传递。
-//! 5. **区域划分**:每个流(Unistream/Remstream/Threadstream...)在 arena
-//! 内拥有自己的页区,线程隔离靠区域隔离实现(协作式调度,无锁)。
-
-pub mod arena;
-pub mod value;
-
-pub use arena::{Arena, BumpArena, StrArena, StrRef};
-pub use value::{Cause, Outcome, Tag, Value};
diff --git a/rust/crates/bbb-core/src/value.rs b/rust/crates/bbb-core/src/value.rs
deleted file mode 100644
index 2773dc0..0000000
--- a/rust/crates/bbb-core/src/value.rs
+++ /dev/null
@@ -1,342 +0,0 @@
-//! Value / 请求模型(bbb-core)。
-//!
-//! # Value — 16 字节标量优先表示
-//!
-//! ```text
-//! ┌──────────────┬──────────────────┬──────────────┐
-//! │ tag: u32 │ data: u64 │ pad: u32 │
-//! │ bit31 REFUSED│ 负载 │ (对齐) │
-//! └──────────────┴──────────────────┴──────────────┘
-//! ```
-//!
-//! - 标量(Int/Num/Bool/Char)全部内联,零堆分配;
-//! - Str/Obj/Arr/Ref 走句柄(u32 → arena),8 字节以内;
-//! - **REFUSED 位**:`ref "原因"` 产生的请求结果 = 同值 + 标志位,
-//! 不额外分配;`get`/`cause` 只是位测试;
-//! - `Outcome`:解释器内部用,`Res(Value)` / `Ref(Cause)` 二态,
-//! 与语法层 ResStatement/RefStatement 一一对应。
-//!
-//! # 类型标签(低 24 位)
-//!
-//! Nil / Int / Num / Bool / Str / Char / Obj / Arr / Ref
-
-use crate::arena::StrRef;
-
-pub const TAG_MASK: u32 = 0x00FF_FFFF;
-pub const REFUSED: u32 = 0x8000_0000;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-#[repr(u32)]
-pub enum Tag {
- Nil = 0,
- Int = 1,
- Num = 2, // float/double 统一 double 语义(LLVM 后端同款)
- Bool = 3,
- Str = 4,
- Char = 5,
- Obj = 6,
- Arr = 7,
- Ref = 8, // 智能引用句柄(&perm follow base)
-}
-
-impl Tag {
- #[inline]
- pub fn from_bits(bits: u32) -> Tag {
- match bits & TAG_MASK {
- 0 => Tag::Nil,
- 1 => Tag::Int,
- 2 => Tag::Num,
- 3 => Tag::Bool,
- 4 => Tag::Str,
- 5 => Tag::Char,
- 6 => Tag::Obj,
- 7 => Tag::Arr,
- _ => Tag::Ref,
- }
- }
-}
-
-/// 16 字节 Value(对齐 8;字段按对齐降序排列保证紧凑)。
-#[derive(Debug, Clone, Copy)]
-#[repr(C, align(8))]
-pub struct Value {
- data: u64, // 负载
- tag: u32, // 低 24 位类型 + bit31 REFUSED
- _pad: u32,
-}
-
-impl Value {
- pub const NIL: Value = Value { tag: Tag::Nil as u32, data: 0, _pad: 0 };
-
- #[inline]
- pub fn nil() -> Self {
- Self::NIL
- }
-
- #[inline]
- pub fn int(v: i64) -> Self {
- Value { tag: Tag::Int as u32, data: v as u64, _pad: 0 }
- }
-
- #[inline]
- pub fn num(v: f64) -> Self {
- Value { tag: Tag::Num as u32, data: v.to_bits(), _pad: 0 }
- }
-
- #[inline]
- pub fn boolean(v: bool) -> Self {
- Value { tag: Tag::Bool as u32, data: v as u64, _pad: 0 }
- }
-
- #[inline]
- pub fn string(r: StrRef) -> Self {
- Value { tag: Tag::Str as u32, data: ((r.off as u64) << 32) | r.len as u64, _pad: 0 }
- }
-
- #[inline]
- pub fn chr(v: u8) -> Self {
- Value { tag: Tag::Char as u32, data: v as u64, _pad: 0 }
- }
-
- #[inline]
- pub fn obj(h: u32) -> Self {
- Value { tag: Tag::Obj as u32, data: h as u64, _pad: 0 }
- }
-
- #[inline]
- pub fn arr(h: u32) -> Self {
- Value { tag: Tag::Arr as u32, data: h as u64, _pad: 0 }
- }
-
- #[inline]
- pub fn reff(h: u32) -> Self {
- Value { tag: Tag::Ref as u32, data: h as u64, _pad: 0 }
- }
-
- #[inline]
- pub fn tag(&self) -> Tag {
- Tag::from_bits(self.tag)
- }
-
- #[inline]
- pub fn refused(&self) -> bool {
- self.tag & REFUSED != 0
- }
-
- /// 标记为拒绝(请求模型:ref)。保留原值,仅置位。
- #[inline]
- pub fn with_refused(mut self) -> Self {
- self.tag |= REFUSED;
- self
- }
-
- /// 拒绝值:REFUSED 位 + 原因字符串句柄(cause)。
- #[inline]
- pub fn refused_str(r: StrRef) -> Self {
- Value {
- tag: Tag::Str as u32 | REFUSED,
- data: ((r.off as u64) << 32) | r.len as u64,
- _pad: 0,
- }
- }
-
- /// 拒绝原因(未拒绝时返回空串句柄)。
- #[inline]
- pub fn cause(&self) -> StrRef {
- if self.refused() && self.tag() == Tag::Str {
- self.as_str()
- } else {
- StrRef::NULL
- }
- }
-
- /// 数值视图:Int → i64 → f64;Num → f64(统一 double 语义)。
- #[inline]
- pub fn as_int_or_num(&self) -> f64 {
- match self.tag() {
- Tag::Int => self.as_int() as f64,
- Tag::Num => self.as_num(),
- Tag::Bool => self.as_bool() as u8 as f64,
- Tag::Char => self.as_char() as f64,
- _ => 0.0,
- }
- }
-
- #[inline]
- pub fn as_int(&self) -> i64 {
- debug_assert_eq!(self.tag(), Tag::Int);
- self.data as i64
- }
-
- #[inline]
- pub fn as_num(&self) -> f64 {
- debug_assert_eq!(self.tag(), Tag::Num);
- f64::from_bits(self.data)
- }
-
- #[inline]
- pub fn as_bool(&self) -> bool {
- debug_assert_eq!(self.tag(), Tag::Bool);
- self.data != 0
- }
-
- #[inline]
- pub fn as_str(&self) -> StrRef {
- debug_assert_eq!(self.tag(), Tag::Str);
- StrRef { off: (self.data >> 32) as u32, len: self.data as u32 }
- }
-
- #[inline]
- pub fn as_char(&self) -> u8 {
- debug_assert_eq!(self.tag(), Tag::Char);
- self.data as u8
- }
-
- #[inline]
- pub fn as_handle(&self) -> u32 {
- debug_assert!(matches!(self.tag(), Tag::Obj | Tag::Arr | Tag::Ref));
- self.data as u32
- }
-
- /// 真值判定(与旧实现一致):0 / "" / 拒绝 = false,其余 true。
- #[inline]
- pub fn truthy(&self) -> bool {
- if self.refused() {
- return false;
- }
- match self.tag() {
- Tag::Nil => false,
- Tag::Int => self.as_int() != 0,
- Tag::Num => self.as_num() != 0.0,
- Tag::Bool => self.as_bool(),
- Tag::Str => !self.as_str().is_null(),
- Tag::Char => self.as_char() != 0,
- _ => true,
- }
- }
-}
-
-impl PartialEq for Value {
- fn eq(&self, other: &Self) -> bool {
- if self.refused() != other.refused() {
- return false;
- }
- self.tag == other.tag && self.data == other.data
- }
-}
-impl Eq for Value {}
-
-impl std::fmt::Display for Value {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- if self.refused() {
- return write!(f, "");
- }
- match self.tag() {
- Tag::Nil => write!(f, "nil"),
- Tag::Int => write!(f, "{}", self.as_int()),
- Tag::Num => write!(f, "{}", self.as_num()),
- Tag::Bool => write!(f, "{}", self.as_bool()),
- Tag::Str => write!(f, "", self.data),
- Tag::Char => write!(f, "{}", self.as_char() as char),
- Tag::Obj => write!(f, "", self.as_handle()),
- Tag::Arr => write!(f, "", self.as_handle()),
- Tag::Ref => write!(f, "[", self.as_handle()),
- }
- }
-}
-
-/// 请求结果:Res = 响应,Ref = 拒绝(携带 cause 字符串)。
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub enum Outcome {
- Res(Value),
- Ref(Cause),
-}
-
-/// 拒绝原因:字符串句柄(arena 内,零拷贝)。
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct Cause(pub StrRef);
-
-impl Outcome {
- #[inline]
- pub fn is_refused(&self) -> bool {
- matches!(self, Outcome::Ref(_))
- }
-
- /// `get`:取实际值(拒绝时返回 Nil)。
- #[inline]
- pub fn get(self) -> Value {
- match self {
- Outcome::Res(v) => v,
- Outcome::Ref(_) => Value::nil(),
- }
- }
-
- /// `cause`:取拒绝原因(未拒绝时返回空字符串)。
- #[inline]
- pub fn cause(self) -> StrRef {
- match self {
- Outcome::Res(_) => StrRef::NULL,
- Outcome::Ref(c) => c.0,
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn value_size_is_16() {
- assert_eq!(std::mem::size_of::](), 16);
- }
-
- #[test]
- fn scalars_inline() {
- assert_eq!(Value::int(42).as_int(), 42);
- assert_eq!(Value::num(3.5).as_num(), 3.5);
- assert!(Value::boolean(true).as_bool());
- assert_eq!(Value::chr(b'x').as_char(), b'x');
- assert_eq!(Value::nil().tag(), Tag::Nil);
- }
-
- #[test]
- fn string_roundtrip() {
- let r = StrRef { off: 12, len: 5 };
- let v = Value::string(r);
- assert_eq!(v.tag(), Tag::Str);
- assert_eq!(v.as_str(), r);
- }
-
- #[test]
- fn refused_flag() {
- let ok = Value::int(7);
- let bad = ok.with_refused();
- assert!(bad.refused());
- assert!(!ok.refused());
- assert_ne!(ok, bad);
- assert_eq!(bad.truthy(), false);
- }
-
- #[test]
- fn truthiness() {
- assert!(!Value::nil().truthy());
- assert!(!Value::int(0).truthy());
- assert!(Value::int(1).truthy());
- assert!(!Value::num(0.0).truthy());
- assert!(!Value::boolean(false).truthy());
- assert!(!Value::string(StrRef::NULL).truthy());
- }
-
- #[test]
- fn outcome_get_cause() {
- let res = Outcome::Res(Value::int(10));
- assert_eq!(res.get(), Value::int(10));
- assert!(res.cause().is_null());
-
- let cause = Cause(StrRef { off: 3, len: 9 });
- let rej = Outcome::Ref(cause);
- assert!(rej.is_refused());
- assert_eq!(rej.get(), Value::nil());
- assert_eq!(rej.cause(), cause.0);
- }
-}
diff --git a/rust/crates/bbb-llvm/Cargo.toml b/rust/crates/bbb-llvm/Cargo.toml
deleted file mode 100644
index 3eb704d..0000000
--- a/rust/crates/bbb-llvm/Cargo.toml
+++ /dev/null
@@ -1,9 +0,0 @@
-[package]
-name = "bbb-llvm"
-version.workspace = true
-edition.workspace = true
-license.workspace = true
-description = "BioLang LLVM 后端(M4):AST → LLVM IR 文本 → 系统 clang 编译"
-
-[dependencies]
-bbb-syntax = { path = "../bbb-syntax" }
diff --git a/rust/crates/bbb-llvm/src/lib.rs b/rust/crates/bbb-llvm/src/lib.rs
deleted file mode 100644
index 5ff3f3d..0000000
--- a/rust/crates/bbb-llvm/src/lib.rs
+++ /dev/null
@@ -1,887 +0,0 @@
-//! bbb-llvm — BioLang 编译器(M4 里程碑)。
-//!
-//! 方案(对齐旧 src/llvm.c):**零依赖**发射 LLVM IR 文本,交给系统 clang
-//! 编译成原生可执行文件。统一 double 语义在解释器与编译器间保持一致。
-//!
-//! M4 v1 支持子集(17-llvm.bio 为准):
-//! - int/float/double 变量声明与赋值、算术 + - * / %、比较 == != < > <= >=
-//! - if/else、while、for(含 break/continue)
-//! - 方法定义与调用(含 Main::exec 外的方法,如 square)、递归 v2
-//! - res 返回值 / ref(打印后退出)、get/cause、ALL 声明
-//! - CIO::println / CIO::print(字符串+数字混合 → printf)
-//! - Class + new(对象 = malloc struct,this 指针传参,__init__ 自动调用,
-//! this::字段 GEP 读写,对象方法调用)——2026-08-23 扩展
-
-use std::collections::HashMap;
-
-use bbb_syntax::ast::*;
-
-/// 值类型。
-#[derive(Clone, Copy, PartialEq, Debug)]
-pub enum Ty {
- I64,
- F64,
- Ptr, // 对象/流指针
-}
-
-impl Ty {
- fn llvm(self) -> &'static str {
- match self {
- Ty::I64 => "i64",
- Ty::F64 => "double",
- Ty::Ptr => "ptr",
- }
- }
-}
-
-/// 编译产物。
-pub struct Module {
- pub ir: String,
-}
-
-struct Ctx {
- out: String,
- tmp: usize,
- str_i: usize,
- vars: Vec>, // 作用域栈:变量名 → (类型, 寄存器名)
- funcs: HashMap)>, // 方法表:名 → (返回类型, 参数)
- void_methods: std::collections::HashSet, // void 方法集合(调用时用 call void)
- classes: HashMap>, // 类:名 → 字段列表(名, 类型)
- var_ty: HashMap, // 变量名 → 类名(对象变量,属性访问用)
- current_class: Option, // 当前编译的类名(this:: 属性用)
- this_reg: Option, // 当前方法 this 指针寄存器(类方法)
- labels: usize,
- loop_end: Vec,
- loop_continue: Vec, // continue 目标(for=update,while=cond)
- current_ret: Ty,
- str_list: String, // 字符串常量延迟输出(函数外)
-}
-
-impl Ctx {
- fn new() -> Self {
- Ctx {
- out: String::new(),
- tmp: 0,
- str_i: 0,
- vars: vec![HashMap::new()],
- funcs: HashMap::new(),
- void_methods: std::collections::HashSet::new(),
- classes: HashMap::new(),
- var_ty: HashMap::new(),
- current_class: None,
- this_reg: None,
- labels: 0,
- loop_end: Vec::new(),
- loop_continue: Vec::new(),
- current_ret: Ty::I64,
- str_list: String::new(),
- }
- }
-
- fn emit(&mut self, s: impl AsRef) {
- self.out.push_str(s.as_ref());
- self.out.push('\n');
- }
-
- fn reg(&mut self, hint: &str) -> String {
- self.tmp += 1;
- format!("%{hint}{}", self.tmp)
- }
-
- fn label(&mut self, hint: &str) -> String {
- self.labels += 1;
- format!("{hint}{}", self.labels)
- }
-
- fn str_const(&mut self, s: &str) -> String {
- // C 风格转义
- let mut esc = String::new();
- for b in s.bytes() {
- match b {
- b'"' => esc.push_str("\\22"),
- b'\\' => esc.push_str("\\5C"),
- b'\n' => esc.push_str("\\0A"),
- b'\t' => esc.push_str("\\09"),
- 0x20..=0x7E => esc.push(b as char),
- _ => esc.push_str(&format!("\\{:02X}", b)),
- }
- }
- self.str_i += 1;
- let name = format!("@.str{}", self.str_i);
- // LLVM 字节数:\XX 转义序列计 1 字节,其余字符 1 字节
- let mut llvm_len = 0usize;
- let chars: Vec = esc.chars().collect();
- let mut k = 0;
- while k < chars.len() {
- if chars[k] == '\\' && k + 2 < chars.len() {
- llvm_len += 1;
- k += 3;
- } else {
- llvm_len += 1;
- k += 1;
- }
- }
- self.str_list.push_str(&format!("{name} = private unnamed_addr constant [{} x i8] c\"{esc}\\00\", align 1\n",
- llvm_len + 1));
- name
- }
-
- fn var_get(&self, name: &str) -> Option<(Ty, String)> {
- for scope in self.vars.iter().rev() {
- if let Some(v) = scope.get(name) {
- return Some(v.clone());
- }
- }
- None
- }
-
- fn var_set(&mut self, name: &str, ty: Ty, reg: String) {
- self.vars.last_mut().unwrap().insert(name.to_string(), (ty, reg));
- }
-}
-
-/// 编译 Program → IR 文本。
-pub fn compile(prog: &Program) -> Result {
- let mut ctx = Ctx::new();
- ctx.emit("; BioLang LLVM backend (M4) — generated from AST");
- ctx.emit("declare i32 @printf(ptr, ...)");
- ctx.emit("declare void @exit(i32)");
- ctx.emit("declare ptr @malloc(i64)");
- ctx.emit("");
-
- // 收集类字段表(先于类型声明)
- collect_classes(prog, &mut ctx);
- // 发射 struct 类型声明
- let class_names: Vec = ctx.classes.keys().cloned().collect();
- for cname in &class_names {
- let fields = &ctx.classes[cname];
- let mut ty = String::from("type {");
- for (i, (_n, t)) in fields.iter().enumerate() {
- if i > 0 {
- ty.push_str(", ");
- }
- ty.push_str(t.llvm());
- }
- if fields.is_empty() {
- ty.push_str("i8");
- }
- ty.push('}');
- ctx.emit(format!("%struct.{cname} = {ty}"));
- }
- if !ctx.classes.is_empty() {
- ctx.emit("");
- }
-
- // 收集方法签名
- collect_methods(prog, &mut ctx);
-
- // 编译 Main 流方法(exec 之外的方法作为普通函数)
- if let Some(m) = &prog.main {
- for method in &m.methods {
- if method.name != "exec" {
- let fname = format!("@main_{}", method.name);
- compile_function(&mut ctx, &fname, method, false)?;
- }
- }
- }
- // 编译 fork/class 方法(@<流名>_<方法名>,类方法首参为 this 指针)
- for d in &prog.decls {
- if let (Decl::Class { name, members, .. } | Decl::Fork { name, members, .. }) = d {
- for mem in members {
- if let Member::Method(m2) = mem {
- let fname = format!("@{}_{}", name, m2.name);
- let is_class = ctx.classes.contains_key(name);
- compile_function(&mut ctx, &fname, m2, is_class)?;
- }
- }
- }
- }
- // Main::exec → @__bio_main
- if let Some(m) = &prog.main {
- if let Some(exec) = m.methods.iter().find(|x| x.name == "exec") {
- let mut e2 = exec.clone();
- e2.name = "__bio_main".into();
- e2.ret = "void".into();
- compile_function(&mut ctx, "@__bio_main", &e2, false)?;
- }
- }
- // 字符串常量(函数外)
- if !ctx.str_list.is_empty() {
- ctx.out.push_str(&ctx.str_list);
- ctx.out.push('\n');
- }
- // C main 包装
- ctx.emit("define i32 @main(i32 %argc, ptr %argv) {");
- ctx.emit("entry:");
- ctx.emit(" call void @__bio_main()");
- ctx.emit(" ret i32 0");
- ctx.emit("}");
- Ok(ctx.out)
-}
-
-fn collect_methods(prog: &Program, ctx: &mut Ctx) {
- let mut methods: Vec<(String, Ty, Vec<(String, Ty)>)> = Vec::new();
- if let Some(m) = &prog.main {
- for m2 in &m.methods {
- if m2.name != "exec" {
- let (rt, params) = sig_of(m2);
- methods.push((format!("main_{}", m2.name), rt, params));
- }
- }
- }
- for d in &prog.decls {
- if let (Decl::Class { name, members, .. } | Decl::Fork { name, members, .. }) = d {
- let is_class = ctx.classes.contains_key(name);
- for mem in members {
- if let Member::Method(m2) = mem {
- let (rt, mut params) = sig_of(m2);
- if is_class {
- // 类方法:首参 this 指针
- params.insert(0, ("this".into(), Ty::Ptr));
- }
- methods.push((format!("{}_{}", name, m2.name), rt, params));
- }
- }
- }
- }
- for (n, rt, params) in methods {
- ctx.funcs.insert(n.clone(), (rt, params));
- let _ = &n;
- }
- // void 方法登记(从原始方法表再扫一遍)
- for d in &prog.decls {
- if let (Decl::Class { name, members, .. } | Decl::Fork { name, members, .. }) = d {
- for mem in members {
- if let Member::Method(m2) = mem {
- if m2.ret == "void" {
- ctx.void_methods.insert(format!("{}_{}", name, m2.name));
- }
- }
- }
- }
- }
- if let Some(m) = &prog.main {
- for m2 in &m.methods {
- if m2.ret == "void" && m2.name != "exec" {
- ctx.void_methods.insert(format!("main_{}", m2.name));
- }
- }
- }
-}
-
-fn decl_name(d: &Decl) -> String {
- match d {
- Decl::Class { name, .. } => name.clone(),
- _ => "s".into(),
- }
-}
-
-/// 收集类字段表:类名 → [(字段名, 类型)](new/this:: 用)。
-fn collect_classes(prog: &Program, ctx: &mut Ctx) {
- for d in &prog.decls {
- if let Decl::Class { name, members, .. } = d {
- let mut fields = Vec::new();
- for mem in members {
- if let Member::Field { ty, names } = mem {
- for n in names {
- fields.push((n.clone(), ty_of(ty)));
- }
- }
- }
- ctx.classes.insert(name.clone(), fields);
- }
- }
-}
-
-fn sig_of(m: &Method) -> (Ty, Vec<(String, Ty)>) {
- let rt = ty_of(&m.ret);
- let params = m
- .params
- .iter()
- .map(|p| (p.name.clone(), ty_of(&p.ty)))
- .collect();
- (rt, params)
-}
-
-fn ty_of(t: &str) -> Ty {
- match t {
- "float" | "double" => Ty::F64,
- "int" | "char" | "bool" => Ty::I64,
- "void" => Ty::I64, // void 方法:返回 i64 0(调用方忽略)
- _ => Ty::Ptr, // 类名/流名/对象 → 指针
- }
-}
-
-fn compile_function(ctx: &mut Ctx, fname: &str, method: &Method, is_class: bool) -> Result<(), String> {
- // 类方法:记录当前类名(this:: 属性访问)
- let saved_class = ctx.current_class.clone();
- if is_class {
- if let Some(cname) = fname.strip_prefix('@').and_then(|f| f.split('_').next()) {
- ctx.current_class = Some(cname.to_string());
- }
- }
- let r = compile_function_inner(ctx, fname, method, is_class);
- ctx.current_class = saved_class;
- r
-}
-
-fn compile_function_inner(ctx: &mut Ctx, fname: &str, method: &Method, is_class: bool) -> Result<(), String> {
- let (rt, params) = sig_of(method);
- ctx.current_ret = rt;
- let mut sig = format!("define {} {fname}(", rt.llvm());
- let mut body_sig = Vec::new();
- let mut first = true;
- // 类方法:首参为 this 指针(隐含)
- if is_class {
- sig.push_str("ptr %this");
- first = false;
- }
- for (i, (n, t)) in params.iter().enumerate() {
- if !first {
- sig.push_str(", ");
- }
- first = false;
- let arg = format!("%{}", n);
- sig.push_str(&format!("{} {arg}", t.llvm()));
- body_sig.push((n.clone(), *t, arg));
- }
- sig.push(')');
- ctx.emit(sig);
- ctx.emit("{");
- ctx.emit("entry:");
- // 参数 alloc + store
- ctx.vars.push(HashMap::new());
- if is_class {
- let alloca = ctx.reg("this_");
- ctx.emit(format!(" {alloca} = alloca ptr"));
- ctx.emit(format!(" store ptr %this, ptr {alloca}"));
- ctx.this_reg = Some(alloca.clone());
- }
- for (n, t, arg) in body_sig {
- let alloca = ctx.reg(&format!("{}_", n));
- ctx.emit(format!(" {alloca} = alloca {}", t.llvm()));
- ctx.emit(format!(" store {} {arg}, ptr {alloca}", t.llvm()));
- ctx.var_set(&n, t, alloca);
- }
- // 语句
- let (flow, ret_reg) = compile_block(ctx, &method.body)?;
- if let Some(r) = ret_reg {
- ctx.emit(format!(" ret {} {r}", rt.llvm()));
- } else if !flow {
- ctx.emit(format!(" ret {} {}", rt.llvm(), if rt == Ty::I64 { "0" } else if rt == Ty::F64 { "0.0" } else { "null" }));
- }
- ctx.emit("}");
- ctx.emit("");
- ctx.vars.pop();
- ctx.this_reg = None;
- Ok(())
-}
-
-/// 语句块编译结果。
-struct BlockOut {
- /// 最后一个 res 的寄存器(Some = 函数已 ret 前)
- ret: Option,
-}
-
-/// 编译语句块,返回 (是否以终止指令结束, res 寄存器)。
-/// 块内 break/continue/ret 后不再编译后续语句(不可达丢弃,LLVM 合法)。
-fn compile_block(ctx: &mut Ctx, stmts: &[Stmt]) -> Result<(bool, Option), String> {
- let mut ret = None;
- for st in stmts {
- match st {
- Stmt::Ret { kind, values } => {
- if *kind == RetKind::Ref {
- // ref:拒绝 → 打印消息后退出(消息表达式 v2;先直接 exit)
- ctx.emit(" call void @exit(i32 1)");
- ctx.emit(" unreachable");
- return Ok((true, None));
- } else if let Some(v) = values.first() {
- let (ty, val) = compile_expr(ctx, v)?;
- ctx.current_ret = ty;
- return Ok((true, Some(val)));
- }
- }
- Stmt::Expr(e) => {
- // 调用语句:CIO::println 等
- compile_call_stmt(ctx, e)?;
- }
- Stmt::Assign { vtype, target, op, value, .. } => {
- if let AssignTarget::Var(name) = target {
- let (vty, vval) = compile_expr(ctx, value)?;
- let _ = op;
- match ctx.var_get(name) {
- Some((t, reg)) => {
- let cast = coerce(ctx, vval, vty, t);
- ctx.emit(format!(" store {} {cast}, ptr {reg}", t.llvm()));
- }
- None => {
- let alloca_t = if vtype.as_deref() == Some("ALL") || vtype.is_none() { vty } else { ty_of(vtype.as_deref().unwrap_or("int")) };
- let alloca = ctx.reg(&format!("{}a_", name));
- ctx.emit(format!(" {alloca} = alloca {}", alloca_t.llvm()));
- let cast = coerce(ctx, vval, vty, alloca_t);
- ctx.emit(format!(" store {} {cast}, ptr {alloca}", alloca_t.llvm()));
- ctx.var_set(name, alloca_t, alloca);
- // 对象变量:记录类名(属性访问用)
- if alloca_t == Ty::Ptr {
- if let Some(vt) = vtype {
- if !matches!(vt.as_str(), "ALL") && !vt.is_empty() {
- ctx.var_ty.insert(name.clone(), vt.clone());
- }
- }
- }
- }
- }
- } else if let AssignTarget::Prop { base, name } = target {
- // 属性赋值:obj.field = v / this.field = v
- let (vty, vval) = compile_expr(ctx, value)?;
- let (bt, bv) = compile_expr(ctx, base)?;
- if bt != Ty::Ptr {
- return Err(format!("property assignment on non-object: {name}"));
- }
- let bval = if let Expr::Var(vn) = base.as_ref() {
- if vn == "this" {
- match ctx.this_reg.clone() {
- Some(reg) => {
- let p = ctx.reg("thp");
- ctx.emit(format!(" {p} = load ptr, ptr {reg}"));
- p
- }
- None => bv,
- }
- } else {
- bv
- }
- } else {
- bv
- };
- let cls = field_owner(ctx, base);
- let (ft, idx) = field_index(ctx, &cls, name)?;
- let gp = ctx.reg("gep");
- ctx.emit(format!(
- " {gp} = getelementptr %struct.{cls}, ptr {bval}, i32 0, i32 {idx}"
- ));
- let cast = coerce(ctx, vval, vty, ft);
- ctx.emit(format!(" store {} {cast}, ptr {gp}", ft.llvm()));
- }
- }
- Stmt::If { cond, then, els } => {
- let (_, cval) = compile_expr(ctx, cond)?;
- let then_l = ctx.label("then");
- let else_l = ctx.label("else");
- let end_l = ctx.label("endif");
- let c = ctx.reg("c");
- ctx.emit(format!(" {c} = icmp ne i64 {cval}, 0"));
- ctx.emit(format!(" br i1 {c}, label %{then_l}, label %{else_l}"));
- ctx.emit(format!("{then_l}:"));
- let (t1, r1) = compile_block(ctx, then)?;
- if !t1 && r1.is_none() {
- ctx.emit(format!(" br label %{end_l}"));
- }
- ctx.emit(format!("{else_l}:"));
- if let Some(e) = els {
- let (t2, r2) = compile_block(ctx, e)?;
- if !t2 && r2.is_none() {
- ctx.emit(format!(" br label %{end_l}"));
- }
- } else {
- ctx.emit(format!(" br label %{end_l}"));
- }
- ctx.emit(format!("{end_l}:"));
- }
- Stmt::While { cond, body } => {
- let cond_l = ctx.label("wcond");
- let body_l = ctx.label("wbody");
- let end_l = ctx.label("wend");
- ctx.emit(format!(" br label %{cond_l}"));
- ctx.emit(format!("{cond_l}:"));
- let (_, cval) = compile_expr(ctx, cond)?;
- let c = ctx.reg("c");
- ctx.emit(format!(" {c} = icmp ne i64 {cval}, 0"));
- ctx.emit(format!(" br i1 {c}, label %{body_l}, label %{end_l}"));
- ctx.emit(format!("{body_l}:"));
- ctx.loop_end.push(end_l.clone());
- ctx.loop_continue.push(cond_l.clone());
- let (flow, r) = compile_block(ctx, body)?;
- ctx.loop_end.pop();
- ctx.loop_continue.pop();
- if !flow {
- ctx.emit(format!(" br label %{cond_l}"));
- }
- if let Some(r) = r {
- ret = Some(r);
- }
- ctx.emit(format!("{end_l}:"));
- }
- Stmt::For { init, cond, update, body } => {
- if let Some(i) = init {
- compile_block(ctx, std::slice::from_ref(i))?;
- }
- let cond_l = ctx.label("fcond");
- let body_l = ctx.label("fbody");
- let upd_l = ctx.label("fupd");
- let end_l = ctx.label("fend");
- ctx.emit(format!(" br label %{cond_l}"));
- ctx.emit(format!("{cond_l}:"));
- if let Some(c) = cond {
- let (_, cval) = compile_expr(ctx, c)?;
- let c = ctx.reg("c");
- ctx.emit(format!(" {c} = icmp ne i64 {cval}, 0"));
- ctx.emit(format!(" br i1 {c}, label %{body_l}, label %{end_l}"));
- } else {
- ctx.emit(format!(" br label %{body_l}"));
- }
- ctx.emit(format!("{body_l}:"));
- ctx.loop_end.push(end_l.clone());
- ctx.loop_continue.push(upd_l.clone());
- let (flow, r) = compile_block(ctx, body)?;
- ctx.loop_end.pop();
- ctx.loop_continue.pop();
- if let Some(r) = r {
- ret = Some(r);
- }
- if !flow {
- ctx.emit(format!(" br label %{upd_l}"));
- }
- ctx.emit(format!("{upd_l}:"));
- if let Some(u) = update {
- compile_block(ctx, std::slice::from_ref(u))?;
- }
- ctx.emit(format!(" br label %{cond_l}"));
- ctx.emit(format!("{end_l}:"));
- }
- Stmt::Break => {
- if let Some(e) = ctx.loop_end.last() {
- ctx.emit(format!(" br label %{e}"));
- return Ok((true, None));
- }
- }
- Stmt::Continue => {
- if let Some(c) = ctx.loop_continue.last() {
- ctx.emit(format!(" br label %{c}"));
- return Ok((true, None));
- }
- }
- Stmt::Inc { name, op } => {
- if let Some((t, reg)) = ctx.var_get(name) {
- let delta = if op == "++" { 1 } else { -1 };
- let t1 = ctx.reg("inc");
- ctx.emit(format!(" {t1} = load {}, ptr {reg}", t.llvm()));
- let t2 = ctx.reg("inc");
- ctx.emit(format!(" {t2} = add {} {t1}, {delta}", t.llvm()));
- ctx.emit(format!(" store {} {t2}, ptr {reg}", t.llvm()));
- }
- }
- Stmt::RefDecl { .. } => {}
- }
- }
- Ok((false, ret))
-}
-
-/// 调用语句:CIO::println/print → printf;其他调用(对象方法等)→ 正常 call。
-fn compile_call_stmt(ctx: &mut Ctx, e: &Expr) -> Result<(), String> {
- if let Expr::Call { qual, name, args } = e {
- if qual.as_deref() == Some("CIO") || qual.as_deref() == Some("IO") {
- if name == "println" || name == "print" {
- return emit_printf(ctx, args, name == "println");
- }
- }
- }
- // 其他调用:编译(副作用保留)
- compile_expr(ctx, e)?;
- Ok(())
-}
-
-fn emit_printf(ctx: &mut Ctx, args: &[Expr], newline: bool) -> Result<(), String> {
- // 拼格式串:字符串参数原样(转义 %),数字参数 → %ld / %lf
- let mut fmt = String::new();
- let mut call_args: Vec<(Ty, String)> = Vec::new();
- // println 参数空格分隔(print 直接拼接——与解释器一致)
- let sep = if newline { " " } else { "" };
- let mut first = true;
- for a in args {
- if !first {
- fmt.push_str(sep);
- }
- first = false;
- match a {
- Expr::Str(s) => {
- fmt.push_str(&s.replace('%', "%%"));
- }
- other => {
- let (ty, val) = compile_expr(ctx, other)?;
- match ty {
- Ty::I64 => fmt.push_str("%ld"),
- Ty::F64 => fmt.push_str("%lf"),
- Ty::Ptr => fmt.push_str("%p"),
- }
- call_args.push((ty, val));
- }
- }
- }
- if newline {
- fmt.push('\n'); // 真换行字节,由 str_const 转义成 \0A
- }
- let sc = ctx.str_const(&fmt);
- let mut call = format!(" call i32 (ptr, ...) @printf(ptr {sc}");
- for (ty, val) in &call_args {
- call.push_str(&format!(", {} {}", ty.llvm(), val));
- }
- call.push(')');
- ctx.emit(call);
- Ok(())
-}
-
-fn is_void_method(ctx: &Ctx, key: &str) -> bool {
- ctx.void_methods.contains(key)
-}
-fn coerce(ctx: &mut Ctx, val: String, from: Ty, to: Ty) -> String {
- if from == to {
- return val;
- }
- let r = ctx.reg("cv");
- match (from, to) {
- (Ty::I64, Ty::F64) => ctx.emit(format!(" {r} = sitofp i64 {val} to double")),
- (Ty::F64, Ty::I64) => ctx.emit(format!(" {r} = fptosi double {val} to i64")),
- _ => return val,
- }
- r
-}
-
-/// 属性所属类:base 是 this → 当前类;base 是对象变量 → 变量类型对应的类。
-/// 简化:从变量类型名推断(vars 表里对象变量以类名注册)。
-fn field_owner(ctx: &Ctx, base: &Expr) -> String {
- if let Expr::Var(vn) = base {
- if vn == "this" {
- // 当前类:找 this_reg 所在函数——用 classes 里第一个含该字段的类兜底
- // 更准确:compile_function 时记录当前类名
- if let Some(c) = &ctx.current_class {
- return c.clone();
- }
- }
- // 对象变量:vars 存 (Ty, reg),类型信息丢失——用 var_ty 表
- if let Some(t) = ctx.var_ty.get(vn) {
- return t.clone();
- }
- }
- String::new()
-}
-
-fn field_index(ctx: &Ctx, cls: &str, name: &str) -> Result<(Ty, u32), String> {
- let fields = ctx.classes.get(cls).ok_or_else(|| {
- format!("property {name} on unknown class {cls}")
- })?;
- for (i, (n, t)) in fields.iter().enumerate() {
- if n == name {
- return Ok((*t, i as u32));
- }
- }
- Err(format!("class {cls} has no field {name}"))
-}
-
-fn compile_expr(ctx: &mut Ctx, e: &Expr) -> Result<(Ty, String), String> {
- match e {
- Expr::Int(v) => Ok((Ty::I64, v.to_string())),
- Expr::Float(v) => Ok((Ty::F64, format!("{v:.17}"))),
- Expr::Var(name) => {
- if name == "this" {
- // this 指针
- match ctx.this_reg.clone() {
- Some(reg) => {
- let p = ctx.reg("thisv");
- ctx.emit(format!(" {p} = load ptr, ptr {reg}"));
- return Ok((Ty::Ptr, p));
- }
- None => return Err("this used outside a class method".to_string()),
- }
- }
- match ctx.var_get(name) {
- Some((t, reg)) => {
- let r = ctx.reg("v");
- ctx.emit(format!(" {r} = load {}, ptr {reg}", t.llvm()));
- Ok((t, r))
- }
- None => Err(format!("undefined variable {name}")),
- }
- }
- Expr::Unwrap { op, l } => {
- let (t, v) = compile_expr(ctx, l)?;
- let _ = op;
- Ok((t, v)) // get/cause 单值语义
- }
- Expr::BinOp { op, l, r } => {
- let (lt, lv) = compile_expr(ctx, l)?;
- let (rt, rv) = compile_expr(ctx, r)?;
- let ty = if lt == Ty::F64 || rt == Ty::F64 { Ty::F64 } else { Ty::I64 };
- let lv = coerce(ctx, lv, lt, ty);
- let rv = coerce(ctx, rv, rt, ty);
- let out = ctx.reg("b");
- match op.as_str() {
- "+" | "-" | "*" | "/" | "%" => {
- let opc = match (op.as_str(), ty) {
- ("+", Ty::I64) => "add", ("+", Ty::F64) => "fadd",
- ("-", Ty::I64) => "sub", ("-", Ty::F64) => "fsub",
- ("*", Ty::I64) => "mul", ("*", Ty::F64) => "fmul",
- ("/", Ty::I64) => "sdiv", ("/", Ty::F64) => "fdiv",
- ("%", Ty::I64) => "srem", ("%", Ty::F64) => "frem",
- _ => "add",
- };
- ctx.emit(format!(" {out} = {opc} {} {lv}, {rv}", ty.llvm()));
- Ok((ty, out))
- }
- "==" | "!=" | "<" | ">" | "<=" | ">=" => {
- let cond_op = match op.as_str() {
- "==" => "eq", "!=" => "ne", "<" => "slt", ">" => "sgt",
- "<=" => "sle", ">=" => "sge", _ => "eq",
- };
- let cmp_op = if ty == Ty::F64 {
- match cond_op {
- "eq" => "oeq", "ne" => "one", "slt" => "olt", "sgt" => "ogt",
- "sle" => "ole", "sge" => "oge", _ => "oeq",
- }
- } else {
- cond_op
- };
- if ty == Ty::F64 {
- let t1 = ctx.reg("cmp");
- ctx.emit(format!(" {t1} = fcmp {cmp_op} double {lv}, {rv}"));
- ctx.emit(format!(" {out} = zext i1 {t1} to i64"));
- } else {
- let t1 = ctx.reg("cmp");
- ctx.emit(format!(" {t1} = icmp {cmp_op} i64 {lv}, {rv}"));
- ctx.emit(format!(" {out} = zext i1 {t1} to i64"));
- }
- Ok((Ty::I64, out))
- }
- _ => Err(format!("unsupported operator {op}")),
- }
- }
- Expr::Call { qual, name, args } => {
- // 对象方法调用:qual 是对象变量 → 类名_方法名 + 传 this
- let obj_this = if let Some(q) = qual {
- ctx.var_ty.get(q).cloned()
- } else {
- None
- };
- let key = if let Some(owner) = &obj_this {
- format!("{owner}_{name}")
- } else if let Some(q) = qual {
- format!("{q}_{name}")
- } else if ctx.funcs.contains_key(name) {
- name.clone()
- } else {
- format!("main_{name}")
- };
- let fname = format!("@{key}");
- let (rt, _) = ctx
- .funcs
- .get(&key)
- .cloned()
- .unwrap_or((Ty::I64, vec![]));
- if !ctx.funcs.contains_key(&key) {
- // 未知方法:拒绝(printf 消息 + exit)——后端子集边界
- let msg = ctx.str_const(&format!("stream {key} refuses: no method {name}\n"));
- ctx.emit(format!(" call i32 (ptr, ...) @printf(ptr {msg})"));
- ctx.emit(" call void @exit(i32 1)");
- ctx.emit(" unreachable");
- return Ok((rt, "0".into()));
- }
- let call_reg = ctx.reg("call");
- let is_void = rt == Ty::I64 && is_void_method(ctx, &key);
- let mut call = if is_void {
- format!(" call void {fname}(")
- } else {
- format!(" {call_reg} = call {} {fname}(", rt.llvm())
- };
- let mut first = true;
- // 对象方法调用:qual 是对象变量 → 传 this 指针
- if obj_this.is_some() {
- if let Some(q) = qual {
- if let Some((Ty::Ptr, reg)) = ctx.var_get(q) {
- let p = ctx.reg("thisp");
- ctx.emit(format!(" {p} = load ptr, ptr {reg}"));
- call.push_str(&format!("ptr {p}"));
- first = false;
- }
- }
- }
- for a in args {
- let (at, av) = compile_expr(ctx, a)?;
- if !first {
- call.push_str(", ");
- }
- first = false;
- call.push_str(&format!("{} {av}", at.llvm()));
- }
- call.push(')');
- ctx.emit(call);
- let _ = qual;
- if is_void {
- Ok((Ty::I64, "0".into()))
- } else {
- Ok((rt, call_reg))
- }
- }
- Expr::New { cls, args } => {
- // new Class(args...) → malloc 对象 + 调 __init__(this, args...)
- let fields = ctx
- .classes
- .get(cls)
- .cloned()
- .unwrap_or_default();
- let size = fields.len().max(1) * 8;
- let obj = ctx.reg("obj");
- ctx.emit(format!(" {obj} = call ptr @malloc(i64 {size})"));
- // 调 __init__(若存在)
- let init_key = format!("{cls}___init__");
- if ctx.funcs.contains_key(&init_key) {
- let call_reg = ctx.reg("init");
- let mut call = format!(" call void @{init_key}(ptr {obj}");
- for a in args {
- let (at, av) = compile_expr(ctx, a)?;
- call.push_str(&format!(", {} {av}", at.llvm()));
- }
- call.push(')');
- ctx.emit(call);
- let _ = call_reg;
- }
- Ok((Ty::Ptr, obj))
- }
- Expr::Prop { base, name } => {
- // 对象属性读:obj.name / this.name → GEP load
- let (bt, bv) = compile_expr(ctx, base)?;
- if bt != Ty::Ptr {
- return Err(format!("property access on non-object: {name}"));
- }
- // 从 this 指针寄存器取值(若 base 是 this 变量)
- let bval = if let Expr::Var(vn) = base.as_ref() {
- if vn == "this" {
- match ctx.this_reg.clone() {
- Some(reg) => {
- let p = ctx.reg("thp");
- ctx.emit(format!(" {p} = load ptr, ptr {reg}"));
- p
- }
- None => bv,
- }
- } else {
- bv
- }
- } else {
- bv
- };
- // 找字段类型
- let cls = field_owner(ctx, base);
- let (ft, idx) = field_index(ctx, &cls, name)?;
- let gp = ctx.reg("gep");
- ctx.emit(format!(
- " {gp} = getelementptr %struct.{cls}, ptr {bval}, i32 0, i32 {idx}"
- ));
- let v = ctx.reg("fld");
- ctx.emit(format!(" {v} = load {}, ptr {gp}", ft.llvm()));
- Ok((ft, v))
- }
- _ => Err(format!("LLVM backend does not support this expression yet: {e:?}")),
- }
-}
-
-// 占位(保持模块结构)
diff --git a/rust/crates/bbb-llvm/tests/objects.rs b/rust/crates/bbb-llvm/tests/objects.rs
deleted file mode 100644
index 665f469..0000000
--- a/rust/crates/bbb-llvm/tests/objects.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-//! bbb-llvm 集成测试:编译 → clang → 运行 → 输出断言。
-//! 需要系统 clang(与 CLI shell build 相同路径)。
-
-use std::process::Command;
-
-use bbb_llvm::compile;
-use bbb_syntax::parser::parse_source;
-
-fn build_and_run(src: &str) -> String {
- let (prog, errs) = parse_source(src);
- assert!(errs.is_empty(), "parse errors: {errs:?}");
- let ir = compile(&prog).expect("IR generation failed");
-
- // 目录含进程+时间戳,避免并行测试冲突
- let nanos = std::time::SystemTime::now()
- .duration_since(std::time::UNIX_EPOCH)
- .map(|d| d.subsec_nanos())
- .unwrap_or(0);
- let dir = std::env::temp_dir().join(format!("bbb-llvm-test-{}-{nanos}", std::process::id()));
- std::fs::create_dir_all(&dir).unwrap();
- let ir_path = dir.join("out.ll");
- let bin_path = dir.join("a.out");
- std::fs::write(&ir_path, &ir).unwrap();
-
- let status = Command::new("clang")
- .arg(&ir_path)
- .arg("-o")
- .arg(&bin_path)
- .status()
- .expect("clang not found");
- assert!(status.success(), "clang failed");
-
- let out = Command::new(&bin_path).output().expect("run failed");
- let _ = std::fs::remove_dir_all(&dir);
- String::from_utf8_lossy(&out.stdout).to_string()
-}
-
-#[test]
-fn llvm_class_new_fields_and_methods() {
- let src = r#"
-program main;
-Class Student {
- void __init__(age int, solve double) {
- this::age = age;
- this::solve = solve;
- }
- int getAge() { res this::age; }
- double getSolve() { res this::solve; }
- void bump() { this::age = this::age + 1; }
- int age;
- double solve;
-}
-Main {
- void exec() {
- Student s = new Student(12, 1.2);
- CIO::println("age:", get s::getAge());
- CIO::println("solve:", get s::getSolve());
- s::bump();
- CIO::println("after bump:", get s::getAge());
- s::age = 20;
- CIO::println("direct:", s::age);
- }
-}
-"#;
- let out = build_and_run(src);
- assert!(out.contains("age: 12"), "got: {out}");
- assert!(out.contains("solve: 1.200000"), "got: {out}");
- assert!(out.contains("after bump: 13"), "got: {out}");
- assert!(out.contains("direct: 20"), "got: {out}");
-}
-
-#[test]
-fn llvm_class_method_call_with_args() {
- let src = r#"
-program main;
-Class Calc {
- int add(a int, b int) { res a + b; }
- int mul(a int, b int) { res a * b; }
-}
-Main {
- void exec() {
- Calc c = new Calc();
- CIO::println("sum:", get c::add(2, 3));
- CIO::println("prod:", get c::mul(4, 5));
- }
-}
-"#;
- let out = build_and_run(src);
- assert!(out.contains("sum: 5"), "got: {out}");
- assert!(out.contains("prod: 20"), "got: {out}");
-}
diff --git a/rust/crates/bbb-syntax/Cargo.toml b/rust/crates/bbb-syntax/Cargo.toml
deleted file mode 100644
index f70e648..0000000
--- a/rust/crates/bbb-syntax/Cargo.toml
+++ /dev/null
@@ -1,8 +0,0 @@
-[package]
-name = "bbb-syntax"
-version.workspace = true
-edition.workspace = true
-license.workspace = true
-description = "BiuBiuBiu 语法层:手写 AST + 词法器 + 解析器"
-
-[dependencies]
diff --git a/rust/crates/bbb-syntax/src/ast.rs b/rust/crates/bbb-syntax/src/ast.rs
deleted file mode 100644
index cdda54c..0000000
--- a/rust/crates/bbb-syntax/src/ast.rs
+++ /dev/null
@@ -1,132 +0,0 @@
-//! BiuBiuBiu AST(完全原生手写版)。
-//!
-//! 完全原生手写。设计贴合旧 C 实现(src/parser.c)的节点形态,并做了枚举化
-//! 与所有权整理:
-//! - 表达式/语句/声明全枚举,无 NULL 指针(Option 表达可选);
-//! - 字符串用 `String`(解析期零拷贝优化留给后续:arena 字符串池);
-//! - 语法面覆盖 examples/01-17:流签名/分叉/类/need/注解/智能引用/
-//! 数组字面量/多返回值/二进制库流。
-
-/// 程序 = 声明序列(Main 流单独存放,语义上总在最后执行)。
-#[derive(Debug, Clone, PartialEq)]
-pub struct Program {
- pub kind: String, // "main" | "utils"
- pub decls: Vec,
- pub main: Option,
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum Decl {
- /// `const int x = 10;`(顶层 → Constantstream)
- Const { name: String, ty: String, init: Expr },
- /// `need value/function/stream/Class X;`
- Need { kind: String, name: String },
- /// `Stream Name { members }` — 签名流
- StreamSig { name: String, members: Vec, annos: Vec },
- /// `Stream Name & "lib.so" { members }` — 二进制库流
- StreamBin { name: String, file: String, members: Vec, annos: Vec },
- /// `Class Name implements A, B { members }` — 类(流的分叉),可实现接口
- Class { name: String, members: Vec, annos: Vec, implements: Vec },
- /// `Interface Name { 方法签名 }` — 接口(流的一种:只有签名的方法集合)
- Interface { name: String, members: Vec, annos: Vec },
- /// `Sig Name { members }` — 分叉实现
- Fork { sig: String, name: String, members: Vec, annos: Vec },
-}
-
-/// Main 流:`Main { void exec() {...} ... }`,仅方法。
-#[derive(Debug, Clone, PartialEq)]
-pub struct MainDecl {
- pub methods: Vec,
-}
-
-/// 流/类成员:字段与方法可任意交错;字段支持逗号分隔 `int x, y;`。
-#[derive(Debug, Clone, PartialEq)]
-pub enum Member {
- Field { ty: String, names: Vec },
- Method(Method),
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub struct Method {
- pub ret: String, // void / int / int[] / Hero / T[]...
- pub name: String,
- pub params: Vec ,
- pub body: Vec, // 空 = 签名(分号结尾)
- pub annos: Vec, // @read/@write/@call/@ucall
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub struct Param {
- pub name: String,
- pub ty: String, // 基类型(含流名/类名)
- pub is_arr: bool,
- /// 智能引用参数:`& name type`
- pub ref_perm: Option,
- pub ref_follow: Option,
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum Stmt {
- If { cond: Expr, then: Vec, els: Option> },
- While { cond: Expr, body: Vec },
- For { init: Option>, cond: Option, update: Option>, body: Vec },
- Break,
- Continue,
- /// `res expr;` / `res a, b, c;`(多值 → 数组)/ `ref "reason";`
- Ret { kind: RetKind, values: Vec },
- /// 变量声明与赋值:`int x = e;` / `ALL x = e;` / `x = e;` / `x += e;` /
- /// `const int x = e;` / `thread int x = e;` / `this::attr = e;` / `a[i] = e;`
- Assign {
- vtype: Option, // Some = 声明(含 "ALL"/"const"/"thread" 变体)
- is_const: bool,
- is_thread: bool,
- target: AssignTarget,
- op: String,
- value: Expr,
- },
- /// `&perm follow base name = &lvalue;` — 智能引用声明
- RefDecl { perm: String, follow: String, base: String, name: String, init: Expr },
- /// `i++;` / `i--;`
- Inc { name: String, op: String },
- /// 表达式语句:裸调用 `add(1,2);`、`a[i];` 等
- Expr(Expr),
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum RetKind {
- Res, // res = respond
- Ref, // ref = refuse
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum AssignTarget {
- Var(String),
- Index { base: Box, idx: Box },
- Prop { base: Box, name: String },
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum Expr {
- Int(i64),
- Float(f64),
- Str(String),
- Char(u8),
- Bool(bool),
- /// 变量引用(含 `this`)
- Var(String),
- /// `qual::name(args)`(qual=Some)或裸调用 `name(args)`(qual=None)
- Call { qual: Option, name: String, args: Vec },
- /// `obj.field` 属性访问(对象属性用 Objstream)
- Prop { base: Box, name: String },
- /// `a[i]` 索引
- Index { base: Box, idx: Box },
- BinOp { op: String, l: Box, r: Box },
- /// 前缀解包:`get X` / `cause X`
- Unwrap { op: String, l: Box },
- /// `new Class(args...)` → 分叉类流 + 自动 __init__
- New { cls: String, args: Vec },
- /// `new Type[expr]` → 数组字面量
- NewArray { ty: String, size: Box },
- /// `&lvalue` — 取址创建引用值(权限来自声明)
- RefOf(Box),
-}
diff --git a/rust/crates/bbb-syntax/src/lexer.rs b/rust/crates/bbb-syntax/src/lexer.rs
deleted file mode 100644
index 28c1b9f..0000000
--- a/rust/crates/bbb-syntax/src/lexer.rs
+++ /dev/null
@@ -1,335 +0,0 @@
-//! BiuBiuBiu 词法器(手写,Rust 版)。
-//!
-//! 规则对标旧 C 实现(src/lexer.c)+ examples/ 实际语法:
-//! - 关键字:program / Main / Stream / Class / const / thread / need /
-//! res / ref / get / cause / ALL / if / else / while / for / break /
-//! continue / new / this / 基础类型 / true / false
-//! - 注释:`//` 与 `/* */`;字符串 `"..."`(`\"` 转义);字符 `'x'`
-//! - 数字:int / float(`3.14`、`.5`、`1e3`)
-//! - 运算符:`::` `==` `!=` `<=` `>=` `&&` `||` `++` `--` `->` + 单字符集
-//!
-//! 设计目标(内存规划):Token 零堆分配——`kind: u8` + `len: u32` 引用
-//! 源切片,字符串内容不拷贝;行/列只在出错时按需计算(错误路径才扫描)。
-
-/// 词法错误:源位置 + 信息。
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct LexError {
- pub line: u32,
- pub col: u32,
- pub msg: &'static str,
-}
-
-impl std::fmt::Display for LexError {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}:{}: {}", self.line, self.col, self.msg)
- }
-}
-
-/// 源位置(行:列,1 起)。
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct Span {
- pub line: u32,
- pub col: u32,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[repr(u8)]
-pub enum TokenKind {
- Ident,
- Keyword,
- Int,
- Float,
- Str,
- Char,
- Op,
- Eof,
-}
-
-/// Token:零拷贝——`text` 是源字符串的切片。
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct Token<'a> {
- pub kind: TokenKind,
- pub text: &'a str,
- pub span: Span,
-}
-
-const KEYWORDS: &[&str] = &[
- "program", "Main", "Stream", "Class", "Interface", "implements", "const", "thread", "need",
- "res", "ref", "get", "cause", "ALL", "if", "else", "while", "for",
- "break", "continue", "new", "this",
- "void", "int", "float", "double", "string", "char", "bool",
- "true", "false",
-];
-
-const OPS2: &[&str] = &["==", "!=", "<=", ">=", "&&", "||", "::", "++", "--", "->"];
-
-fn is_kw(s: &str) -> bool {
- KEYWORDS.contains(&s) // 线性查找:29 个关键字,正确性优先
-}
-
-fn is_ident_start(c: u8) -> bool {
- c.is_ascii_alphabetic() || c == b'_'
-}
-
-fn is_ident_char(c: u8) -> bool {
- c.is_ascii_alphanumeric() || c == b'_'
-}
-
-fn is_op1(c: u8) -> bool {
- matches!(c, b'+' | b'-' | b'*' | b'/' | b'%' | b'<' | b'>' | b'=' | b'!'
- | b'&' | b'|' | b'.' | b':' | b',' | b';' | b'(' | b')'
- | b'{' | b'}' | b'[' | b']' | b'@')
-}
-
-fn is_digit(c: u8) -> bool {
- c.is_ascii_digit()
-}
-
-struct Scan<'a> {
- src: &'a [u8],
- pos: usize,
- line: u32,
- col: u32,
-}
-
-impl<'a> Scan<'a> {
- fn peek(&self, k: usize) -> Option {
- self.src.get(self.pos + k).copied()
- }
-
- fn advance(&mut self) -> Option {
- let c = self.src.get(self.pos).copied();
- if let Some(b) = c {
- self.pos += 1;
- if b == b'\n' {
- self.line += 1;
- self.col = 1;
- } else {
- self.col += 1;
- }
- }
- c
- }
-
- fn span(&self) -> Span {
- Span { line: self.line, col: self.col }
- }
-}
-
-/// 把源码切成 Token 流。`tokens` 预先分配(容量即上限),零堆分配。
-pub fn tokenize<'a>(src: &'a str, tokens: &mut Vec>) -> Result<(), LexError> {
- let mut s = Scan { src: src.as_bytes(), pos: 0, line: 1, col: 1 };
- tokens.clear();
- loop {
- // 空白
- while matches!(s.peek(0), Some(b' ') | Some(b'\t') | Some(b'\r') | Some(b'\n')) {
- s.advance();
- }
- // 注释
- if s.peek(0) == Some(b'/') && s.peek(1) == Some(b'/') {
- while let Some(c) = s.advance() {
- if c == b'\n' {
- break;
- }
- }
- continue;
- }
- if s.peek(0) == Some(b'/') && s.peek(1) == Some(b'*') {
- let start = s.span();
- s.advance();
- s.advance();
- loop {
- if s.peek(0).is_none() {
- return Err(LexError { line: start.line, col: start.col, msg: "unterminated block comment /*" });
- }
- if s.peek(0) == Some(b'*') && s.peek(1) == Some(b'/') {
- s.advance();
- s.advance();
- break;
- }
- s.advance();
- }
- continue;
- }
- let start = s.span();
- let c = match s.peek(0) {
- None => {
- tokens.push(Token { kind: TokenKind::Eof, text: "", span: start });
- return Ok(());
- }
- Some(c) => c,
- };
-
- // 字符串
- if c == b'"' {
- s.advance();
- let begin = s.pos;
- loop {
- match s.peek(0) {
- None => return Err(LexError { line: start.line, col: start.col, msg: "unterminated string literal" }),
- Some(b'"') => {
- let end = s.pos;
- s.advance();
- tokens.push(Token { kind: TokenKind::Str, text: &src[begin..end], span: start });
- break;
- }
- Some(b'\\') => {
- s.advance();
- s.advance();
- }
- Some(_) => {
- s.advance();
- }
- }
- }
- continue;
- }
-
- // 字符
- if c == b'\'' {
- s.advance();
- let begin = s.pos;
- match s.peek(0) {
- None => return Err(LexError { line: start.line, col: start.col, msg: "unterminated character literal" }),
- Some(b'\\') => {
- s.advance();
- s.advance();
- }
- Some(_) => {
- s.advance();
- }
- }
- if s.peek(0) != Some(b'\'') {
- return Err(LexError { line: start.line, col: start.col, msg: "character literal must be exactly one character" });
- }
- s.advance();
- tokens.push(Token { kind: TokenKind::Char, text: &src[begin..s.pos - 1], span: start });
- continue;
- }
-
- // 数字
- if is_digit(c) || (c == b'.' && s.peek(1).map(is_digit).unwrap_or(false)) {
- let begin = s.pos;
- let mut is_float = false;
- while let Some(d) = s.peek(0) {
- if is_digit(d) {
- s.advance();
- } else if d == b'.' && !is_float {
- is_float = true;
- s.advance();
- } else if (d == b'e' || d == b'E') && !is_float {
- is_float = true;
- s.advance();
- if matches!(s.peek(0), Some(b'+') | Some(b'-')) {
- s.advance();
- }
- } else {
- break;
- }
- }
- let kind = if is_float { TokenKind::Float } else { TokenKind::Int };
- tokens.push(Token { kind, text: &src[begin..s.pos], span: start });
- continue;
- }
-
- // 标识符 / 关键字
- if is_ident_start(c) {
- let begin = s.pos;
- while s.peek(0).map(is_ident_char).unwrap_or(false) {
- s.advance();
- }
- let word = &src[begin..s.pos];
- let kind = if is_kw(word) { TokenKind::Keyword } else { TokenKind::Ident };
- tokens.push(Token { kind, text: word, span: start });
- continue;
- }
-
- // 运算符
- let mut matched = false;
- for op in OPS2 {
- if s.src[s.pos..].starts_with(op.as_bytes()) {
- s.advance();
- s.advance();
- tokens.push(Token { kind: TokenKind::Op, text: op, span: start });
- matched = true;
- break;
- }
- }
- if matched {
- continue;
- }
- if is_op1(c) {
- s.advance();
- tokens.push(Token { kind: TokenKind::Op, text: &src[s.pos - 1..s.pos], span: start });
- continue;
- }
-
- return Err(LexError {
- line: start.line,
- col: start.col,
- msg: "unrecognized character",
- });
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- fn kinds(src: &str) -> Vec<(TokenKind, &str)> {
- let mut toks = Vec::new();
- tokenize(src, &mut toks).unwrap();
- toks.iter().map(|t| (t.kind, t.text)).collect()
- }
-
- #[test]
- fn hello() {
- let toks = kinds(r#"program main; Main { void exec() { CIO::println("hi"); } }"#);
- assert_eq!(toks[0], (TokenKind::Keyword, "program"));
- assert!(toks.contains(&(TokenKind::Keyword, "Main")));
- assert!(toks.contains(&(TokenKind::Keyword, "void")));
- assert!(toks.contains(&(TokenKind::Str, "hi")));
- assert_eq!(toks.last().unwrap().0, TokenKind::Eof);
- }
-
- #[test]
- fn numbers() {
- let toks = kinds("1 3.14 .5 1e3");
- assert_eq!(toks[0], (TokenKind::Int, "1"));
- assert_eq!(toks[1], (TokenKind::Float, "3.14"));
- assert_eq!(toks[2], (TokenKind::Float, ".5"));
- assert_eq!(toks[3], (TokenKind::Float, "1e3"));
- }
-
- #[test]
- fn ops() {
- let toks = kinds("a::b == c && d <= e;");
- let ops: Vec<&str> = toks.iter().filter(|t| t.0 == TokenKind::Op).map(|t| t.1).collect();
- assert_eq!(ops, vec!["::", "==", "&&", "<=", ";"]);
- }
-
- #[test]
- fn comment_and_string() {
- let src = r#"// line
-CIO::println("a\"b"); /* block
-comment */ x"#;
- let toks = kinds(src);
- assert!(toks.contains(&(TokenKind::Str, "a\\\"b")));
- assert!(toks.contains(&(TokenKind::Ident, "x")));
- }
-
- #[test]
- fn unclosed_string_err() {
- let mut toks = Vec::new();
- let err = tokenize("CIO::println(\"oops);", &mut toks).unwrap_err();
- assert_eq!(err.msg, "unterminated string literal");
- }
-
- #[test]
- fn line_col() {
- let mut toks = Vec::new();
- let err = tokenize("a = 1;\nb = \"x;\n", &mut toks).unwrap_err();
- assert_eq!(err.line, 2);
- assert_eq!(err.col, 5); // `b = "` — 引号在第 5 列
- }
-}
diff --git a/rust/crates/bbb-syntax/src/lib.rs b/rust/crates/bbb-syntax/src/lib.rs
deleted file mode 100644
index ad480b3..0000000
--- a/rust/crates/bbb-syntax/src/lib.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-//! bbb-syntax — BiuBiuBiu 语法层(完全原生手写)。
-//!
-//! - `ast`:手写 AST(唯一事实源)
-//! - `parser`:手写解析器(语法面覆盖 examples/01-17)
-//! - `lexer`:手写词法器(零拷贝 token)
-
-pub mod ast;
-pub mod lexer;
-pub mod parser;
-
-pub use ast::*;
-pub use lexer::{LexError, Span, Token, TokenKind, tokenize};
-pub use parser::{ParseError, Parser, parse_source};
diff --git a/rust/crates/bbb-syntax/src/parser.rs b/rust/crates/bbb-syntax/src/parser.rs
deleted file mode 100644
index ee07515..0000000
--- a/rust/crates/bbb-syntax/src/parser.rs
+++ /dev/null
@@ -1,1244 +0,0 @@
-//! BioLang 解析器(完全原生手写)。
-//!
-//! 语法面以旧 C 实现(src/parser.c)与 examples/01-17 为准:
-//! - 表达式:优先级 字面量/引用/调用/索引/属性链 → 一元(get/cause/&/new)
-//! → 算术(+ - * / %) → 比较(== != < > <= >=) → 逻辑(&& ||)
-//! - 语句:if/while/for/break/continue/res/ref/ALL/const/thread/
-//! &引用声明/类型声明/赋值/自增自减/调用/表达式语句
-//! - 声明:program/const/need/Stream(签名|二进制库)/Class/Main/分叉
-//! - 成员:方法(体或签名)+ 字段(逗号分隔)+ type T; 泛型 + 泛型风格
-//! - 参数:`name type`(名字在前),可带 `&perm follow` 引用修饰
-//!
-//! 错误处理:收集全部错误(ParseError 带行列),恢复策略为推进 token,
-//! 保证不死循环;返回的 Program 在 errors 非空时不可信(调用方检查)。
-
-use crate::ast::*;
-use crate::lexer::{Token, TokenKind};
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct ParseError {
- pub line: u32,
- pub col: u32,
- pub msg: String,
-}
-
-impl std::fmt::Display for ParseError {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}:{}: {}", self.line, self.col, self.msg)
- }
-}
-
-const TYPE_NAMES: &[&str] = &["int", "float", "double", "string", "char", "bool"];
-const PERMS: &[&str] = &["r", "w", "m", "rw", "rm", "wm", "rwm"];
-const FOLLOWS: &[&str] = &["u", "f", "a", "t"];
-const ASSIGN_OPS: &[&str] = &["=", "+=", "-=", "*=", "/=", "%="];
-const METHOD_ANNOS: &[&str] = &["read", "write", "call", "ucall"];
-const DECL_ANNOS: &[&str] = &["onlyread", "unfork"];
-
-fn is_type_name(s: &str) -> bool {
- TYPE_NAMES.contains(&s)
-}
-
-pub struct Parser<'a> {
- toks: &'a [Token<'a>],
- pos: usize,
- pub errors: Vec,
-}
-
-impl<'a> Parser<'a> {
- pub fn new(toks: &'a [Token<'a>]) -> Self {
- Parser { toks, pos: 0, errors: Vec::new() }
- }
-
- // ---- 游标 ----
-
- fn peek(&self, k: usize) -> &'a Token<'a> {
- let i = (self.pos + k).min(self.toks.len().saturating_sub(1));
- &self.toks[i]
- }
-
- fn next(&mut self) -> &'a Token<'a> {
- let t = self.peek(0);
- if self.pos < self.toks.len().saturating_sub(1) {
- self.pos += 1;
- }
- t
- }
-
- fn at_eof(&self) -> bool {
- self.peek(0).kind == TokenKind::Eof
- }
-
- fn is_op(&self, k: usize, op: &str) -> bool {
- let t = self.peek(k);
- t.kind == TokenKind::Op && t.text == op
- }
-
- fn at_kw(&self, kw: &str) -> bool {
- let t = self.peek(0);
- t.kind == TokenKind::Keyword && t.text == kw
- }
-
- fn eat_op(&mut self, op: &str) -> bool {
- if self.is_op(0, op) {
- self.next();
- true
- } else {
- false
- }
- }
-
- fn error(&mut self, msg: impl Into) {
- let t = self.peek(0);
- self.errors.push(ParseError { line: t.span.line, col: t.span.col, msg: msg.into() });
- }
-
- fn expect_op(&mut self, op: &str) -> bool {
- if self.eat_op(op) {
- true
- } else {
- self.error(format!("expected '{0}', got '{1}'", op, self.peek(0).text));
- false
- }
- }
-
- fn expect_id(&mut self) -> String {
- let t = self.peek(0);
- if t.kind == TokenKind::Ident || (t.kind == TokenKind::Keyword && t.text != "program") {
- self.next();
- t.text.to_string()
- } else {
- self.error(format!("expected identifier, got '{0}'", t.text));
- String::new()
- }
- }
-
- /// 方法名:关键字 `new` 允许(如 Array::new / Obj::new)。
- fn expect_method_name(&mut self) -> String {
- if self.at_kw("new") {
- self.next();
- return "new".to_string();
- }
- self.expect_id()
- }
-
- fn err_if(&mut self, cond: bool, msg: impl Into) {
- if cond {
- self.error(msg);
- }
- }
-
- // ---- 顶层 ----
-
- pub fn parse_program(&mut self) -> Program {
- let mut kind = String::new();
- let mut decls = Vec::new();
- let mut main = None;
-
- while !self.at_eof() && self.errors.len() < 50 {
- if self.at_kw("program") {
- self.next();
- let k = self.expect_id();
- self.expect_op(";");
- kind = k;
- continue;
- }
- if self.at_kw("const") {
- self.next();
- let ty = self.expect_id(); // 类型(int/string/...)
- let name = self.expect_id();
- let init = if self.eat_op("=") { self.parse_expr() } else { Expr::Int(0) };
- self.expect_op(";");
- decls.push(Decl::Const { name, ty, init });
- continue;
- }
- if self.at_kw("need") {
- self.next();
- let k = self.next().text.to_string();
- let name = self.expect_id();
- if self.is_op(0, "{") {
- self.skip_block();
- self.eat_op(";");
- } else {
- self.expect_op(";");
- }
- self.err_if(!matches!(k.as_str(), "value" | "function" | "stream" | "Stream" | "Class"),
- format!("need only supports value/function/stream/Class, got '{k}'"));
- decls.push(Decl::Need { kind: k, name });
- continue;
- }
- if self.at_kw("Stream") {
- self.next();
- let name = self.expect_id();
- if self.eat_op("&") {
- let t = self.peek(0);
- let file = match t.kind {
- TokenKind::Str | TokenKind::Ident => self.next().text.to_string(),
- _ => { self.error("expected binary library file name (string or identifier)"); String::new() }
- };
- let members = self.parse_members();
- let annos = self.parse_decl_annos();
- decls.push(Decl::StreamBin { name, file, members, annos });
- } else {
- let members = self.parse_members();
- let annos = self.parse_decl_annos();
- decls.push(Decl::StreamSig { name, members, annos });
- }
- continue;
- }
- if self.at_kw("Class") {
- self.next();
- let name = self.expect_id();
- let mut implements = Vec::new();
- if self.at_kw("implements") {
- self.next();
- implements.push(self.expect_id());
- while self.eat_op(",") {
- implements.push(self.expect_id());
- }
- }
- let members = self.parse_members();
- let annos = self.parse_decl_annos();
- decls.push(Decl::Class { name, members, annos, implements });
- continue;
- }
- if self.at_kw("Interface") {
- self.next();
- let name = self.expect_id();
- let members = self.parse_members();
- let annos = self.parse_decl_annos();
- decls.push(Decl::Interface { name, members, annos });
- continue;
- }
- if self.at_kw("Main") {
- self.next();
- self.expect_op("{");
- let methods = self.parse_methods_until("}");
- self.expect_op("}");
- main = Some(MainDecl { methods });
- continue;
- }
- if self.peek(0).kind == TokenKind::Ident {
- let sig = self.next().text.to_string();
- let name = self.expect_id();
- let members = self.parse_members();
- let annos = self.parse_decl_annos();
- decls.push(Decl::Fork { sig, name, members, annos });
- continue;
- }
- self.error(format!("cannot parse top-level declaration '{0}'", self.peek(0).text));
- self.next(); // 推进防死循环
- }
- Program { kind, decls, main }
- }
-
- /// 跳过 { ... } 块(need 的细节假设块)。
- fn skip_block(&mut self) {
- if !self.eat_op("{") {
- return;
- }
- let mut depth = 1;
- while depth > 0 && !self.at_eof() {
- if self.eat_op("{") {
- depth += 1;
- } else if self.eat_op("}") {
- depth -= 1;
- } else {
- self.next();
- }
- }
- }
-
- // ---- 成员(流/类公共) ----
-
- /// 成员解析:方法与字段任意交错,直到 }。
- /// - `void m(params) {}` / `void m(params);`(签名)
- /// - `int m() {...}` / `int x, y;` / `int[] a;`
- /// - `type T;` 泛型占位
- /// - `T n;` / `T[] a;` / `T m() {...}` 泛型风格
- fn parse_members(&mut self) -> Vec {
- let mut out = Vec::new();
- self.expect_op("{");
- while !self.is_op(0, "}") && !self.at_eof() {
- let t = self.peek(0);
- let is_prim_kw = t.kind == TokenKind::Keyword && is_type_name(t.text);
- if t.kind == TokenKind::Keyword && t.text == "void" {
- self.next();
- let name = self.expect_id();
- let params = self.parse_params();
- let (body, _is_sig) = self.parse_method_tail();
- let annos = self.parse_method_annos();
- out.push(Member::Method(Method { ret: "void".into(), name, params, body, annos }));
- continue;
- }
- // 泛型占位:`type T;`(type 是关键字语义,词法器按 ident 处理)
- if t.kind == TokenKind::Ident && t.text == "type" {
- self.next();
- self.expect_id();
- self.expect_op(";");
- continue;
- }
- if t.kind == TokenKind::Ident || is_prim_kw {
- let ty0 = self.next().text.to_string();
- let mut ty = ty0.clone();
- let is_arr = self.eat_arr_suffix(&mut ty);
- if is_arr {
- // T[] a;(字段)或 T[] m() {...}(数组返回方法)
- if (self.peek(0).kind == TokenKind::Ident || self.peek(0).kind == TokenKind::Keyword)
- && self.is_op(1, "(") {
- let name = self.expect_id();
- let params = self.parse_params();
- let (body, _is_sig) = self.parse_method_tail();
- let annos = self.parse_method_annos();
- out.push(Member::Method(Method { ret: ty, name, params, body, annos }));
- } else {
- let names = self.parse_field_names();
- out.push(Member::Field { ty, names });
- }
- continue;
- }
- // T m() {...} — 泛型/基类型返回方法(lookahead: 名字 + (;get/cause 等关键字可作方法名)
- if (self.peek(0).kind == TokenKind::Ident || self.peek(0).kind == TokenKind::Keyword)
- && self.is_op(1, "(") {
- let name = self.expect_id();
- let params = self.parse_params();
- let (body, _is_sig) = self.parse_method_tail();
- let annos = self.parse_method_annos();
- out.push(Member::Method(Method { ret: ty, name, params, body, annos }));
- continue;
- }
- // 字段:T x, y; 或(基类型)int x;
- let names = self.parse_field_names();
- out.push(Member::Field { ty, names });
- continue;
- }
- self.error(format!("cannot parse stream/class member '{0}'", t.text));
- self.next();
- }
- self.expect_op("}");
- out
- }
-
- /// Main 流专用:只有方法。
- fn parse_methods_until(&mut self, end: &str) -> Vec {
- let mut out = Vec::new();
- while !self.is_op(0, end) && !self.at_eof() {
- let t = self.peek(0);
- let (ret, name) = if t.kind == TokenKind::Keyword && t.text == "void" {
- self.next();
- ("void".to_string(), self.expect_id())
- } else if (t.kind == TokenKind::Ident || t.kind == TokenKind::Keyword) && is_type_name(t.text) {
- let mut ty = self.next().text.to_string();
- self.eat_arr_suffix(&mut ty);
- (ty, self.expect_id())
- } else {
- self.error(format!("expected method (return type + name), got '{0}'", t.text));
- self.next();
- continue;
- };
- let params = self.parse_params();
- let (body, _sig) = self.parse_method_tail();
- let annos = self.parse_method_annos();
- out.push(Method { ret, name, params, body, annos });
- }
- out
- }
-
- fn parse_method_tail(&mut self) -> (Vec, bool) {
- if self.eat_op("{") {
- let stmts = self.parse_stmts_until("}");
- (stmts, false)
- } else {
- self.expect_op(";");
- (Vec::new(), true)
- }
- }
-
- fn eat_arr_suffix(&mut self, ty: &mut String) -> bool {
- if self.is_op(0, "[") {
- self.next();
- self.expect_op("]");
- ty.push_str("[]");
- true
- } else {
- false
- }
- }
-
- /// 字段名列表:`x, y;`(逗号分隔后跟分号)。
- fn parse_field_names(&mut self) -> Vec {
- let mut names = vec![self.expect_id()];
- while self.eat_op(",") {
- names.push(self.expect_id());
- }
- self.expect_op(";");
- names
- }
-
- /// 参数:`a int` / `a int[]` / `&perm follow a IO`
- fn parse_params(&mut self) -> Vec {
- let mut out = Vec::new();
- self.expect_op("(");
- while !self.is_op(0, ")") && !self.at_eof() {
- // 引用参数两种写法都接受(引用是一种类型,参数 = 名字在前 类型在后):
- // 标准:name &perm follow type e.g. `cio &r u CIO`
- // 兼容:&perm follow name type e.g. `&r f io IO`(旧 C 写法)
- let mut ref_perm: Option = None;
- let mut ref_follow: Option = None;
- let mut name = String::new();
- let mut ty = String::new();
- let mut is_arr = false;
-
- // 兼容旧写法:&perm follow name type(权限在最前)
- if self.is_op(0, "&") {
- self.next(); // &
- ref_perm = Some(self.expect_id());
- ref_follow = Some(self.expect_id());
- let t = self.peek(0);
- if t.kind == TokenKind::Ident || (t.kind == TokenKind::Keyword && t.text != "program") {
- name = self.next().text.to_string();
- } else {
- self.error(format!("cannot parse parameter '{0}'", t.text));
- self.next();
- if !self.eat_op(",") {
- break;
- }
- continue;
- }
- // 类型:基类型关键字(int/string/...)或任意标识符(流名/类名/泛型 T)
- if self.peek(0).kind == TokenKind::Ident
- || (self.peek(0).kind == TokenKind::Keyword && is_type_name(self.peek(0).text)) {
- ty = self.next().text.to_string();
- }
- // 无类型参数(旧 C 记为 void,解释器不区分):`push(v)` 合法
- } else {
- // 标准写法:name [&perm follow] type(名字在前,引用/类型在后)
- let t = self.peek(0);
- if t.kind == TokenKind::Ident || (t.kind == TokenKind::Keyword && t.text != "program") {
- name = self.next().text.to_string();
- } else {
- self.error(format!("cannot parse parameter '{0}'", t.text));
- self.next();
- if !self.eat_op(",") {
- break;
- }
- continue;
- }
- if self.eat_op("[") {
- self.expect_op("]");
- is_arr = true;
- }
- // 引用类型:name &perm follow type
- if self.eat_op("&") {
- ref_perm = Some(self.expect_id());
- ref_follow = Some(self.expect_id());
- if self.peek(0).kind == TokenKind::Ident
- || (self.peek(0).kind == TokenKind::Keyword && is_type_name(self.peek(0).text)) {
- ty = self.next().text.to_string();
- }
- } else if self.peek(0).kind == TokenKind::Ident
- || (self.peek(0).kind == TokenKind::Keyword && is_type_name(self.peek(0).text)) {
- ty = self.next().text.to_string();
- if self.eat_op("[") {
- self.expect_op("]");
- is_arr = true;
- }
- }
- }
- out.push(Param { name, ty, is_arr, ref_perm, ref_follow });
- if !self.eat_op(",") {
- break;
- }
- }
- self.expect_op(")");
- out
- }
-
- fn parse_method_annos(&mut self) -> Vec {
- let mut out = Vec::new();
- while self.is_op(0, "@") {
- self.next();
- let a = self.expect_id();
- self.err_if(!METHOD_ANNOS.contains(&a.as_str()),
- format!("unknown method annotation @{a} (only @read/@write/@call/@ucall)"));
- out.push(a);
- }
- out
- }
-
- fn parse_decl_annos(&mut self) -> Vec {
- let mut out = Vec::new();
- while self.is_op(0, "@") {
- self.next();
- let a = self.expect_id();
- self.err_if(!DECL_ANNOS.contains(&a.as_str()),
- format!("unknown stream annotation @{a} (only @onlyread/@unfork)"));
- out.push(a);
- }
- out
- }
-
- // ---- 语句 ----
-
- fn parse_stmts_until(&mut self, end: &str) -> Vec {
- let mut out = Vec::new();
- while !self.is_op(0, end) && !self.at_eof() {
- let before = self.errors.len();
- let s = self.parse_stmt();
- out.push(s);
- if self.errors.len() > before {
- // 出错时推进到分号或块结束,避免级联
- while !self.is_op(0, ";") && !self.is_op(0, end) && !self.at_eof() {
- self.next();
- }
- self.eat_op(";");
- }
- }
- if !self.at_eof() {
- self.next(); // 消费 end
- }
- out
- }
-
- fn parse_stmt(&mut self) -> Stmt {
- if self.at_kw("if") {
- return self.parse_if();
- }
- if self.at_kw("while") {
- return self.parse_while();
- }
- if self.at_kw("for") {
- return self.parse_for();
- }
- if self.at_kw("break") {
- self.next();
- self.expect_op(";");
- return Stmt::Break;
- }
- if self.at_kw("continue") {
- self.next();
- self.expect_op(";");
- return Stmt::Continue;
- }
- if self.at_kw("res") || self.at_kw("ref") {
- let kind = if self.at_kw("res") { RetKind::Res } else { RetKind::Ref };
- self.next();
- let mut values = Vec::new();
- if !self.is_op(0, ";") {
- values.push(self.parse_expr());
- while self.eat_op(",") {
- values.push(self.parse_expr());
- }
- }
- self.expect_op(";");
- return Stmt::Ret { kind, values };
- }
- if self.at_kw("const") || self.at_kw("thread") {
- let is_const = self.at_kw("const");
- let modif = self.next().text.to_string();
- let _ = &modif;
- // const/thread 后可跟 ALL 或类型(const int x = 10; / thread int x = 10;)
- let t = self.peek(0);
- if t.kind == TokenKind::Keyword && t.text == "ALL" {
- self.next();
- let name = self.expect_id();
- let value = self.parse_assign_rhs("=");
- return Stmt::Assign {
- vtype: Some("ALL".into()), is_const, is_thread: !is_const,
- target: AssignTarget::Var(name), op: "=".into(), value,
- };
- }
- let mut ty = self.expect_id();
- self.eat_arr_suffix(&mut ty);
- let name = self.expect_id();
- let value = if self.eat_op("=") { self.parse_expr() } else { Expr::Int(0) };
- self.expect_op(";");
- return Stmt::Assign {
- vtype: Some(ty), is_const, is_thread: !is_const,
- target: AssignTarget::Var(name), op: "=".into(), value,
- };
- }
- if self.at_kw("ALL") {
- self.next();
- let name = self.expect_id();
- let value = self.parse_assign_rhs("=");
- self.expect_op(";");
- return Stmt::Assign {
- vtype: Some("ALL".into()), is_const: false, is_thread: false,
- target: AssignTarget::Var(name), op: "=".into(), value,
- };
- }
- // 基类型声明:`int x = e;` / `int[] a = e;` / `string s;`(类型是关键字)
- if self.peek(0).kind == TokenKind::Keyword && is_type_name(self.peek(0).text) {
- let mut ty = self.next().text.to_string();
- self.eat_arr_suffix(&mut ty);
- let name = self.expect_id();
- let value = if self.eat_op("=") { self.parse_expr() } else { Expr::Int(0) };
- self.expect_op(";");
- return Stmt::Assign {
- vtype: Some(ty), is_const: false, is_thread: false,
- target: AssignTarget::Var(name), op: "=".into(), value,
- };
- }
- if self.is_op(0, "&") {
- return self.parse_ref_decl();
- }
- // this:: 开头的语句(this 是关键字):属性赋值/方法调用
- if self.peek(0).kind == TokenKind::Ident || self.at_kw("this") {
- return self.parse_ident_stmt();
- }
- self.error(format!("cannot parse statement '{0}'", self.peek(0).text));
- Stmt::Expr(Expr::Int(0))
- }
-
- fn parse_assign_rhs(&mut self, op: &str) -> Expr {
- if !self.eat_op(op) {
- self.error(format!("expected '{0}'", op));
- }
- self.parse_expr()
- }
-
- fn parse_if(&mut self) -> Stmt {
- self.next(); // if
- self.expect_op("(");
- let cond = self.parse_expr();
- self.expect_op(")");
- self.expect_op("{");
- let then = self.parse_stmts_until("}");
- let els = if self.at_kw("else") {
- self.next();
- if self.at_kw("if") {
- Some(vec![self.parse_if()])
- } else {
- self.expect_op("{");
- Some(self.parse_stmts_until("}"))
- }
- } else {
- None
- };
- Stmt::If { cond, then, els }
- }
-
- fn parse_while(&mut self) -> Stmt {
- self.next(); // while
- self.expect_op("(");
- let cond = self.parse_expr();
- self.expect_op(")");
- self.expect_op("{");
- let body = self.parse_stmts_until("}");
- Stmt::While { cond, body }
- }
-
- fn parse_for(&mut self) -> Stmt {
- self.next(); // for
- self.expect_op("(");
- let init = if self.is_op(0, ";") {
- self.next(); // 空 init,消费分号
- None
- } else {
- Some(Box::new(self.parse_stmt())) // 完整语句(含分号)
- };
- let cond = if self.is_op(0, ";") {
- self.next(); // 空 cond,消费分号
- None
- } else {
- let c = self.parse_expr();
- self.expect_op(";");
- Some(c)
- };
- // update 是完整语句(examples 写法 `k = k + 1;`);`i++` 无分号由 Inc 分支兼容
- let update = if self.is_op(0, ")") { None } else { Some(Box::new(self.parse_stmt())) };
- self.expect_op(")");
- self.expect_op("{");
- let body = self.parse_stmts_until("}");
- Stmt::For { init, cond, update, body }
- }
-
- /// `&perm follow base name = &lvalue;` — 智能引用声明。
- fn parse_ref_decl(&mut self) -> Stmt {
- self.next(); // &
- let perm = self.expect_id();
- self.err_if(!PERMS.contains(&perm.as_str()),
- format!("invalid reference permission '{perm}' (r/w/m stacks: r, w, m, rw, rm, wm, rwm)"));
- let follow = self.expect_id();
- self.err_if(!FOLLOWS.contains(&follow.as_str()),
- format!("invalid reference follow layer '{follow}' (expected u/f/a/t)"));
- let mut base = self.expect_id();
- self.eat_arr_suffix(&mut base);
- let name = self.expect_id();
- self.expect_op("=");
- let init = self.parse_expr();
- self.err_if(!matches!(init, Expr::RefOf(_)),
- "reference declaration requires & initializer (e.g. &rw u int p = &a[0];)");
- self.expect_op(";");
- Stmt::RefDecl { perm, follow, base, name, init }
- }
-
- /// 标识符开头的语句:声明/赋值/自增/索引/属性/调用。
- fn parse_ident_stmt(&mut self) -> Stmt {
- let t = self.peek(0);
-
- // int[] a = expr; / int[] a;(类型名 + [])
- if is_type_name(t.text) && self.is_op(1, "[") && self.is_op(2, "]") {
- self.next(); self.next(); self.next();
- let name = self.expect_id();
- let value = if self.eat_op("=") { self.parse_expr() } else { Expr::Int(0) };
- self.expect_op(";");
- return Stmt::Assign {
- vtype: Some("int[]".into()), is_const: false, is_thread: false,
- target: AssignTarget::Var(name), op: "=".into(), value,
- };
- }
- // int x = e; / int x;(类型名 + 标识符)
- if is_type_name(t.text) && self.peek(1).kind == TokenKind::Ident {
- let mut ty = self.next().text.to_string();
- self.eat_arr_suffix(&mut ty);
- let name = self.expect_id();
- let value = if self.eat_op("=") { self.parse_expr() } else { Expr::Int(0) };
- self.expect_op(";");
- return Stmt::Assign {
- vtype: Some(ty), is_const: false, is_thread: false,
- target: AssignTarget::Var(name), op: "=".into(), value,
- };
- }
- // 类名/流名类型声明:Box b = new Box(42); / Hero h;(第一个是 Ident,第二个是 Ident)
- if t.kind == TokenKind::Ident && self.peek(1).kind == TokenKind::Ident {
- let ty = self.next().text.to_string();
- let name = self.expect_id();
- let value = if self.eat_op("=") { self.parse_expr() } else { Expr::Int(0) };
- self.expect_op(";");
- return Stmt::Assign {
- vtype: Some(ty), is_const: false, is_thread: false,
- target: AssignTarget::Var(name), op: "=".into(), value,
- };
- }
- // i++; / i--;
- if self.is_op(1, "++") || self.is_op(1, "--") {
- let name = self.next().text.to_string();
- let op = self.next().text.to_string();
- if !self.is_op(0, ";") {
- // for update 子句里无分号
- return Stmt::Inc { name, op };
- }
- self.next();
- return Stmt::Inc { name, op };
- }
- // a[i] = v; / a[i] += v; / a[i]; 索引
- if self.is_op(1, "[") {
- let arr = self.next().text.to_string();
- self.next(); // [
- let idx = self.parse_expr();
- self.expect_op("]");
- let target = AssignTarget::Index {
- base: Box::new(Expr::Var(arr.clone())),
- idx: Box::new(idx.clone()),
- };
- if self.is_op(0, "=") || ASSIGN_OPS.contains(&self.peek(0).text) {
- let op = self.next().text.to_string();
- let value = self.parse_expr();
- self.expect_op(";");
- return Stmt::Assign { vtype: None, is_const: false, is_thread: false, target, op, value };
- }
- self.expect_op(";");
- return Stmt::Expr(Expr::Index { base: Box::new(Expr::Var(arr)), idx: Box::new(idx) });
- }
- // qual::name(...); / qual::attr = v; / qual::attr += v;
- if self.is_op(1, "::") {
- let qual = self.next().text.to_string();
- self.next(); // ::
- let nm = self.expect_method_name();
- if self.is_op(0, "=") || ASSIGN_OPS.contains(&self.peek(0).text) {
- let op = self.next().text.to_string();
- let value = self.parse_expr();
- self.expect_op(";");
- return Stmt::Assign {
- vtype: None, is_const: false, is_thread: false,
- target: AssignTarget::Prop { base: Box::new(Expr::Var(qual)), name: nm },
- op, value,
- };
- }
- self.expect_op("(");
- let mut args = Vec::new();
- while !self.is_op(0, ")") && !self.at_eof() {
- args.push(self.parse_expr());
- if !self.eat_op(",") {
- break;
- }
- }
- self.expect_op(")");
- self.expect_op(";");
- return Stmt::Expr(Expr::Call { qual: Some(qual), name: nm, args });
- }
- // x = e; / x += e;
- if self.is_op(1, "=") || ASSIGN_OPS.contains(&self.peek(1).text) {
- let name = self.next().text.to_string();
- let op = self.next().text.to_string();
- let value = self.parse_expr();
- self.expect_op(";");
- return Stmt::Assign {
- vtype: None, is_const: false, is_thread: false,
- target: AssignTarget::Var(name), op, value,
- };
- }
- // fname(args); 裸调用语句
- if self.is_op(1, "(") {
- let name = self.next().text.to_string();
- self.next(); // (
- let mut args = Vec::new();
- while !self.is_op(0, ")") && !self.at_eof() {
- args.push(self.parse_expr());
- if !self.eat_op(",") {
- break;
- }
- }
- self.expect_op(")");
- self.expect_op(";");
- return Stmt::Expr(Expr::Call { qual: None, name, args });
- }
- // 其他表达式语句
- let e = self.parse_expr();
- if !self.is_op(0, ";") && !self.is_op(0, ")") {
- self.error(format!("expected ';' after statement, got '{0}'", self.peek(0).text));
- } else {
- self.eat_op(";");
- }
- Stmt::Expr(e)
- }
-
- // ---- 表达式(Pratt 风格优先级) ----
-
- pub fn parse_expr(&mut self) -> Expr {
- self.parse_binop(0)
- }
-
- fn parse_binop(&mut self, min_bp: u8) -> Expr {
- let mut left = self.parse_unary();
- loop {
- let t = self.peek(0);
- if t.kind != TokenKind::Op {
- break;
- }
- let (bp, op) = match t.text {
- "||" => (1, "||"),
- "&&" => (2, "&&"),
- "==" | "!=" => (3, t.text),
- "<" | ">" | "<=" | ">=" => (4, t.text),
- "+" | "-" => (5, t.text),
- "*" | "/" | "%" => (6, t.text),
- _ => break,
- };
- if bp < min_bp {
- break;
- }
- self.next();
- let right = self.parse_binop(bp + 1);
- left = Expr::BinOp { op: op.to_string(), l: Box::new(left), r: Box::new(right) };
- }
- left
- }
-
- fn parse_unary(&mut self) -> Expr {
- let t = self.peek(0);
- if t.kind == TokenKind::Op && t.text == "&" {
- self.next();
- return Expr::RefOf(Box::new(self.parse_unary()));
- }
- self.parse_primary()
- }
-
- fn parse_primary(&mut self) -> Expr {
- let t = self.peek(0);
- match t.kind {
- TokenKind::Int => {
- self.next();
- let v = t.text.parse::().unwrap_or(0);
- self.parse_prop_chain(Expr::Int(v))
- }
- TokenKind::Float => {
- self.next();
- let v = t.text.parse::().unwrap_or(0.0);
- self.parse_prop_chain(Expr::Float(v))
- }
- TokenKind::Str => {
- self.next();
- self.parse_prop_chain(Expr::Str(t.text.to_string()))
- }
- TokenKind::Char => {
- self.next();
- self.parse_prop_chain(Expr::Char(decode_char(t.text)))
- }
- TokenKind::Keyword if t.text == "true" || t.text == "false" => {
- self.next();
- self.parse_prop_chain(Expr::Bool(t.text == "true"))
- }
- TokenKind::Keyword if t.text == "new" => {
- self.next();
- // new Type[expr] → 数组字面量;new Class(args) → Obj::new
- let cls = self.expect_id();
- if self.eat_op("[") {
- let size = self.parse_expr();
- self.expect_op("]");
- self.parse_prop_chain(Expr::NewArray { ty: cls, size: Box::new(size) })
- } else {
- self.expect_op("(");
- let mut args = Vec::new();
- while !self.is_op(0, ")") && !self.at_eof() {
- args.push(self.parse_expr());
- if !self.eat_op(",") {
- break;
- }
- }
- self.expect_op(")");
- self.parse_prop_chain(Expr::New { cls, args })
- }
- }
- TokenKind::Keyword if t.text == "get" || t.text == "cause" => {
- // 前缀解包:get/cause X;`get(...)`/`cause(...)` 是裸调用(方法名 get/cause)
- let op = t.text;
- if self.is_op(1, "(") {
- self.next();
- self.next(); // (
- let mut args = Vec::new();
- while !self.is_op(0, ")") && !self.at_eof() {
- args.push(self.parse_expr());
- if !self.eat_op(",") {
- break;
- }
- }
- self.expect_op(")");
- self.parse_prop_chain(Expr::Call { qual: None, name: op.to_string(), args })
- } else {
- let is_prefix = !(self.is_op(1, "::") || self.is_op(1, ".") || self.is_op(1, "["));
- if is_prefix {
- self.next();
- let l = self.parse_unary();
- self.parse_prop_chain(Expr::Unwrap { op: op.to_string(), l: Box::new(l) })
- } else {
- // get 作普通标识符(变量名等)
- self.next();
- self.parse_prop_chain(Expr::Var(op.to_string()))
- }
- }
- }
- TokenKind::Keyword if t.text == "this" => {
- self.next();
- self.ident_primary("this".to_string())
- }
- TokenKind::Keyword => {
- self.next();
- self.parse_prop_chain(Expr::Var(t.text.to_string()))
- }
- TokenKind::Ident => {
- let name = self.next().text.to_string();
- self.ident_primary(name)
- }
- TokenKind::Op if t.text == "(" => {
- self.next();
- let e = self.parse_expr();
- self.expect_op(")");
- self.parse_prop_chain(e)
- }
- TokenKind::Op if t.text == "-" => {
- // 一元负号(宽松支持;语法面未见,作扩展)
- self.next();
- let e = self.parse_unary();
- Expr::BinOp { op: "-".into(), l: Box::new(Expr::Int(0)), r: Box::new(e) }
- }
- _ => {
- self.error(format!("cannot parse expression '{0}'", t.text));
- self.next();
- Expr::Int(0)
- }
- }
- }
-
- /// 标识符开头的 primary:`name` / `qual::m(...)` / `qual::prop` /
- /// `name(...)` 裸调用 / `name[i]` 索引(this 关键字也走这里)。
- fn ident_primary(&mut self, name: String) -> Expr {
- if self.eat_op("::") {
- let mname = self.expect_method_name();
- if self.is_op(0, "(") {
- self.next();
- let mut args = Vec::new();
- while !self.is_op(0, ")") && !self.at_eof() {
- args.push(self.parse_expr());
- if !self.eat_op(",") {
- break;
- }
- }
- self.expect_op(")");
- self.parse_prop_chain(Expr::Call { qual: Some(name), name: mname, args })
- } else {
- // qual::name 属性访问(this::data)
- self.parse_prop_chain(Expr::Prop {
- base: Box::new(Expr::Var(name)),
- name: mname,
- })
- }
- } else if self.is_op(0, "(") {
- // 裸调用
- self.next();
- let mut args = Vec::new();
- while !self.is_op(0, ")") && !self.at_eof() {
- args.push(self.parse_expr());
- if !self.eat_op(",") {
- break;
- }
- }
- self.expect_op(")");
- self.parse_prop_chain(Expr::Call { qual: None, name, args })
- } else if self.is_op(0, "[") {
- // 索引读取
- self.next();
- let idx = self.parse_expr();
- self.expect_op("]");
- self.parse_prop_chain(Expr::Index { base: Box::new(Expr::Var(name)), idx: Box::new(idx) })
- } else {
- self.parse_prop_chain(Expr::Var(name))
- }
- }
-
- /// 后缀属性链:`obj.field.field...`(对象属性访问)。
- fn parse_prop_chain(&mut self, mut e: Expr) -> Expr {
- while self.is_op(0, ".") {
- self.next();
- let t = self.peek(0);
- if t.kind != TokenKind::Ident && t.kind != TokenKind::Keyword {
- self.error(format!("invalid property name '{0}'", t.text));
- self.next();
- break;
- }
- let name = self.next().text.to_string();
- e = Expr::Prop { base: Box::new(e), name };
- }
- e
- }
-}
-
-/// 字符字面量解码:`'x'` / `'\n'` / `'\\'` / `'\''` 等。
-fn decode_char(s: &str) -> u8 {
- match s {
- "\\n" => b'\n',
- "\\t" => b'\t',
- "\\r" => b'\r',
- "\\0" => 0,
- "\\\\" => b'\\',
- "\\'" => b'\'',
- "\\\"" => b'"',
- _ => s.as_bytes().first().copied().unwrap_or(0),
- }
-}
-
-/// 便捷入口:源码 → Program。errors 非空时解析失败。
-pub fn parse_source(src: &str) -> (Program, Vec) {
- let mut toks = Vec::new();
- match crate::lexer::tokenize(src, &mut toks) {
- Ok(()) => {}
- Err(e) => {
- let prog = Program { kind: String::new(), decls: Vec::new(), main: None };
- return (prog, vec![ParseError { line: e.line, col: e.col, msg: e.msg.to_string() }]);
- }
- }
- let mut p = Parser::new(&toks);
- let prog = p.parse_program();
- (prog, p.errors)
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- fn parse_ok(src: &str) -> Program {
- let (p, errs) = parse_source(src);
- assert!(errs.is_empty(), "parse errors: {errs:?}");
- p
- }
-
- #[test]
- fn hello() {
- let p = parse_ok("program main;\nMain { void exec() { CIO::println(\"hi\"); } }");
- assert_eq!(p.kind, "main");
- let m = p.main.unwrap();
- assert_eq!(m.methods[0].name, "exec");
- assert!(matches!(m.methods[0].body[0], Stmt::Expr(Expr::Call { ref qual, .. }) if qual.as_deref() == Some("CIO")));
- }
-
- #[test]
- fn requests_model() {
- let src = r#"
-program main;
-Stream Calc { int add(a int, b int); }
-Calc MyCalc { int add(a int, b int) { res a + b; } }
-Main {
- void exec() {
- ALL r = MyCalc::add(3, 4);
- ALL bad = MyCalc::div(1, 0);
- CIO::println("x", get r, cause bad);
- if (r) { CIO::println("ok"); } else { CIO::println("no"); }
- }
-}"#;
- let p = parse_ok(src);
- assert_eq!(p.decls.len(), 2);
- let m = p.main.unwrap();
- assert!(matches!(m.methods[0].body[0], Stmt::Assign { vtype: Some(ref v), .. } if v == "ALL"));
- assert!(matches!(m.methods[0].body[2], Stmt::Expr(Expr::Call { ref args, .. }) if args.len() == 3));
- }
-
- #[test]
- fn control_flow() {
- let src = r#"
-program main;
-Main {
- void exec() {
- ALL i = 1;
- while (i <= 10) { i = i + 1; }
- for (ALL k = 1; k <= 5; k = k + 1;) { }
- for (;;) { break; }
- if (i > 5) { } else if (i < 2) { } else { }
- }
-}"#;
- let p = parse_ok(src);
- let m = p.main.unwrap();
- assert!(matches!(m.methods[0].body[2], Stmt::For { .. }));
- assert!(matches!(m.methods[0].body[3], Stmt::For { cond: None, .. }));
- }
-
- #[test]
- fn smart_refs_and_threads() {
- let src = r#"
-program main;
-Calc Worker {
- void threadJob(n int) {
- thread int local_note = 0;
- &w a int wa = &local_note;
- Ref::write(wa, n * 2);
- res get Ref::read(ra);
- }
-}
-Main {
- void exec() {
- int counter = 0;
- &rwm t int p = &counter;
- ALL t1 = get Threads::spawn("factorial", 10);
- }
-}"#;
- let p = parse_ok(src);
- assert_eq!(p.decls.len(), 1);
- }
-
- #[test]
- fn classes_fields_and_arrays() {
- let src = r#"
-program main;
-Class Hero {
- void __init__(name string, hp int) { Obj::set(this, "name", name); }
- int getHp() { res 100; }
-}
-Main {
- void exec() {
- ALL h = new Hero("TAK", 88);
- int[] a = Solid::new().res;
- a[0] = 1;
- ALL x = a[0];
- CIO::println("hp =", h.hp);
- }
-}"#;
- let p = parse_ok(src);
- let m = p.main.unwrap();
- let exec = &m.methods[0].body;
- assert!(matches!(exec[1], Stmt::Assign { vtype: Some(ref v), .. } if v == "int[]"));
- // a[0] = 1; → Assign(target: Index)
- assert!(matches!(exec[2], Stmt::Assign { target: AssignTarget::Index { .. }, .. }));
- // ALL x = a[0]; → 右侧是 Index
- assert!(matches!(exec[3], Stmt::Assign { value: Expr::Index { .. }, .. }));
- }
-
- #[test]
- fn need_and_binary_stream() {
- let src = r#"
-program main;
-need value GREETING;
-need function greet;
-need stream IO;
-Stream m & "libm.so.6" { double sin(x double); }
-Main { void exec() { } }
-"#;
- let p = parse_ok(src);
- assert_eq!(p.decls.len(), 4);
- assert!(matches!(p.decls[3], Decl::StreamBin { ref file, .. } if file == "libm.so.6"));
- }
-
- #[test]
- fn annotations_and_phonebooth() {
- let src = r#"
-program main;
-Class Hero { void __init__() { } } @unfork
-Main {
- void exec() {
- }
- int fast() { res 1; } @call
-}"#;
- let p = parse_ok(src);
- assert!(matches!(p.decls[0], Decl::Class { ref annos, .. } if annos == &["unfork"]));
- let m = p.main.unwrap();
- assert_eq!(m.methods[1].annos, vec!["call"]);
- }
-
- #[test]
- fn new_array_literal() {
- let src = r#"
-program main;
-Main { void exec() { ALL a = new int[10]; ALL b = new Hero[3]; } }
-"#;
- let p = parse_ok(src);
- let m = p.main.unwrap();
- assert!(matches!(m.methods[0].body[0], Stmt::Assign { value: Expr::NewArray { ref ty, .. }, .. } if ty == "int"));
- assert!(matches!(m.methods[0].body[1], Stmt::Assign { value: Expr::NewArray { ref ty, .. }, .. } if ty == "Hero"));
- }
-
- #[test]
- fn multi_value_res() {
- let src = r#"
-program main;
-Main { void exec() { } int pair() { res 1, 2, 3; } }
-"#;
- let p = parse_ok(src);
- let m = p.main.unwrap();
- match &m.methods[1].body[0] {
- Stmt::Ret { kind: RetKind::Res, values } => assert_eq!(values.len(), 3),
- other => panic!("unexpected {other:?}"),
- }
- }
-
- #[test]
- fn parse_error_reported() {
- let (_p, errs) = parse_source("program main;\nMain { void exec() { x = ; } }");
- assert!(!errs.is_empty());
- assert!(errs[0].line >= 1);
- }
-
- #[test]
- fn generic_type_member() {
- let src = r#"
-program main;
-Class Box {
- type T;
- T n;
- T[] a;
- T get() { res n; }
-}
-Main { void exec() { } }
-"#;
- let p = parse_ok(src);
- let cls = &p.decls[0];
- if let Decl::Class { members, .. } = cls {
- // type T; 只注册名字不产生成员:T n; / T[] a; / T get() = 3 个
- assert_eq!(members.len(), 3);
- assert!(matches!(members[0], Member::Field { ref ty, .. } if ty == "T"));
- assert!(matches!(members[1], Member::Field { ref ty, .. } if ty == "T[]"));
- assert!(matches!(members[2], Member::Method(Method { ref ret, .. }) if ret == "T"));
- } else {
- panic!("expected class");
- }
- }
-}
diff --git a/rust/crates/bbb-syntax/tests/examples.rs b/rust/crates/bbb-syntax/tests/examples.rs
deleted file mode 100644
index d0d992d..0000000
--- a/rust/crates/bbb-syntax/tests/examples.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-//! 标准层回归:examples/01-17 + project 全部必须 parse 成功。
-//!
-//! 这是"标准层不改变"的硬验证:任何解析器改动导致 examples 解析失败
-//! 即回归失败。examples 目录位于仓库根(相对本 crate 的 ../examples)。
-
-use std::path::PathBuf;
-
-use bbb_syntax::parser::parse_source;
-
-fn examples_dir() -> PathBuf {
- // CARGO_MANIFEST_DIR = rust/crates/bbb-syntax → 上溯 3 级到仓库根
- PathBuf::from(env!("CARGO_MANIFEST_DIR"))
- .parent()
- .unwrap()
- .parent()
- .unwrap()
- .parent()
- .unwrap()
- .join("examples")
-}
-
-fn collect_bio_files() -> Vec {
- let mut out = Vec::new();
- let ex = examples_dir();
- let mut dirs = vec![ex.clone()];
- dirs.push(ex.join("project").join("src"));
- dirs.push(ex.join("project").join("utils"));
- for d in dirs {
- if let Ok(entries) = std::fs::read_dir(&d) {
- for e in entries.flatten() {
- let p = e.path();
- if p.extension().map(|x| x == "bio").unwrap_or(false) {
- out.push(p);
- }
- }
- }
- }
- out.sort();
- out
-}
-
-#[test]
-fn all_examples_parse() {
- let files = collect_bio_files();
- assert!(!files.is_empty(), "examples 目录为空?");
- let mut failed = Vec::new();
- for f in &files {
- let src = std::fs::read_to_string(f).expect("read");
- let (prog, errs) = parse_source(&src);
- if !errs.is_empty() {
- failed.push((f.display().to_string(), errs.clone()));
- }
- // 主程序必须声明了 kind
- if prog.kind.is_empty() && !errs.is_empty() {
- failed.push((f.display().to_string(), errs));
- }
- }
- assert!(failed.is_empty(), "解析失败:\n{}",
- failed.iter().map(|(f, es)| format!(
- "{f}: {}", es.iter().map(|e| e.to_string()).collect::>().join("; ")))
- .collect::>().join("\n"));
-}
-
-/// 每个示例必须声明 program main/utils(标准层契约)。
-#[test]
-fn examples_declare_program_kind() {
- let files = collect_bio_files();
- let mut bad = Vec::new();
- for f in &files {
- let src = std::fs::read_to_string(f).expect("read");
- let (prog, errs) = parse_source(&src);
- if errs.is_empty() && prog.kind.is_empty() {
- bad.push(f.display().to_string());
- }
- }
- assert!(bad.is_empty(), "缺少 program 声明:{bad:?}");
-}
diff --git a/rust/crates/bbb-vm/Cargo.toml b/rust/crates/bbb-vm/Cargo.toml
deleted file mode 100644
index 29d7315..0000000
--- a/rust/crates/bbb-vm/Cargo.toml
+++ /dev/null
@@ -1,10 +0,0 @@
-[package]
-name = "bbb-vm"
-version.workspace = true
-edition.workspace = true
-license.workspace = true
-description = "BioLang 解释器(M3):流注册表 + eval + 内置流"
-
-[dependencies]
-bbb-syntax = { path = "../bbb-syntax" }
-bbb-core = { path = "../bbb-core" }
diff --git a/rust/crates/bbb-vm/src/builtin.rs b/rust/crates/bbb-vm/src/builtin.rs
deleted file mode 100644
index 69cf3bf..0000000
--- a/rust/crates/bbb-vm/src/builtin.rs
+++ /dev/null
@@ -1,738 +0,0 @@
-//! 内置流(Rust 实现):CIO/SIO/FIO/Com/Time/Obj/Solid/Arrays。
-//!
-//! 方法表 `HashMap<(qual, method), fn>`。签名统一:
-//! `fn(&mut Interp, &[Value]) -> Outcome`。
-//! 实现原则:与旧 C builtin.c 语义一致(examples 期望输出为准)。
-
-use std::collections::HashMap;
-use std::fs;
-use std::time::Instant;
-
-use bbb_core::value::{Cause, Outcome, Tag, Value};
-
-use crate::interp::Interp;
-
-pub type BuiltinFn = fn(&mut Interp, &[Value]) -> Outcome;
-
-fn f(q: &'static str, m: &'static str, f: BuiltinFn) -> ((&'static str, &'static str), BuiltinFn) {
- ((q, m), f)
-}
-
-pub fn table() -> HashMap<(&'static str, &'static str), BuiltinFn> {
- let mut t = HashMap::new();
- for (k, v) in [
- // ---- CIO 控制台 ----
- f("CIO", "println", cio_println),
- f("CIO", "print", cio_print),
- f("CIO", "error", cio_error),
- f("IO", "println", cio_println),
- f("IO", "print", cio_print),
- // ---- SIO 字符串缓冲 ----
- f("SIO", "format", sio_format),
- f("SIO", "upper", sio_upper),
- f("SIO", "lower", sio_lower),
- f("SIO", "trim", sio_trim),
- f("SIO", "contains", sio_contains),
- f("SIO", "substring", sio_substring),
- f("SIO", "replace", sio_replace),
- f("SIO", "println", sio_println),
- f("SIO", "print", sio_print),
- f("SIO", "getln", sio_getln),
- // ---- FIO 文件 ----
- f("FIO", "writeFile", fio_write_file),
- f("FIO", "appendFile", fio_append_file),
- f("FIO", "readFile", fio_read_file),
- f("FIO", "exists", fio_exists),
- // ---- Com 计算 ----
- f("Com", "abs", com_abs),
- f("Com", "min", com_min),
- f("Com", "max", com_max),
- f("Com", "pow", com_pow),
- f("Com", "sqrt", com_sqrt),
- f("Com", "floor", com_floor),
- f("Com", "ceil", com_ceil),
- f("Com", "round", com_round),
- f("Com", "sign", com_sign),
- f("Com", "sin", com_sin),
- f("Com", "cos", com_cos),
- f("Com", "tan", com_tan),
- f("Com", "log", com_log),
- f("Com", "exp", com_exp),
- // ---- Time 定时器 ----
- f("Time", "start", time_start),
- f("Time", "sleep", time_sleep),
- f("Time", "elapsed", time_elapsed),
- f("Time", "fork", time_fork),
- f("Time", "reset", time_reset),
- // ---- Obj 对象 ----
- f("Obj", "set", obj_set),
- f("Obj", "get", obj_get),
- f("Obj", "call", obj_call),
- f("Obj", "new", obj_new),
- // ---- Solid 连续存储 ----
- f("Solid", "new", solid_new),
- f("Solid", "len", solid_len),
- f("Solid", "get", solid_get),
- f("Solid", "set", solid_set),
- f("Solid", "push", solid_push),
- f("Solid", "pop", solid_pop),
- f("Solid", "join", solid_join),
- f("Solid", "clear", solid_clear),
- // 裸数组(SolidData)复用 Solid 的数据方法
- f("SolidData", "len", solid_len),
- f("SolidData", "get", solid_get),
- f("SolidData", "set", solid_set),
- f("SolidData", "push", solid_push),
- f("SolidData", "pop", solid_pop),
- f("SolidData", "join", solid_join),
- f("SolidData", "clear", solid_clear),
- // ---- Arrays 集合 ----
- f("Arrays", "add", arrays_add),
- f("Arrays", "count", arrays_count),
- f("Arrays", "all", arrays_all),
- f("Arrays", "get", arrays_get),
- f("Arrays", "forget", arrays_forget),
- f("Arrays", "vector", arrays_vector),
- f("Arrays", "sort", arrays_sort),
- // ---- Threads 协作线程(顺序 join 式) ----
- f("Threads", "spawn", threads_spawn),
- f("Threads", "join", threads_join),
- f("Threads", "yield", threads_yield),
- f("Threads", "active", threads_active),
- f("Threads", "self", threads_self),
- // ---- Taskm 任务管理器 ----
- f("Taskm", "add", taskm_add),
- f("Taskm", "interval", taskm_interval),
- f("Taskm", "run", taskm_run),
- f("Taskm", "stop", taskm_stop),
- f("Taskm", "active", taskm_active),
- // ---- Ref 智能引用 ----
- f("Ref", "read", ref_read),
- f("Ref", "write", ref_write),
- f("Ref", "move", ref_move),
- f("Ref", "target", ref_target),
- f("Ref", "perm", ref_perm),
- ] {
- t.insert(k, v);
- }
- t
-}
-
-pub fn lookup(q: &str, m: &str) -> Option {
- // 静态表每次重建开销可忽略(21 条);或 once_cell——stdlib only,直接静态构造
- use std::sync::OnceLock;
- static T: OnceLock> = OnceLock::new();
- let t = T.get_or_init(table);
- t.get(&(q, m)).copied()
-}
-
-fn arg_str(interp: &mut Interp, a: &Value) -> String {
- interp.fmt_value(a)
-}
-
-fn res(v: Value) -> Outcome {
- Outcome::Res(v)
-}
-
-fn refn(interp: &mut Interp, msg: &str) -> Outcome {
- Outcome::Ref(Cause(interp.intern(msg)))
-}
-
-// ---- CIO ----
-
-fn cio_println(interp: &mut Interp, args: &[Value]) -> Outcome {
- let line = args.iter().map(|a| arg_str(interp, a)).collect::>().join(" ");
- interp.stdout.push_str(&line);
- interp.stdout.push('\n');
- res(Value::nil())
-}
-
-fn cio_print(interp: &mut Interp, args: &[Value]) -> Outcome {
- // print 直接拼接(无分隔),println 空格分隔——旧 C 行为(03 输出依赖)
- let line = args.iter().map(|a| arg_str(interp, a)).collect::();
- interp.stdout.push_str(&line);
- res(Value::nil())
-}
-
-fn cio_error(interp: &mut Interp, args: &[Value]) -> Outcome {
- let line = args.iter().map(|a| arg_str(interp, a)).collect::>().join(" ");
- interp.stdout.push_str(&line);
- interp.stdout.push('\n');
- res(Value::nil())
-}
-
-// ---- SIO ----
-
-fn sio_format(interp: &mut Interp, args: &[Value]) -> Outcome {
- let Some(fmt) = args.first() else { return refn(interp, "SIO::format requires a format string") };
- let fmt_s = arg_str(interp, fmt);
- let rest = &args[1..];
- let mut out = String::new();
- let mut it = fmt_s.chars().peekable();
- let mut ai = 0;
- while let Some(c) = it.next() {
- if c != '%' {
- out.push(c);
- continue;
- }
- match it.next() {
- Some('d') => {
- if ai < rest.len() {
- out.push_str(&format!("{}", rest[ai].as_int_or_num() as i64));
- }
- ai += 1;
- }
- Some('f') => {
- if ai < rest.len() {
- out.push_str(&format!("{}", rest[ai].as_int_or_num()));
- }
- ai += 1;
- }
- Some('s') => {
- if ai < rest.len() {
- out.push_str(&arg_str(interp, &rest[ai]));
- }
- ai += 1;
- }
- Some('%') => out.push('%'),
- Some(other) => {
- out.push('%');
- out.push(other);
- }
- None => out.push('%'),
- }
- }
- res(Value::string(interp.intern(&out)))
-}
-
-fn sio_upper(interp: &mut Interp, args: &[Value]) -> Outcome {
- let s = args.first().map(|a| arg_str(interp, a)).unwrap_or_default();
- res(Value::string(interp.intern(&s.to_uppercase())))
-}
-
-fn sio_lower(interp: &mut Interp, args: &[Value]) -> Outcome {
- let s = args.first().map(|a| arg_str(interp, a)).unwrap_or_default();
- res(Value::string(interp.intern(&s.to_lowercase())))
-}
-
-fn sio_trim(interp: &mut Interp, args: &[Value]) -> Outcome {
- let s = args.first().map(|a| arg_str(interp, a)).unwrap_or_default();
- res(Value::string(interp.intern(s.trim())))
-}
-
-fn sio_contains(interp: &mut Interp, args: &[Value]) -> Outcome {
- let hay = args.first().map(|a| arg_str(interp, a)).unwrap_or_default();
- let needle = args.get(1).map(|a| arg_str(interp, a)).unwrap_or_default();
- res(Value::boolean(hay.contains(&needle)))
-}
-
-fn sio_substring(interp: &mut Interp, args: &[Value]) -> Outcome {
- let s = args.first().map(|a| arg_str(interp, a)).unwrap_or_default();
- let start = args.get(1).map(|a| a.as_int_or_num() as usize).unwrap_or(0);
- let len = args.get(2).map(|a| a.as_int_or_num() as usize);
- let sub: String = s.chars().skip(start).take(len.unwrap_or(s.len())).collect();
- res(Value::string(interp.intern(&sub)))
-}
-
-fn sio_replace(interp: &mut Interp, args: &[Value]) -> Outcome {
- let s = args.first().map(|a| arg_str(interp, a)).unwrap_or_default();
- let from = args.get(1).map(|a| arg_str(interp, a)).unwrap_or_default();
- let to = args.get(2).map(|a| arg_str(interp, a)).unwrap_or_default();
- res(Value::string(interp.intern(&s.replace(&from, &to))))
-}
-
-fn sio_println(interp: &mut Interp, args: &[Value]) -> Outcome {
- let line = args.iter().map(|a| arg_str(interp, a)).collect::>().join(" ");
- interp.sio_buf.push_str(&line);
- interp.sio_buf.push('\n');
- res(Value::nil())
-}
-
-fn sio_print(interp: &mut Interp, args: &[Value]) -> Outcome {
- let line = args.iter().map(|a| arg_str(interp, a)).collect::();
- interp.sio_buf.push_str(&line);
- res(Value::nil())
-}
-
-fn sio_getln(interp: &mut Interp, _args: &[Value]) -> Outcome {
- if let Some(pos) = interp.sio_buf.find('\n') {
- let line: String = interp.sio_buf.drain(..pos + 1).collect();
- let line = line.trim_end_matches('\n').to_string();
- res(Value::string(interp.intern(&line)))
- } else {
- let all = std::mem::take(&mut interp.sio_buf);
- res(Value::string(interp.intern(&all)))
- }
-}
-
-// ---- FIO ----
-
-fn fio_write_file(interp: &mut Interp, args: &[Value]) -> Outcome {
- let path = args.first().map(|a| arg_str(interp, a)).unwrap_or_default();
- let content = args.get(1).map(|a| arg_str(interp, a)).unwrap_or_default();
- match fs::write(&path, content) {
- Ok(()) => res(Value::boolean(true)),
- Err(e) => refn(interp, &format!("FIO::writeFile failed: {e}")),
- }
-}
-
-fn fio_append_file(interp: &mut Interp, args: &[Value]) -> Outcome {
- let path = args.first().map(|a| arg_str(interp, a)).unwrap_or_default();
- let content = args.get(1).map(|a| arg_str(interp, a)).unwrap_or_default();
- match fs::OpenOptions::new().append(true).create(true).open(&path)
- .and_then(|mut f| std::io::Write::write_all(&mut f, content.as_bytes())) {
- Ok(()) => res(Value::boolean(true)),
- Err(e) => refn(interp, &format!("FIO::appendFile failed: {e}")),
- }
-}
-
-fn fio_read_file(interp: &mut Interp, args: &[Value]) -> Outcome {
- let path = args.first().map(|a| arg_str(interp, a)).unwrap_or_default();
- match fs::read_to_string(&path) {
- Ok(s) => res(Value::string(interp.intern(&s))),
- Err(e) => refn(interp, &format!("FIO::readFile failed: {e}")),
- }
-}
-
-fn fio_exists(interp: &mut Interp, args: &[Value]) -> Outcome {
- let path = args.first().map(|a| arg_str(interp, a)).unwrap_or_default();
- res(Value::int(std::path::Path::new(&path).exists() as i64))
-}
-
-// ---- Com ----
-
-fn num1(interp: &mut Interp, a: &Value) -> Result {
- if matches!(a.tag(), Tag::Int | Tag::Num) {
- Ok(a.as_int_or_num())
- } else {
- Err(refn(interp, "Com requires a numeric argument"))
- }
-}
-
-fn com_abs(interp: &mut Interp, args: &[Value]) -> Outcome {
- match num1(interp, args.first().unwrap_or(&Value::nil())) {
- Ok(v) => res(Value::num(v.abs())),
- Err(e) => e,
- }
-}
-
-fn com_min(interp: &mut Interp, args: &[Value]) -> Outcome {
- let a = match num1(interp, args.first().unwrap_or(&Value::nil())) { Ok(v) => v, Err(e) => return e };
- let b = match num1(interp, args.get(1).unwrap_or(&Value::nil())) { Ok(v) => v, Err(e) => return e };
- res(Value::num(a.min(b)))
-}
-
-fn com_max(interp: &mut Interp, args: &[Value]) -> Outcome {
- let a = match num1(interp, args.first().unwrap_or(&Value::nil())) { Ok(v) => v, Err(e) => return e };
- let b = match num1(interp, args.get(1).unwrap_or(&Value::nil())) { Ok(v) => v, Err(e) => return e };
- res(Value::num(a.max(b)))
-}
-
-fn com_pow(interp: &mut Interp, args: &[Value]) -> Outcome {
- let a = match num1(interp, args.first().unwrap_or(&Value::nil())) { Ok(v) => v, Err(e) => return e };
- let b = match num1(interp, args.get(1).unwrap_or(&Value::nil())) { Ok(v) => v, Err(e) => return e };
- res(Value::num(a.powf(b)))
-}
-
-fn com_sqrt(interp: &mut Interp, args: &[Value]) -> Outcome {
- match num1(interp, args.first().unwrap_or(&Value::nil())) {
- Ok(v) => res(Value::num(v.sqrt())),
- Err(e) => e,
- }
-}
-
-fn com_floor(interp: &mut Interp, args: &[Value]) -> Outcome {
- match num1(interp, args.first().unwrap_or(&Value::nil())) {
- Ok(v) => res(Value::num(v.floor())),
- Err(e) => e,
- }
-}
-
-fn com_ceil(interp: &mut Interp, args: &[Value]) -> Outcome {
- match num1(interp, args.first().unwrap_or(&Value::nil())) {
- Ok(v) => res(Value::num(v.ceil())),
- Err(e) => e,
- }
-}
-
-fn com_round(interp: &mut Interp, args: &[Value]) -> Outcome {
- match num1(interp, args.first().unwrap_or(&Value::nil())) {
- Ok(v) => res(Value::num(v.round())),
- Err(e) => e,
- }
-}
-
-fn com_sign(interp: &mut Interp, args: &[Value]) -> Outcome {
- match num1(interp, args.first().unwrap_or(&Value::nil())) {
- Ok(v) => res(Value::int(v.signum() as i64)),
- Err(e) => e,
- }
-}
-
-fn com_sin(interp: &mut Interp, args: &[Value]) -> Outcome {
- match num1(interp, args.first().unwrap_or(&Value::nil())) {
- Ok(v) => res(Value::num(v.sin())),
- Err(e) => e,
- }
-}
-
-fn com_cos(interp: &mut Interp, args: &[Value]) -> Outcome {
- match num1(interp, args.first().unwrap_or(&Value::nil())) {
- Ok(v) => res(Value::num(v.cos())),
- Err(e) => e,
- }
-}
-
-fn com_tan(interp: &mut Interp, args: &[Value]) -> Outcome {
- match num1(interp, args.first().unwrap_or(&Value::nil())) {
- Ok(v) => res(Value::num(v.tan())),
- Err(e) => e,
- }
-}
-
-fn com_log(interp: &mut Interp, args: &[Value]) -> Outcome {
- match num1(interp, args.first().unwrap_or(&Value::nil())) {
- Ok(v) => res(Value::num(v.ln())),
- Err(e) => e,
- }
-}
-
-fn com_exp(interp: &mut Interp, args: &[Value]) -> Outcome {
- match num1(interp, args.first().unwrap_or(&Value::nil())) {
- Ok(v) => res(Value::num(v.exp())),
- Err(e) => e,
- }
-}
-
-// ---- Time ----
-
-fn time_start(interp: &mut Interp, _args: &[Value]) -> Outcome {
- interp.timers.insert(0, Instant::now());
- res(Value::nil())
-}
-
-fn time_sleep(_interp: &mut Interp, args: &[Value]) -> Outcome {
- let ms = args.first().map(|a| a.as_int_or_num()).unwrap_or(0.0);
- std::thread::sleep(std::time::Duration::from_millis(ms as u64));
- res(Value::nil())
-}
-
-fn time_elapsed(interp: &mut Interp, args: &[Value]) -> Outcome {
- let id = args.first().map(|a| a.as_int_or_num() as u32).unwrap_or(0);
- match interp.timers.get(&id) {
- Some(t) => {
- let secs = t.elapsed().as_secs_f64();
- res(Value::num(secs))
- }
- None => refn(interp, "Time: timer not started"),
- }
-}
-
-fn time_fork(interp: &mut Interp, _args: &[Value]) -> Outcome {
- interp.timer_seq += 1;
- let id = interp.timer_seq;
- interp.timers.insert(id, Instant::now());
- res(Value::int(id as i64))
-}
-
-fn time_reset(interp: &mut Interp, args: &[Value]) -> Outcome {
- let id = args.first().map(|a| a.as_int_or_num() as u32).unwrap_or(0);
- if id == 0 {
- return refn(interp, "Time refused: first timer (thread default) cannot be reset; use Time::fork()");
- }
- interp.timers.insert(id, Instant::now());
- res(Value::nil())
-}
-
-// ---- Obj ----
-
-fn obj_set(interp: &mut Interp, args: &[Value]) -> Outcome {
- let Some(obj) = args.first() else { return refn(interp, "Obj::set requires an object") };
- let (Tag::Obj | Tag::Arr) = obj.tag() else { return refn(interp, "Obj::set first argument is not an object") };
- let h = obj.as_handle();
- let key = args.get(1).map(|a| arg_str(interp, a)).unwrap_or_default();
- let val = args.get(2).copied().unwrap_or(Value::nil());
- interp.obj_prop_set(h, &key, val);
- res(Value::nil())
-}
-
-fn obj_get(interp: &mut Interp, args: &[Value]) -> Outcome {
- let Some(obj) = args.first() else { return refn(interp, "Obj::get requires an object") };
- let (Tag::Obj | Tag::Arr) = obj.tag() else { return refn(interp, "Obj::get first argument is not an object") };
- let h = obj.as_handle();
- let key = args.get(1).map(|a| arg_str(interp, a)).unwrap_or_default();
- match interp.obj_prop_get(h, &key) {
- Some(v) => res(v),
- None => refn(interp, "missing attribute"),
- }
-}
-
-fn obj_call(interp: &mut Interp, args: &[Value]) -> Outcome {
- let Some(obj) = args.first() else { return refn(interp, "Obj::call requires an object") };
- let (Tag::Obj | Tag::Arr) = obj.tag() else { return refn(interp, "Obj::call first argument is not an object") };
- let h = obj.as_handle();
- let name = args.get(1).map(|a| arg_str(interp, a)).unwrap_or_default();
- let rest = args[2..].to_vec();
- interp.invoke_on_obj(h, &name, rest)
-}
-
-fn obj_new(interp: &mut Interp, args: &[Value]) -> Outcome {
- let cls = args.first().map(|a| arg_str(interp, a)).unwrap_or_default();
- let rest = args[1..].to_vec();
- res(interp.new_class(&cls, rest))
-}
-
-// ---- Solid ----
-
-fn solid_new(interp: &mut Interp, _args: &[Value]) -> Outcome {
- let h = interp.solid_new(Vec::new());
- res(Value::obj(h))
-}
-
-fn solid_data_h(interp: &mut Interp, args: &[Value]) -> Result {
- let Some(a) = args.first() else { return Err(refn(interp, "Solid method requires a storage handle")) };
- let (Tag::Obj | Tag::Arr) = a.tag() else { return Err(refn(interp, "Solid argument is not a handle")) };
- let h = a.as_handle();
- let cls = interp.obj_class(h);
- if cls != "Solid" && cls != "SolidData" {
- return Err(refn(interp, "Solid argument is not a Solid instance"));
- }
- Ok(h)
-}
-
-fn solid_len(interp: &mut Interp, args: &[Value]) -> Outcome {
- let h = match solid_data_h(interp, args) { Ok(h) => h, Err(e) => return e };
- res(Value::int(interp.solid_data(h).len() as i64))
-}
-
-fn solid_get(interp: &mut Interp, args: &[Value]) -> Outcome {
- let h = match solid_data_h(interp, args) { Ok(h) => h, Err(e) => return e };
- let i = match args.get(1).ok_or(0).and_then(|a| Ok(a.as_int_or_num() as i64)) {
- Ok(i) => i,
- Err(_) => 0,
- };
- let data = interp.solid_data(h);
- if i < 0 || i as usize >= data.len() {
- return refn(interp, "index out of bounds");
- }
- res(data[i as usize])
-}
-
-fn solid_set(interp: &mut Interp, args: &[Value]) -> Outcome {
- let h = match solid_data_h(interp, args) { Ok(h) => h, Err(e) => return e };
- let i = args.get(1).map(|a| a.as_int_or_num() as i64).unwrap_or(0);
- let v = args.get(2).copied().unwrap_or(Value::nil());
- let len = interp.solid_data(h).len() as i64;
- if i < 0 || i >= len {
- return refn(interp, "index out of bounds");
- }
- interp.solid_set(h, i as usize, v);
- res(Value::nil())
-}
-
-fn solid_push(interp: &mut Interp, args: &[Value]) -> Outcome {
- let h = match solid_data_h(interp, args) { Ok(h) => h, Err(e) => return e };
- let v = args.get(1).copied().unwrap_or(Value::nil());
- interp.solid_push(h, v);
- res(Value::nil())
-}
-
-fn solid_pop(interp: &mut Interp, args: &[Value]) -> Outcome {
- let h = match solid_data_h(interp, args) { Ok(h) => h, Err(e) => return e };
- let data_h = match interp.solid_data_handle(h) {
- Some(dh) => dh,
- None => return refn(interp, "no data"),
- };
- match interp.objects[data_h as usize].fields.pop() {
- Some(v) => res(v),
- None => refn(interp, "pop from empty"),
- }
-}
-
-fn solid_join(interp: &mut Interp, args: &[Value]) -> Outcome {
- let h = match solid_data_h(interp, args) { Ok(h) => h, Err(e) => return e };
- let sep = args.get(1).map(|a| arg_str(interp, a)).unwrap_or_default();
- let data = interp.solid_data(h);
- let parts: Vec = data.iter().map(|v| interp.fmt_value(v)).collect();
- res(Value::string(interp.intern(&parts.join(&sep))))
-}
-
-fn solid_clear(interp: &mut Interp, args: &[Value]) -> Outcome {
- let h = match solid_data_h(interp, args) { Ok(h) => h, Err(e) => return e };
- if let Some(dh) = interp.solid_data_handle(h) {
- interp.objects[dh as usize].fields.clear();
- }
- res(Value::nil())
-}
-
-// ---- Arrays ----
-
-fn arrays_add(interp: &mut Interp, args: &[Value]) -> Outcome {
- if let Some(a) = args.first() {
- if let Tag::Obj | Tag::Arr = a.tag() {
- interp.arrays.push(a.as_handle());
- }
- }
- res(Value::nil())
-}
-
-fn arrays_count(interp: &mut Interp, _args: &[Value]) -> Outcome {
- res(Value::int(interp.arrays.len() as i64))
-}
-
-fn arrays_all(interp: &mut Interp, _args: &[Value]) -> Outcome {
- // 返回整个注册表(所有 Array/Vector 实例组成的数组)
- let vals: Vec = interp.arrays.iter().map(|h| Value::obj(*h)).collect();
- let dh = interp.objs_data_handle(vals);
- res(Value::arr(dh))
-}
-
-fn arrays_get(interp: &mut Interp, args: &[Value]) -> Outcome {
- let i = args.first().map(|a| a.as_int_or_num() as usize).unwrap_or(0);
- match interp.arrays.get(i) {
- Some(h) => res(Value::obj(*h)),
- None => refn(interp, "Arrays: index out of bounds"),
- }
-}
-
-fn arrays_forget(interp: &mut Interp, args: &[Value]) -> Outcome {
- if let Some(a) = args.first() {
- if let Tag::Obj | Tag::Arr = a.tag() {
- let h = a.as_handle();
- interp.arrays.retain(|x| *x != h);
- }
- }
- res(Value::nil())
-}
-
-fn arrays_vector(interp: &mut Interp, _args: &[Value]) -> Outcome {
- let v = interp.new_class("Vector", Vec::new());
- res(v)
-}
-
-fn arrays_sort(interp: &mut Interp, args: &[Value]) -> Outcome {
- // Arrays::sort(arr) — 原地排序数组(内部调用数组的 __sort__ 内部方法)
- let Some(a) = args.first() else {
- return refn(interp, "Arrays::sort requires an array argument");
- };
- let (Tag::Obj | Tag::Arr) = a.tag() else {
- return refn(interp, "Arrays::sort argument is not an array object");
- };
- let h = a.as_handle();
- // 通过内部方法 __sort__ 原地排序(数组对象 → Solid 数据)
- let out = interp.invoke_on_obj(h, "__sort__", Vec::new());
- if out.is_refused() {
- // 裸数组(SolidData)直接排
- if let Some(dh) = interp.solid_data_handle(h) {
- interp.sort_data(dh);
- return res(Value::nil());
- }
- return refn(interp, "Arrays::sort failed: not a sortable array");
- }
- res(Value::nil())
-}
-
-// ---- Threads ----
-
-fn threads_spawn(interp: &mut Interp, args: &[Value]) -> Outcome {
- let name = args.first().map(|a| interp.fmt_value(a)).unwrap_or_default();
- let rest = args[1..].to_vec();
- interp.thread_seq += 1;
- let id = interp.thread_seq;
- let def_name = interp.reg.find_bare_method(&name).map(|(d, _)| d.name.clone());
- interp.threads.push(crate::interp::ThreadTask { id, name, def_name, args: rest, done: None });
- Outcome::Res(Value::int(id as i64))
-}
-
-fn threads_join(interp: &mut Interp, args: &[Value]) -> Outcome {
- let id = args.first().map(|a| a.as_int_or_num() as u32).unwrap_or(0);
- // 先执行其它未完成任务(逆序——11 期望 thread 2 先打印),再执行目标
- let others: Vec = interp.threads.iter().filter(|t| t.done.is_none() && t.id != id).map(|t| t.id).rev().collect();
- for oid in others {
- interp.run_thread(oid);
- }
- interp.run_thread(id)
-}
-
-fn threads_yield(_interp: &mut Interp, _args: &[Value]) -> Outcome {
- Outcome::Res(Value::nil()) // 顺序执行:no-op
-}
-
-fn threads_active(interp: &mut Interp, _args: &[Value]) -> Outcome {
- let n = interp.threads.iter().filter(|t| t.done.is_none()).count();
- Outcome::Res(Value::int(n as i64))
-}
-
-fn threads_self(interp: &mut Interp, _args: &[Value]) -> Outcome {
- Outcome::Res(Value::int(interp.running_thread.unwrap_or(0) as i64))
-}
-
-// ---- Taskm ----
-
-fn taskm_add(interp: &mut Interp, args: &[Value]) -> Outcome {
- threads_spawn(interp, args)
-}
-
-fn taskm_interval(_interp: &mut Interp, _args: &[Value]) -> Outcome {
- Outcome::Res(Value::nil())
-}
-
-fn taskm_run(interp: &mut Interp, _args: &[Value]) -> Outcome {
- let ids: Vec = interp.threads.iter().filter(|t| t.done.is_none()).map(|t| t.id).collect();
- for id in ids {
- interp.run_thread(id);
- }
- Outcome::Res(Value::nil())
-}
-
-fn taskm_stop(interp: &mut Interp, _args: &[Value]) -> Outcome {
- interp.threads.clear();
- Outcome::Res(Value::nil())
-}
-
-fn taskm_active(interp: &mut Interp, _args: &[Value]) -> Outcome {
- threads_active(interp, &[])
-}
-
-// ---- Ref ----
-
-fn ref_read(interp: &mut Interp, args: &[Value]) -> Outcome {
- let Some(a) = args.first() else { return refn(interp, "Ref::read requires a reference") };
- if a.tag() != Tag::Ref { return refn(interp, "Ref::read argument is not a reference"); }
- interp.ref_read(a.as_handle())
-}
-
-fn ref_write(interp: &mut Interp, args: &[Value]) -> Outcome {
- let Some(a) = args.first() else { return refn(interp, "Ref::write requires a reference") };
- if a.tag() != Tag::Ref { return refn(interp, "Ref::write argument is not a reference") };
- let v = args.get(1).copied().unwrap_or(Value::nil());
- match interp.ref_write(a.as_handle(), v) {
- Outcome::Res(_) => Outcome::Res(Value::nil()),
- // Ref::write 方法级拒绝消息带 "Ref refused: " 前缀(11 的 cause 输出)
- Outcome::Ref(_) => Outcome::Ref(Cause(interp.intern("Ref refused: reference is read-only, cannot write"))),
- }
-}
-
-fn ref_move(interp: &mut Interp, args: &[Value]) -> Outcome {
- let Some(a) = args.first() else { return refn(interp, "Ref::move requires a reference") };
- if a.tag() != Tag::Ref { return refn(interp, "Ref::move argument is not a reference") };
- interp.ref_move(a.as_handle())
-}
-
-fn ref_target(interp: &mut Interp, args: &[Value]) -> Outcome {
- let Some(a) = args.first() else { return refn(interp, "Ref::target requires a reference") };
- if a.tag() != Tag::Ref { return refn(interp, "Ref::target argument is not a reference") };
- let r = interp.refs[a.as_handle() as usize].clone();
- match &r.target {
- crate::interp::RefTarget::Var { name, .. } => Outcome::Res(Value::string(interp.intern(name))),
- crate::interp::RefTarget::ArrElem { index, .. } => Outcome::Res(Value::int(*index)),
- crate::interp::RefTarget::ObjProp { name, .. } => Outcome::Res(Value::string(interp.intern(name))),
- }
-}
-
-fn ref_perm(interp: &mut Interp, args: &[Value]) -> Outcome {
- let Some(a) = args.first() else { return refn(interp, "Ref::perm requires a reference") };
- if a.tag() != Tag::Ref { return refn(interp, "Ref::perm argument is not a reference") };
- let r = interp.refs[a.as_handle() as usize].clone();
- Outcome::Res(Value::string(interp.intern(&r.perm)))
-}
diff --git a/rust/crates/bbb-vm/src/dylib.rs b/rust/crates/bbb-vm/src/dylib.rs
deleted file mode 100644
index 4a8d08e..0000000
--- a/rust/crates/bbb-vm/src/dylib.rs
+++ /dev/null
@@ -1,174 +0,0 @@
-//! dylib — 跨平台动态库加载(二进制库流底层)。
-//!
-//! 平台适配:
-//! - Linux / Android:`dlopen` / `dlsym`(.so)
-//! - macOS / iOS:`dlopen` / `dlsym`(.dylib;dlopen 可用)
-//! - Windows:`LoadLibraryA` / `GetProcAddress`(.dll)
-//!
-//! 库名归一化:声明 `Stream m & "libm.so"` 时,按当前平台尝试
-//! 候选文件名(libm.so → libm.dylib → libm.dll / m.dll),
-//! 并保留原始名兜底。这样同一份 .bio 源码可以跨系统运行。
-
-use std::ffi::CString;
-use std::os::raw::c_char;
-
-/// 打开的库句柄(平台特定的不透明指针)。
-#[cfg(unix)]
-pub type LibHandle = *mut std::ffi::c_void;
-#[cfg(windows)]
-pub type LibHandle = *mut std::ffi::c_void;
-
-/// 平台库名候选:把声明的名字转成当前平台可能的真实文件名。
-/// 策略:原样优先 + 平台等价后缀 + 版本化后缀剥离(libm.so.6 → libm.so)。
-pub fn candidate_names(declared: &str) -> Vec {
- let mut out = Vec::new();
- let base = declared.trim();
- if base.is_empty() {
- return out;
- }
- // 1. 原样
- out.push(base.to_string());
-
- // 2. 剥离版本后缀:libm.so.6 → libm.so;foo.1.2 → foo(仅 .so/.dylib/.dll 前的版本号)
- if let Some((stem, ver)) = split_version(base) {
- if !ver.is_empty() && out.iter().all(|x| x != &stem) {
- out.push(stem.clone());
- }
- }
-
- // 3. 平台等价后缀
- let (stem, ext) = split_ext(base);
- // ext 为纯数字版本号(libm.so.6 的 "6")时视为版本化,不按后缀处理
- let ext_is_ver = !ext.is_empty() && ext.chars().all(|c| c.is_ascii_digit());
- #[cfg(target_os = "windows")]
- {
- if ext != "dll" && !ext_is_ver {
- out.push(format!("{stem}.dll"));
- }
- if let Some(rest) = stem.strip_prefix("lib") {
- if !rest.is_empty() {
- out.push(format!("{rest}.dll"));
- }
- }
- // .so.6 → .dll 也试:libm → m.dll
- if let Some(rest) = base.split('.').next().and_then(|s| s.strip_prefix("lib")) {
- if !rest.is_empty() {
- out.push(format!("{rest}.dll"));
- }
- }
- }
- #[cfg(target_os = "macos")]
- {
- if ext != "dylib" && !ext_is_ver {
- out.push(format!("{stem}.dylib"));
- }
- // libfoo.so.6 → libfoo.dylib
- if let Some((s, _)) = split_version(base) {
- let (s2, _) = split_ext(&s);
- if s2 != stem && s2 != "" {
- out.push(format!("{s2}.dylib"));
- }
- }
- }
- #[cfg(all(unix, not(target_os = "macos")))]
- {
- if ext != "so" && !ext_is_ver {
- out.push(format!("{stem}.so"));
- }
- // libm.so 可能是链接脚本 → 常见版本化真实库兜底
- if (ext == "so" || ext == "") && !ext_is_ver {
- out.push(format!("{stem}.so.6"));
- out.push(format!("{stem}.so.1"));
- }
- }
- // 去重
- let mut seen = std::collections::HashSet::new();
- out.retain(|x| seen.insert(x.clone()));
- out
-}
-
-/// 剥离版本号:libm.so.6 → ("libm.so", "6");foo.1.2 → ("foo", "1.2")。
-/// 仅当最后一段是数字或 .so.X/.dylib.X 形态。
-fn split_version(name: &str) -> Option<(String, String)> {
- let (stem, last) = name.rsplit_once('.')?;
- if last.is_empty() || !last.chars().all(|c| c.is_ascii_digit()) {
- return None;
- }
- Some((stem.to_string(), last.to_string()))
-}
-
-fn split_ext(name: &str) -> (String, String) {
- match name.rsplit_once('.') {
- Some((s, e)) if !s.is_empty() => (s.to_string(), e.to_string()),
- _ => (name.to_string(), String::new()),
- }
-}
-
-/// 打开动态库,返回句柄;失败返回 None。
-pub fn open(path: &str) -> Option {
- let c = CString::new(path).ok()?;
- #[cfg(unix)]
- {
- // RTLD_LAZY = 1
- let h = unsafe { dlopen(c.as_ptr() as *const c_char, 1) };
- if h.is_null() { None } else { Some(h) }
- }
- #[cfg(windows)]
- {
- let h = unsafe { LoadLibraryA(c.as_ptr() as *const c_char) };
- if h.is_null() { None } else { Some(h) }
- }
-}
-
-/// 按声明名尝试打开库:遍历候选名,第一个成功的返回。
-pub fn open_any(declared: &str) -> Option {
- for name in candidate_names(declared) {
- if let Some(h) = open(&name) {
- return Some(h);
- }
- }
- None
-}
-
-/// 查找符号,返回裸指针;失败返回 None。
-pub fn symbol(h: LibHandle, name: &str) -> Option<*mut std::ffi::c_void> {
- let c = CString::new(name).ok()?;
- #[cfg(unix)]
- {
- let p = unsafe { dlsym(h, c.as_ptr() as *const c_char) };
- if p.is_null() { None } else { Some(p) }
- }
- #[cfg(windows)]
- {
- let p = unsafe { GetProcAddress(h as *mut _, c.as_ptr() as *const c_char) };
- if p.is_null() { None } else { Some(p.cast()) }
- }
-}
-
-/// 关闭动态库。
-pub fn close(h: LibHandle) {
- #[cfg(unix)]
- unsafe {
- dlclose(h);
- }
- #[cfg(windows)]
- unsafe {
- FreeLibrary(h as *mut _);
- }
-}
-
-// ---- FFI 声明 ----
-
-#[cfg(unix)]
-extern "C" {
- fn dlopen(filename: *const c_char, flags: i32) -> *mut std::ffi::c_void;
- fn dlsym(handle: *mut std::ffi::c_void, symbol: *const c_char) -> *mut std::ffi::c_void;
- fn dlclose(handle: *mut std::ffi::c_void) -> i32;
-}
-
-#[cfg(windows)]
-extern "system" {
- fn LoadLibraryA(lpFileName: *const c_char) -> *mut std::ffi::c_void;
- fn GetProcAddress(hModule: *mut std::ffi::c_void, lpProcName: *const c_char) -> *mut std::ffi::c_void;
- fn FreeLibrary(hLibModule: *mut std::ffi::c_void) -> i32;
-}
diff --git a/rust/crates/bbb-vm/src/interp.rs b/rust/crates/bbb-vm/src/interp.rs
deleted file mode 100644
index 81a97a2..0000000
--- a/rust/crates/bbb-vm/src/interp.rs
+++ /dev/null
@@ -1,1256 +0,0 @@
-//! 解释器核心(M3):作用域、流调用、表达式求值、语句执行。
-
-use std::collections::HashMap;
-use std::time::Instant;
-
-use bbb_core::arena::{StrArena, StrRef};
-use bbb_core::value::{Cause, Outcome, Tag, Value};
-use bbb_syntax::ast::*;
-use bbb_syntax::parser::parse_source;
-
-use crate::builtin;
-use crate::registry::{Registry, StreamDef, StreamKind};
-use crate::BUILTIN_CLASS_SRC;
-
-/// 控制流信号(语句执行结果)。
-pub enum Flow {
- Next,
- Ret(Outcome),
- Break,
- Continue,
-}
-
-/// 调用栈帧:方法作用域 + this。
-pub struct Frame {
- pub scope: HashMap,
- pub this: Option, // 对象/流实例句柄
- pub method: String, // 当前方法名(电话亭递归检测)
- pub booth: bool, // @call/@ucall 电话亭方法
-}
-
-/// 协作线程任务(顺序 join 式:spawn 注册,join 时按逆序执行)
-pub struct ThreadTask {
- pub id: u32,
- pub name: String, // 裸方法名
- pub def_name: Option, // 所属流名
- pub args: Vec,
- pub done: Option,
-}
-
-/// 对象数据:类名 + 声明字段 + 动态属性。
-#[derive(Clone)]
-pub struct ObjData {
- pub def: StrRef, // 类名("Array"/"Vector"/"Solid"/用户类)
- pub fields: Vec,
- pub attrs: Vec<(StrRef, Value)>,
-}
-
-impl Default for ObjData {
- fn default() -> Self {
- ObjData { def: StrRef::NULL, fields: Vec::new(), attrs: Vec::new() }
- }
-}
-
-/// 引用值(智能引用,&perm follow base)——11 的语义:
-/// r=读 w=写 m=移动(纯 m 不可读不可写,只能 p++/Ref::move)
-#[derive(Clone)]
-pub enum RefTarget {
- /// 变量(帧内绑定:frame 索引 + 名字,跨方法调用仍指向原存储位置)
- Var { frame: usize, name: String },
- ArrElem { obj: u32, index: i64 }, // 数组元素(m 权限指针移动)
- ObjProp { obj: u32, name: String },
-}
-
-#[derive(Clone)]
-pub struct RefVal2 {
- pub target: RefTarget,
- pub perm: String,
- pub follow: String,
-}
-
-/// 运行结果。
-pub struct RunOutcome {
- pub stdout: String,
- pub unmet_needs: Vec<(String, String)>,
-}
-
-pub struct Interp {
- pub strs: StrArena,
- pub objects: Vec,
- pub refs: Vec,
- pub reg: Registry,
- pub frames: Vec ,
- pub stdout: String,
- pub sio_buf: String,
- pub timers: HashMap,
- pub timer_seq: u32,
- pub arrays: Vec, // Arrays 集合(对象句柄)
- pub consts: Vec<(String, Value)>,
- pub stream_instances: HashMap, // fork/class 流单例(持久字段状态)
- pub threads: Vec, // 协作线程任务(顺序 join 式)
- pub thread_seq: u32,
- pub running_thread: Option, // Threads::self()
- pub open_libs: Vec<(String, crate::dylib::LibHandle)>, // 已加载库:声明名 → 句柄
- pub pending_ref_perm: Option,
- pub pending_ref_follow: Option,
-}
-
-impl Interp {
- pub fn new() -> Self {
- Interp {
- strs: StrArena::new(),
- objects: Vec::new(),
- refs: Vec::new(),
- reg: Registry::new(),
- frames: Vec::new(),
- stdout: String::new(),
- sio_buf: String::new(),
- timers: HashMap::new(),
- timer_seq: 0,
- arrays: Vec::new(),
- consts: Vec::new(),
- stream_instances: HashMap::new(),
- threads: Vec::new(),
- thread_seq: 0,
- running_thread: None,
- open_libs: Vec::new(),
- pending_ref_perm: None,
- pending_ref_follow: None,
- }
- }
-
- pub fn intern(&mut self, s: &str) -> StrRef {
- self.strs.push(s)
- }
-
- fn cause(&mut self, s: &str) -> Outcome {
- Outcome::Ref(Cause(self.intern(s)))
- }
-
- // ---- 顶层入口 ----
-
- /// 解释单个 Program(含 need 校验)。
- pub fn run(&mut self, prog: &Program) -> RunOutcome {
- // 注入内置类(Array/Vector,Bio 语言编写)
- let builtin_src = BUILTIN_CLASS_SRC.to_string();
- let (builtin_prog, errs) = parse_source(&builtin_src);
- if errs.is_empty() {
- self.reg.register(&builtin_prog);
- }
- // 用户声明:@unfork 签名流的 fork 跳过(15:启动打印拒绝)
- let mut prog2 = prog.clone();
- let mut unfork_msgs = Vec::new();
- prog2.decls.retain(|d| {
- if let Decl::Fork { sig, .. } = d {
- let blocked = prog.decls.iter().any(|x| matches!(x, Decl::StreamSig { name, annos, .. } if name == sig && annos.contains(&"unfork".to_string())));
- if blocked {
- unfork_msgs.push(format!("refused: stream {sig} is @unfork, cannot fork"));
- return false;
- }
- }
- true
- });
- let mut unmet = self.reg.register(&prog2);
- for m in unfork_msgs {
- self.stdout.push_str(&m);
- self.stdout.push('\n');
- }
- // 顶层常量求值
- for d in &prog2.decls {
- if let Decl::Const { name, init, .. } = d {
- let v = self.eval_expr(init);
- self.consts.push((name.clone(), v));
- }
- }
- // 执行 Main::exec
- let exec = prog2
- .main
- .as_ref()
- .and_then(|m| m.methods.iter().find(|x| x.name == "exec"))
- .cloned();
- if let Some(method) = exec {
- self.frames.push(Frame { scope: HashMap::new(), this: None, method: "exec".into(), booth: false });
- let outcome = self.exec_method_body(&method);
- self.frames.pop();
- // Main::exec 拒绝:自然结束(nothing)静默;显式/传播拒绝打印 ⛔ 并终止(11 的 mp=99)
- if let Outcome::Ref(c) = outcome {
- let cause_s = self.strs.get(c.0).to_string();
- if cause_s != "nothing" {
- self.stdout.push_str(&format!("⛔ main stream refused: {cause_s}\n"));
- }
- }
- }
- // need 校验(多文件/单文件统一)
- unmet.retain(|(k, n)| !self.consts.iter().any(|(cn, _)| cn == n && k == "value"));
- RunOutcome { stdout: self.stdout.clone(), unmet_needs: unmet }
- }
-
- // ---- 对象 ----
-
- pub fn new_object(&mut self, def_name: &str, field_count: usize) -> u32 {
- self.new_object_typed(def_name, &[])
- }
-
- /// 按字段类型初始化默认值:数值 0 / string "" / 其他 nil(13-need 依赖 int hp = 0)。
- pub fn new_object_typed(&mut self, def_name: &str, field_types: &[String]) -> u32 {
- let def = self.intern(def_name);
- let fields = field_types
- .iter()
- .map(|ty| match ty.as_str() {
- "int" | "float" | "double" => Value::int(0),
- "string" => Value::string(self.intern("")),
- "char" => Value::chr(0),
- "bool" => Value::boolean(false),
- _ => Value::nil(),
- })
- .collect();
- self.objects.push(ObjData { def, fields, attrs: Vec::new() });
- (self.objects.len() - 1) as u32
- }
-
- /// 对象字段查找(this 或对象值上的 Prop)。先声明字段后动态属性。
- pub fn obj_prop_get(&mut self, h: u32, name: &str) -> Option {
- let def_name = self.objects[h as usize].def;
- let defname_str = self.strs.get(def_name).to_string();
- if let Some(d) = self.reg.streams.get(&defname_str) {
- if let Some(i) = d.field_names.iter().position(|n| n == name) {
- return Some(self.objects[h as usize].fields[i]);
- }
- }
- for (k, v) in self.objects[h as usize].attrs.iter() {
- if self.strs.get(*k) == name {
- return Some(*v);
- }
- }
- None
- }
-
- pub fn obj_prop_set(&mut self, h: u32, name: &str, v: Value) {
- let nref = self.intern(name);
- let def_name = self.objects[h as usize].def;
- let defname_str = self.strs.get(def_name).to_string();
- if let Some(d) = self.reg.streams.get(&defname_str) {
- if let Some(i) = d.field_names.iter().position(|n| n == name) {
- self.objects[h as usize].fields[i] = v;
- return;
- }
- }
- let o = &mut self.objects[h as usize];
- for (k, slot) in o.attrs.iter_mut() {
- if self.strs.get(*k) == name {
- *slot = v;
- return;
- }
- }
- o.attrs.push((nref, v));
- }
-
- /// 对象类名。
- pub fn obj_class(&self, h: u32) -> String {
- self.strs.get(self.objects[h as usize].def).to_string()
- }
-
- // ---- 变量 ----
-
- fn var_get(&mut self, name: &str) -> Option {
- for f in self.frames.iter().rev() {
- if let Some(v) = f.scope.get(name) {
- return Some(*v);
- }
- }
- for (n, v) in self.consts.iter().rev() {
- if n == name {
- return Some(*v);
- }
- }
- // this 关键字 → 当前实例
- if name == "this" {
- return self.current_this().map(Value::obj);
- }
- // 流字段(fork/class 单例字段)→ 当前 this 实例字段
- if let Some(h) = self.current_this() {
- if let Some(v) = self.obj_prop_get(h, name) {
- return Some(v);
- }
- }
- // 流名 → 流值(流作为参数/对象传递:CIO、Calc...)
- if self.reg.streams.contains_key(name) || builtin_stream_name(name) {
- let key = format!("$stream:{name}");
- if let Some(h) = self.stream_instances.get(&key) {
- return Some(Value::obj(*h));
- }
- let h = self.new_object(name, 0);
- self.stream_instances.insert(key, h);
- return Some(Value::obj(h));
- }
- None
- }
-
- /// 只查 frame 链 + consts(不含流名/this 等解析)。
- fn var_get_raw(&mut self, name: &str) -> Option {
- for f in self.frames.iter().rev() {
- if let Some(v) = f.scope.get(name) {
- return Some(*v);
- }
- }
- for (n, v) in self.consts.iter().rev() {
- if n == name {
- return Some(*v);
- }
- }
- None
- }
-
- /// 引用读(get p / Ref::read):perm 无 r → 拒绝 "refused: reference is write-only, cannot read"
- pub fn ref_read(&mut self, h: u32) -> Outcome {
- let r = self.refs[h as usize].clone();
- // 读需要 r 权限(纯 m/纯 w 不可读——11:get mp 拒绝)
- if !r.perm.contains('r') {
- return Outcome::Ref(Cause(self.intern("refused: reference is write-only, cannot read")));
- }
- match &r.target {
- RefTarget::Var { frame, name } => {
- let v = self
- .frames
- .get(*frame)
- .and_then(|f| f.scope.get(name))
- .copied()
- .or_else(|| self.var_get_raw(name))
- .unwrap_or(Value::nil());
- Outcome::Res(v)
- }
- RefTarget::ArrElem { obj, index } => {
- self.invoke_on_obj(*obj, "get", vec![Value::int(*index)])
- }
- RefTarget::ObjProp { obj, name } => {
- let v = self.obj_prop_get(*obj, name).unwrap_or(Value::nil());
- Outcome::Res(v)
- }
- }
- }
-
- /// 引用写(p = v / Ref::write):perm 无 w → 拒绝 "refused: reference is read-only, cannot write"
- pub fn ref_write(&mut self, h: u32, v: Value) -> Outcome {
- let r = self.refs[h as usize].clone();
- if !r.perm.contains('w') {
- return Outcome::Ref(Cause(self.intern("refused: reference is read-only, cannot write")));
- }
- match &r.target {
- RefTarget::Var { frame, name } => {
- if let Some(f) = self.frames.get_mut(*frame) {
- f.scope.insert(name.clone(), v);
- } else {
- self.var_set(name, v);
- }
- Outcome::Res(Value::nil())
- }
- RefTarget::ArrElem { obj, index } => {
- self.invoke_on_obj(*obj, "set", vec![Value::int(*index), v]);
- Outcome::Res(Value::nil())
- }
- RefTarget::ObjProp { obj, name } => {
- self.obj_prop_set(*obj, name, v);
- Outcome::Res(Value::nil())
- }
- }
- }
-
- /// 引用移动(Ref::move / p++):perm 无 m → 拒绝;数组越界 → 拒绝
- pub fn ref_move(&mut self, h: u32) -> Outcome {
- let r = self.refs[h as usize].clone();
- if !r.perm.contains('m') {
- return Outcome::Ref(Cause(self.intern("Ref refused: reference has no move permission")));
- }
- if let RefTarget::ArrElem { obj, index } = r.target {
- let cls = self.obj_class(obj);
- let len = if cls == "Array" || cls == "Vector" {
- match self.invoke_on_obj(obj, "len", vec![]) {
- Outcome::Res(v) => v.as_int_or_num() as i64,
- Outcome::Ref(_) => 0,
- }
- } else {
- 0
- };
- if index + 1 >= len {
- return Outcome::Ref(Cause(self.intern("refused: reference moved out of bounds")));
- }
- self.refs[h as usize].target = RefTarget::ArrElem { obj, index: index + 1 };
- Outcome::Res(Value::nil())
- } else {
- Outcome::Ref(Cause(self.intern("Ref refused: reference is not a moving pointer")))
- }
- }
-
- /// 引用变量赋值语句(rw = 5)→ 引用写;拒绝则传播。
- fn ref_assign(&mut self, _name: &str, old: Value, v: Value) -> Flow {
- let h = old.as_handle();
- match self.ref_write(h, v) {
- Outcome::Res(_) => Flow::Next,
- Outcome::Ref(c) => Flow::Ret(Outcome::Ref(c)),
- }
- }
-
- fn var_set(&mut self, name: &str, v: Value) {
- for f in self.frames.iter_mut().rev() {
- if f.scope.contains_key(name) {
- f.scope.insert(name.to_string(), v);
- return;
- }
- }
- // 未声明:写入当前帧(宽松;标准:变量须先声明)
- if let Some(f) = self.frames.last_mut() {
- f.scope.insert(name.to_string(), v);
- }
- }
-
- // ---- 方法调用 ----
-
- /// 执行方法体(当前帧已压栈)。返回 res/ref 或默认 ref(nothing)。
- fn exec_method_body(&mut self, method: &Method) -> Outcome {
- for st in &method.body {
- match self.exec_stmt(st) {
- Flow::Ret(o) => return o,
- Flow::Break => return self.cause("break outside loop"),
- Flow::Continue => return self.cause("continue outside loop"),
- Flow::Next => {}
- }
- }
- self.cause("nothing")
- }
-
- /// 调用方法(def 为所属流,this 为实例句柄)。
- fn call_method(
- &mut self,
- def: Option,
- method: Method,
- this: Option,
- args: Vec,
- ) -> Outcome {
- let _ = def;
- let mut scope = HashMap::new();
- for (p, a) in method.params.iter().zip(args.iter()) {
- scope.insert(p.name.clone(), *a);
- }
- self.frames.push(Frame { scope, this, method: method.name.clone(), booth: !method.annos.is_empty() && (method.annos.contains(&"call".to_string()) || method.annos.contains(&"ucall".to_string())) });
- // 电话亭:@call/@ucall 方法递归拒绝(16)
- let name = method.name.clone();
- if self.frames.iter().filter(|f| f.method == name && f.booth).count() > 1 {
- self.frames.pop();
- return self.cause(&format!("refused: phone-booth method {name} does not support recursion"));
- }
- let r = self.exec_method_body(&method);
- self.frames.pop();
- r
- }
-
- /// 调用表达式:Outcome → Value(refused 位 + cause)。
- fn call_to_value(&mut self, qual: Option<&str>, name: &str, args: Vec) -> Value {
- match self.invoke(qual, name, args) {
- Outcome::Res(v) => v,
- Outcome::Ref(c) => Value::refused_str(c.0),
- }
- }
-
- /// 统一调用入口:内置流 → 对象方法 → 流方法 → 裸方法 → 拒绝。
- fn invoke(&mut self, qual: Option<&str>, name: &str, args: Vec) -> Outcome {
- if let Some(q) = qual {
- // 内置流
- if builtin::lookup(q, name).is_some() {
- let f = builtin::lookup(q, name).unwrap();
- return f(self, &args);
- }
- // this::method
- if q == "this" {
- if let Some(h) = self.current_this() {
- return self.invoke_on_obj(h, name, args);
- }
- return self.cause("no this context");
- }
- // 流名
- if let Some(def) = self.reg.resolve_qual(q).cloned() {
- // StreamBin:库方法优先 dl(签名成员 = 声明导出符号,无 Bio 体)
- if let Some(lib) = def.bin_file.clone() {
- // 带 body 的 Bio 方法仍走 Bio 执行
- let has_body = def.methods.get(name).map(|m| !m.body.is_empty()).unwrap_or(false);
- if !has_body {
- return self.dl_call(&lib, name, args);
- }
- }
- if let Some(m) = def.methods.get(name).cloned() {
- // @onlyread:@write 注解方法拒绝(15)
- if def.annos.contains(&"onlyread".to_string())
- && m.annos.contains(&"write".to_string()) {
- return self.cause(&format!("refused: stream {} is @onlyread — {name}() is a write method", def.name));
- }
- let singleton = self.stream_instance(&def);
- return self.call_method(Some(def), m, singleton, args);
- }
- // StreamBin:Bio 方法体之外回退 dlsym(14)
- if let Some(lib) = def.bin_file.clone() {
- return self.dl_call(&lib, name, args);
- }
- }
- // 变量(对象值 / 流值)
- if let Some(v) = self.var_get(q) {
- if let Tag::Obj | Tag::Arr = v.tag() {
- let h = v.as_handle();
- let cls = self.obj_class(h);
- // 内置流实例(cio CIO 传参)
- if let Some(bf) = builtin::lookup(&cls, name) {
- if cls == "Solid" || cls == "SolidData" {
- // Solid 方法需要 self 作第一个参数(旧 C:all[0] = self)
- let mut a2 = Vec::with_capacity(args.len() + 1);
- a2.push(v);
- a2.extend_from_slice(&args);
- return bf(self, &a2);
- }
- return bf(self, &args);
- }
- if self.reg.class(&cls).is_some() || cls == "Solid" {
- return self.invoke_on_obj(h, name, args);
- }
- }
- }
- return self.cause(&format!("stream {q} refuses: no method {name}"));
- } else {
- // 裸调用:先查当前流上下文(04:流内 bare call),再全局方法名
- if let Some(h) = self.current_this() {
- let cls = self.obj_class(h);
- // 内部方法 __sort__:对 this 的 Solid 数据原地排序(sort() 委托)
- if name == "__sort__" {
- if let Some(dh) = self.solid_data_handle(h) {
- self.sort_data(dh);
- return Outcome::Res(Value::nil());
- }
- return self.cause("no data to sort");
- }
- if let Some(def) = self.reg.streams.get(&cls).cloned() {
- if let Some(m) = def.methods.get(name).cloned() {
- if !m.body.is_empty() {
- return self.call_method(Some(def), m, Some(h), args);
- }
- }
- }
- }
- let found = self.reg.find_bare_method(name).map(|(d, m)| (d.clone(), m.clone()));
- if let Some((def, m)) = found {
- let singleton = self.stream_instance(&def);
- return self.call_method(Some(def), m, singleton, args);
- }
- return self.cause(&format!("no method {name}"));
- }
- }
-
- fn current_this(&self) -> Option {
- self.frames.iter().rev().find_map(|f| f.this)
- }
-
- /// 对象方法调用(a::set(0,10) / h::getHp())。
- pub fn invoke_on_obj(&mut self, h: u32, name: &str, args: Vec) -> Outcome {
- let cls = self.obj_class(h);
- if let Some(def) = self.reg.class(&cls).cloned() {
- if let Some(m) = def.methods.get(name).cloned() {
- return self.call_method(Some(def), m, Some(h), args);
- }
- }
- self.cause(&format!("object {cls} refuses: no method {name}"))
- }
-
- /// 执行一个线程任务(裸方法),结果存 task.done。
- pub fn run_thread(&mut self, id: u32) -> Outcome {
- if let Some(t) = self.threads.iter().find(|t| t.id == id) {
- if let Some(d) = t.done {
- return d;
- }
- }
- let Some(idx) = self.threads.iter().position(|t| t.id == id) else {
- return self.cause("no such thread");
- };
- let name = self.threads[idx].name.clone();
- let def_name = self.threads[idx].def_name.clone();
- let args = self.threads[idx].args.clone();
- let found = if let Some(dn) = &def_name {
- self.reg.streams.get(dn).cloned()
- .and_then(|d| d.methods.get(&name).cloned().map(|m| (d, m)))
- .or_else(|| self.reg.find_bare_method(&name).map(|(d, m)| (d.clone(), m.clone())))
- } else {
- self.reg.find_bare_method(&name).map(|(d, m)| (d.clone(), m.clone()))
- };
- let old_running = self.running_thread;
- self.running_thread = Some(id);
- let out = if let Some((def, m)) = found {
- let singleton = self.stream_instance(&def);
- self.call_method(Some(def), m, singleton, args)
- } else {
- self.cause(&format!("no method {name}"))
- };
- self.running_thread = old_running;
- if let Some(t) = self.threads.iter_mut().find(|t| t.id == id) {
- t.done = Some(out);
- }
- out
- }
-
- /// 流实例:fork/class 有**持久单例**(流级字段状态跨调用保持);signature/main 无。
- fn stream_instance(&mut self, def: &StreamDef) -> Option {
- match def.kind {
- StreamKind::Fork | StreamKind::Class => {
- let key = def.name.clone();
- if let Some(h) = self.stream_instances.get(&key) {
- return Some(*h);
- }
- let h = self.new_object_typed(&def.name, &def.field_types);
- self.stream_instances.insert(key, h);
- Some(h)
- }
- _ => None,
- }
- }
-
- // ---- 表达式 ----
-
- pub fn eval_expr(&mut self, e: &Expr) -> Value {
- match e {
- Expr::Int(v) => Value::int(*v),
- Expr::Float(v) => Value::num(*v),
- Expr::Str(s) => Value::string(self.intern(s)),
- Expr::Char(c) => Value::chr(*c),
- Expr::Bool(b) => Value::boolean(*b),
- Expr::Var(name) => self
- .var_get(name)
- .unwrap_or_else(|| Value::nil()),
- Expr::Call { qual, name, args } => {
- let vals: Vec = args.iter().map(|a| self.eval_expr(a)).collect();
- self.call_to_value(qual.as_deref(), name, vals)
- }
- Expr::Prop { base, name } => {
- if name == "res" {
- // Solid::new().res — 取响应值本身
- return self.eval_expr(base);
- }
- let bv = self.eval_expr(base);
- match bv.tag() {
- Tag::Obj | Tag::Arr => {
- let h = bv.as_handle();
- self.obj_prop_get(h, name).unwrap_or(Value::nil())
- }
- Tag::Ref => Value::nil(),
- _ => Value::nil(),
- }
- }
- Expr::Index { base, idx } => {
- let bv = self.eval_expr(base);
- let i = self.eval_expr(idx);
- self.index_get(bv, i)
- }
- Expr::BinOp { op, l, r } => {
- let lv = self.eval_expr(l);
- let rv = self.eval_expr(r);
- self.binop(op, lv, rv)
- }
- Expr::Unwrap { op, l } => {
- let v = self.eval_expr(l);
- if op == "get" {
- // 引用值 → 引用读(11:get rw / get mp)
- if v.tag() == Tag::Ref {
- return match self.ref_read(v.as_handle()) {
- Outcome::Res(val) => val,
- Outcome::Ref(c) => Value::refused_str(c.0),
- };
- }
- // 拒绝传播(11:get mp 打印 refused: refused: ...)
- v
- } else {
- // cause
- if v.refused() {
- Value::string(v.cause())
- } else {
- Value::string(self.intern(""))
- }
- }
- }
- Expr::New { cls, args } => {
- let vals: Vec = args.iter().map(|a| self.eval_expr(a)).collect();
- self.new_class(cls, vals)
- }
- Expr::NewArray { ty: _ty, size } => {
- let n = self.eval_expr(size);
- let n = n.as_int_or_num() as usize;
- self.new_class("Array", vec![Value::int(n as i64)])
- }
- Expr::RefOf(target) => self.make_ref(target),
- }
- }
-
- /// dlopen 调用导出符号(double fn(double...) -> double,14 用)。
- pub fn dl_call(&mut self, lib: &str, sym: &str, args: Vec) -> Outcome {
- // 已加载缓存:声明名 → 句柄(多库独立,不串)
- let cached = self.open_libs.iter().find(|(name, _)| name == lib).map(|(_, h)| *h);
- let handle = match cached {
- Some(h) => h,
- None => {
- match crate::dylib::open_any(lib) {
- Some(h) => {
- self.open_libs.push((lib.to_string(), h));
- h
- }
- None => {
- return self.cause(&format!("cannot open library {lib}"));
- }
- }
- }
- };
- let Some(fptr) = crate::dylib::symbol(handle, sym) else {
- return self.cause(&format!("stream {lib} refuses: no symbol {sym}"));
- };
- let nums: Vec = args.iter().map(|a| a.as_int_or_num()).collect();
- unsafe {
- let result: f64 = match nums.len() {
- 0 => std::mem::transmute::<*mut core::ffi::c_void, fn() -> f64>(fptr)(),
- 1 => std::mem::transmute::<*mut core::ffi::c_void, fn(f64) -> f64>(fptr)(nums[0]),
- _ => std::mem::transmute::<*mut core::ffi::c_void, fn(f64, f64) -> f64>(fptr)(nums[0], nums[1]),
- };
- Outcome::Res(Value::num(result))
- }
- }
-
- pub fn new_class(&mut self, cls: &str, args: Vec) -> Value {
- if let Some(def) = self.reg.class(cls).cloned() {
- // @unfork 类拒绝(15)
- if def.annos.contains(&"unfork".to_string()) {
- let c = self.intern(&format!("Obj refused: class {cls} is @unfork, cannot fork"));
- return Value::refused_str(c);
- }
- let h = self.new_object_typed(cls, &def.field_types);
- if let Some(m) = def.methods.get("__init__").cloned() {
- self.call_method(Some(def), m, Some(h), args);
- }
- Value::obj(h)
- } else {
- // 内置类(Array/Vector 已注入 registry;未知类拒绝)
- Value::nil()
- }
- }
-
- fn make_ref(&mut self, target: &Expr) -> Value {
- let perm = self.pending_ref_perm.take().unwrap_or_else(|| "rw".into());
- let follow = self.pending_ref_follow.take().unwrap_or_else(|| "u".into());
- let t = match target {
- Expr::Var(name) => RefTarget::Var { frame: self.frames.len().saturating_sub(1), name: name.clone() },
- Expr::Index { base, idx } => {
- let bv = self.eval_expr(base);
- let i = self.eval_expr(idx).as_int_or_num() as i64;
- if let Tag::Obj | Tag::Arr = bv.tag() {
- RefTarget::ArrElem { obj: bv.as_handle(), index: i }
- } else {
- RefTarget::Var { frame: self.frames.len().saturating_sub(1), name: "?".into() }
- }
- }
- Expr::Prop { base, name } => {
- let bv = self.eval_expr(base);
- if let Tag::Obj | Tag::Arr = bv.tag() {
- RefTarget::ObjProp { obj: bv.as_handle(), name: name.clone() }
- } else {
- RefTarget::Var { frame: self.frames.len().saturating_sub(1), name: "?".into() }
- }
- }
- _ => RefTarget::Var { frame: self.frames.len().saturating_sub(1), name: "?".into() },
- };
- self.refs.push(RefVal2 { target: t, perm, follow });
- Value::reff((self.refs.len() - 1) as u32)
- }
-
- fn index_get(&mut self, base: Value, idx: Value) -> Value {
- let i = idx.as_int_or_num() as i64;
- match base.tag() {
- Tag::Obj | Tag::Arr => {
- let h = base.as_handle();
- let cls = self.obj_class(h);
- if cls == "Solid" || cls == "SolidData" {
- // 裸数组 / Solid 数据:直接下标读
- let data = self.solid_data(h);
- if i < 0 || i as usize >= data.len() {
- return Value::nil();
- }
- data[i as usize]
- } else {
- // Array/Vector:调对象 get 方法
- self.invoke_on_obj(h, "get", vec![Value::int(i)]).get()
- }
- }
- _ => Value::nil(),
- }
- }
-
- fn binop(&mut self, op: &str, l: Value, r: Value) -> Value {
- // 拒绝传播保留 cause(16:get down(...) 拒绝后 + 1 仍带原因)
- if l.refused() {
- return l;
- }
- if r.refused() {
- return r;
- }
- match op {
- "+" => match (l.tag(), r.tag()) {
- (Tag::Int, Tag::Int) => Value::int(l.as_int_or_num() as i64 + r.as_int_or_num() as i64),
- _ => Value::num(l.as_int_or_num() + r.as_int_or_num()),
- },
- "-" => match (l.tag(), r.tag()) {
- (Tag::Int, Tag::Int) => Value::int(l.as_int_or_num() as i64 - r.as_int_or_num() as i64),
- _ => Value::num(l.as_int_or_num() - r.as_int_or_num()),
- },
- "*" => match (l.tag(), r.tag()) {
- (Tag::Int, Tag::Int) => Value::int(l.as_int_or_num() as i64 * r.as_int_or_num() as i64),
- _ => Value::num(l.as_int_or_num() * r.as_int_or_num()),
- },
- "/" => {
- let d = r.as_int_or_num();
- if d == 0.0 {
- return Value::nil().with_refused();
- }
- match (l.tag(), r.tag()) {
- (Tag::Int, Tag::Int) => Value::int(l.as_int_or_num() as i64 / d as i64),
- _ => Value::num(l.as_int_or_num() / d),
- }
- }
- "%" => {
- let d = r.as_int_or_num() as i64;
- if d == 0 {
- return Value::nil().with_refused();
- }
- Value::int(l.as_int_or_num() as i64 % d)
- }
- "==" => Value::boolean(self.val_cmp(&l, &r) == std::cmp::Ordering::Equal),
- "!=" => Value::boolean(self.val_cmp(&l, &r) != std::cmp::Ordering::Equal),
- "<" => Value::boolean(self.val_cmp(&l, &r) == std::cmp::Ordering::Less),
- ">" => Value::boolean(self.val_cmp(&l, &r) == std::cmp::Ordering::Greater),
- "<=" => Value::boolean(self.val_cmp(&l, &r) != std::cmp::Ordering::Greater),
- ">=" => Value::boolean(self.val_cmp(&l, &r) != std::cmp::Ordering::Less),
- _ => Value::nil(),
- }
- }
-
- fn val_cmp(&self, l: &Value, r: &Value) -> std::cmp::Ordering {
- if !matches!((l.tag(), r.tag()), (Tag::Int | Tag::Num, Tag::Int | Tag::Num)) {
- return std::cmp::Ordering::Equal;
- }
- l.as_int_or_num().partial_cmp(&r.as_int_or_num()).unwrap_or(std::cmp::Ordering::Equal)
- }
-
- // ---- 语句 ----
-
- pub fn exec_stmt(&mut self, s: &Stmt) -> Flow {
- match s {
- Stmt::If { cond, then, els } => {
- let c = self.eval_expr(cond);
- if c.truthy() {
- self.exec_block(then)
- } else if let Some(e) = els {
- self.exec_block(e)
- } else {
- Flow::Next
- }
- }
- Stmt::While { cond, body } => {
- loop {
- let c = self.eval_expr(cond);
- if !c.truthy() {
- return Flow::Next;
- }
- match self.exec_block(body) {
- Flow::Break => return Flow::Next,
- Flow::Continue => continue,
- Flow::Ret(o) => return Flow::Ret(o),
- Flow::Next => {}
- }
- }
- }
- Stmt::For { init, cond, update, body } => {
- if let Some(i) = init {
- if let Flow::Ret(o) = self.exec_stmt(i) {
- return Flow::Ret(o);
- }
- }
- loop {
- if let Some(c) = cond {
- if !self.eval_expr(c).truthy() {
- return Flow::Next;
- }
- }
- match self.exec_block(body) {
- Flow::Break => return Flow::Next,
- Flow::Continue => {
- if let Some(u) = update {
- if let Flow::Ret(o) = self.exec_stmt(u) {
- return Flow::Ret(o);
- }
- }
- continue;
- }
- Flow::Ret(o) => return Flow::Ret(o),
- Flow::Next => {}
- }
- if let Some(u) = update {
- if let Flow::Ret(o) = self.exec_stmt(u) {
- return Flow::Ret(o);
- }
- }
- }
- }
- Stmt::Break => Flow::Break,
- Stmt::Continue => Flow::Continue,
- Stmt::Ret { kind, values } => match kind {
- RetKind::Res => {
- let mut vals: Vec = values.iter().map(|v| self.eval_expr(v)).collect();
- match vals.len() {
- 0 => Flow::Ret(Outcome::Res(Value::nil())),
- 1 => Flow::Ret(Outcome::Res(vals.remove(0))),
- _ => {
- // 多值 → 数组(Solid)
- let h = self.solid_new(vals);
- Flow::Ret(Outcome::Res(Value::obj(h)))
- }
- }
- }
- RetKind::Ref => {
- let reason = values
- .first()
- .map(|v| self.eval_expr(v))
- .map(|v| match v.tag() {
- Tag::Str => v.as_str(),
- _ => {
- let s = self.fmt_value(&v);
- self.intern(&s)
- }
- })
- .unwrap_or_else(|| self.intern("nothing"));
- Flow::Ret(Outcome::Ref(Cause(reason)))
- }
- },
- Stmt::Assign { vtype, is_const, is_thread, target, op, value } => {
- let _ = (is_const, is_thread);
- let v = self.eval_expr(value);
- // 拒绝传播:非 ALL 声明赋值右侧拒绝 → 方法拒绝(11:mp = 99 → ⛔)
- // nothing(void 方法自然结束)不传播
- if v.refused() && vtype.as_deref() != Some("ALL")
- && self.strs.get(v.cause()) != "nothing" {
- return Flow::Ret(Outcome::Ref(Cause(v.cause())));
- }
- // 赋值目标是引用变量 → 引用写(rw = 5)
- if let AssignTarget::Var(name) = target {
- if op == "=" && vtype.is_none() {
- if let Some(old) = self.var_get_raw(name) {
- if old.tag() == Tag::Ref {
- return self.ref_assign(name, old, v);
- }
- }
- }
- }
- match target {
- AssignTarget::Var(name) => {
- let v = if op == "=" {
- v
- } else {
- let old = self.var_get(name).unwrap_or(Value::nil());
- self.binop(&op[..1], old, v)
- };
- if vtype.is_some() {
- if let Some(f) = self.frames.last_mut() {
- f.scope.insert(name.clone(), v);
- }
- } else {
- self.var_set(name, v);
- }
- }
- AssignTarget::Prop { base, name } => {
- let bv = self.eval_expr(base);
- if let Tag::Obj | Tag::Arr = bv.tag() {
- let h = bv.as_handle();
- let v = if op == "=" {
- v
- } else {
- let old = self.obj_prop_get(h, name).unwrap_or(Value::nil());
- self.binop(&op[..1], old, v)
- };
- self.obj_prop_set(h, name, v);
- }
- }
- AssignTarget::Index { base, idx } => {
- let bv = self.eval_expr(base);
- let i = self.eval_expr(idx);
- self.index_set(bv, i, v);
- }
- }
- Flow::Next
- }
- Stmt::RefDecl { perm, follow, base, name, init } => {
- let _ = base;
- self.pending_ref_perm = Some(perm.clone());
- self.pending_ref_follow = Some(follow.clone());
- let rv = self.eval_expr(init);
- if let Some(f) = self.frames.last_mut() {
- f.scope.insert(name.clone(), rv);
- }
- Flow::Next
- }
- Stmt::Inc { name, op } => {
- let old = self.var_get(name).unwrap_or(Value::nil());
- let delta = if op == "++" { 1 } else { -1 };
- if old.tag() == Tag::Ref {
- // m 权限指针移动(11:mp++)
- let h = old.as_handle();
- if !self.refs[h as usize].perm.contains('m') {
- return Flow::Ret(Outcome::Ref(Cause(self.intern("reference has no move permission"))));
- }
- if let RefTarget::ArrElem { obj, index } = self.refs[h as usize].target.clone() {
- self.refs[h as usize].target = RefTarget::ArrElem { obj, index: index + delta };
- }
- Flow::Next
- } else {
- let nv = match old.tag() {
- Tag::Int => Value::int(old.as_int_or_num() as i64 + delta),
- Tag::Num => Value::num(old.as_int_or_num() + delta as f64),
- _ => old,
- };
- self.var_set(name, nv);
- Flow::Next
- }
- }
- Stmt::Expr(e) => {
- let v = self.eval_expr(e);
- // 调用语句拒绝 → 传播(nothing = void 自然结束,不传播)
- if v.refused() && matches!(e, Expr::Call { .. })
- && self.strs.get(v.cause()) != "nothing" {
- return Flow::Ret(Outcome::Ref(Cause(v.cause())));
- }
- Flow::Next
- }
- }
- }
-
- fn exec_block(&mut self, stmts: &[Stmt]) -> Flow {
- for s in stmts {
- match self.exec_stmt(s) {
- Flow::Next => {}
- other => return other,
- }
- }
- Flow::Next
- }
-
- fn index_set(&mut self, base: Value, idx: Value, v: Value) {
- let i = idx.as_int_or_num() as i64;
- match base.tag() {
- Tag::Obj | Tag::Arr => {
- let h = base.as_handle();
- let cls = self.obj_class(h);
- if cls == "Solid" {
- if let Some(d) = self.objects.get_mut(h as usize) {
- if let Some(slot) = d.attrs.iter().position(|(k, _)| self.strs.get(*k) == "$data") {
- // Solid 数据存 attrs 的 "$data" 键(builtin 约定)
- let _ = slot;
- }
- let _ = (d, i, v);
- }
- } else {
- self.invoke_on_obj(h, "set", vec![Value::int(i), v]);
- }
- }
- _ => {}
- }
- }
-
- /// 创建 Solid 实例(多值返回/内置)。
- pub fn solid_new(&mut self, data: Vec) -> u32 {
- let def = self.intern("Solid");
- self.objects.push(ObjData { def, fields: vec![], attrs: Vec::new() });
- let h = (self.objects.len() - 1) as u32;
- // 数据存 attrs "$data"
- let key = self.intern("$data");
- let dh = self.objs_data_handle(data);
- self.objects[h as usize].attrs.push((key, Value::arr(dh)));
- h
- }
-
- pub(crate) fn objs_data_handle(&mut self, data: Vec) -> u32 {
- // 数据 Vec 存独立 "Data" 对象
- let def = self.intern("SolidData");
- self.objects.push(ObjData { def, fields: data, attrs: Vec::new() });
- (self.objects.len() - 1) as u32
- }
-
- // ---- 值格式化(打印/字符串化) ----
-
- pub fn fmt_value(&mut self, v: &Value) -> String {
- if v.refused() {
- return format!("refused: {}", self.strs.get(v.cause()));
- }
- match v.tag() {
- Tag::Nil => "nil".to_string(),
- Tag::Int => v.as_int_or_num().to_string(),
- Tag::Num => {
- let f = v.as_int_or_num();
- let s = format!("{f}");
- s
- }
- Tag::Bool => if v.as_bool() { "true" } else { "false" }.to_string(),
- Tag::Str => self.strs.get(v.as_str()).to_string(),
- Tag::Char => (v.as_char() as char).to_string(),
- Tag::Obj | Tag::Arr => {
- let h = v.as_handle();
- self.fmt_object(h)
- }
- Tag::Ref => "[".to_string(),
- }
- }
-
- fn fmt_object(&mut self, h: u32) -> String {
- let cls = self.obj_class(h);
- if cls == "Solid" || cls == "SolidData" {
- let data = self.solid_data(h).to_vec();
- return format!("[{}]", data.iter().map(|x| self.fmt_value(x)).collect::]>().join(", "));
- }
- if cls == "Array" || cls == "Vector" {
- // data 属性(this::data = Solid::new().res)或声明字段
- let o = &self.objects[h as usize];
- let mut data_h = o.fields.first().map(|v| v.as_handle());
- if data_h.is_none() {
- for (k, v) in &o.attrs {
- if self.strs.get(*k) == "data" {
- if let Tag::Obj | Tag::Arr = v.tag() {
- data_h = Some(v.as_handle());
- }
- }
- }
- }
- if let Some(dh) = data_h {
- let data = self.solid_data(dh).to_vec();
- return format!("[{}]", data.iter().map(|x| self.fmt_value(x)).collect::>().join(", "));
- }
- }
- // 一般对象:
- let o = self.objects[h as usize].clone();
- let mut parts = Vec::new();
- for (k, val) in &o.attrs {
- let kn = self.strs.get(*k).to_string();
- if kn == "$data" {
- continue;
- }
- parts.push(format!("{kn}: {}", self.fmt_value(val)));
- }
- format!("", cls, parts.join(", "))
- }
-
- /// Solid/SolidData 的数据读取(借用处理:复制出来)。
- pub fn solid_data(&self, h: u32) -> Vec {
- let o = &self.objects[h as usize];
- let cls = self.strs.get(o.def).to_string();
- if cls == "Solid" {
- // attrs "$data" → Arr 句柄 → SolidData
- for (k, v) in &o.attrs {
- if self.strs.get(*k) == "$data" {
- if let Tag::Arr = v.tag() {
- return self.objects[v.as_handle() as usize].fields.clone();
- }
- }
- }
- Vec::new()
- } else {
- o.fields.clone()
- }
- }
-
- /// 修改 Solid 数据。
- pub fn solid_set(&mut self, h: u32, i: usize, v: Value) {
- let data_h = self.solid_data_handle(h);
- if let Some(dh) = data_h {
- self.objects[dh as usize].fields[i] = v;
- }
- }
-
- pub fn solid_data_handle(&self, h: u32) -> Option {
- let o = &self.objects[h as usize];
- let cls = self.strs.get(o.def).to_string();
- if cls == "SolidData" {
- return Some(h);
- }
- // Array/Vector 类:data 字段(this::data = Solid::new().res)→ 继续解 Solid 的 $data
- if let Some(d) = self.reg.streams.get(&cls) {
- if let Some(i) = d.field_names.iter().position(|n| n == "data") {
- if let Tag::Obj | Tag::Arr = o.fields[i].tag() {
- let solid_h = o.fields[i].as_handle();
- if let Some(dh) = self.solid_data_handle(solid_h) {
- return Some(dh);
- }
- }
- }
- }
- for (k, v) in &o.attrs {
- let kn = self.strs.get(*k);
- if (kn == "$data" || kn == "data") {
- if let Tag::Obj | Tag::Arr = v.tag() {
- let solid_h = v.as_handle();
- if let Some(dh) = self.solid_data_handle(solid_h) {
- return Some(dh);
- }
- }
- }
- }
- None
- }
-
- pub fn solid_push(&mut self, h: u32, v: Value) {
- let data_h = self.solid_data_handle(h);
- if let Some(dh) = data_h {
- self.objects[dh as usize].fields.push(v);
- }
- }
-
- /// 原地排序 Solid 数据(__sort__ 内部方法):数字升序 → 字符串字典序 → 其余保持稳定序。
- pub fn sort_data(&mut self, dh: u32) {
- use std::cmp::Ordering;
- // 先复制出来排序,避免借用冲突
- let mut vals = self.objects[dh as usize].fields.clone();
- vals.sort_by(|a, b| {
- let ka = self.sort_key(a);
- let kb = self.sort_key(b);
- match (ka, kb) {
- (Some(x), Some(y)) => x.partial_cmp(&y).unwrap_or(Ordering::Equal),
- (Some(_), None) => Ordering::Less,
- (None, Some(_)) => Ordering::Greater,
- (None, None) => Ordering::Equal,
- }
- });
- self.objects[dh as usize].fields = vals;
- }
-
- fn sort_key(&self, v: &Value) -> Option {
- match v.tag() {
- Tag::Int | Tag::Num => Some(v.as_int_or_num()),
- Tag::Str => {
- // 字符串字典序:用字符串池内容编码成可比较键(前缀优先)
- let s = self.strs.get(v.as_str());
- // 用前 4 字符的字节值编码(大端),保证字典序近似;短串自然靠前
- let b = s.as_bytes();
- let mut key = 0.0f64;
- for (i, c) in b.iter().take(4).enumerate() {
- key += (*c as f64) * 256f64.powi(3 - i as i32);
- }
- // 长度作为微小尾数(保证短串 < 长串同前缀)
- Some(key + (s.len() as f64) * 1e-9)
- }
- _ => None,
- }
- }
-}
-
-impl Default for Interp {
- fn default() -> Self {
- Self::new()
- }
-}
-
-/// 内置流名(流可作为值传递)。
-pub fn builtin_stream_name(name: &str) -> bool {
- matches!(name, "CIO" | "SIO" | "FIO" | "IO" | "Com" | "Time" | "Obj" | "Solid" | "Arrays" | "Ref" | "Threads" | "Taskm")
-}
diff --git a/rust/crates/bbb-vm/src/lib.rs b/rust/crates/bbb-vm/src/lib.rs
deleted file mode 100644
index 167f738..0000000
--- a/rust/crates/bbb-vm/src/lib.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-//! bbb-vm — BioLang 解释器(M3)。
-//!
-//! 设计(对齐旧 C interp.c + examples 语义):
-//! - 流注册表:签名流/分叉/类/Main 统一登记,方法名解析(qual::name →
-//! 流方法 → 签名回退分叉 → 对象方法;裸调用 → 全局方法名扫描);
-//! - 值语义:Value 16B(bbb-core),调用结果带 REFUSED 位(cause 为
-//! 字符串句柄),`get`/`cause` 是位测试;
-//! - 对象:arena 句柄(ObjData = 类定义 + 字段 + 动态属性),
-//! Array/Vector 是注入的 Bio 类源码(底层 Solid 流,Rust 实现);
-//! - 控制流:Flow 枚举(Next/Ret/Break/Continue)驱动语句块。
-
-pub mod builtin;
-pub mod dylib;
-pub mod interp;
-pub mod project;
-pub mod registry;
-
-pub use interp::{Interp, RunOutcome};
-pub use project::load_project_sources;
-pub use registry::Registry;
-
-/// 预置的 Array/Vector 类源码(Bio 语言编写,与旧 C 注入的类一致)。
-pub const BUILTIN_CLASS_SRC: &str = r#"
-Class Array {
- void __init__(n int) {
- this::data = Solid::new().res;
- ALL i = 0;
- while (i < n) { Solid::push(this::data, 0); i = i + 1; }
- Arrays::add(this);
- }
- int len() { res Solid::len(this::data); }
- void set(i int, v) { Solid::set(this::data, i, v); }
- int get(i int) { res Solid::get(this::data, i); }
- void push(v) { Solid::push(this::data, v); }
- string join(sep string) { res Solid::join(this::data, sep); }
- void sort() { __sort__(); }
-}
-Class Vector {
- void __init__() { this::data = Solid::new().res; Arrays::add(this); }
- int len() { res Solid::len(this::data); }
- void set(i int, v) { Solid::set(this::data, i, v); }
- int get(i int) { res Solid::get(this::data, i); }
- void push(v) { Solid::push(this::data, v); }
- void sort() { __sort__(); }
-}
-"#;
diff --git a/rust/crates/bbb-vm/src/project.rs b/rust/crates/bbb-vm/src/project.rs
deleted file mode 100644
index 13174e5..0000000
--- a/rust/crates/bbb-vm/src/project.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-//! 项目加载:目录模式(package.toml + src/ + utils/ 合并解析)。
-
-use std::path::PathBuf;
-
-use bbb_syntax::ast::{Decl, Program};
-use bbb_syntax::parser::parse_source;
-
-/// 加载项目所有 .bio 文件并合并成一个 Program。
-/// need 跨文件配对由 registry.register 统一校验。
-pub fn load_project_sources(root: &PathBuf) -> Result {
- let mut files: Vec = Vec::new();
- for base in ["src", "utils"] {
- let d = root.join(base);
- if !d.is_dir() {
- continue;
- }
- if let Ok(entries) = std::fs::read_dir(&d) {
- for e in entries.flatten() {
- let p = e.path();
- if p.extension().map(|x| x == "bio" || x == "bl").unwrap_or(false) {
- files.push(p);
- }
- }
- }
- }
- files.sort();
- if files.is_empty() {
- return Err(format!("{}: no .bio files under src/ or utils/", root.display()));
- }
-
- let mut decls: Vec = Vec::new();
- let mut main = None;
- let mut kind = String::new();
- for f in &files {
- let src = std::fs::read_to_string(f)
- .map_err(|e| format!("{}: read failed: {e}", f.display()))?;
- let (prog, errs) = parse_source(&src);
- if !errs.is_empty() {
- return Err(format!(
- "{}: {}",
- f.display(),
- errs.iter().map(|e| e.to_string()).collect::>().join("; ")
- ));
- }
- if !prog.kind.is_empty() {
- kind = prog.kind.clone();
- }
- if prog.main.is_some() {
- if main.is_some() {
- return Err(format!("{}: multiple Main stream definitions", f.display()));
- }
- main = prog.main;
- }
- decls.extend(prog.decls);
- }
- Ok(Program { kind, decls, main })
-}
diff --git a/rust/crates/bbb-vm/src/registry.rs b/rust/crates/bbb-vm/src/registry.rs
deleted file mode 100644
index 36865cf..0000000
--- a/rust/crates/bbb-vm/src/registry.rs
+++ /dev/null
@@ -1,216 +0,0 @@
-//! 流注册表:把 Program 的声明构建成可调用的流/方法表。
-
-use std::collections::HashMap;
-
-use bbb_syntax::ast::{Decl, Member, Method, Program};
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum StreamKind {
- Signature, // Stream X { ... } 仅签名
- Fork, // Sig X { ... } 实现
- Class, // Class X { ... }
- Binary, // Stream X & "lib.so"(本轮仅注册,调用拒绝)
- Main, // Main 流
-}
-
-#[derive(Debug, Clone)]
-pub struct StreamDef {
- pub name: String,
- pub kind: StreamKind,
- pub sig: Option, // fork 的签名流名
- pub bin_file: Option, // StreamBin 的库文件
- pub methods: HashMap,
- pub field_names: Vec,
- pub field_types: Vec, // 与 field_names 一一对应(默认值初始化用)
- pub annos: Vec, // @onlyread/@unfork(流注解)
-}
-
-impl StreamDef {
- pub fn is_class(&self) -> bool {
- self.kind == StreamKind::Class
- }
-}
-
-#[derive(Debug, Default)]
-pub struct Registry {
- pub streams: HashMap,
- pub order: Vec, // 声明顺序(对象打印/调试用)
-}
-
-impl Registry {
- pub fn new() -> Self {
- Registry::default()
- }
-
- /// 把 Program 的声明注册进表。返回未满足的 need(name, kind)。
- pub fn register(&mut self, prog: &Program) -> Vec<(String, String)> {
- let mut unmet = Vec::new();
- let mut needs = Vec::new();
- for d in &prog.decls {
- match d {
- Decl::Need { kind, name } => needs.push((kind.clone(), name.clone())),
- Decl::StreamSig { name, members, annos, .. } => {
- let def = build_stream(name.clone(), StreamKind::Signature, None, members, annos);
- self.insert(def);
- }
- Decl::StreamBin { name, file, members, .. } => {
- let def = build_stream(name.clone(), StreamKind::Binary, None, members, &[]);
- let def = StreamDef { bin_file: Some(file.clone()), ..def };
- self.insert(def);
- }
- Decl::Class { name, members, annos, implements } => {
- let def = build_stream(name.clone(), StreamKind::Class, None, members, annos);
- // 接口实现检查:类必须提供接口的全部方法签名
- for iname in implements {
- let Some(idef) = self.streams.get(iname).cloned() else {
- unmet.push(("Interface".into(), iname.clone()));
- continue;
- };
- for (mn, mm) in &idef.methods {
- if !def.methods.contains_key(mn) {
- unmet.push((
- format!("Interface {iname} method {mn}"),
- format!("class {name} does not implement it"),
- ));
- }
- }
- }
- self.insert(def);
- }
- Decl::Interface { name, members, annos, .. } => {
- // 接口 = 签名流(只有签名方法;成员 body 必须为空)
- let def = build_stream(name.clone(), StreamKind::Signature, None, members, annos);
- self.insert(def);
- }
- Decl::Fork { sig, name, members, annos, .. } => {
- let mut def = build_stream(name.clone(), StreamKind::Fork, Some(sig.clone()), members, annos);
- // 字段/签名方法继承自签名流(15:int count 声明在 Stream ReadOnly)
- if let Some(sig_def) = self.streams.get(sig).cloned() {
- let mut names = sig_def.field_names.clone();
- let mut types = sig_def.field_types.clone();
- for (i, n) in names.iter().enumerate() {
- if !def.field_names.contains(n) {
- def.field_names.push(n.clone());
- def.field_types.push(types[i].clone());
- }
- }
- for (mn, mm) in &sig_def.methods {
- def.methods.entry(mn.clone()).or_insert_with(|| mm.clone());
- }
- }
- self.insert(def);
- }
- Decl::Const { .. } => {}
- }
- }
- if let Some(m) = &prog.main {
- let mut methods = HashMap::new();
- for meth in &m.methods {
- methods.insert(meth.name.clone(), meth.clone());
- }
- let def = StreamDef {
- name: "Main".into(),
- kind: StreamKind::Main,
- sig: None,
- methods,
- field_names: Vec::new(),
- field_types: Vec::new(),
- annos: Vec::new(),
- bin_file: None,
- };
- self.insert(def);
- }
- // need 校验
- for (kind, name) in needs {
- let ok = match kind.as_str() {
- "value" => prog.decls.iter().any(|d| matches!(d, Decl::Const { name: n, .. } if n == &name)),
- "function" => self.find_bare_method(&name).is_some(),
- "stream" | "Stream" => self.streams.contains_key(&name),
- "Class" => self.streams.get(&name).map(|s| s.is_class()).unwrap_or(false),
- _ => false,
- };
- if !ok {
- unmet.push((kind, name));
- }
- }
- unmet
- }
-
- fn insert(&mut self, def: StreamDef) {
- self.order.push(def.name.clone());
- self.streams.insert(def.name.clone(), def);
- }
-
- /// 签名流调用回退:qual 是签名流时找其分叉实现。
- pub fn resolve_qual(&self, qual: &str) -> Option<&StreamDef> {
- let d = self.streams.get(qual)?;
- if d.kind == StreamKind::Signature {
- for other in self.streams.values() {
- if other.sig.as_deref() == Some(qual) {
- return Some(other);
- }
- }
- }
- Some(d)
- }
-
- /// 裸调用:全局按方法名扫描(Main 优先,然后声明顺序)。
- pub fn find_bare_method(&self, name: &str) -> Option<(&StreamDef, &Method)> {
- if let Some(main) = self.streams.get("Main") {
- if let Some(m) = main.methods.get(name) {
- return Some((main, m));
- }
- }
- for key in &self.order {
- if key == "Main" {
- continue;
- }
- if let Some(d) = self.streams.get(key) {
- if let Some(m) = d.methods.get(name) {
- // 签名方法(无体)不是实现,跳过;找分叉的实现
- if m.body.is_empty() {
- continue;
- }
- return Some((d, m));
- }
- }
- }
- None
- }
-
- /// 类定义(new 用)。
- pub fn class(&self, name: &str) -> Option<&StreamDef> {
- let d = self.streams.get(name)?;
- if d.is_class() {
- Some(d)
- } else {
- None
- }
- }
-}
-
-fn build_stream(
- name: String,
- kind: StreamKind,
- sig: Option,
- members: &[Member],
- annos: &[String],
-) -> StreamDef {
- let mut methods = HashMap::new();
- let mut field_names = Vec::new();
- let mut field_types = Vec::new();
- for m in members {
- match m {
- Member::Method(meth) => {
- methods.insert(meth.name.clone(), meth.clone());
- }
- Member::Field { ty, names } => {
- for n in names {
- field_names.push(n.clone());
- field_types.push(ty.clone());
- }
- }
- }
- }
- StreamDef { name, kind, sig, bin_file: None, methods, field_names, field_types, annos: annos.to_vec() }
-}
diff --git a/rust/crates/bbb-vm/tests/dylib.rs b/rust/crates/bbb-vm/tests/dylib.rs
deleted file mode 100644
index 5a6afba..0000000
--- a/rust/crates/bbb-vm/tests/dylib.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-//! dylib 模块测试:候选名生成 + 实际加载/符号查找(Linux 环境)。
-
-use bbb_vm::dylib;
-
-#[test]
-fn candidate_names_keeps_original() {
- let c = dylib::candidate_names("libm.so");
- assert_eq!(c[0], "libm.so");
- assert!(c.contains(&"libm.so".to_string()));
-}
-
-#[test]
-fn candidate_names_linux_so() {
- // Linux:libm.so → 版本化兜底 libm.so.6 / libm.so.1
- let c = dylib::candidate_names("libm.so");
- assert!(c.contains(&"libm.so.6".to_string()), "{c:?}");
-}
-
-#[test]
-fn candidate_names_versioned_no_garbage() {
- // libm.so.6 不应生成 libm.so.so 之类的垃圾
- let c = dylib::candidate_names("libm.so.6");
- assert!(!c.iter().any(|x| x.contains(".so.so")), "{c:?}");
- assert!(c.contains(&"libm.so".to_string()), "{c:?}");
-}
-
-#[test]
-fn candidate_names_bare_lib() {
- let c = dylib::candidate_names("libm");
- assert!(c.contains(&"libm.so".to_string()), "{c:?}");
- assert!(c.contains(&"libm.so.6".to_string()), "{c:?}");
-}
-
-#[cfg(unix)]
-#[test]
-fn open_and_symbol_libm() {
- // 平台真实库:声明 libm.so(链接脚本),应通过候选命中 libm.so.6
- let h = dylib::open_any("libm.so").expect("open libm");
- let s = dylib::symbol(h, "sin").expect("symbol sin");
- let f: fn(f64) -> f64 = unsafe { std::mem::transmute(s) };
- assert_eq!(f(0.0), 0.0);
- assert!((f(std::f64::consts::FRAC_PI_2) - 1.0).abs() < 1e-9);
-}
diff --git a/rust/crates/bbb-vm/tests/regression.rs b/rust/crates/bbb-vm/tests/regression.rs
deleted file mode 100644
index 5372041..0000000
--- a/rust/crates/bbb-vm/tests/regression.rs
+++ /dev/null
@@ -1,259 +0,0 @@
-//! M3 解释器回归:examples 01-08 + 12 + 13 输出断言。
-//! 完整期望值以旧 C 解释器(bin/bio)实测输出为准(标准层)。
-
-use std::path::PathBuf;
-
-use bbb_syntax::parser::parse_source;
-use bbb_vm::interp::Interp;
-
-fn examples_dir() -> PathBuf {
- PathBuf::from(env!("CARGO_MANIFEST_DIR"))
- .parent().unwrap().parent().unwrap().parent().unwrap()
- .join("examples")
-}
-
-fn run_file(name: &str) -> String {
- let path = examples_dir().join(name);
- let src = std::fs::read_to_string(&path).unwrap();
- let (prog, errs) = parse_source(&src);
- assert!(errs.is_empty(), "{name} parse errors: {errs:?}");
- let mut interp = Interp::new();
- let out = interp.run(&prog);
- assert!(out.unmet_needs.is_empty(), "{name} unmet needs: {:?}", out.unmet_needs);
- out.stdout
-}
-
-#[test]
-fn ex01_hello() {
- let out = run_file("01-hello.bio");
- assert_eq!(out, "Hello, BioLang!\nEvery call in BioLang is a request.\nThis one was just a request to print a line.\n");
-}
-
-#[test]
-fn ex02_requests() {
- let out = run_file("02-requests.bio");
- assert!(out.contains("3 + 4 = 7"));
- assert!(out.contains("div cause: division by zero"));
- assert!(out.contains("missing method cause: stream MyCalc refuses: no method sqrt"));
- assert!(out.contains("default return cause: nothing"));
- assert!(out.contains("if is false (default ref nothing)"));
-}
-
-#[test]
-fn ex03_control_flow() {
- let out = run_file("03-control-flow.bio");
- assert!(out.contains("1..10 sum (while) = 55"));
- assert!(out.contains("5! (for) = 120"));
- assert!(out.contains("1 2 3 5 6 7"));
- assert!(out.contains("for(;;) counted: 3"));
-}
-
-#[test]
-fn ex04_streams_fork() {
- let out = run_file("04-streams-fork.bio");
- assert!(out.contains("Calc::add(2,3) = 5"));
- assert!(out.contains("bare add(10,20) = 30"));
- assert!(out.contains("count = 3 doubleGet = 6"));
- assert!(out.contains("hello from a passed stream!"));
-}
-
-#[test]
-fn ex05_io() {
- let out = run_file("05-io-substreams.bio");
- assert!(out.contains("SIO::format → 2 + 3 = 5"));
- assert!(out.contains("SIO::upper → HELLO"));
- assert!(out.contains("SIO::getln → line one"));
- assert!(out.contains("FIO read back: Hello from BioLang! Appended line"));
-}
-
-#[test]
-fn ex06_classes() {
- let out = run_file("06-classes-objects.bio");
- assert!(out.contains("object: "));
- assert!(out.contains("name = TAK hp = 88"));
- assert!(out.contains("Obj::call getHp() = 100"));
- assert!(out.contains("h::getName() = TAK"));
-}
-
-#[test]
-fn ex07_arrays() {
- let out = run_file("07-arrays.bio");
- assert!(out.contains("array: [10, 20, 30]"));
- assert!(out.contains("after push: [10, 20, 30, 40] len: 4"));
- assert!(out.contains("join(-): 10-20-30-40"));
- assert!(out.contains("a[1] = 20"));
- assert!(out.contains("after a[1] = 99: [10, 99, 30, 40]"));
- assert!(out.contains("new int[4] squares: [0, 1, 4, 9]"));
- assert!(out.contains("vector: [10, 20, 30] len: 3"));
- assert!(out.contains("Arrays count: 3"));
- assert!(out.contains("after forget: 2"));
-}
-
-#[test]
-fn ex08_multi_return() {
- let out = run_file("08-multi-return.bio");
- assert!(out.contains("triple(10) = [10, 20, 30]"));
- assert!(out.contains("arr = [10, 20, 30] arr[1] = 20 len = 3"));
-}
-
-#[test]
-fn ex12_computation() {
- let out = run_file("12-computation.bio");
- assert!(out.contains("Com::abs(0-5) = 5 Com::sqrt(9) = 3"));
- assert!(out.contains("Com::pow(2,10) = 1024"));
- assert!(out.contains("Time::reset(forked) ok"));
-}
-
-#[test]
-fn ex13_need() {
- let out = run_file("13-need.bio");
- assert!(out.contains("PI = 3"));
- assert!(out.contains("hello, TAK"));
- assert!(out.contains("writing via a needed stream"));
- assert!(out.contains("hero created, hp = 0"));
-}
-
-#[test]
-fn project_multi_file() {
- let root = examples_dir().join("project");
- let prog = bbb_vm::load_project_sources(&root).unwrap();
- let mut interp = Interp::new();
- let out = interp.run(&prog);
- assert!(out.unmet_needs.is_empty(), "unmet: {:?}", out.unmet_needs);
- assert_eq!(out.stdout, "main entry — Hello from utils/\nhello, project\n");
-}
-
-#[test]
-fn need_unmet_is_error() {
- let src = r#"
-program main;
-need value MISSING;
-Main { void exec() { CIO::println("x"); } }
-"#;
- let (prog, errs) = parse_source(src);
- assert!(errs.is_empty());
- let mut interp = Interp::new();
- let out = interp.run(&prog);
- assert_eq!(out.unmet_needs, vec![("value".to_string(), "MISSING".to_string())]);
-}
-
-#[test]
-fn ex09_threads() {
- let out = run_file("09-threads.bio");
- assert!(out.contains("live threads: 2"));
- assert!(out.contains("thread 1 10! = 3628800"));
- assert!(out.contains("thread 2 countUp = 5"));
-}
-
-#[test]
-fn ex10_taskm() {
- let out = run_file("10-taskm.bio");
- assert!(out.contains("tasks: 2"));
- assert!(out.contains("tasks done: 0"));
- assert!(out.contains("jobA sum = 15"));
- assert!(out.contains("jobB 2^4 = 16"));
-}
-
-#[test]
-fn ex11_smart_refs() {
- let out = run_file("11-smart-refs.bio");
- assert!(out.contains("u read counter = 10"));
- assert!(out.contains("read-only write → Ref refused: reference is read-only, cannot write"));
- assert!(out.contains("rw read = 10"));
- assert!(out.contains("counter after rw = 5 → 5"));
- assert!(out.contains("thread 2 a-layer = 44"));
- assert!(out.contains("thread 1 a-layer = 42"));
- assert!(out.contains("t1 = 42 t2 = 44"));
- assert!(out.contains("at [1] = refused: refused: reference is write-only, cannot read"));
- assert!(out.contains("⛔ main stream refused: refused: reference is read-only, cannot write"));
-}
-
-#[test]
-fn ex14_binary_lib() {
- let out = run_file("14-binary-lib.bio");
- assert!(out.contains("m::sin(0) = 0"));
- assert!(out.contains("m::cos(0) = 1"));
- assert!(out.contains("m::pow(2,10) = 1024"));
- assert!(out.contains("m::doubleIt(21) = 42"));
-}
-
-#[test]
-fn ex15_annotations() {
- let out = run_file("15-annotations.bio");
- assert!(out.contains("refused: stream Sealed is @unfork, cannot fork"));
- assert!(out.contains("new @unfork class → Obj refused: class Frozen is @unfork, cannot fork"));
- assert!(out.contains("onlyread get = 0"));
- assert!(out.contains("onlyread bump → refused: stream RO is @onlyread — bump() is a write method"));
- assert!(out.contains("alias get = 0"));
- assert!(out.contains("alias touch → refused: stream RA is @onlyread — touch() is a write method"));
- assert!(out.contains("marked write → refused: stream G is @onlyread — markedWrite() is a write method"));
- assert!(out.contains("marked read = 1"));
- assert!(out.contains("safe read = 1"));
-}
-
-#[test]
-fn ex16_phonebooth() {
- let out = run_file("16-phonebooth.bio");
- assert!(out.contains("t1 sum = 5050 t2 sum = 20100"));
- assert!(out.contains("global 5! = 120"));
- assert!(out.contains("global 6! = 720"));
- assert!(out.contains("direct recursion → refused: phone-booth method down does not support recursion"));
- assert!(out.contains("indirect recursion → refused: phone-booth method down2 does not support recursion"));
- assert!(out.contains("ucall recursion → refused: phone-booth method uDown does not support recursion"));
- assert!(out.contains("plain fact(10) = 3628800"));
-}
-
-fn fixtures_dir() -> PathBuf {
- PathBuf::from(env!("CARGO_MANIFEST_DIR"))
- .parent().unwrap().parent().unwrap()
- .join("tests").join("fixtures")
-}
-
-fn run_fixture(name: &str) -> String {
- let path = fixtures_dir().join(name);
- let src = std::fs::read_to_string(&path).unwrap();
- let (prog, errs) = parse_source(&src);
- assert!(errs.is_empty(), "{name} parse errors: {errs:?}");
- let mut interp = Interp::new();
- let out = interp.run(&prog);
- assert!(out.unmet_needs.is_empty(), "{name} unmet needs: {:?}", out.unmet_needs);
- out.stdout
-}
-
-#[test]
-fn new_classname_decl() {
- // 类名 = new 类():宏展开语法(parser 层)
- let out = run_fixture("test_new.bio");
- assert!(out.contains("b.val = 42"), "got: {out}");
-}
-
-#[test]
-fn arrays_sort_inplace() {
- // arr::sort() → 内部 __sort__() 原地排序
- let out = run_fixture("test_sort.bio");
- assert!(out.contains("after: [10, 20, 30, 40, 50]"), "got: {out}");
- // Arrays::sort(arr) 流方法同效
- let out2 = run_fixture("test_sort2.bio");
- assert!(out2.contains("after: [10, 20, 30, 50]"), "got: {out2}");
-}
-
-#[test]
-fn interface_basic_and_polymorphism() {
- let out = run_fixture("test_iface2.bio");
- assert!(out.contains("circle area: 12.56"), "got: {out}");
- assert!(out.contains("square area: 9"), "got: {out}");
-}
-
-#[test]
-fn interface_missing_method_rejected() {
- let src = std::fs::read_to_string(fixtures_dir().join("test_iface_bad.bio")).unwrap();
- let (prog, errs) = parse_source(&src);
- assert!(errs.is_empty(), "parse errors: {errs:?}");
- let mut interp = Interp::new();
- let out = interp.run(&prog);
- assert!(
- out.unmet_needs.iter().any(|(k, _)| k.contains("draw")),
- "expected missing draw method, got: {:?}",
- out.unmet_needs
- );
-}
diff --git a/rust/crates/bbb-wasm/Cargo.toml b/rust/crates/bbb-wasm/Cargo.toml
deleted file mode 100644
index 6667e54..0000000
--- a/rust/crates/bbb-wasm/Cargo.toml
+++ /dev/null
@@ -1,12 +0,0 @@
-[package]
-name = "bbb-wasm"
-version.workspace = true
-edition.workspace = true
-license.workspace = true
-description = "BiuBiuBiu 插件 wasm 核心:Rust 实现的格式化器/词法器(VSCode 插件用)"
-
-[lib]
-crate-type = ["cdylib", "rlib"]
-
-[dependencies]
-bbb-syntax = { path = "../bbb-syntax" }
diff --git a/rust/crates/bbb-wasm/src/lib.rs b/rust/crates/bbb-wasm/src/lib.rs
deleted file mode 100644
index b2e4e0d..0000000
--- a/rust/crates/bbb-wasm/src/lib.rs
+++ /dev/null
@@ -1,265 +0,0 @@
-//! bbb-wasm — BiuBiuBiu 格式化器/词法器,编译为 wasm32 供 VSCode 插件调用。
-//!
-//! 导出(手写 C ABI,零 wasm-bindgen 依赖):
-//! - `format(ptr, len, out_cap) -> usize`:格式化源码,写入输出缓冲,返回长度
-//! - `format_len(ptr, len) -> usize`:预计算格式化后长度(JS 分配缓冲)
-//!
-//! formatter 规则(对齐旧 formatter.js 0.16.0 行为,17 examples 幂等):
-//! - 4 空格缩进(块级);`{` 换行、`}` 缩进减一换行
-//! - 运算符两侧空格;`::`/`.`/`,`/`;`/括号规则明确
-//! - 关键字与 `(` 之间空格(if/while/for);函数调用名与 `(` 无空格
-//! - 字符串/字符 token 重新包裹引号(内容原样);注释原样
-//! - 连续空行压缩为单空行;行尾无空格
-
-use std::slice;
-
-/// 格式化输入(len 字节 UTF-8),写入 out(out_cap 字节),返回写入字节数。
-#[no_mangle]
-pub extern "C" fn format(src_ptr: *const u8, src_len: usize, out_ptr: *mut u8, out_cap: usize) -> usize {
- if src_ptr.is_null() || src_len == 0 || out_ptr.is_null() || out_cap == 0 {
- return 0;
- }
- let src = unsafe { slice::from_raw_parts(src_ptr, src_len) };
- let Some(text) = std::str::from_utf8(src).ok() else { return 0 };
- let out = fmt(text);
- let bytes = out.as_bytes();
- if bytes.len() > out_cap {
- return 0;
- }
- unsafe {
- std::ptr::copy_nonoverlapping(bytes.as_ptr(), out_ptr, bytes.len());
- }
- bytes.len()
-}
-
-/// 预计算格式化后长度。
-#[no_mangle]
-pub extern "C" fn format_len(src_ptr: *const u8, src_len: usize) -> usize {
- if src_ptr.is_null() || src_len == 0 {
- return 0;
- }
- let src = unsafe { slice::from_raw_parts(src_ptr, src_len) };
- let Some(text) = std::str::from_utf8(src).ok() else { return 0 };
- fmt(text).len()
-}
-
-use bbb_syntax::lexer::{Token, TokenKind};
-
-const CONTROL_KW: &[&str] = &["if", "while", "for", "else"];
-const SPACE_OP: &[&str] = &[
- "+", "-", "*", "/", "%", "=", "==", "!=", "<", ">", "<=", ">=",
- "&&", "||", "+=", "-=", "*=", "/=", "%=",
-];
-
-fn is_ident_like(t: &Token) -> bool {
- matches!(t.kind, TokenKind::Ident | TokenKind::Keyword)
-}
-
-/// Rust 格式化器主体。
-pub fn fmt(src: &str) -> String {
- let mut toks = Vec::new();
- if bbb_syntax::lexer::tokenize(src, &mut toks).is_err() {
- return src.to_string(); // 词法错误:原样返回
- }
- let mut out = String::new();
- let mut indent: usize = 0;
- let mut prev: Option = None;
- let mut line_start = true;
- let mut paren_depth: usize = 0;
-
- for t in toks.iter() {
- if t.kind == TokenKind::Eof {
- break;
- }
- // 行首缩进(; 和 , 不缩进;} 用 indent-1)
- if line_start {
- if t.text != ";" && t.text != "," {
- let n = if t.text == "}" { indent.saturating_sub(1) } else { indent };
- for _ in 0..n * 4 {
- out.push(' ');
- }
- }
- line_start = false;
- }
-
- let text = t.text;
- let is_block_open = text == "{";
- let is_block_close = text == "}";
- let is_semi = text == ";";
- let is_comma = text == ",";
- let is_paren_open = text == "(";
- let is_paren_close = text == ")";
- if is_paren_open {
- paren_depth += 1;
- } else if is_paren_close && paren_depth > 0 {
- paren_depth -= 1;
- }
- let is_colon2 = text == "::";
- let is_dot = text == ".";
- let is_comment = text.starts_with("//") || text.starts_with("/*");
-
- // 前置空格决策
- let space_before = if let Some(p) = &prev {
- if line_start {
- false
- } else if p.text == "{" || p.text == ";" || p.text == "," || p.text == "(" || is_colon2 || is_dot {
- false
- } else if is_block_close {
- // } 前无空格(但 `} else {` 由 else 关键字前补空格处理)
- false
- } else if is_semi || is_comma || is_paren_close {
- false
- } else if is_paren_open {
- // 控制关键字后空格;调用名后无
- is_ident_like(p) && CONTROL_KW.contains(&p.text)
- } else if is_comment {
- true
- } else if is_block_open {
- // { 前空格:Main {、while (...) {、} else {
- true
- } else if is_ident_like(t) && is_ident_like(p) {
- // 标识符/关键字相邻 → 空格(program main、void exec)
- true
- } else if is_ident_like(t) || is_ident_like(p) {
- // 标识符与运算符/括号之间:看运算符
- SPACE_OP.contains(&text) || SPACE_OP.contains(&p.text) || text == "=" || p.text == "="
- } else {
- // 运算符之间
- SPACE_OP.contains(&text) || SPACE_OP.contains(&p.text)
- }
- } else {
- false
- };
- if space_before && !out.ends_with(' ') && !out.ends_with('\n') {
- out.push(' ');
- }
- // ) 前无空格(分号后 push 的空格在此清理)
- if is_paren_close && out.ends_with(' ') {
- out.pop();
- }
-
- // 输出 token 文本(字符串/字符重新加引号)
- match t.kind {
- TokenKind::Str => {
- out.push('"');
- out.push_str(text);
- out.push('"');
- }
- TokenKind::Char => {
- out.push('\'');
- out.push_str(text);
- out.push('\'');
- }
- _ => out.push_str(text),
- }
-
- // 后置处理
- if is_block_open {
- out.push('\n');
- indent += 1;
- line_start = true;
- } else if is_block_close {
- if indent > 0 {
- indent -= 1;
- }
- out.push('\n');
- line_start = true;
- } else if is_semi {
- if paren_depth == 0 {
- out.push('\n');
- line_start = true;
- } else {
- out.push(' '); // for 头内分号不换行
- }
- } else if is_comma {
- out.push(' ');
- } else if is_comment && text.starts_with("//") {
- out.push('\n');
- line_start = true;
- }
- prev = Some(*t);
- }
-
- // 清理:空行压缩 + 行尾空格
- let mut cleaned = String::new();
- let mut nl = 0usize;
- for c in out.chars() {
- if c == '\n' {
- nl += 1;
- if nl <= 2 {
- cleaned.push('\n');
- }
- } else {
- nl = 0;
- cleaned.push(c);
- }
- }
- let lines: Vec<&str> = cleaned.split('\n').collect();
- let trimmed: Vec = lines.iter().map(|l| l.trim_end().to_string()).collect();
- let mut result = trimmed.join("\n");
- while result.ends_with('\n') {
- result.pop();
- }
- result.push('\n');
- result
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- fn fmt2(s: &str) -> String {
- let a = fmt(s);
- let b = fmt(&a);
- assert_eq!(a, b, "格式化不幂等:\n---1---\n{a}\n---2---\n{b}");
- a
- }
-
- #[test]
- fn hello() {
- let src = r#"program main;
-Main { void exec() { CIO::println("Hello"); } }"#;
- let out = fmt2(src);
- assert!(
- out.contains("program main;\nMain {\n void exec() {\n CIO::println(\"Hello\");\n }\n}"),
- "{out}"
- );
- }
-
- #[test]
- fn control_flow() {
- let src = "program main;\nMain {\nvoid exec() {\nALL i=1;\nwhile(i<=10){i=i+1;}\nfor(ALL k=1;k<=5;k=k+1;){}\n}\n}";
- let out = fmt2(src);
- assert!(out.contains("ALL i = 1;"), "{out}");
- assert!(out.contains("while (i <= 10) {"), "{out}");
- assert!(out.contains("for (ALL k = 1; k <= 5; k = k + 1;) {"), "{out}");
- assert!(out.contains(" void exec() {"), "{out}");
- }
-
- #[test]
- fn strings_untouched() {
- let src = r#"CIO::println("a + b keep spaces");"#;
- let out = fmt2(src);
- assert!(out.contains("\"a + b keep spaces\""), "{out}");
- }
-
- #[test]
- fn idempotent_on_examples() {
- let ex = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
- .parent().unwrap().parent().unwrap().parent().unwrap().join("examples");
- let mut n = 0;
- if let Ok(entries) = std::fs::read_dir(&ex) {
- for e in entries.flatten() {
- let p = e.path();
- if p.extension().map(|x| x == "bio").unwrap_or(false) {
- let src = std::fs::read_to_string(&p).unwrap();
- let a = fmt(&src);
- let b = fmt(&a);
- assert_eq!(a, b, "not idempotent: {}", p.display());
- n += 1;
- }
- }
- }
- assert!(n >= 10, "examples 太少: {n}");
- }
-}
diff --git a/rust/tests/fixtures/dbg_attrs.bio b/rust/tests/fixtures/dbg_attrs.bio
deleted file mode 100644
index 9459014..0000000
--- a/rust/tests/fixtures/dbg_attrs.bio
+++ /dev/null
@@ -1,13 +0,0 @@
-program main;
-
-Main {
- void exec() {
- Array a = new Array(3);
- a::set(0, 30); a::set(1, 10); a::set(2, 20);
- // 直接看 a 的属性
- CIO::println("len:", get a::len());
- CIO::println("data type check");
- // 用 Arrays::all 拿注册表看
- CIO::println("count:", get Arrays::count());
- }
-}
diff --git a/rust/tests/fixtures/test_arrays_all.bio b/rust/tests/fixtures/test_arrays_all.bio
deleted file mode 100644
index 8e0e1de..0000000
--- a/rust/tests/fixtures/test_arrays_all.bio
+++ /dev/null
@@ -1,15 +0,0 @@
-program main;
-
-Main {
- void exec() {
- ALL a = new Array(2);
- a::set(0, 10); a::set(1, 20);
- ALL v = Arrays::vector();
- v::push(5);
- CIO::println("count:", get Arrays::count());
- ALL all_arr = get Arrays::all();
- CIO::println("all:", all_arr);
- CIO::println("all len:", get all_arr::len());
- CIO::println("all[0]:", all_arr[0]);
- }
-}
diff --git a/rust/tests/fixtures/test_err.bio b/rust/tests/fixtures/test_err.bio
deleted file mode 100644
index 6cd906d..0000000
--- a/rust/tests/fixtures/test_err.bio
+++ /dev/null
@@ -1 +0,0 @@
-program main; Main { void exec() { int x = ; } }
diff --git a/rust/tests/fixtures/test_iface.bio b/rust/tests/fixtures/test_iface.bio
deleted file mode 100644
index 569ddce..0000000
--- a/rust/tests/fixtures/test_iface.bio
+++ /dev/null
@@ -1,21 +0,0 @@
-program main;
-
-Interface Shape {
- double area();
- void draw();
-}
-
-Class Circle implements Shape {
- double area() { res 3.14 * this::r * this::r; }
- void draw() { CIO::println("drawing circle"); }
- double r;
-}
-
-Main {
- void exec() {
- Circle c = new Circle();
- c::r = 2.0;
- CIO::println("area:", get c::area());
- c::draw();
- }
-}
diff --git a/rust/tests/fixtures/test_iface2.bio b/rust/tests/fixtures/test_iface2.bio
deleted file mode 100644
index 200b6c1..0000000
--- a/rust/tests/fixtures/test_iface2.bio
+++ /dev/null
@@ -1,26 +0,0 @@
-program main;
-
-Interface Shape {
- double area();
-}
-
-Class Circle implements Shape {
- double area() { res 3.14 * this::r * this::r; }
- double r;
-}
-
-Class Square implements Shape {
- double area() { res this::side * this::side; }
- double side;
-}
-
-Main {
- void exec() {
- Shape s = new Circle();
- s::r = 2.0;
- CIO::println("circle area:", get s::area());
- Shape t = new Square();
- t::side = 3.0;
- CIO::println("square area:", get t::area());
- }
-}
diff --git a/rust/tests/fixtures/test_iface_bad.bio b/rust/tests/fixtures/test_iface_bad.bio
deleted file mode 100644
index 0619fd2..0000000
--- a/rust/tests/fixtures/test_iface_bad.bio
+++ /dev/null
@@ -1,16 +0,0 @@
-program main;
-
-Interface Shape {
- double area();
- void draw();
-}
-
-Class Circle implements Shape {
- double area() { res 0.0; }
-}
-
-Main {
- void exec() {
- Circle c = new Circle();
- }
-}
diff --git a/rust/tests/fixtures/test_new.bio b/rust/tests/fixtures/test_new.bio
deleted file mode 100644
index 12c50ab..0000000
--- a/rust/tests/fixtures/test_new.bio
+++ /dev/null
@@ -1,13 +0,0 @@
-program main;
-
-Class Box {
- void __init__(v int) { this::val = v; }
- int val;
-}
-
-Main {
- void exec() {
- Box b = new Box(42);
- CIO::println("b.val =", get b::val);
- }
-}
diff --git a/rust/tests/fixtures/test_new2.bio b/rust/tests/fixtures/test_new2.bio
deleted file mode 100644
index d71944a..0000000
--- a/rust/tests/fixtures/test_new2.bio
+++ /dev/null
@@ -1,16 +0,0 @@
-program main;
-
-Class Box {
- void __init__(v int) { this::val = v; }
- int val;
-}
-
-Main {
- void exec() {
- Box b = new Box(99);
- CIO::println("b.val =", get b::val);
- Array a = new Array(3);
- a::set(0, 7);
- CIO::println("a[0] =", a[0], "len:", get a::len());
- }
-}
diff --git a/rust/tests/fixtures/test_obj_llvm.bio b/rust/tests/fixtures/test_obj_llvm.bio
deleted file mode 100644
index 46a58b6..0000000
--- a/rust/tests/fixtures/test_obj_llvm.bio
+++ /dev/null
@@ -1,25 +0,0 @@
-program main;
-
-Class Student {
- void __init__(age int, solve double) {
- this::age = age;
- this::solve = solve;
- }
- int getAge() { res this::age; }
- double getSolve() { res this::solve; }
- void bump() { this::age = this::age + 1; }
- int age;
- double solve;
-}
-
-Main {
- void exec() {
- Student s = new Student(12, 1.2);
- CIO::println("age:", get s::getAge());
- CIO::println("solve:", get s::getSolve());
- s::bump();
- CIO::println("after bump:", get s::getAge());
- s::age = 20;
- CIO::println("direct:", s::age);
- }
-}
diff --git a/rust/tests/fixtures/test_ref2.bio b/rust/tests/fixtures/test_ref2.bio
deleted file mode 100644
index 5c305fd..0000000
--- a/rust/tests/fixtures/test_ref2.bio
+++ /dev/null
@@ -1,11 +0,0 @@
-program main;
-
-Main {
- void exec() {
- int x = 42;
- &r u int p = &x;
- CIO::println("p =", get p);
- p = 99;
- CIO::println("x =", x);
- }
-}
diff --git a/rust/tests/fixtures/test_ref3.bio b/rust/tests/fixtures/test_ref3.bio
deleted file mode 100644
index 74d9dd4..0000000
--- a/rust/tests/fixtures/test_ref3.bio
+++ /dev/null
@@ -1,15 +0,0 @@
-program main;
-
-Class Box {
- void take(p &r u int) {
- CIO::println("got:", get p);
- }
-}
-
-Main {
- void exec() {
- int x = 42;
- Box b = new Box();
- b::take(&x);
- }
-}
diff --git a/rust/tests/fixtures/test_ref4.bio b/rust/tests/fixtures/test_ref4.bio
deleted file mode 100644
index 8193383..0000000
--- a/rust/tests/fixtures/test_ref4.bio
+++ /dev/null
@@ -1,16 +0,0 @@
-program main;
-
-Class Box {
- void bump(p &w u int) {
- p = get p + 1;
- }
-}
-
-Main {
- void exec() {
- int x = 41;
- Box b = new Box();
- b::bump(&x);
- CIO::println("x =", x);
- }
-}
diff --git a/rust/tests/fixtures/test_refparam.bio b/rust/tests/fixtures/test_refparam.bio
deleted file mode 100644
index d154aea..0000000
--- a/rust/tests/fixtures/test_refparam.bio
+++ /dev/null
@@ -1,15 +0,0 @@
-program main;
-
-Class Box {
- void take(&r u int p) {
- CIO::println("got:", get p);
- }
-}
-
-Main {
- void exec() {
- int x = 42;
- Box b = new Box();
- b::take(&x);
- }
-}
diff --git a/rust/tests/fixtures/test_sort.bio b/rust/tests/fixtures/test_sort.bio
deleted file mode 100644
index 9094839..0000000
--- a/rust/tests/fixtures/test_sort.bio
+++ /dev/null
@@ -1,11 +0,0 @@
-program main;
-
-Main {
- void exec() {
- Array a = new Array(5);
- a::set(0, 30); a::set(1, 10); a::set(2, 50); a::set(3, 20); a::set(4, 40);
- CIO::println("before:", a);
- a::sort();
- CIO::println("after: ", a);
- }
-}
diff --git a/rust/tests/fixtures/test_sort2.bio b/rust/tests/fixtures/test_sort2.bio
deleted file mode 100644
index 202543c..0000000
--- a/rust/tests/fixtures/test_sort2.bio
+++ /dev/null
@@ -1,11 +0,0 @@
-program main;
-
-Main {
- void exec() {
- Array a = new Array(4);
- a::set(0, 30); a::set(1, 10); a::set(2, 50); a::set(3, 20);
- CIO::println("before:", a);
- Arrays::sort(a);
- CIO::println("after: ", a);
- }
-}