1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#![doc = include_str!("../README.md")]
#![cfg_attr(all(doc, not(doctest)), feature(doc_cfg))]
pub mod device {
//! I/O devices.
//!
//! ## Examples
//!
//! ### Communication via standard I/O
//!
//! You can pass in-memory standard input and output to Scheme scripts for
//! communicate information between Rust and Scheme.
//!
//! ```rust
//! use core::{
//! error::Error,
//! str::{self, FromStr},
//! };
//! use stak::{
//! device::ReadWriteDevice,
//! file::VoidFileSystem,
//! include_module,
//! module::Module,
//! process_context::VoidProcessContext,
//! r7rs::{SmallError, SmallPrimitiveSet},
//! time::VoidClock,
//! vm::Vm,
//! };
//!
//! const BUFFER_SIZE: usize = 1 << 8;
//! const HEAP_SIZE: usize = 1 << 16;
//!
//! fn main() -> Result<(), Box<dyn Error>> {
//! let input = 15;
//! let mut output = vec![];
//! let mut error = vec![];
//!
//! run(
//! &include_module!("fibonacci.scm").bytecode(),
//! input.to_string().as_bytes(),
//! &mut output,
//! &mut error,
//! )?;
//!
//! // If stderr is not empty, we assume that some error has occurred.
//! if !error.is_empty() {
//! return Err(str::from_utf8(&error)?.into());
//! }
//!
//! // Decode and test the output.
//! assert_eq!(isize::from_str(&str::from_utf8(&output)?)?, 610);
//!
//! Ok(())
//! }
//!
//! fn run(
//! bytecode: &[u8],
//! input: &[u8],
//! output: &mut Vec<u8>,
//! error: &mut Vec<u8>,
//! ) -> Result<(), SmallError> {
//! let mut heap = [Default::default(); HEAP_SIZE];
//! let mut vm = Vm::new(
//! &mut heap,
//! SmallPrimitiveSet::new(
//! // Create and attach an in-memory I/O device.
//! ReadWriteDevice::new(input, output, error),
//! VoidFileSystem::new(),
//! VoidProcessContext::new(),
//! VoidClock::new(),
//! ),
//! )?;
//!
//! vm.run(bytecode.iter().copied())
//! }
//! ```
pub use stak_device::*;
}
#[cfg(feature = "alloc")]
pub mod dynamic {
//! Dynamically-defined primitives.
pub use stak_dynamic::*;
}
#[cfg(feature = "alloc")]
pub mod engine {
//! A scripting engine.
pub use stak_engine::*;
}
pub mod file {
//! File systems.
pub use stak_file::*;
}
pub mod module {
//! Modules.
pub use stak_module::*;
}
pub mod process_context {
//! Process context.
pub use stak_process_context::*;
}
pub mod r7rs {
//! Primitives for R7RS Scheme.
pub use stak_r7rs::*;
}
pub mod sac {
//! Standalone complex.
pub use stak_sac::*;
}
pub mod time {
//! Time measurement.
pub use stak_time::*;
}
pub mod vm {
//! A virtual machine and its runtime values.
//!
//! # Examples
//!
//! ## Embedding Scheme scripts in Rust with a custom virtual machine
//!
//! First, prepare a Scheme script named `src/hello.scm`.
//!
//! ```scheme
//! (import (scheme base))
//!
//! (write-string "Hello, world!\n")
//! ```
//!
//! Then, add a build script at `build.rs` to build the Scheme source file
//! into bytecode.
//!
//! ```rust no_run
//! use stak_build::{build_r7rs, BuildError};
//!
//! fn main() -> Result<(), BuildError> {
//! build_r7rs()
//! }
//! ```
//!
//! Finally, you can include the Scheme script into a Rust program using
//! [`include_module`][super::include_module] macro and run the script.
//!
//! ```rust
//! use core::error::Error;
//! use stak::{
//! device::StdioDevice,
//! file::VoidFileSystem,
//! include_module,
//! process_context::VoidProcessContext,
//! module::Module,
//! r7rs::{SmallError, SmallPrimitiveSet},
//! time::VoidClock,
//! vm::Vm,
//! };
//!
//! const HEAP_SIZE: usize = 1 << 16;
//!
//! fn main() -> Result<(), Box<dyn Error>> {
//! // Include and run a Scheme script in the bytecode format built by the build script above.
//! run(&include_module!("hello.scm").bytecode())?;
//!
//! Ok(())
//! }
//!
//! fn run(bytecode: &[u8]) -> Result<(), SmallError> {
//! // Prepare a heap memory of a virtual machine.
//! let mut heap = [Default::default(); HEAP_SIZE];
//! // Create a virtual machine with its heap memory primitive procedures.
//! let mut vm = Vm::new(
//! &mut heap,
//! SmallPrimitiveSet::new(
//! // Attach standard input, output, and error of this process to a virtual machine.
//! StdioDevice::new(),
//! // Use void system interfaces for security because we don't need them for this example.
//! VoidFileSystem::new(),
//! VoidProcessContext::new(),
//! VoidClock::new(),
//! ),
//! )?;
//!
//! // Run bytecode on a virtual machine.
//! vm.run(bytecode.iter().copied())
//! }
//! ```
pub use stak_vm::*;
}
pub use stak_macro::include_module;