Skip to content

Commit

Permalink
Scripting engine (#2038)
Browse files Browse the repository at this point in the history
# References

- #1932
  • Loading branch information
raviqqe authored Jan 23, 2025
1 parent be38f58 commit b5bdf5a
Show file tree
Hide file tree
Showing 16 changed files with 389 additions and 18 deletions.
80 changes: 80 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 @@ -12,6 +12,7 @@ members = [
"compiler",
"configuration",
"device",
"engine",
"examples/embedded-script",
"examples/hot-reload",
"file",
Expand Down
2 changes: 2 additions & 0 deletions device/src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod libc;
mod read_write;
#[cfg(feature = "std")]
mod stdio;
mod void;

pub use buffer_error::BufferError;
use core::error::Error;
Expand All @@ -14,6 +15,7 @@ pub use fixed_buffer::FixedBufferDevice;
pub use read_write::ReadWriteDevice;
#[cfg(feature = "std")]
pub use stdio::StdioDevice;
pub use void::*;

/// A device.
pub trait Device {
Expand Down
28 changes: 28 additions & 0 deletions device/src/device/void.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::{BufferError, Device};

/// A void device where all I/O operations succeed with no side effect.
#[derive(Debug, Default)]
pub struct VoidDevice {}

impl VoidDevice {
/// Creates a device.
pub fn new() -> Self {
Self::default()
}
}

impl Device for VoidDevice {
type Error = BufferError;

fn read(&mut self) -> Result<Option<u8>, Self::Error> {
Ok(None)
}

fn write(&mut self, _byte: u8) -> Result<(), Self::Error> {
Ok(())
}

fn write_error(&mut self, _byte: u8) -> Result<(), Self::Error> {
Ok(())
}
}
35 changes: 35 additions & 0 deletions engine/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "stak-engine"
description = "Stak Scheme scripting engine for Rust"
version = "0.1.0"
edition.workspace = true
keywords.workspace = true
license-file.workspace = true
readme.workspace = true
repository.workspace = true

[features]
libc = ["stak-device/libc"]
std = ["stak-device/std"]

[dependencies]
any-fn = "0.4.1"
cfg-elif = "0.6.1"
stak-device = { version = "0.2.93", path = "../device" }
stak-file = { version = "0.5.7", path = "../file" }
stak-module = { version = "0.1.13", path = "../module" }
stak-native = { version = "0.2.0", path = "../native", features = ["alloc"] }
stak-process-context = { version = "0.2.51", path = "../process_context" }
stak-r7rs = { version = "0.9.7", path = "../r7rs" }
stak-time = { version = "0.1.34", path = "../time" }
stak-vm = { version = "0.7.22", path = "../vm" }

[dev-dependencies]
rand = "0.8.5"
stak = { path = "../root" }

[build-dependencies]
stak-build = { version = "0.1.52", path = "../build" }

[lints]
workspace = true
7 changes: 7 additions & 0 deletions engine/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! A build script.
use stak_build::{build_r7rs, BuildError};

fn main() -> Result<(), BuildError> {
build_r7rs()
}
26 changes: 26 additions & 0 deletions engine/src/engine.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use crate::{primitive_set::EnginePrimitiveSet, EngineError};
use any_fn::AnyFn;
use stak_module::Module;
use stak_vm::{Error, Value, Vm};

const DEFAULT_VALUE_COUNT: usize = 1 << 10;

/// A scripting engine.
pub struct Engine<'a, 'b, const N: usize = DEFAULT_VALUE_COUNT> {
vm: Vm<'a, EnginePrimitiveSet<'a, 'b, N>>,
}

impl<'a, 'b, const N: usize> Engine<'a, 'b, N> {
/// Creates a scripting engine.
pub fn new(heap: &'a mut [Value], functions: &'a mut [AnyFn<'b>]) -> Result<Self, Error> {
Ok(Self {
vm: Vm::new(heap, EnginePrimitiveSet::new(functions))?,
})
}

/// Runs a module.
pub fn run(&mut self, module: &'static impl Module<'static>) -> Result<(), EngineError> {
self.vm.initialize(module.bytecode().iter().copied())?;
self.vm.run()
}
}
47 changes: 47 additions & 0 deletions engine/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use core::{
error::Error,
fmt::{self, Display, Formatter},
};
use stak_native::dynamic::DynamicError;
use stak_r7rs::SmallError;

/// An engine error
#[derive(Debug)]
pub enum EngineError {
/// A dynamic primitive error.
Dynamic(DynamicError),
/// An R7RS-small error.
Small(SmallError),
/// A virtual machine error.
Vm(stak_vm::Error),
}

impl From<DynamicError> for EngineError {
fn from(error: DynamicError) -> Self {
Self::Dynamic(error)
}
}

impl From<SmallError> for EngineError {
fn from(error: SmallError) -> Self {
Self::Small(error)
}
}

impl From<stak_vm::Error> for EngineError {
fn from(error: stak_vm::Error) -> Self {
Self::Vm(error)
}
}

impl Error for EngineError {}

impl Display for EngineError {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
match self {
Self::Dynamic(error) => write!(formatter, "{error}"),
Self::Small(error) => write!(formatter, "{error}"),
Self::Vm(error) => write!(formatter, "{error}"),
}
}
}
13 changes: 13 additions & 0 deletions engine/src/fight.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
(import (stak base))
(import (scheme base))

(define make-person (primitive 1000))
(define throw-pie (primitive 1001))

(define me (make-person 4 0.2))
(define you (make-person 2 0.6))

(throw-pie me you)
(throw-pie you you)
(throw-pie me you)
(throw-pie you you)
70 changes: 70 additions & 0 deletions engine/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//! Stak Scheme scripting engine for Rust.
//!
//! # Examples
//!
//! ```rust no_run
//! use any_fn::IntoAnyFn;
//! use core::error::Error;
//! use rand::random;
//! use stak::{
//! engine::{Engine, EngineError},
//! include_module,
//! module::UniversalModule,
//! };
//!
//! const HEAP_SIZE: usize = 1 << 16;
//! const FOREIGN_VALUE_CAPACITY: usize = 1 << 10;
//!
//! struct Person {
//! pies: usize,
//! dodge: f64,
//! wasted: bool,
//! }
//!
//! impl Person {
//! pub fn new(pies: usize, dodge: f64) -> Self {
//! Self {
//! pies,
//! dodge,
//! wasted: false,
//! }
//! }
//!
//! pub fn throw_pie(&mut self, other: &mut Person) {
//! if self.wasted {
//! return;
//! }
//!
//! self.pies -= 1;
//!
//! if random::<f64>() > other.dodge {
//! other.wasted = true;
//! }
//! }
//! }
//!
//! fn main() -> Result<(), Box<dyn Error>> {
//! static MODULE: UniversalModule = include_module!("fight.scm");
//!
//! run(&MODULE)?;
//!
//! Ok(())
//! }
//!
//! fn run(module: &'static UniversalModule) -> Result<(), EngineError> {
//! let mut heap = [Default::default(); HEAP_SIZE];
//! let mut functions = [Person::new.into_any_fn(), Person::throw_pie.into_any_fn()];
//! let mut engine = Engine::<FOREIGN_VALUE_CAPACITY>::new(&mut heap, &mut functions)?;
//!
//! engine.run(module)
//! }
//! ```
#![no_std]

mod engine;
mod error;
mod primitive_set;

pub use engine::*;
pub use error::*;
Loading

0 comments on commit b5bdf5a

Please sign in to comment.