Skip to content

Commit

Permalink
Some work on scripting interop
Browse files Browse the repository at this point in the history
  • Loading branch information
mattkleiny committed Jul 14, 2024
1 parent 0f46277 commit ca81458
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 47 deletions.
74 changes: 74 additions & 0 deletions crates/scripting/src/callbacks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use super::*;

/// A callback that can be called from a script.
pub trait ScriptCallback<R> {
/// Returns the number of arguments the callback expects.
fn argument_count(&self) -> usize;

/// Calls the callback with the given arguments.
fn call(&self, args: &[ScriptValue]) -> Result<ScriptValue, ScriptError>;
}

/// Implements the `ScriptCallback` trait for functions with up to 5 arguments.
macro_rules! impl_callback {
(@call $len:literal $self:ident $args:ident ) => {
$self()
};

(@call $len:literal $self:ident $args:ident $( $arg:ident ),* ) => {
{
let mut iter = $args.into_iter();

$self(
$($arg::from_script_value(iter.next().unwrap()),)*
)
}
};

[ $( $len:literal : ( $( $arg:ident, )* ), )* ] => {
$(
impl<
$( $arg, )*
R,
F,
> ScriptCallback<std::marker::PhantomData<(
$( &$arg, )*
&R,
&F,
)>> for F
where
$( $arg: FromScriptValue, )*
R: ToScriptValue,
F: Fn( $( $arg, )* ) -> R,
{
fn argument_count(&self) -> usize {
$len
}

fn call(&self, args: &[ScriptValue]) -> Result<ScriptValue, ScriptError> {
if args.len() != $len {
return Err(ScriptError::ExecutionError(format!(
"Invalid argument count: Expected {}, got {}",
self.argument_count(),
args.len()
)));
}

// recursive
let result = impl_callback!(@call $len self args $($arg),* );

Ok(result.to_script_value())
}
}
)*
};
}

impl_callback![
0: (),
1: (A1,),
2: (A1, A2,),
3: (A1, A2, A3,),
4: (A1, A2, A3, A4,),
5: (A1, A2, A3, A4, A5,),
];
36 changes: 16 additions & 20 deletions crates/scripting/src/lang/javascript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,11 @@ impl ScriptRuntime for JavascriptRuntime {
self
.context
.eval(code)
.map(|it| R::from_script_value(it.to_script_value()))
.map(|it| R::from_script_value(&it.to_script_value()))
.map_err(|it| ScriptError::ExecutionError(it.to_string()))
}

fn add_callback<R: ToScriptValue + FromScriptValue>(
&mut self,
_name: &str,
_callback: impl ScriptCallback<R> + 'static,
) -> Result<(), ScriptError> {
fn add_callback<R>(&mut self, _name: &str, _callback: impl ScriptCallback<R> + 'static) {
todo!()
}
}
Expand All @@ -61,21 +57,21 @@ impl ToScriptValue for JsValue {
}

impl FromScriptValue for JsValue {
fn from_script_value(value: ScriptValue) -> Self {
match value.into_variant() {
fn from_script_value(value: &ScriptValue) -> Self {
match value.as_variant() {
Variant::Null => JsValue::Null,
Variant::Bool(value) => JsValue::Bool(value),
Variant::U8(value) => JsValue::Int(value as i32),
Variant::U16(value) => JsValue::Int(value as i32),
Variant::U32(value) => JsValue::Int(value as i32),
Variant::U64(value) => JsValue::Int(value as i32),
Variant::I8(value) => JsValue::Int(value as i32),
Variant::I16(value) => JsValue::Int(value as i32),
Variant::I32(value) => JsValue::Int(value),
Variant::I64(value) => JsValue::Int(value as i32),
Variant::F32(value) => JsValue::Float(value as f64),
Variant::F64(value) => JsValue::Float(value),
Variant::String(value) => JsValue::String(value),
Variant::Bool(value) => JsValue::Bool(*value),
Variant::U8(value) => JsValue::Int(*value as i32),
Variant::U16(value) => JsValue::Int(*value as i32),
Variant::U32(value) => JsValue::Int(*value as i32),
Variant::U64(value) => JsValue::Int(*value as i32),
Variant::I8(value) => JsValue::Int(*value as i32),
Variant::I16(value) => JsValue::Int(*value as i32),
Variant::I32(value) => JsValue::Int(*value),
Variant::I64(value) => JsValue::Int(*value as i32),
Variant::F32(value) => JsValue::Float(*value as f64),
Variant::F64(value) => JsValue::Float(*value),
Variant::String(value) => JsValue::String(value.clone()),
Variant::StringName(value) => JsValue::String(value.to_string()),
Variant::Vec2(value) => JsValue::Array(vec![JsValue::Float(value.x as f64), JsValue::Float(value.y as f64)]),
Variant::Vec3(value) => JsValue::Array(vec![
Expand Down
8 changes: 2 additions & 6 deletions crates/scripting/src/lang/lua.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,11 @@ impl ScriptRuntime for LuaScriptRuntime {
.lua
.load(code)
.eval()
.map(|it| R::from_script_value(it))
.map(|it| R::from_script_value(&it))
.map_err(|it| ScriptError::ExecutionError(it.to_string()))
}

fn add_callback<R: ToScriptValue + FromScriptValue>(
&mut self,
_name: &str,
_callback: impl ScriptCallback<R> + 'static,
) -> Result<(), ScriptError> {
fn add_callback<R>(&mut self, _name: &str, _callback: impl ScriptCallback<R> + 'static) {
todo!()
}
}
Expand Down
21 changes: 5 additions & 16 deletions crates/scripting/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! Scripting engine for Surreal
pub use interop::*;
pub use callbacks::*;
pub use lang::*;
pub use values::*;

mod interop;
mod callbacks;
mod lang;
mod values;

/// An error that can occur during script execution.
#[derive(Debug)]
Expand All @@ -15,15 +17,6 @@ pub enum ScriptError {
ConversionError(String),
}

/// A callback that can be called from a script.
pub trait ScriptCallback<R: FromScriptValue> {
/// Returns the number of arguments the callback expects.
fn argument_count(&self) -> usize;

/// Calls the callback with the given arguments.
fn call(&self, args: &[ScriptValue]) -> Result<R, ScriptError>;
}

/// Represents a runtime that allows script execution.
pub trait ScriptRuntime {
/// Evaluates the given code and returns the result.
Expand All @@ -33,9 +26,5 @@ pub trait ScriptRuntime {
fn eval_as<R: FromScriptValue>(&self, code: &str) -> Result<R, ScriptError>;

/// Adds a callback that can be called from scripts.
fn add_callback<R: ToScriptValue + FromScriptValue>(
&mut self,
name: &str,
callback: impl ScriptCallback<R> + 'static,
) -> Result<(), ScriptError>;
fn add_callback<R>(&mut self, name: &str, callback: impl ScriptCallback<R> + 'static);
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub trait ToScriptValue {
/// A type that can be converted from a [`ScriptValue`].
pub trait FromScriptValue {
/// Converts a [`ScriptValue`] into the type.
fn from_script_value(value: ScriptValue) -> Self;
fn from_script_value(value: &ScriptValue) -> Self;
}

macro_rules! impl_script_value {
Expand All @@ -66,9 +66,9 @@ macro_rules! impl_script_value {

impl FromScriptValue for $type {
#[inline]
fn from_script_value(value: ScriptValue) -> Self {
match value.0 {
Variant::$kind(value) => value,
fn from_script_value(value: &ScriptValue) -> Self {
match &value.0 {
Variant::$kind(value) => value.clone(),
_ => panic!("ScriptValue is not convertible"),
}
}
Expand Down Expand Up @@ -103,7 +103,7 @@ mod tests {
#[test]
fn test_script_value_conversion() {
let value = true.to_script_value();
let result = bool::from_script_value(value);
let result = bool::from_script_value(&value);

assert_eq!(result, true);
}
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub extern crate editor;
pub extern crate graphics;
#[cfg(feature = "input")]
pub extern crate input;
#[cfg(feature = "scripting")]
pub extern crate scripting;

pub mod backends {
#[cfg(feature = "sdl")]
Expand Down

0 comments on commit ca81458

Please sign in to comment.