@raviqqe/stak 0.11.9 → 0.11.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -159
- package/package.json +1 -1
- package/stak_wasm.d.ts +6 -6
- package/stak_wasm.js +31 -32
- package/stak_wasm_bg.wasm +0 -0
package/README.md
CHANGED
|
@@ -16,16 +16,23 @@ Stak Scheme aims to be:
|
|
|
16
16
|
- A subset of [Chibi Scheme](https://github.com/ashinn/chibi-scheme), [Gauche](https://github.com/shirok/Gauche), and [Guile](https://www.gnu.org/software/guile/)
|
|
17
17
|
- A portable scripting environment that supports even no-`std` and no-`alloc` platforms
|
|
18
18
|
|
|
19
|
-
For
|
|
19
|
+
For the usage and examples, see [the documentation](https://raviqqe.com/stak/install).
|
|
20
20
|
|
|
21
21
|
## Install
|
|
22
22
|
|
|
23
|
-
###
|
|
23
|
+
### Commands
|
|
24
24
|
|
|
25
|
-
To install the
|
|
25
|
+
To install [the interpreter](https://crates.io/crates/stak) and [REPL](https://crates.io/crates/stak-repl), run:
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
28
|
cargo install stak
|
|
29
|
+
cargo install stak-repl
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
To install [the minimal interpreter](https://crates.io/crates/mstak), run:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
cargo install mstak
|
|
29
36
|
```
|
|
30
37
|
|
|
31
38
|
### Libraries
|
|
@@ -38,166 +45,13 @@ cargo add --build stak-build
|
|
|
38
45
|
cargo install stak-compile
|
|
39
46
|
```
|
|
40
47
|
|
|
41
|
-
For full examples, see [the `examples` directory](https://github.com/raviqqe/stak/tree/main/examples).
|
|
42
|
-
|
|
43
|
-
## Examples
|
|
44
|
-
|
|
45
|
-
### Dynamic scripting in Rust
|
|
46
|
-
|
|
47
|
-
First, prepare a Scheme script named `src/fight.scm`:
|
|
48
|
-
|
|
49
|
-
```scheme
|
|
50
|
-
; Import a base library and the library named `(stak rust)` for Rust integration.
|
|
51
|
-
(import (scheme base) (stak rust))
|
|
52
|
-
|
|
53
|
-
; Make two people with a number of pies they have and their dodge rates.
|
|
54
|
-
(define me (make-person 4 0.2))
|
|
55
|
-
(define friend (make-person 2 0.6))
|
|
56
|
-
|
|
57
|
-
; The fight begins. Let's throw pies to each other!
|
|
58
|
-
(do ()
|
|
59
|
-
((or
|
|
60
|
-
(person-wasted me)
|
|
61
|
-
(person-wasted friend)
|
|
62
|
-
(and
|
|
63
|
-
(zero? (person-pies me))
|
|
64
|
-
(zero? (person-pies friend)))))
|
|
65
|
-
(person-throw-pie me friend)
|
|
66
|
-
(person-throw-pie friend me))
|
|
67
|
-
|
|
68
|
-
; Output the winner.
|
|
69
|
-
(write-string
|
|
70
|
-
(cond
|
|
71
|
-
((person-wasted friend)
|
|
72
|
-
"You won!")
|
|
73
|
-
((person-wasted me)
|
|
74
|
-
"You lost...")
|
|
75
|
-
(else
|
|
76
|
-
"Draw...")))
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Then, add a build script at `build.rs` to build the Scheme source file
|
|
80
|
-
into bytecode.
|
|
81
|
-
|
|
82
|
-
```rust no_run
|
|
83
|
-
use stak_build::{build_r7rs, BuildError};
|
|
84
|
-
|
|
85
|
-
fn main() -> Result<(), BuildError> {
|
|
86
|
-
build_r7rs()
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
Finally, you can embed and run the Scheme script in a Rust program.
|
|
91
|
-
|
|
92
|
-
```rust
|
|
93
|
-
use any_fn::{r#fn, Ref};
|
|
94
|
-
use core::error::Error;
|
|
95
|
-
use rand::random;
|
|
96
|
-
use stak::{
|
|
97
|
-
engine::{Engine, EngineError},
|
|
98
|
-
include_module,
|
|
99
|
-
module::UniversalModule,
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const HEAP_SIZE: usize = 1 << 16;
|
|
103
|
-
|
|
104
|
-
/// A person who holds pies to throw.
|
|
105
|
-
struct Person {
|
|
106
|
-
pies: usize,
|
|
107
|
-
dodge: f64,
|
|
108
|
-
wasted: bool,
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
impl Person {
|
|
112
|
-
/// Creates a person.
|
|
113
|
-
pub fn new(pies: usize, dodge: f64) -> Self {
|
|
114
|
-
Self {
|
|
115
|
-
pies,
|
|
116
|
-
dodge,
|
|
117
|
-
wasted: false,
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/// Returns a number of pies the person has.
|
|
122
|
-
pub fn pies(&self) -> usize {
|
|
123
|
-
self.pies
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/// Returns `true` if a person is wasted.
|
|
127
|
-
pub fn wasted(&self) -> bool {
|
|
128
|
-
self.wasted
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/// Throws a pie to another person.
|
|
132
|
-
pub fn throw_pie(&mut self, other: &mut Person) {
|
|
133
|
-
if self.pies == 0 || self.wasted {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
self.pies -= 1;
|
|
138
|
-
|
|
139
|
-
if random::<f64>() > other.dodge {
|
|
140
|
-
other.wasted = true;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
fn main() -> Result<(), Box<dyn Error>> {
|
|
146
|
-
// Include and run the Scheme module.
|
|
147
|
-
run_scheme(&include_module!("fight.scm"))?;
|
|
148
|
-
|
|
149
|
-
Ok(())
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
fn run_scheme(module: &UniversalModule) -> Result<(), EngineError> {
|
|
153
|
-
// Initialize a heap memory for a Scheme scripting engine.
|
|
154
|
-
let mut heap = [Default::default(); HEAP_SIZE];
|
|
155
|
-
// Define Rust functions to pass to the engine.
|
|
156
|
-
let mut functions = [
|
|
157
|
-
("make-person", r#fn(Person::new)),
|
|
158
|
-
("person-pies", r#fn::<(Ref<_>,), _>(Person::pies)),
|
|
159
|
-
("person-wasted", r#fn::<(Ref<_>,), _>(Person::wasted)),
|
|
160
|
-
("person-throw-pie", r#fn(Person::throw_pie)),
|
|
161
|
-
];
|
|
162
|
-
// Initialize the engine.
|
|
163
|
-
let mut engine = Engine::new(&mut heap, &mut functions)?;
|
|
164
|
-
|
|
165
|
-
// Finally, run the module!
|
|
166
|
-
engine.run(module)
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
48
|
## Performance
|
|
171
49
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
The Stak Scheme interpreter runs 1.6 to 2.3 times slower than Python 3 at computationally heavy tasks depending on its configuration and benchmarks. For all the benchmark results, see [the GitHub Action](https://github.com/raviqqe/stak/actions/workflows/bench.yaml).
|
|
175
|
-
|
|
176
|
-
- Baseline: Python 3.13
|
|
177
|
-
- Environment: Ubuntu 24.04, x86-64
|
|
178
|
-
|
|
179
|
-
| Benchmark | Stak (minimal [^1]) | Stak (full [^2]) |
|
|
180
|
-
| ---------------- | ------------------: | ---------------: |
|
|
181
|
-
| Fibonacci number | 1.80x slower | 1.98x slower |
|
|
182
|
-
| Integer sum | 1.61x slower | 1.87x slower |
|
|
183
|
-
| Tak function | 2.10x slower | 2.27x slower |
|
|
184
|
-
|
|
185
|
-
### Startup benchmarks
|
|
186
|
-
|
|
187
|
-
Although Stak Scheme's minimality comes at the cost of speed, it is very fast at startup.
|
|
188
|
-
|
|
189
|
-
This means that Stak Scheme is suitable for embedding many small pieces of Scheme programs in Rust due to its tiny overhead on program initialization.
|
|
190
|
-
|
|
191
|
-
- Environment: Ubuntu 24.04, x86-64
|
|
192
|
-
|
|
193
|
-
| Benchmark | Stak (full [^2]) | Lua 5.4 |
|
|
194
|
-
| ---------------- | ---------------: | ------: |
|
|
195
|
-
| Empty program | 0.534 us | 48.9 us |
|
|
196
|
-
| Integer addition | 22.9 us | 50.0 us |
|
|
50
|
+
See [Performance](https://raviqqe.com/stak/performance).
|
|
197
51
|
|
|
198
|
-
|
|
52
|
+
## Limitations
|
|
199
53
|
|
|
200
|
-
[
|
|
54
|
+
See [Limitations](https://raviqqe.com/stak/limitations).
|
|
201
55
|
|
|
202
56
|
## References
|
|
203
57
|
|
package/package.json
CHANGED
package/stak_wasm.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/* tslint:disable */
|
|
2
2
|
/* eslint-disable */
|
|
3
|
+
/**
|
|
4
|
+
* Compiles source codes in Scheme.
|
|
5
|
+
*/
|
|
6
|
+
export function compile(source: string): Uint8Array;
|
|
3
7
|
/**
|
|
4
8
|
* Runs a Scheme script with standard input and returns its standard output.
|
|
5
9
|
*/
|
|
@@ -8,10 +12,6 @@ export function run(source: string, input: Uint8Array, heap_size: number): Uint8
|
|
|
8
12
|
* Interprets bytecode with standard input and returns its standard output.
|
|
9
13
|
*/
|
|
10
14
|
export function interpret(bytecode: Uint8Array, input: Uint8Array, heap_size: number): Uint8Array;
|
|
11
|
-
/**
|
|
12
|
-
* Compiles source codes in Scheme.
|
|
13
|
-
*/
|
|
14
|
-
export function compile(source: string): Uint8Array;
|
|
15
15
|
/**
|
|
16
16
|
* Runs a REPL interpreter.
|
|
17
17
|
*/
|
|
@@ -33,8 +33,8 @@ export interface InitOutput {
|
|
|
33
33
|
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
34
34
|
readonly __externref_table_dealloc: (a: number) => void;
|
|
35
35
|
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
|
36
|
-
readonly
|
|
37
|
-
readonly
|
|
36
|
+
readonly closure19_externref_shim: (a: number, b: number, c: any) => void;
|
|
37
|
+
readonly closure43_externref_shim: (a: number, b: number, c: any, d: any) => void;
|
|
38
38
|
readonly __wbindgen_start: () => void;
|
|
39
39
|
}
|
|
40
40
|
|
package/stak_wasm.js
CHANGED
|
@@ -198,13 +198,6 @@ function getDataViewMemory0() {
|
|
|
198
198
|
return cachedDataViewMemory0;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
function passArray8ToWasm0(arg, malloc) {
|
|
202
|
-
const ptr = malloc(arg.length * 1, 1) >>> 0;
|
|
203
|
-
getUint8ArrayMemory0().set(arg, ptr / 1);
|
|
204
|
-
WASM_VECTOR_LEN = arg.length;
|
|
205
|
-
return ptr;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
201
|
function takeFromExternrefTable0(idx) {
|
|
209
202
|
const value = wasm.__wbindgen_export_2.get(idx);
|
|
210
203
|
wasm.__externref_table_dealloc(idx);
|
|
@@ -215,6 +208,29 @@ function getArrayU8FromWasm0(ptr, len) {
|
|
|
215
208
|
ptr = ptr >>> 0;
|
|
216
209
|
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
|
|
217
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* Compiles source codes in Scheme.
|
|
213
|
+
* @param {string} source
|
|
214
|
+
* @returns {Uint8Array}
|
|
215
|
+
*/
|
|
216
|
+
export function compile(source) {
|
|
217
|
+
const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
218
|
+
const len0 = WASM_VECTOR_LEN;
|
|
219
|
+
const ret = wasm.compile(ptr0, len0);
|
|
220
|
+
if (ret[3]) {
|
|
221
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
222
|
+
}
|
|
223
|
+
var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
|
|
224
|
+
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
|
225
|
+
return v2;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function passArray8ToWasm0(arg, malloc) {
|
|
229
|
+
const ptr = malloc(arg.length * 1, 1) >>> 0;
|
|
230
|
+
getUint8ArrayMemory0().set(arg, ptr / 1);
|
|
231
|
+
WASM_VECTOR_LEN = arg.length;
|
|
232
|
+
return ptr;
|
|
233
|
+
}
|
|
218
234
|
/**
|
|
219
235
|
* Runs a Scheme script with standard input and returns its standard output.
|
|
220
236
|
* @param {string} source
|
|
@@ -257,23 +273,6 @@ export function interpret(bytecode, input, heap_size) {
|
|
|
257
273
|
return v3;
|
|
258
274
|
}
|
|
259
275
|
|
|
260
|
-
/**
|
|
261
|
-
* Compiles source codes in Scheme.
|
|
262
|
-
* @param {string} source
|
|
263
|
-
* @returns {Uint8Array}
|
|
264
|
-
*/
|
|
265
|
-
export function compile(source) {
|
|
266
|
-
const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
267
|
-
const len0 = WASM_VECTOR_LEN;
|
|
268
|
-
const ret = wasm.compile(ptr0, len0);
|
|
269
|
-
if (ret[3]) {
|
|
270
|
-
throw takeFromExternrefTable0(ret[2]);
|
|
271
|
-
}
|
|
272
|
-
var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
|
|
273
|
-
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
|
274
|
-
return v2;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
276
|
/**
|
|
278
277
|
* Runs a REPL interpreter.
|
|
279
278
|
* @param {number} heap_size
|
|
@@ -285,11 +284,11 @@ export function repl(heap_size) {
|
|
|
285
284
|
}
|
|
286
285
|
|
|
287
286
|
function __wbg_adapter_24(arg0, arg1, arg2) {
|
|
288
|
-
wasm.
|
|
287
|
+
wasm.closure19_externref_shim(arg0, arg1, arg2);
|
|
289
288
|
}
|
|
290
289
|
|
|
291
|
-
function
|
|
292
|
-
wasm.
|
|
290
|
+
function __wbg_adapter_39(arg0, arg1, arg2, arg3) {
|
|
291
|
+
wasm.closure43_externref_shim(arg0, arg1, arg2, arg3);
|
|
293
292
|
}
|
|
294
293
|
|
|
295
294
|
async function __wbg_load(module, imports) {
|
|
@@ -341,7 +340,7 @@ function __wbg_get_imports() {
|
|
|
341
340
|
const a = state0.a;
|
|
342
341
|
state0.a = 0;
|
|
343
342
|
try {
|
|
344
|
-
return
|
|
343
|
+
return __wbg_adapter_39(a, state0.b, arg0, arg1);
|
|
345
344
|
} finally {
|
|
346
345
|
state0.a = a;
|
|
347
346
|
}
|
|
@@ -363,7 +362,7 @@ function __wbg_get_imports() {
|
|
|
363
362
|
const ret = arg0.queueMicrotask;
|
|
364
363
|
return ret;
|
|
365
364
|
};
|
|
366
|
-
imports.wbg.
|
|
365
|
+
imports.wbg.__wbg_readstdin_fa4d8873c2345612 = function() {
|
|
367
366
|
const ret = read_stdin();
|
|
368
367
|
return ret;
|
|
369
368
|
};
|
|
@@ -395,11 +394,11 @@ function __wbg_get_imports() {
|
|
|
395
394
|
const ret = arg0.then(arg1, arg2);
|
|
396
395
|
return ret;
|
|
397
396
|
};
|
|
398
|
-
imports.wbg.
|
|
397
|
+
imports.wbg.__wbg_writestderr_d506f2ff6c400d2a = function(arg0) {
|
|
399
398
|
const ret = write_stderr(arg0);
|
|
400
399
|
return ret;
|
|
401
400
|
};
|
|
402
|
-
imports.wbg.
|
|
401
|
+
imports.wbg.__wbg_writestdout_4b685361b836fbaf = function(arg0) {
|
|
403
402
|
const ret = write_stdout(arg0);
|
|
404
403
|
return ret;
|
|
405
404
|
};
|
|
@@ -413,7 +412,7 @@ function __wbg_get_imports() {
|
|
|
413
412
|
return ret;
|
|
414
413
|
};
|
|
415
414
|
imports.wbg.__wbindgen_closure_wrapper84 = function(arg0, arg1, arg2) {
|
|
416
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
415
|
+
const ret = makeMutClosure(arg0, arg1, 20, __wbg_adapter_24);
|
|
417
416
|
return ret;
|
|
418
417
|
};
|
|
419
418
|
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
|
package/stak_wasm_bg.wasm
CHANGED
|
Binary file
|