-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add basic adapters for Lua and Javascript runtime
- Loading branch information
1 parent
260fa3a
commit 0f46277
Showing
12 changed files
with
642 additions
and
168 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
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 type that can be converted from a [`ScriptValue`]. | ||
pub trait FromScriptValue { | ||
/// Converts a [`ScriptValue`] into the type. | ||
fn from_script_value(value: ScriptValue) -> Self; | ||
} | ||
|
||
macro_rules! impl_script_value { | ||
($type:ty, $kind:ident) => { | ||
impl ToScriptValue for $type { | ||
#[inline] | ||
fn to_script_value(&self) -> ScriptValue { | ||
ScriptValue(Variant::from(self.clone())) | ||
} | ||
} | ||
|
||
impl FromScriptValue for $type { | ||
#[inline] | ||
fn from_script_value(value: ScriptValue) -> Self { | ||
match value.0 { | ||
Variant::$kind(value) => value, | ||
_ => panic!("ScriptValue is not convertible"), | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
|
||
impl_script_value!(bool, Bool); | ||
impl_script_value!(u8, U8); | ||
impl_script_value!(u16, U16); | ||
impl_script_value!(u32, U32); | ||
impl_script_value!(u64, U64); | ||
impl_script_value!(i8, I8); | ||
impl_script_value!(i16, I16); | ||
impl_script_value!(i32, I32); | ||
impl_script_value!(i64, I64); | ||
impl_script_value!(f32, F32); | ||
impl_script_value!(f64, F64); | ||
impl_script_value!(String, String); | ||
impl_script_value!(StringName, StringName); | ||
impl_script_value!(Vec2, Vec2); | ||
impl_script_value!(Vec3, Vec3); | ||
impl_script_value!(Vec4, Vec4); | ||
impl_script_value!(Quat, Quat); | ||
impl_script_value!(Color, Color); | ||
impl_script_value!(Color32, Color32); | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_script_value_conversion() { | ||
let value = true.to_script_value(); | ||
let result = bool::from_script_value(value); | ||
|
||
assert_eq!(result, true); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,64 +1,13 @@ | ||
//! Scripting language abstractions | ||
use common::{ToVirtualPath, Variant}; | ||
use super::*; | ||
|
||
#[cfg(feature = "basic")] | ||
pub mod basic; | ||
#[cfg(feature = "lisp")] | ||
pub mod lisp; | ||
#[cfg(feature = "javascript")] | ||
mod javascript; | ||
#[cfg(feature = "lua")] | ||
pub mod lua; | ||
#[cfg(feature = "wren")] | ||
pub mod wren; | ||
mod lua; | ||
|
||
/// Represents a scripting language | ||
pub trait ScriptLanguage { | ||
/// Loads a script from the given path | ||
fn load(path: impl ToVirtualPath) -> Result<Script, ScriptError>; | ||
} | ||
|
||
/// Represents a parsed script | ||
pub struct Script { | ||
module: ast::Module, | ||
} | ||
|
||
impl Script { | ||
/// Loads a script from the given path | ||
pub fn from_path<L: ScriptLanguage>(path: impl ToVirtualPath) -> Result<Self, ScriptError> { | ||
L::load(path) | ||
} | ||
|
||
/// Executes the script | ||
pub fn execute(&self) { | ||
todo!() | ||
} | ||
|
||
/// Evaluates the script with the given arguments | ||
pub fn evaluate(&self, _arguments: &[Variant]) -> Vec<Variant> { | ||
todo!() | ||
} | ||
|
||
/// Calls the given top-level function with the given arguments | ||
pub fn call(&self, _name: &str, _arguments: &[Variant]) -> Vec<Variant> { | ||
todo!() | ||
} | ||
} | ||
|
||
/// Represents an error that occurred while parsing a script | ||
#[derive(Debug, Eq, PartialEq)] | ||
pub enum ScriptError { | ||
NotFound, | ||
ParseError, | ||
} | ||
|
||
mod ast { | ||
//! The internal AST representation of a script | ||
pub struct Module { | ||
pub name: String, | ||
} | ||
|
||
pub enum Statement {} | ||
pub enum Expression {} | ||
pub enum Literal {} | ||
} | ||
#[cfg(feature = "javascript")] | ||
pub use javascript::*; | ||
#[cfg(feature = "lua")] | ||
pub use lua::*; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
//! Javascript scripting language support. | ||
use common::Variant; | ||
use quick_js::{Context, JsValue}; | ||
|
||
use super::*; | ||
|
||
/// A runtime for executing Javascript scripts. | ||
pub struct JavascriptRuntime { | ||
context: Context, | ||
} | ||
|
||
impl JavascriptRuntime { | ||
pub fn new() -> Self { | ||
Self { | ||
context: Context::new().unwrap(), | ||
} | ||
} | ||
} | ||
|
||
impl ScriptRuntime for JavascriptRuntime { | ||
fn eval(&self, code: &str) -> Result<ScriptValue, ScriptError> { | ||
self | ||
.context | ||
.eval(code) | ||
.map(|it| it.to_script_value()) | ||
.map_err(|it| ScriptError::ExecutionError(it.to_string())) | ||
} | ||
|
||
fn eval_as<R: FromScriptValue>(&self, code: &str) -> Result<R, ScriptError> { | ||
self | ||
.context | ||
.eval(code) | ||
.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> { | ||
todo!() | ||
} | ||
} | ||
|
||
impl ToScriptValue for JsValue { | ||
fn to_script_value(&self) -> ScriptValue { | ||
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::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.into_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::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![ | ||
JsValue::Float(value.x as f64), | ||
JsValue::Float(value.y as f64), | ||
JsValue::Float(value.z as f64), | ||
]), | ||
Variant::Vec4(value) => JsValue::Array(vec![ | ||
JsValue::Float(value.x as f64), | ||
JsValue::Float(value.y as f64), | ||
JsValue::Float(value.z as f64), | ||
JsValue::Float(value.w as f64), | ||
]), | ||
Variant::Quat(value) => JsValue::Array(vec![ | ||
JsValue::Float(value.x as f64), | ||
JsValue::Float(value.y as f64), | ||
JsValue::Float(value.z as f64), | ||
JsValue::Float(value.w as f64), | ||
]), | ||
Variant::Color(value) => JsValue::Array(vec![ | ||
JsValue::Float(value.r as f64), | ||
JsValue::Float(value.g as f64), | ||
JsValue::Float(value.b as f64), | ||
JsValue::Float(value.a as f64), | ||
]), | ||
Variant::Color32(value) => JsValue::Array(vec![ | ||
JsValue::Int(value.r as i32), | ||
JsValue::Int(value.g as i32), | ||
JsValue::Int(value.b as i32), | ||
JsValue::Int(value.a as i32), | ||
]), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_basic_javascript_evaluation() { | ||
let runtime = JavascriptRuntime::new(); | ||
let result = runtime.eval("1 + 2").unwrap(); | ||
|
||
assert_eq!(result, ScriptValue(Variant::I32(3))); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.