Skip to content

Commit

Permalink
Update documentation (#2069)
Browse files Browse the repository at this point in the history
  • Loading branch information
raviqqe authored Feb 5, 2025
1 parent 15ec14c commit 4fff4d4
Show file tree
Hide file tree
Showing 27 changed files with 668 additions and 130 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/document.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ jobs:
- uses: actions/setup-node@v4
- uses: actions/configure-pages@v5
- run: tools/document.sh
- uses: actions/upload-artifact@v4
with:
name: document
path: doc/dist
- uses: actions/upload-pages-artifact@v3
with:
path: doc/dist
Expand All @@ -41,11 +45,32 @@ jobs:
working-directory: doc
- run: npm run lint
working-directory: doc
web_test:
needs:
- web_build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- uses: actions/download-artifact@v4
with:
name: document
path: doc/dist
- run: npm ci
working-directory: doc
- run: npm run preview &
working-directory: doc
- run: >
go run github.com/raviqqe/muffet/v2@latest
--accepted-status-codes 200,403
--buffer-size 8192
http://localhost:4321/stak/
document:
needs:
- rust_build
- web_build
- web_lint
- web_test
if: always()
runs-on: ubuntu-latest
steps:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: raviqqe/markdown-link-check@v1
with:
paths: README.md examples
lint:
needs:
- clippy
Expand Down
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ members = [
"engine",
"examples/embedded-script",
"examples/hot-reload",
"examples/no-std-no-alloc",
"file",
"inexact",
"macro",
Expand Down
154 changes: 63 additions & 91 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,34 @@ cargo install stak

## Examples

### Embedding Scheme scripts in Rust
### Dynamic scripting in Rust

First, prepare a Scheme script at `src/hello.scm`.
First, prepare a Scheme script named `src/fight.scm`:

```scheme
(import (scheme base))
(import (scheme base) (stak rust))
(write-string "Hello, world!\n")
(define-rust
make-person
person-throw-pie
person-wasted)
(define me (make-person 4 0.2))
(define you (make-person 2 0.6))
(person-throw-pie me you)
(person-throw-pie you me)
(person-throw-pie me you)
(person-throw-pie you me)
(when (person-wasted you)
(write-string "Congrats!"))
(when (person-wasted me)
(write-string "Oh, no!"))
```

Then, add a build script at `build.rs` to build the Scheme source file into bytecodes.
Then, add a build script at `build.rs` to build the Scheme source file
into bytecodes.

```rust no_run
use stak_build::{build_r7rs, BuildError};
Expand All @@ -61,115 +78,70 @@ fn main() -> Result<(), BuildError> {
}
```

Now, you can include the Scheme script into a program in Rust using [the `stak::include_module` macro](https://docs.rs/stak/latest/stak/macro.include_module.html).
Finally, you can embed and run the Scheme script in a Rust program.

```rust
use any_fn::{r#fn, Ref};
use core::error::Error;
use rand::random;
use stak::{
device::StdioDevice,
file::VoidFileSystem,
engine::{Engine, EngineError},
include_module,
process_context::VoidProcessContext,
module::{Module, UniversalModule},
r7rs::{SmallError, SmallPrimitiveSet},
time::VoidClock,
vm::Vm,
module::UniversalModule,
};

const HEAP_SIZE: usize = 1 << 16;

// Include a Scheme script in the bytecode format built by the build script above.
static MODULE: UniversalModule = include_module!("hello.scm");

fn main() -> Result<(), Box<dyn Error>> {
run(&MODULE.bytecode())?;

Ok(())
}

fn run(bytecodes: &[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(),
),
)?;

// Initialize a virtual machine with bytecodes.
vm.initialize(bytecodes.iter().copied())?;
// Run bytecodes on a virtual machine.
vm.run()
struct Person {
pies: usize,
dodge: f64,
wasted: bool,
}
```

### Communication between Scheme and Rust
impl Person {
pub fn new(pies: usize, dodge: f64) -> Self {
Self {
pies,
dodge,
wasted: false,
}
}

Currently, in-memory standard input (`stdin`) and output (`stdout`) to Scheme scripts are the only way to communicate information between Rust programs and Scheme scripts.
pub fn wasted(&self) -> bool {
self.wasted
}

```rust
use core::{error::Error, str::{self, FromStr}};
use stak::{
device::ReadWriteDevice,
file::VoidFileSystem,
include_module,
process_context::VoidProcessContext,
module::{Module, UniversalModule},
r7rs::{SmallError, SmallPrimitiveSet},
time::VoidClock,
vm::Vm,
};
pub fn throw_pie(&mut self, other: &mut Person) {
if self.wasted {
return;
}

const BUFFER_SIZE: usize = 1 << 8;
const HEAP_SIZE: usize = 1 << 16;
self.pies -= 1;

static MODULE: UniversalModule = include_module!("fibonacci.scm");
if random::<f64>() > other.dodge {
other.wasted = true;
}
}
}

fn main() -> Result<(), Box<dyn Error>> {
let input = 15;
let mut output = vec![];
let mut error = vec![];

run(&MODULE.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());
}
static MODULE: UniversalModule = include_module!("fight.scm");

// Decode and test the output.
assert_eq!(isize::from_str(&str::from_utf8(&output)?)?, 610);
run(&MODULE)?;

Ok(())
}

fn run(
bytecodes: &[u8],
input: &[u8],
output: &mut Vec<u8>,
error: &mut Vec<u8>,
) -> Result<(), SmallError> {
fn run(module: &'static UniversalModule) -> Result<(), EngineError> {
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.initialize(bytecodes.iter().copied())?;
vm.run()
let mut functions = [
r#fn(Person::new),
r#fn(Person::throw_pie),
r#fn::<(Ref<_>,), _>(Person::wasted),
];
let mut engine = Engine::new(&mut heap, &mut functions)?;

engine.run(module)
}
```

Expand Down
2 changes: 1 addition & 1 deletion bench/src/fibonacci/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ def fibonacci(x):
return x if x < 2 else fibonacci(x - 1) + fibonacci(x - 2)


fibonacci(32)
print(fibonacci(32))
4 changes: 2 additions & 2 deletions bench/src/fibonacci/main.scm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(import (scheme base))
(import (scheme base) (scheme write))

(define (fibonacci x)
(if (< x 2)
Expand All @@ -7,4 +7,4 @@
(fibonacci (- x 1))
(fibonacci (- x 2)))))

(fibonacci 32)
(write (fibonacci 32))
2 changes: 1 addition & 1 deletion bench/src/sum/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ def sum(x):
return z


sum(10000000)
print(sum(10000000))
4 changes: 2 additions & 2 deletions bench/src/sum/main.scm
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
(import (scheme base))
(import (scheme base) (scheme write))

(define (sum x)
(let loop ((x x) (y 0))
(if (zero? x)
y
(loop (- x 1) (+ x y)))))

(sum 10000000)
(write (sum 10000000))
2 changes: 1 addition & 1 deletion bench/src/tak/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ def tak(x, y, z):
return z


tak(16, 8, 0)
print(tak(16, 8, 0))
4 changes: 2 additions & 2 deletions bench/src/tak/main.scm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(import (scheme base))
(import (scheme base) (scheme write))

(define (tak x y z)
(if (< y x)
Expand All @@ -8,4 +8,4 @@
(tak (- z 1) x y))
z))

(tak 16 8 0)
(write (tak 16 8 0))
14 changes: 14 additions & 0 deletions device/src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,17 @@ pub trait Device {
/// Writes to standard error.
fn write_error(&mut self, byte: u8) -> Result<(), Self::Error>;
}

impl<T: Device> Device for &mut T {
type Error = T::Error;

fn read(&mut self) -> Result<Option<u8>, Self::Error> {
(**self).read()
}
fn write(&mut self, byte: u8) -> Result<(), Self::Error> {
(**self).write(byte)
}
fn write_error(&mut self, byte: u8) -> Result<(), Self::Error> {
(**self).write_error(byte)
}
}
Loading

0 comments on commit 4fff4d4

Please sign in to comment.