Merge branch 'master' into plugins
This commit is contained in:
17
src/any.rs
17
src/any.rs
@@ -137,7 +137,7 @@ pub enum Union {
|
||||
Array(Box<Array>),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Map(Box<Map>),
|
||||
FnPtr(FnPtr),
|
||||
FnPtr(Box<FnPtr>),
|
||||
Variant(Box<Box<dyn Variant>>),
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ impl fmt::Debug for Dynamic {
|
||||
f.write_str("#")?;
|
||||
fmt::Debug::fmt(value, f)
|
||||
}
|
||||
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
||||
Union::FnPtr(value) => fmt::Debug::fmt(value, f),
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
|
||||
@@ -481,7 +481,7 @@ impl Dynamic {
|
||||
}
|
||||
if type_id == TypeId::of::<FnPtr>() {
|
||||
return match self.0 {
|
||||
Union::FnPtr(value) => unsafe_try_cast(value),
|
||||
Union::FnPtr(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@@ -582,7 +582,7 @@ impl Dynamic {
|
||||
}
|
||||
if type_id == TypeId::of::<FnPtr>() {
|
||||
return match &self.0 {
|
||||
Union::FnPtr(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||
Union::FnPtr(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@@ -656,7 +656,7 @@ impl Dynamic {
|
||||
}
|
||||
if type_id == TypeId::of::<FnPtr>() {
|
||||
return match &mut self.0 {
|
||||
Union::FnPtr(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||
Union::FnPtr(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@@ -735,7 +735,7 @@ impl Dynamic {
|
||||
pub(crate) fn take_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
||||
match self.0 {
|
||||
Union::Str(s) => Ok(s),
|
||||
Union::FnPtr(f) => Ok(f.take_fn_name()),
|
||||
Union::FnPtr(f) => Ok(f.take_data().0),
|
||||
_ => Err(self.type_name()),
|
||||
}
|
||||
}
|
||||
@@ -801,6 +801,11 @@ impl<K: Into<ImmutableString>, T: Variant + Clone> From<HashMap<K, T>> for Dynam
|
||||
}
|
||||
impl From<FnPtr> for Dynamic {
|
||||
fn from(value: FnPtr) -> Self {
|
||||
Box::new(value).into()
|
||||
}
|
||||
}
|
||||
impl From<Box<FnPtr>> for Dynamic {
|
||||
fn from(value: Box<FnPtr>) -> Self {
|
||||
Self(Union::FnPtr(value))
|
||||
}
|
||||
}
|
||||
|
161
src/engine.rs
161
src/engine.rs
@@ -11,15 +11,13 @@ use crate::parser::{Expr, FnAccess, ImmutableString, ReturnType, ScriptFnDef, St
|
||||
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||
use crate::syntax::{CustomSyntax, EvalContext, Expression};
|
||||
use crate::token::Position;
|
||||
use crate::utils::StaticVec;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
use crate::syntax::{CustomSyntax, EvalContext};
|
||||
|
||||
use crate::stdlib::{
|
||||
any::{type_name, TypeId},
|
||||
borrow::Cow,
|
||||
@@ -81,6 +79,7 @@ pub const KEYWORD_TYPE_OF: &str = "type_of";
|
||||
pub const KEYWORD_EVAL: &str = "eval";
|
||||
pub const KEYWORD_FN_PTR: &str = "Fn";
|
||||
pub const KEYWORD_FN_PTR_CALL: &str = "call";
|
||||
pub const KEYWORD_FN_PTR_CURRY: &str = "curry";
|
||||
pub const KEYWORD_THIS: &str = "this";
|
||||
pub const FN_TO_STRING: &str = "to_string";
|
||||
pub const FN_GET: &str = "get$";
|
||||
@@ -88,45 +87,10 @@ pub const FN_SET: &str = "set$";
|
||||
pub const FN_IDX_GET: &str = "index$get$";
|
||||
pub const FN_IDX_SET: &str = "index$set$";
|
||||
pub const FN_ANONYMOUS: &str = "anon$";
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
pub const MARKER_EXPR: &str = "$expr$";
|
||||
#[cfg(feature = "internals")]
|
||||
pub const MARKER_BLOCK: &str = "$block$";
|
||||
#[cfg(feature = "internals")]
|
||||
pub const MARKER_IDENT: &str = "$ident$";
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct Expression<'a>(&'a Expr);
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
impl<'a> From<&'a Expr> for Expression<'a> {
|
||||
fn from(expr: &'a Expr) -> Self {
|
||||
Self(expr)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
impl Expression<'_> {
|
||||
/// If this expression is a variable name, return it. Otherwise `None`.
|
||||
#[cfg(feature = "internals")]
|
||||
pub fn get_variable_name(&self) -> Option<&str> {
|
||||
match self.0 {
|
||||
Expr::Variable(x) => Some((x.0).0.as_str()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Get the expression.
|
||||
pub(crate) fn expr(&self) -> &Expr {
|
||||
&self.0
|
||||
}
|
||||
/// Get the position of this expression.
|
||||
pub fn position(&self) -> Position {
|
||||
self.0.position()
|
||||
}
|
||||
}
|
||||
|
||||
/// A type specifying the method of chaining.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
enum ChainType {
|
||||
@@ -203,7 +167,7 @@ impl Target<'_> {
|
||||
.as_char()
|
||||
.map_err(|_| EvalAltResult::ErrorCharMismatch(Position::none()))?;
|
||||
|
||||
let mut chars: StaticVec<char> = s.chars().collect();
|
||||
let mut chars = s.chars().collect::<StaticVec<_>>();
|
||||
let ch = chars[*index];
|
||||
|
||||
// See if changed - if so, update the String
|
||||
@@ -316,7 +280,6 @@ pub struct Engine {
|
||||
/// A hashset containing custom keywords and precedence to recognize.
|
||||
pub(crate) custom_keywords: Option<HashMap<String, u8>>,
|
||||
/// Custom syntax.
|
||||
#[cfg(feature = "internals")]
|
||||
pub(crate) custom_syntax: Option<HashMap<String, CustomSyntax>>,
|
||||
|
||||
/// Callback closure for implementing the `print` command.
|
||||
@@ -376,8 +339,6 @@ impl Default for Engine {
|
||||
type_names: None,
|
||||
disabled_symbols: None,
|
||||
custom_keywords: None,
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
custom_syntax: None,
|
||||
|
||||
// default print/debug implementations
|
||||
@@ -605,8 +566,6 @@ impl Engine {
|
||||
type_names: None,
|
||||
disabled_symbols: None,
|
||||
custom_keywords: None,
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
custom_syntax: None,
|
||||
|
||||
print: Box::new(|_| {}),
|
||||
@@ -1066,7 +1025,7 @@ impl Engine {
|
||||
lib: &Module,
|
||||
target: &mut Target,
|
||||
expr: &Expr,
|
||||
mut idx_val: Dynamic,
|
||||
idx_val: Dynamic,
|
||||
level: usize,
|
||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||
let ((name, native, pos), _, hash, _, def_val) = match expr {
|
||||
@@ -1079,17 +1038,22 @@ impl Engine {
|
||||
|
||||
// Get a reference to the mutation target Dynamic
|
||||
let obj = target.as_mut();
|
||||
let idx = idx_val.downcast_mut::<StaticVec<Dynamic>>().unwrap();
|
||||
let mut idx = idx_val.cast::<StaticVec<Dynamic>>();
|
||||
let mut fn_name = name.as_ref();
|
||||
|
||||
let (result, updated) = if fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
|
||||
// FnPtr call
|
||||
let fn_ptr = obj.downcast_ref::<FnPtr>().unwrap();
|
||||
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
||||
// Redirect function name
|
||||
let fn_name = obj.as_str().unwrap();
|
||||
let fn_name = fn_ptr.fn_name();
|
||||
// Recalculate hash
|
||||
let hash = calc_fn_hash(empty(), fn_name, idx.len(), empty());
|
||||
// Arguments are passed as-is
|
||||
let mut arg_values = idx.iter_mut().collect::<StaticVec<_>>();
|
||||
let hash = calc_fn_hash(empty(), fn_name, curry.len() + idx.len(), empty());
|
||||
// Arguments are passed as-is, adding the curried arguments
|
||||
let mut arg_values = curry
|
||||
.iter_mut()
|
||||
.chain(idx.iter_mut())
|
||||
.collect::<StaticVec<_>>();
|
||||
let args = arg_values.as_mut();
|
||||
|
||||
// Map it to name(args) in function-call style
|
||||
@@ -1098,17 +1062,16 @@ impl Engine {
|
||||
)
|
||||
} else if fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::<FnPtr>() {
|
||||
// FnPtr call on object
|
||||
let fn_ptr = idx.remove(0).cast::<FnPtr>();
|
||||
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
||||
// Redirect function name
|
||||
let fn_name = idx[0]
|
||||
.downcast_ref::<FnPtr>()
|
||||
.unwrap()
|
||||
.get_fn_name()
|
||||
.clone();
|
||||
let fn_name = fn_ptr.get_fn_name().clone();
|
||||
// Recalculate hash
|
||||
let hash = calc_fn_hash(empty(), &fn_name, idx.len() - 1, empty());
|
||||
// Replace the first argument with the object pointer
|
||||
let hash = calc_fn_hash(empty(), &fn_name, curry.len() + idx.len(), empty());
|
||||
// Replace the first argument with the object pointer, adding the curried arguments
|
||||
let mut arg_values = once(obj)
|
||||
.chain(idx.iter_mut().skip(1))
|
||||
.chain(curry.iter_mut())
|
||||
.chain(idx.iter_mut())
|
||||
.collect::<StaticVec<_>>();
|
||||
let args = arg_values.as_mut();
|
||||
|
||||
@@ -1116,8 +1079,24 @@ impl Engine {
|
||||
self.exec_fn_call(
|
||||
state, lib, &fn_name, *native, hash, args, is_ref, true, *def_val, level,
|
||||
)
|
||||
} else if fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
|
||||
// Curry call
|
||||
let fn_ptr = obj.downcast_ref::<FnPtr>().unwrap();
|
||||
Ok((
|
||||
FnPtr::new_unchecked(
|
||||
fn_ptr.get_fn_name().clone(),
|
||||
fn_ptr
|
||||
.curry()
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(idx.into_iter())
|
||||
.collect(),
|
||||
)
|
||||
.into(),
|
||||
false,
|
||||
))
|
||||
} else {
|
||||
let redirected: Option<ImmutableString>;
|
||||
let redirected;
|
||||
let mut hash = *hash;
|
||||
|
||||
// Check if it is a map method call in OOP style
|
||||
@@ -1126,9 +1105,8 @@ impl Engine {
|
||||
if let Some(val) = map.get(fn_name) {
|
||||
if let Some(f) = val.downcast_ref::<FnPtr>() {
|
||||
// Remap the function name
|
||||
redirected = Some(f.get_fn_name().clone());
|
||||
fn_name = redirected.as_ref().unwrap();
|
||||
|
||||
redirected = f.get_fn_name().clone();
|
||||
fn_name = &redirected;
|
||||
// Recalculate the hash based on the new function name
|
||||
hash = calc_fn_hash(empty(), fn_name, idx.len(), empty());
|
||||
}
|
||||
@@ -1734,8 +1712,6 @@ impl Engine {
|
||||
/// ## WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level. It evaluates an expression from an AST.
|
||||
#[cfg(feature = "internals")]
|
||||
#[deprecated(note = "this method is volatile and may change")]
|
||||
pub fn eval_expression_tree(
|
||||
&self,
|
||||
context: &mut EvalContext,
|
||||
@@ -1775,7 +1751,7 @@ impl Engine {
|
||||
Expr::FloatConstant(x) => Ok(x.0.into()),
|
||||
Expr::StringConstant(x) => Ok(x.0.to_string().into()),
|
||||
Expr::CharConstant(x) => Ok(x.0.into()),
|
||||
Expr::FnPointer(x) => Ok(FnPtr::new_unchecked(x.0.clone()).into()),
|
||||
Expr::FnPointer(x) => Ok(FnPtr::new_unchecked(x.0.clone(), Default::default()).into()),
|
||||
Expr::Variable(x) if (x.0).0 == KEYWORD_THIS => {
|
||||
if let Some(val) = this_ptr {
|
||||
Ok(val.clone())
|
||||
@@ -1963,6 +1939,34 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle curry()
|
||||
if name == KEYWORD_FN_PTR_CURRY && args_expr.len() > 1 {
|
||||
let expr = args_expr.get(0);
|
||||
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||
|
||||
if !fn_ptr.is::<FnPtr>() {
|
||||
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
|
||||
self.map_type_name(type_name::<FnPtr>()).into(),
|
||||
self.map_type_name(fn_ptr.type_name()).into(),
|
||||
expr.position(),
|
||||
)));
|
||||
}
|
||||
|
||||
let (fn_name, fn_curry) = fn_ptr.cast::<FnPtr>().take_data();
|
||||
|
||||
let curry: StaticVec<_> = args_expr
|
||||
.iter()
|
||||
.skip(1)
|
||||
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
return Ok(FnPtr::new_unchecked(
|
||||
fn_name,
|
||||
fn_curry.into_iter().chain(curry.into_iter()).collect(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
// Handle eval()
|
||||
if name == KEYWORD_EVAL && args_expr.len() == 1 {
|
||||
let hash_fn =
|
||||
@@ -1992,6 +1996,7 @@ impl Engine {
|
||||
let redirected;
|
||||
let mut name = name.as_ref();
|
||||
let mut args_expr = args_expr.as_ref();
|
||||
let mut curry: StaticVec<_> = Default::default();
|
||||
let mut hash = *hash;
|
||||
|
||||
if name == KEYWORD_FN_PTR_CALL
|
||||
@@ -1999,20 +2004,22 @@ impl Engine {
|
||||
&& !self.has_override(lib, 0, hash)
|
||||
{
|
||||
let expr = args_expr.get(0).unwrap();
|
||||
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||
let fn_name = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||
|
||||
if fn_ptr.is::<FnPtr>() {
|
||||
if fn_name.is::<FnPtr>() {
|
||||
let fn_ptr = fn_name.cast::<FnPtr>();
|
||||
curry = fn_ptr.curry().iter().cloned().collect();
|
||||
// Redirect function name
|
||||
redirected = Some(fn_ptr.cast::<FnPtr>().take_fn_name());
|
||||
name = redirected.as_ref().unwrap();
|
||||
redirected = fn_ptr.take_data().0;
|
||||
name = &redirected;
|
||||
// Skip the first argument
|
||||
args_expr = &args_expr.as_ref()[1..];
|
||||
// Recalculate hash
|
||||
hash = calc_fn_hash(empty(), name, args_expr.len(), empty());
|
||||
hash = calc_fn_hash(empty(), name, curry.len() + args_expr.len(), empty());
|
||||
} else {
|
||||
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
|
||||
self.map_type_name(type_name::<FnPtr>()).into(),
|
||||
fn_ptr.type_name().into(),
|
||||
fn_name.type_name().into(),
|
||||
expr.position(),
|
||||
)));
|
||||
}
|
||||
@@ -2023,7 +2030,7 @@ impl Engine {
|
||||
let mut args: StaticVec<_>;
|
||||
let mut is_ref = false;
|
||||
|
||||
if args_expr.is_empty() {
|
||||
if args_expr.is_empty() && curry.is_empty() {
|
||||
// No arguments
|
||||
args = Default::default();
|
||||
} else {
|
||||
@@ -2046,7 +2053,10 @@ impl Engine {
|
||||
self.inc_operations(state)
|
||||
.map_err(|err| err.new_position(pos))?;
|
||||
|
||||
args = once(target).chain(arg_values.iter_mut()).collect();
|
||||
args = once(target)
|
||||
.chain(curry.iter_mut())
|
||||
.chain(arg_values.iter_mut())
|
||||
.collect();
|
||||
|
||||
is_ref = true;
|
||||
}
|
||||
@@ -2059,7 +2069,7 @@ impl Engine {
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
args = arg_values.iter_mut().collect();
|
||||
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2216,10 +2226,9 @@ impl Engine {
|
||||
Expr::False(_) => Ok(false.into()),
|
||||
Expr::Unit(_) => Ok(().into()),
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
Expr::Custom(x) => {
|
||||
let func = (x.0).1.as_ref();
|
||||
let ep: StaticVec<_> = (x.0).0.iter().map(|e| e.into()).collect();
|
||||
let ep = (x.0).0.iter().map(|e| e.into()).collect::<StaticVec<_>>();
|
||||
let mut context = EvalContext {
|
||||
mods,
|
||||
state,
|
||||
|
@@ -1,14 +1,15 @@
|
||||
//! Module containing interfaces with native-Rust functions.
|
||||
use crate::any::Dynamic;
|
||||
use crate::engine::Engine;
|
||||
use crate::module::Module;
|
||||
use crate::module::{FuncReturn, Module};
|
||||
use crate::parser::ScriptFnDef;
|
||||
use crate::plugin::PluginFunction;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::token::{is_valid_identifier, Position};
|
||||
use crate::utils::ImmutableString;
|
||||
use crate::utils::{ImmutableString, StaticVec};
|
||||
use crate::Scope;
|
||||
|
||||
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, rc::Rc, string::String, sync::Arc};
|
||||
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, mem, rc::Rc, string::String, sync::Arc};
|
||||
|
||||
/// Trait that maps to `Send + Sync` only under the `sync` feature.
|
||||
#[cfg(feature = "sync")]
|
||||
@@ -50,14 +51,15 @@ pub fn shared_take<T: Clone>(value: Shared<T>) -> T {
|
||||
|
||||
pub type FnCallArgs<'a> = [&'a mut Dynamic];
|
||||
|
||||
/// A general function pointer.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
|
||||
pub struct FnPtr(ImmutableString);
|
||||
/// A general function pointer, which may carry additional (i.e. curried) argument values
|
||||
/// to be passed onto a function during a call.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct FnPtr(ImmutableString, Vec<Dynamic>);
|
||||
|
||||
impl FnPtr {
|
||||
/// Create a new function pointer.
|
||||
pub(crate) fn new_unchecked<S: Into<ImmutableString>>(name: S) -> Self {
|
||||
Self(name.into())
|
||||
pub(crate) fn new_unchecked<S: Into<ImmutableString>>(name: S, curry: Vec<Dynamic>) -> Self {
|
||||
Self(name.into(), curry)
|
||||
}
|
||||
/// Get the name of the function.
|
||||
pub fn fn_name(&self) -> &str {
|
||||
@@ -67,9 +69,38 @@ impl FnPtr {
|
||||
pub(crate) fn get_fn_name(&self) -> &ImmutableString {
|
||||
&self.0
|
||||
}
|
||||
/// Get the name of the function.
|
||||
pub(crate) fn take_fn_name(self) -> ImmutableString {
|
||||
self.0
|
||||
/// Get the underlying data of the function pointer.
|
||||
pub(crate) fn take_data(self) -> (ImmutableString, Vec<Dynamic>) {
|
||||
(self.0, self.1)
|
||||
}
|
||||
/// Get the curried arguments.
|
||||
pub fn curry(&self) -> &[Dynamic] {
|
||||
&self.1
|
||||
}
|
||||
|
||||
/// Call the function pointer with curried arguments (if any).
|
||||
///
|
||||
/// ## WARNING
|
||||
///
|
||||
/// All the arguments are _consumed_, meaning that they're replaced by `()`.
|
||||
/// This is to avoid unnecessarily cloning the arguments.
|
||||
/// Do not use the arguments after this call. If they are needed afterwards,
|
||||
/// clone them _before_ calling this function.
|
||||
pub fn call_dynamic(
|
||||
&self,
|
||||
engine: &Engine,
|
||||
lib: impl AsRef<Module>,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
mut arg_values: impl AsMut<[Dynamic]>,
|
||||
) -> FuncReturn<Dynamic> {
|
||||
let args = self
|
||||
.1
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(arg_values.as_mut().iter_mut().map(|v| mem::take(v)))
|
||||
.collect::<StaticVec<_>>();
|
||||
|
||||
engine.call_fn_dynamic(&mut Scope::new(), lib, self.0.as_str(), this_ptr, args)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +115,7 @@ impl TryFrom<ImmutableString> for FnPtr {
|
||||
|
||||
fn try_from(value: ImmutableString) -> Result<Self, Self::Error> {
|
||||
if is_valid_identifier(value.chars()) {
|
||||
Ok(Self(value))
|
||||
Ok(Self(value, Default::default()))
|
||||
} else {
|
||||
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
|
||||
value.into(),
|
||||
|
@@ -95,7 +95,6 @@ mod scope;
|
||||
mod serde;
|
||||
mod settings;
|
||||
mod stdlib;
|
||||
#[cfg(feature = "internals")]
|
||||
mod syntax;
|
||||
mod token;
|
||||
mod r#unsafe;
|
||||
@@ -110,6 +109,7 @@ pub use module::Module;
|
||||
pub use parser::{ImmutableString, AST, INT};
|
||||
pub use result::EvalAltResult;
|
||||
pub use scope::Scope;
|
||||
pub use syntax::{EvalContext, Expression};
|
||||
pub use token::Position;
|
||||
pub use utils::calc_fn_spec as calc_fn_hash;
|
||||
|
||||
@@ -173,11 +173,7 @@ pub use parser::{CustomExpr, Expr, ReturnType, ScriptFnDef, Stmt};
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
#[deprecated(note = "this type is volatile and may change")]
|
||||
pub use engine::{Expression, Imports, State as EvalState};
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
#[deprecated(note = "this type is volatile and may change")]
|
||||
pub use syntax::EvalContext;
|
||||
pub use engine::{Imports, State as EvalState};
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
#[deprecated(note = "this type is volatile and may change")]
|
||||
|
@@ -2,23 +2,19 @@
|
||||
|
||||
use crate::any::{Dynamic, Union};
|
||||
use crate::calc_fn_hash;
|
||||
use crate::engine::{make_getter, make_setter, Engine, FN_ANONYMOUS, KEYWORD_THIS};
|
||||
use crate::engine::{
|
||||
make_getter, make_setter, Engine, FN_ANONYMOUS, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR,
|
||||
MARKER_IDENT,
|
||||
};
|
||||
use crate::error::{LexError, ParseError, ParseErrorType};
|
||||
use crate::fn_native::Shared;
|
||||
use crate::module::{Module, ModuleRef};
|
||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||
use crate::syntax::FnCustomSyntaxEval;
|
||||
use crate::token::{is_valid_identifier, Position, Token, TokenStream};
|
||||
use crate::utils::{StaticVec, StraightHasherBuilder};
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
use crate::engine::{MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
use crate::fn_native::Shared;
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
use crate::syntax::FnCustomSyntaxEval;
|
||||
|
||||
use crate::stdlib::{
|
||||
borrow::Cow,
|
||||
boxed::Box,
|
||||
@@ -587,17 +583,14 @@ impl Stmt {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg(feature = "internals")]
|
||||
pub struct CustomExpr(pub StaticVec<Expr>, pub Shared<FnCustomSyntaxEval>);
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
impl fmt::Debug for CustomExpr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
impl Hash for CustomExpr {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
@@ -683,7 +676,6 @@ pub enum Expr {
|
||||
/// ()
|
||||
Unit(Position),
|
||||
/// Custom syntax
|
||||
#[cfg(feature = "internals")]
|
||||
Custom(Box<(CustomExpr, Position)>),
|
||||
}
|
||||
|
||||
@@ -781,7 +773,6 @@ impl Expr {
|
||||
|
||||
Self::Dot(x) | Self::Index(x) => x.0.position(),
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
Self::Custom(x) => x.1,
|
||||
}
|
||||
}
|
||||
@@ -816,8 +807,6 @@ impl Expr {
|
||||
Self::Assignment(x) => x.3 = new_pos,
|
||||
Self::Dot(x) => x.2 = new_pos,
|
||||
Self::Index(x) => x.2 = new_pos,
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
Self::Custom(x) => x.1 = new_pos,
|
||||
}
|
||||
|
||||
@@ -925,7 +914,6 @@ impl Expr {
|
||||
_ => false,
|
||||
},
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
Self::Custom(_) => false,
|
||||
}
|
||||
}
|
||||
@@ -2148,7 +2136,6 @@ fn parse_expr(
|
||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||
|
||||
// Check if it is a custom syntax.
|
||||
#[cfg(feature = "internals")]
|
||||
if let Some(ref custom) = state.engine.custom_syntax {
|
||||
let (token, pos) = input.peek().unwrap();
|
||||
let token_pos = *pos;
|
||||
|
@@ -1,14 +1,13 @@
|
||||
//! Module containing implementation for custom syntax.
|
||||
#![cfg(feature = "internals")]
|
||||
|
||||
use crate::any::Dynamic;
|
||||
use crate::engine::{Engine, Expression, Imports, State, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
||||
use crate::error::LexError;
|
||||
use crate::engine::{Engine, Imports, State, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
||||
use crate::error::{LexError, ParseError};
|
||||
use crate::fn_native::{SendSync, Shared};
|
||||
use crate::module::Module;
|
||||
use crate::parser::Expr;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::scope::Scope;
|
||||
use crate::token::{is_valid_identifier, Token};
|
||||
use crate::token::{is_valid_identifier, Position, Token};
|
||||
use crate::utils::StaticVec;
|
||||
|
||||
use crate::stdlib::{
|
||||
@@ -18,7 +17,7 @@ use crate::stdlib::{
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// A general function trail object.
|
||||
/// A general expression evaluation trait object.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type FnCustomSyntaxEval = dyn Fn(
|
||||
&Engine,
|
||||
@@ -26,12 +25,40 @@ pub type FnCustomSyntaxEval = dyn Fn(
|
||||
&mut Scope,
|
||||
&[Expression],
|
||||
) -> Result<Dynamic, Box<EvalAltResult>>;
|
||||
/// A general function trail object.
|
||||
/// A general expression evaluation trait object.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type FnCustomSyntaxEval = dyn Fn(&Engine, &mut EvalContext, &mut Scope, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync;
|
||||
|
||||
/// An expression sub-tree in an AST.
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct Expression<'a>(&'a Expr);
|
||||
|
||||
impl<'a> From<&'a Expr> for Expression<'a> {
|
||||
fn from(expr: &'a Expr) -> Self {
|
||||
Self(expr)
|
||||
}
|
||||
}
|
||||
|
||||
impl Expression<'_> {
|
||||
/// If this expression is a variable name, return it. Otherwise `None`.
|
||||
pub fn get_variable_name(&self) -> Option<&str> {
|
||||
match self.0 {
|
||||
Expr::Variable(x) => Some((x.0).0.as_str()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Get the expression.
|
||||
pub(crate) fn expr(&self) -> &Expr {
|
||||
&self.0
|
||||
}
|
||||
/// Get the position of this expression.
|
||||
pub fn position(&self) -> Position {
|
||||
self.0.position()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CustomSyntax {
|
||||
pub segments: StaticVec<String>,
|
||||
@@ -45,6 +72,8 @@ impl fmt::Debug for CustomSyntax {
|
||||
}
|
||||
}
|
||||
|
||||
/// Context of a script evaluation process.
|
||||
#[derive(Debug)]
|
||||
pub struct EvalContext<'a, 'b: 'a, 's, 'm, 't, 'd: 't> {
|
||||
pub(crate) mods: &'a mut Imports<'b>,
|
||||
pub(crate) state: &'s mut State,
|
||||
@@ -56,7 +85,7 @@ pub struct EvalContext<'a, 'b: 'a, 's, 'm, 't, 'd: 't> {
|
||||
impl Engine {
|
||||
pub fn register_custom_syntax<S: AsRef<str> + ToString>(
|
||||
&mut self,
|
||||
value: &[S],
|
||||
keywords: &[S],
|
||||
scope_delta: isize,
|
||||
func: impl Fn(
|
||||
&Engine,
|
||||
@@ -66,15 +95,18 @@ impl Engine {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ SendSync
|
||||
+ 'static,
|
||||
) -> Result<&mut Self, Box<LexError>> {
|
||||
if value.is_empty() {
|
||||
return Err(Box::new(LexError::ImproperSymbol("".to_string())));
|
||||
}
|
||||
|
||||
) -> Result<&mut Self, Box<ParseError>> {
|
||||
let mut segments: StaticVec<_> = Default::default();
|
||||
|
||||
for s in value {
|
||||
let seg = match s.as_ref() {
|
||||
for s in keywords {
|
||||
let s = s.as_ref().trim();
|
||||
|
||||
// skip empty keywords
|
||||
if s.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let seg = match s {
|
||||
// Markers not in first position
|
||||
MARKER_EXPR | MARKER_BLOCK | MARKER_IDENT if !segments.is_empty() => s.to_string(),
|
||||
// Standard symbols not in first position
|
||||
@@ -115,12 +147,25 @@ impl Engine {
|
||||
s.into()
|
||||
}
|
||||
// Anything else is an error
|
||||
_ => return Err(Box::new(LexError::ImproperSymbol(s.to_string()))),
|
||||
_ => {
|
||||
return Err(LexError::ImproperSymbol(format!(
|
||||
"Improper symbol for custom syntax: '{}'",
|
||||
s
|
||||
))
|
||||
.into_err(Position::none())
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
segments.push(seg);
|
||||
}
|
||||
|
||||
// If the syntax has no keywords, just ignore the registration
|
||||
if segments.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
|
||||
// Remove the first keyword as the discriminator
|
||||
let key = segments.remove(0);
|
||||
|
||||
let syntax = CustomSyntax {
|
||||
|
@@ -1,8 +1,8 @@
|
||||
//! Main module defining the lexer and parser.
|
||||
|
||||
use crate::engine::{
|
||||
Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_PRINT,
|
||||
KEYWORD_THIS, KEYWORD_TYPE_OF,
|
||||
Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY,
|
||||
KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF,
|
||||
};
|
||||
|
||||
use crate::error::LexError;
|
||||
@@ -404,7 +404,7 @@ impl Token {
|
||||
Reserved(syntax.into())
|
||||
}
|
||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
||||
| KEYWORD_FN_PTR_CALL | KEYWORD_THIS => Reserved(syntax.into()),
|
||||
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS => Reserved(syntax.into()),
|
||||
|
||||
_ => return None,
|
||||
})
|
||||
|
@@ -387,7 +387,7 @@ impl<T> StaticVec<T> {
|
||||
let value = self.extract_from_list(index);
|
||||
|
||||
// Move all items one slot to the left
|
||||
for x in index + 1..self.len - 1 {
|
||||
for x in index + 1..self.len {
|
||||
let orig_value = self.extract_from_list(x);
|
||||
self.set_into_list(x - 1, orig_value, false);
|
||||
}
|
||||
|
Reference in New Issue
Block a user