Skip to content

Commit

Permalink
Some work on script values
Browse files Browse the repository at this point in the history
  • Loading branch information
mattkleiny committed Jul 15, 2024
1 parent c990ce2 commit 0d8f328
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 122 deletions.
16 changes: 8 additions & 8 deletions crates/scripting/src/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use super::*;
/// A callback that can be called from a script.
pub trait ScriptCallback<R>: RefUnwindSafe {
/// Calls the callback with the given arguments.
fn call(&self, args: &[ScriptValue]) -> Result<ScriptValue, ScriptError>;
fn call(&self, args: &[Variant]) -> Result<Variant, ScriptError>;
}

/// Implements the `ScriptCallback` trait for functions with up to 5 arguments.
Expand All @@ -20,7 +20,7 @@ macro_rules! impl_callback {
{
let mut iter = $args.into_iter();

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

Expand All @@ -36,19 +36,19 @@ macro_rules! impl_callback {
&F,
)>> for F
where
$( $arg: FromScriptValue, )*
R: ToScriptValue,
$( $arg: FromScriptVariant, )*
R: ToScriptVariant,
F: Fn( $( $arg, )* ) -> R + RefUnwindSafe,
{
fn call(&self, args: &[ScriptValue]) -> Result<ScriptValue, ScriptError> {
fn call(&self, args: &[Variant]) -> Result<Variant, ScriptError> {
if args.len() != $len {
return Err(ScriptError::ExecutionError(format!("Invalid argument count: Expected {}, got {}", $len, args.len())));
}

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

Ok(result.to_script_value())
Ok(result.to_script_variant())
}
}
)*
Expand All @@ -69,9 +69,9 @@ impl<F> ScriptCallback<PhantomData<&F>> for F
where
F: Fn() + RefUnwindSafe,
{
fn call(&self, _args: &[ScriptValue]) -> Result<ScriptValue, ScriptError> {
fn call(&self, _args: &[Variant]) -> Result<Variant, ScriptError> {
self();

Ok(ScriptValue::from(Variant::Null))
Ok(Variant::from(Variant::Null))
}
}
36 changes: 18 additions & 18 deletions crates/scripting/src/lang/javascript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@ impl JavascriptRuntime {
}

impl ScriptRuntime for JavascriptRuntime {
fn eval(&self, code: &str) -> Result<ScriptValue, ScriptError> {
fn eval(&self, code: &str) -> Result<Variant, ScriptError> {
self
.context
.eval(code)
.map(|it| it.to_script_value())
.map(|it| it.to_script_variant())
.map_err(|it| ScriptError::ExecutionError(it.to_string()))
}

fn eval_as<R: FromScriptValue>(&self, code: &str) -> Result<R, ScriptError> {
fn eval_as<R: FromScriptVariant>(&self, code: &str) -> Result<R, ScriptError> {
self
.context
.eval(code)
.map(|it| R::from_script_value(&it.to_script_value()))
.map(|it| R::from_script_variant(&it.to_script_variant()))
.map_err(|it| ScriptError::ExecutionError(it.to_string()))
}

Expand All @@ -43,13 +43,13 @@ impl ScriptRuntime for JavascriptRuntime {
let args = args
.into_vec()
.iter()
.map(|it| it.to_script_value())
.map(|it| it.to_script_variant())
.collect::<Vec<_>>();

// convert result
let result = callback
.call(&args)
.map(|it| JsValue::from_script_value(&it))
.map(|it| JsValue::from_script_variant(&it))
.map_err(|it| format!("{:?}", it));

result
Expand All @@ -58,25 +58,25 @@ impl ScriptRuntime for JavascriptRuntime {
}
}

impl ToScriptValue for JsValue {
fn to_script_value(&self) -> ScriptValue {
impl ToScriptVariant for JsValue {
fn to_script_variant(&self) -> Variant {
match self {
JsValue::Undefined => ScriptValue::from(Variant::Null),
JsValue::Null => ScriptValue::from(Variant::Null),
JsValue::Bool(value) => ScriptValue::from(Variant::Bool(*value)),
JsValue::Int(value) => ScriptValue::from(Variant::I32(*value)),
JsValue::Float(value) => ScriptValue::from(Variant::F64(*value)),
JsValue::String(value) => ScriptValue::from(Variant::String(value.clone())),
JsValue::Undefined => Variant::Null,
JsValue::Null => Variant::Null,
JsValue::Bool(value) => Variant::Bool(*value),
JsValue::Int(value) => Variant::I32(*value),
JsValue::Float(value) => Variant::F64(*value),
JsValue::String(value) => Variant::String(value.clone()),
JsValue::Array(_) => todo!("Array conversion not implemented"),
JsValue::Object(_) => todo!("Object conversion not implemented"),
_ => panic!("Unsupported JsValue type"),
}
}
}

impl FromScriptValue for JsValue {
fn from_script_value(value: &ScriptValue) -> Self {
match value.as_variant() {
impl FromScriptVariant for JsValue {
fn from_script_variant(value: &Variant) -> Self {
match value {
Variant::Null => JsValue::Null,
Variant::Bool(value) => JsValue::Bool(*value),
Variant::U8(value) => JsValue::Int(*value as i32),
Expand Down Expand Up @@ -137,6 +137,6 @@ mod tests {

let result = runtime.eval("add(1, 2)").unwrap();

assert_eq!(result, ScriptValue(Variant::I32(3)));
assert_eq!(result, Variant::I32(3));
}
}
39 changes: 6 additions & 33 deletions crates/scripting/src/lang/lua.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Lua scripting language support
use common::Variant;
use mlua::{FromLua, Lua, StdLib, Value};
use mlua::{Lua, StdLib};

use super::*;

Expand All @@ -21,46 +21,19 @@ impl LuaScriptRuntime {
}

impl ScriptRuntime for LuaScriptRuntime {
fn eval(&self, code: &str) -> Result<ScriptValue, ScriptError> {
self
.lua
.load(code)
.eval()
.map_err(|it| ScriptError::ExecutionError(it.to_string()))
fn eval(&self, _code: &str) -> Result<Variant, ScriptError> {
todo!()
}

fn eval_as<R: FromScriptValue>(&self, code: &str) -> Result<R, ScriptError> {
self
.lua
.load(code)
.eval()
.map(|it| R::from_script_value(&it))
.map_err(|it| ScriptError::ExecutionError(it.to_string()))
fn eval_as<R: FromScriptVariant>(&self, _code: &str) -> Result<R, ScriptError> {
todo!()
}

fn add_callback<F>(&mut self, _name: &str, _callback: impl ScriptCallback<F> + 'static) {
todo!()
}
}

impl<'lua> FromLua<'lua> for ScriptValue {
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> mlua::Result<Self> {
Ok(match value {
Value::Nil => ScriptValue::from(Variant::Null),
Value::Boolean(value) => ScriptValue::from(Variant::Bool(value)),
Value::LightUserData(_) => todo!("LightUserData conversion not implemented"),
Value::Integer(value) => ScriptValue::from(Variant::I32(value as i32)),
Value::Number(value) => ScriptValue::from(Variant::F64(value)),
Value::String(value) => ScriptValue::from(Variant::String(value.to_str()?.to_string())),
Value::Table(_) => todo!("Table conversion not implemented"),
Value::Function(_) => todo!("Function conversion not implemented"),
Value::Thread(_) => todo!("Thread conversion not implemented"),
Value::UserData(_) => todo!("UserData conversion not implemented"),
Value::Error(_) => todo!("Error conversion not implemented"),
})
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -71,6 +44,6 @@ mod tests {

let result = runtime.eval("return 42").unwrap();

assert_eq!(result, ScriptValue::from(Variant::I32(42)));
assert_eq!(result, Variant::I32(42));
}
}
6 changes: 4 additions & 2 deletions crates/scripting/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ mod callbacks;
mod lang;
mod values;

use common::Variant;

/// An error that can occur during script execution.
#[derive(Debug)]
pub enum ScriptError {
Expand All @@ -20,10 +22,10 @@ pub enum ScriptError {
/// Represents a runtime that allows script execution.
pub trait ScriptRuntime {
/// Evaluates the given code and returns the result.
fn eval(&self, code: &str) -> Result<ScriptValue, ScriptError>;
fn eval(&self, code: &str) -> Result<Variant, ScriptError>;

/// Evaluates the given code and returns the result as the specified type.
fn eval_as<R: FromScriptValue>(&self, code: &str) -> Result<R, ScriptError>;
fn eval_as<R: FromScriptVariant>(&self, code: &str) -> Result<R, ScriptError>;

/// Adds a callback that can be called from scripts.
fn add_callback<F>(&mut self, name: &str, callback: impl ScriptCallback<F> + 'static);
Expand Down
77 changes: 17 additions & 60 deletions crates/scripting/src/values.rs
Original file line number Diff line number Diff line change
@@ -1,75 +1,32 @@
use common::{Color, Color32, Quat, StringName, Variant, Vec2, Vec3, Vec4};

/// A value that can be passed to and from a scripting language.
///
/// This is a new-type pattern over [`Variant`] to allow simple interop with
/// many different scripting language vendor crates.
#[repr(transparent)]
#[derive(Clone, Debug, PartialEq)]
pub struct ScriptValue(pub Variant);

impl ScriptValue {
/// Creates a new `ScriptValue` from a `Variant`.
pub const fn new(variant: Variant) -> Self {
Self(variant)
}

/// Returns a reference to the inner `Variant` value.
#[inline]
pub fn as_variant(&self) -> &Variant {
&self.0
}

/// Returns the inner `Variant` value.
#[inline]
pub fn into_variant(self) -> Variant {
self.0
}
}

/// Allow conversion from [`Variant`].
impl From<Variant> for ScriptValue {
#[inline]
fn from(variant: Variant) -> Self {
Self(variant)
}
}

/// Allow conversion into [`Variant`].
impl From<ScriptValue> for Variant {
#[inline]
fn from(value: ScriptValue) -> Self {
value.0
}
}

/// A type that can be converted to a [`ScriptValue`].
pub trait ToScriptValue {
/// Converts the type into a [`ScriptValue`].
fn to_script_value(&self) -> ScriptValue;
/// A local type that can be converted to a [`Variant`].
pub trait ToScriptVariant {
/// Converts the type into a [`Variant`].
fn to_script_variant(&self) -> Variant;
}

/// 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;
/// A type that can be converted from a [`Variant`].
pub trait FromScriptVariant {
/// Converts a [`Variant`] into the type.
fn from_script_variant(value: &Variant) -> Self;
}

macro_rules! impl_script_value {
($type:ty, $kind:ident) => {
impl ToScriptValue for $type {
impl ToScriptVariant for $type {
#[inline]
fn to_script_value(&self) -> ScriptValue {
ScriptValue(Variant::from(self.clone()))
fn to_script_variant(&self) -> Variant {
Variant::from(self.clone())
}
}

impl FromScriptValue for $type {
impl FromScriptVariant for $type {
#[inline]
fn from_script_value(value: &ScriptValue) -> Self {
match &value.0 {
fn from_script_variant(value: &Variant) -> Self {
match &value {
Variant::$kind(value) => value.clone(),
_ => panic!("ScriptValue is not convertible"),
_ => panic!("Variant is not convertible"),
}
}
}
Expand Down Expand Up @@ -102,8 +59,8 @@ mod tests {

#[test]
fn test_script_value_conversion() {
let value = true.to_script_value();
let result = bool::from_script_value(&value);
let value = true.to_script_variant();
let result = bool::from_script_variant(&value);

assert_eq!(result, true);
}
Expand Down
2 changes: 1 addition & 1 deletion examples/hello-script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn main() {
});

while window.update() {
runtime.eval("clear_screen(1, 0, 1)").unwrap();
runtime.eval("clear_screen(1, 0, 1);").unwrap();
window.present();
}
}

0 comments on commit 0d8f328

Please sign in to comment.