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); - } -}