Make sure all features compile correctly.

This commit is contained in:
Stephen Chung 2020-05-06 19:45:17 +08:00
parent 88fec57394
commit d75a8bc6cd
11 changed files with 640 additions and 179 deletions

View File

@ -1,12 +1,19 @@
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
use crate::engine::{Array, Map};
#[cfg(not(feature = "no_module"))]
use crate::module::Module;
use crate::parser::INT;
#[cfg(not(feature = "no_float"))]
use crate::parser::FLOAT;
#[cfg(not(feature = "no_index"))]
use crate::engine::Array;
#[cfg(not(feature = "no_object"))]
use crate::engine::Map;
use crate::stdlib::{
any::{type_name, Any, TypeId},
boxed::Box,
@ -134,8 +141,11 @@ pub enum Union {
Int(INT),
#[cfg(not(feature = "no_float"))]
Float(FLOAT),
#[cfg(not(feature = "no_index"))]
Array(Box<Array>),
#[cfg(not(feature = "no_object"))]
Map(Box<Map>),
#[cfg(not(feature = "no_module"))]
Module(Box<Module>),
Variant(Box<Box<dyn Variant>>),
}
@ -165,8 +175,11 @@ impl Dynamic {
Union::Int(_) => TypeId::of::<INT>(),
#[cfg(not(feature = "no_float"))]
Union::Float(_) => TypeId::of::<FLOAT>(),
#[cfg(not(feature = "no_index"))]
Union::Array(_) => TypeId::of::<Array>(),
#[cfg(not(feature = "no_object"))]
Union::Map(_) => TypeId::of::<Map>(),
#[cfg(not(feature = "no_module"))]
Union::Module(_) => TypeId::of::<Module>(),
Union::Variant(value) => (***value).type_id(),
}
@ -182,8 +195,11 @@ impl Dynamic {
Union::Int(_) => type_name::<INT>(),
#[cfg(not(feature = "no_float"))]
Union::Float(_) => type_name::<FLOAT>(),
#[cfg(not(feature = "no_index"))]
Union::Array(_) => "array",
#[cfg(not(feature = "no_object"))]
Union::Map(_) => "map",
#[cfg(not(feature = "no_module"))]
Union::Module(_) => "sub-scope",
#[cfg(not(feature = "no_std"))]
@ -203,8 +219,11 @@ impl fmt::Display for Dynamic {
Union::Int(value) => write!(f, "{}", value),
#[cfg(not(feature = "no_float"))]
Union::Float(value) => write!(f, "{}", value),
#[cfg(not(feature = "no_index"))]
Union::Array(value) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => write!(f, "#{:?}", value),
#[cfg(not(feature = "no_module"))]
Union::Module(value) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_std"))]
@ -224,8 +243,11 @@ impl fmt::Debug for Dynamic {
Union::Int(value) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_float"))]
Union::Float(value) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_index"))]
Union::Array(value) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => write!(f, "#{:?}", value),
#[cfg(not(feature = "no_module"))]
Union::Module(value) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_std"))]
@ -245,8 +267,11 @@ impl Clone for Dynamic {
Union::Int(value) => Self(Union::Int(value)),
#[cfg(not(feature = "no_float"))]
Union::Float(value) => Self(Union::Float(value)),
#[cfg(not(feature = "no_index"))]
Union::Array(ref value) => Self(Union::Array(value.clone())),
#[cfg(not(feature = "no_object"))]
Union::Map(ref value) => Self(Union::Map(value.clone())),
#[cfg(not(feature = "no_module"))]
Union::Module(ref value) => Self(Union::Module(value.clone())),
Union::Variant(ref value) => (***value).clone_into_dynamic(),
}
@ -260,13 +285,13 @@ impl Default for Dynamic {
}
/// Cast a Boxed type into another type.
fn cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<T, Box<X>> {
fn cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, Box<X>> {
// Only allow casting to the exact same type
if TypeId::of::<X>() == TypeId::of::<T>() {
// SAFETY: just checked whether we are pointing to the correct type
unsafe {
let raw: *mut dyn Any = Box::into_raw(item as Box<dyn Any>);
Ok(*Box::from_raw(raw as *mut T))
Ok(Box::from_raw(raw as *mut T))
}
} else {
// Return the consumed item for chaining.
@ -320,31 +345,33 @@ impl Dynamic {
}
}
let var = Box::new(value);
let mut var = Box::new(value);
Self(
cast_box::<_, Dynamic>(var)
.map(|x| x.0)
.or_else(|var| {
cast_box::<_, String>(var)
.map(Box::new)
.map(Union::Str)
.or_else(|var| {
cast_box::<_, Array>(var)
.map(Box::new)
.map(Union::Array)
.or_else(|var| {
cast_box::<_, Map>(var)
.map(Box::new)
.map(Union::Map)
.or_else(|var| -> Result<Union, ()> {
Ok(Union::Variant(Box::new(var)))
})
})
})
})
.unwrap(),
)
var = match cast_box::<_, Dynamic>(var) {
Ok(d) => return *d,
Err(var) => var,
};
var = match cast_box::<_, String>(var) {
Ok(s) => return Self(Union::Str(s)),
Err(var) => var,
};
#[cfg(not(feature = "no_index"))]
{
var = match cast_box::<_, Array>(var) {
Ok(array) => return Self(Union::Array(array)),
Err(var) => var,
};
}
#[cfg(not(feature = "no_object"))]
{
var = match cast_box::<_, Map>(var) {
Ok(map) => return Self(Union::Map(map)),
Err(var) => var,
}
}
Self(Union::Variant(Box::new(var)))
}
/// Get a copy of the `Dynamic` value as a specific type.
@ -363,20 +390,23 @@ impl Dynamic {
/// ```
pub fn try_cast<T: Variant + Clone>(self) -> Option<T> {
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
return cast_box::<_, T>(Box::new(self)).ok();
return cast_box::<_, T>(Box::new(self)).ok().map(|v| *v);
}
match self.0 {
Union::Unit(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
Union::Bool(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
Union::Str(value) => cast_box::<_, T>(value).ok(),
Union::Str(value) => cast_box::<_, T>(value).ok().map(|v| *v),
Union::Char(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
Union::Int(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
#[cfg(not(feature = "no_float"))]
Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
Union::Array(value) => cast_box::<_, T>(value).ok(),
Union::Map(value) => cast_box::<_, T>(value).ok(),
Union::Module(value) => cast_box::<_, T>(value).ok(),
#[cfg(not(feature = "no_index"))]
Union::Array(value) => cast_box::<_, T>(value).ok().map(|v| *v),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => cast_box::<_, T>(value).ok().map(|v| *v),
#[cfg(not(feature = "no_module"))]
Union::Module(value) => cast_box::<_, T>(value).ok().map(|v| *v),
Union::Variant(value) => value.as_any().downcast_ref::<T>().cloned(),
}
}
@ -401,20 +431,23 @@ impl Dynamic {
//self.try_cast::<T>().unwrap()
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
return cast_box::<_, T>(Box::new(self)).unwrap();
return *cast_box::<_, T>(Box::new(self)).unwrap();
}
match self.0 {
Union::Unit(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
Union::Bool(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
Union::Str(value) => cast_box::<_, T>(value).unwrap(),
Union::Str(value) => *cast_box::<_, T>(value).unwrap(),
Union::Char(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
Union::Int(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
#[cfg(not(feature = "no_float"))]
Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
Union::Array(value) => cast_box::<_, T>(value).unwrap(),
Union::Map(value) => cast_box::<_, T>(value).unwrap(),
Union::Module(value) => cast_box::<_, T>(value).unwrap(),
#[cfg(not(feature = "no_index"))]
Union::Array(value) => *cast_box::<_, T>(value).unwrap(),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => *cast_box::<_, T>(value).unwrap(),
#[cfg(not(feature = "no_module"))]
Union::Module(value) => *cast_box::<_, T>(value).unwrap(),
Union::Variant(value) => value.as_any().downcast_ref::<T>().unwrap().clone(),
}
}
@ -435,8 +468,11 @@ impl Dynamic {
Union::Int(value) => (value as &dyn Any).downcast_ref::<T>(),
#[cfg(not(feature = "no_float"))]
Union::Float(value) => (value as &dyn Any).downcast_ref::<T>(),
#[cfg(not(feature = "no_index"))]
Union::Array(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(),
#[cfg(not(feature = "no_module"))]
Union::Module(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(),
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
}
@ -458,8 +494,11 @@ impl Dynamic {
Union::Int(value) => (value as &mut dyn Any).downcast_mut::<T>(),
#[cfg(not(feature = "no_float"))]
Union::Float(value) => (value as &mut dyn Any).downcast_mut::<T>(),
#[cfg(not(feature = "no_index"))]
Union::Array(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(),
#[cfg(not(feature = "no_module"))]
Union::Module(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(),
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
}
@ -542,6 +581,7 @@ impl From<String> for Dynamic {
Self(Union::Str(Box::new(value)))
}
}
#[cfg(not(feature = "no_index"))]
impl<T: Variant + Clone> From<Vec<T>> for Dynamic {
fn from(value: Vec<T>) -> Self {
Self(Union::Array(Box::new(
@ -549,6 +589,7 @@ impl<T: Variant + Clone> From<Vec<T>> for Dynamic {
)))
}
}
#[cfg(not(feature = "no_object"))]
impl<T: Variant + Clone> From<HashMap<String, T>> for Dynamic {
fn from(value: HashMap<String, T>) -> Self {
Self(Union::Map(Box::new(

View File

@ -1,7 +1,7 @@
//! Module that defines the extern API of `Engine`.
use crate::any::{Dynamic, Variant};
use crate::engine::{make_getter, make_setter, Engine, Map, State, FUNC_INDEXER};
use crate::engine::{make_getter, make_setter, Engine, State, FUNC_INDEXER};
use crate::error::ParseError;
use crate::fn_call::FuncArgs;
use crate::fn_register::RegisterFn;
@ -11,6 +11,9 @@ use crate::result::EvalAltResult;
use crate::scope::Scope;
use crate::token::{lex, Position};
#[cfg(not(feature = "no_object"))]
use crate::engine::Map;
use crate::stdlib::{
any::{type_name, TypeId},
boxed::Box,

View File

@ -3,7 +3,6 @@
use crate::any::{Dynamic, Union};
use crate::calc_fn_hash;
use crate::error::ParseErrorType;
use crate::module::{resolvers, Module, ModuleResolver};
use crate::optimize::OptimizationLevel;
use crate::packages::{CorePackage, Package, PackageLibrary, StandardPackage};
use crate::parser::{Expr, FnDef, ModuleRef, ReturnType, Stmt, AST};
@ -12,6 +11,9 @@ use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::token::Position;
use crate::utils::{calc_fn_def, StaticVec};
#[cfg(not(feature = "no_module"))]
use crate::module::{resolvers, Module, ModuleResolver};
use crate::stdlib::{
any::TypeId,
boxed::Box,
@ -30,11 +32,13 @@ use crate::stdlib::{
/// An dynamic array of `Dynamic` values.
///
/// Not available under the `no_index` feature.
#[cfg(not(feature = "no_index"))]
pub type Array = Vec<Dynamic>;
/// An dynamic hash map of `Dynamic` values with `String` keys.
///
/// Not available under the `no_object` feature.
#[cfg(not(feature = "no_object"))]
pub type Map = HashMap<String, Dynamic>;
pub type FnCallArgs<'a> = [&'a mut Dynamic];
@ -262,6 +266,7 @@ pub struct Engine {
pub(crate) type_iterators: HashMap<TypeId, Box<IteratorFn>>,
/// A module resolution service.
#[cfg(not(feature = "no_module"))]
pub(crate) module_resolver: Option<Box<dyn ModuleResolver>>,
/// A hashmap mapping type names to pretty-print names.
@ -301,7 +306,8 @@ impl Default for Engine {
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_std"))]
module_resolver: Some(Box::new(resolvers::FileModuleResolver::new())),
#[cfg(any(feature = "no_std", feature = "no_module"))]
#[cfg(not(feature = "no_module"))]
#[cfg(feature = "no_std")]
module_resolver: None,
type_names: Default::default(),
@ -391,6 +397,8 @@ fn search_scope<'a>(
index: Option<NonZeroUsize>,
pos: Position,
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
#[cfg(not(feature = "no_module"))]
{
if let Some(modules) = modules {
let (id, root_pos) = modules.get(0); // First module
@ -401,17 +409,19 @@ fn search_scope<'a>(
.downcast_mut::<Module>()
.unwrap()
} else {
scope
.find_module(id)
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))?
scope.find_module(id).ok_or_else(|| {
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
})?
};
Ok((
return Ok((
module.get_qualified_var_mut(name, modules.as_ref(), pos)?,
// Module variables are constant
ScopeEntryType::Constant,
))
} else {
));
}
}
let index = if let Some(index) = index {
scope.len() - index.get()
} else {
@ -423,7 +433,6 @@ fn search_scope<'a>(
Ok(scope.get_mut(index))
}
}
impl Engine {
/// Create a new `Engine`
@ -438,7 +447,10 @@ impl Engine {
packages: Default::default(),
functions: HashMap::with_capacity(FUNCTIONS_COUNT / 2),
type_iterators: Default::default(),
#[cfg(not(feature = "no_module"))]
module_resolver: None,
type_names: Default::default(),
print: Box::new(|_| {}),
debug: Box::new(|_| {}),
@ -721,7 +733,7 @@ impl Engine {
}
let statements = mem::take(ast.statements_mut());
ast = AST::new(statements, fn_lib.clone());
let ast = AST::new(statements, fn_lib.clone());
// Evaluate the AST
self.eval_ast_with_scope_raw(scope, &ast)
@ -779,7 +791,7 @@ impl Engine {
// xxx.fn_name(arg_expr_list)
Expr::FnCall(fn_name, None,_, def_val, pos) => {
let mut args: Vec<_> = once(obj)
.chain(idx_val.downcast_mut::<Array>().unwrap().iter_mut())
.chain(idx_val.downcast_mut::<Vec<Dynamic>>().unwrap().iter_mut())
.collect();
let def_val = def_val.as_deref();
// A function call is assumed to have side effects, so the value is changed
@ -789,6 +801,7 @@ impl Engine {
// xxx.module::fn_name(...) - syntax error
Expr::FnCall(_,_,_,_,_) => unreachable!(),
// {xxx:map}.id = ???
#[cfg(not(feature = "no_object"))]
Expr::Property(id, pos) if obj.is::<Map>() && new_val.is_some() => {
let mut indexed_val =
self.get_indexed_mut(fn_lib, obj, id.to_string().into(), *pos, op_pos, true)?;
@ -796,6 +809,7 @@ impl Engine {
Ok((Default::default(), true))
}
// {xxx:map}.id
#[cfg(not(feature = "no_object"))]
Expr::Property(id, pos) if obj.is::<Map>() => {
let indexed_val =
self.get_indexed_mut(fn_lib, obj, id.to_string().into(), *pos, op_pos, false)?;
@ -813,6 +827,7 @@ impl Engine {
let mut args = [obj];
self.exec_fn_call(fn_lib, &fn_name, &mut args, None, *pos, 0).map(|v| (v, false))
}
#[cfg(not(feature = "no_object"))]
// {xxx:map}.idx_lhs[idx_expr]
Expr::Index(dot_lhs, dot_rhs, pos) |
// {xxx:map}.dot_lhs.rhs
@ -959,7 +974,10 @@ impl Engine {
.map(|arg_expr| self.eval_expr(scope, state, fn_lib, arg_expr, level))
.collect::<Result<Vec<_>, _>>()?;
idx_values.push(arg_values)
#[cfg(not(feature = "no_index"))]
idx_values.push(arg_values);
#[cfg(feature = "no_index")]
idx_values.push(Dynamic::from(arg_values));
}
Expr::FnCall(_, _, _, _, _) => unreachable!(),
Expr::Property(_, _) => idx_values.push(()), // Store a placeholder - no need to copy the property name
@ -994,6 +1012,7 @@ impl Engine {
let type_name = self.map_type_name(val.type_name());
match val {
#[cfg(not(feature = "no_index"))]
Dynamic(Union::Array(arr)) => {
// val_array[idx]
let index = idx
@ -1015,6 +1034,7 @@ impl Engine {
}
}
#[cfg(not(feature = "no_object"))]
Dynamic(Union::Map(map)) => {
// val_map[idx]
let index = idx
@ -1030,6 +1050,7 @@ impl Engine {
})
}
#[cfg(not(feature = "no_index"))]
Dynamic(Union::Str(s)) => {
// val_string[idx]
let index = idx
@ -1088,6 +1109,7 @@ impl Engine {
let rhs_value = self.eval_expr(scope, state, fn_lib, rhs, level)?;
match rhs_value {
#[cfg(not(feature = "no_index"))]
Dynamic(Union::Array(mut rhs_value)) => {
let def_value = false.into();
@ -1108,6 +1130,7 @@ impl Engine {
Ok(false.into())
}
#[cfg(not(feature = "no_object"))]
Dynamic(Union::Map(rhs_value)) => match lhs_value {
// Only allows String or char
Dynamic(Union::Str(s)) => Ok(rhs_value.contains_key(s.as_ref()).into()),
@ -1230,7 +1253,8 @@ impl Engine {
.collect::<Result<HashMap<_, _>, _>>()?,
)))),
Expr::FnCall(fn_name, modules, arg_exprs, def_val, pos) => {
// Normal function call
Expr::FnCall(fn_name, None, arg_exprs, def_val, pos) => {
let mut arg_values = arg_exprs
.iter()
.map(|expr| self.eval_expr(scope, state, fn_lib, expr, level))
@ -1238,32 +1262,7 @@ impl Engine {
let mut args: Vec<_> = arg_values.iter_mut().collect();
if let Some(modules) = modules {
// Module-qualified function call
let modules = modules.as_ref();
let (id, root_pos) = modules.get(0); // First module
let module = scope.find_module(id).ok_or_else(|| {
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
})?;
// First search in script-defined functions (can override built-in)
if let Some(fn_def) =
module.get_qualified_fn_lib(fn_name, args.len(), modules)?
{
self.call_fn_from_lib(None, fn_lib, fn_def, &mut args, *pos, level)
} else {
// Then search in Rust functions
let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id()));
match module.get_qualified_fn(fn_name, hash, modules, *pos) {
Ok(func) => func(&mut args, *pos),
Err(_) if def_val.is_some() => Ok(def_val.as_deref().unwrap().clone()),
Err(err) => Err(err),
}
}
} else if fn_name.as_ref() == KEYWORD_EVAL
if fn_name.as_ref() == KEYWORD_EVAL
&& args.len() == 1
&& !self.has_override(fn_lib, KEYWORD_EVAL)
{
@ -1288,6 +1287,39 @@ impl Engine {
}
}
// Module-qualified function call
#[cfg(not(feature = "no_module"))]
Expr::FnCall(fn_name, Some(modules), arg_exprs, def_val, pos) => {
let modules = modules.as_ref();
let mut arg_values = arg_exprs
.iter()
.map(|expr| self.eval_expr(scope, state, fn_lib, expr, level))
.collect::<Result<Vec<_>, _>>()?;
let mut args: Vec<_> = arg_values.iter_mut().collect();
let (id, root_pos) = modules.get(0); // First module
let module = scope.find_module(id).ok_or_else(|| {
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
})?;
// First search in script-defined functions (can override built-in)
if let Some(fn_def) = module.get_qualified_fn_lib(fn_name, args.len(), modules)? {
self.call_fn_from_lib(None, fn_lib, fn_def, &mut args, *pos, level)
} else {
// Then search in Rust functions
let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id()));
match module.get_qualified_fn(fn_name, hash, modules, *pos) {
Ok(func) => func(&mut args, *pos),
Err(_) if def_val.is_some() => Ok(def_val.as_deref().unwrap().clone()),
Err(err) => Err(err),
}
}
}
Expr::In(lhs, rhs, _) => {
self.eval_in_expr(scope, state, fn_lib, lhs.as_ref(), rhs.as_ref(), level)
}
@ -1519,6 +1551,8 @@ impl Engine {
#[cfg(feature = "no_module")]
unreachable!();
#[cfg(not(feature = "no_module"))]
{
if let Some(path) = self
.eval_expr(scope, state, fn_lib, expr, level)?
.try_cast::<String>()
@ -1542,6 +1576,7 @@ impl Engine {
}
}
}
}
/// Map a type_name into a pretty-print name
pub(crate) fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str {

View File

@ -1,9 +1,10 @@
//! Module defining external-loaded modules for Rhai.
#![cfg(not(feature = "no_module"))]
use crate::any::{Dynamic, Variant};
use crate::calc_fn_hash;
use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib};
use crate::parser::FnDef;
use crate::parser::{FnDef, AST};
use crate::result::EvalAltResult;
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
use crate::token::Position;
@ -12,11 +13,12 @@ use crate::utils::StaticVec;
use crate::stdlib::{
any::TypeId,
boxed::Box,
collections::HashMap,
fmt, mem,
ops::{Deref, DerefMut},
rc::Rc,
string::String,
string::{String, ToString},
sync::Arc,
};
@ -70,21 +72,61 @@ impl fmt::Debug for Module {
impl Module {
/// Create a new module.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// ```
pub fn new() -> Self {
Default::default()
}
/// Does a variable exist in the module?
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert!(module.contains_var("answer"));
/// ```
pub fn contains_var(&self, name: &str) -> bool {
self.variables.contains_key(name)
}
/// Get the value of a module variable.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// ```
pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
self.get_var(name).and_then(|v| v.try_cast::<T>())
}
/// Get a module variable.
/// Get a module variable as a `Dynamic`.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var("answer").unwrap().cast::<i64>(), 42);
/// ```
pub fn get_var(&self, name: &str) -> Option<Dynamic> {
self.variables.get(name).cloned()
}
@ -97,6 +139,16 @@ impl Module {
/// Set a variable into the module.
///
/// If there is an existing variable of the same name, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// ```
pub fn set_var<K: Into<String>, T: Into<Dynamic>>(&mut self, name: K, value: T) {
self.variables.insert(name.into(), value.into());
}
@ -115,16 +167,49 @@ impl Module {
}
/// Does a sub-module exist in the module?
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let sub_module = Module::new();
/// module.set_sub_module("question", sub_module);
/// assert!(module.contains_sub_module("question"));
/// ```
pub fn contains_sub_module(&self, name: &str) -> bool {
self.modules.contains_key(name)
}
/// Get a sub-module.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let sub_module = Module::new();
/// module.set_sub_module("question", sub_module);
/// assert!(module.get_sub_module("question").is_some());
/// ```
pub fn get_sub_module(&self, name: &str) -> Option<&Module> {
self.modules.get(name)
}
/// Get a mutable reference to a sub-module.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let sub_module = Module::new();
/// module.set_sub_module("question", sub_module);
/// assert!(module.get_sub_module_mut("question").is_some());
/// ```
pub fn get_sub_module_mut(&mut self, name: &str) -> Option<&mut Module> {
self.modules.get_mut(name)
}
@ -132,6 +217,17 @@ impl Module {
/// Set a sub-module into the module.
///
/// If there is an existing sub-module of the same name, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let sub_module = Module::new();
/// module.set_sub_module("question", sub_module);
/// assert!(module.get_sub_module("question").is_some());
/// ```
pub fn set_sub_module<K: Into<String>>(&mut self, name: K, sub_module: Module) {
self.modules.insert(name.into(), sub_module.into());
}
@ -160,6 +256,16 @@ impl Module {
///
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
/// It is also returned by the `set_fn_XXX` calls.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
/// assert!(module.contains_fn(hash));
/// ```
pub fn contains_fn(&self, hash: u64) -> bool {
self.functions.contains_key(&hash)
}
@ -181,6 +287,16 @@ impl Module {
/// Set a Rust function taking no parameters into the module, returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_0<T: Into<Dynamic>>(
&mut self,
fn_name: &str,
@ -199,6 +315,16 @@ impl Module {
/// Set a Rust function taking one parameter into the module, returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_1<A: Variant + Clone, T: Into<Dynamic>>(
&mut self,
fn_name: &str,
@ -217,6 +343,16 @@ impl Module {
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_1_mut<A: Variant + Clone, T: Into<Dynamic>>(
&mut self,
fn_name: &str,
@ -235,6 +371,18 @@ impl Module {
/// Set a Rust function taking two parameters into the module, returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_2("calc", |x: i64, y: String| {
/// Ok(x + y.len() as i64)
/// });
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
&mut self,
fn_name: &str,
@ -256,7 +404,17 @@ impl Module {
/// Set a Rust function taking two parameters (the first one mutable) into the module,
/// returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: String| {
/// *x += y.len() as i64; Ok(*x)
/// });
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
&mut self,
fn_name: &str,
@ -278,6 +436,18 @@ impl Module {
/// Set a Rust function taking three parameters into the module, returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_3("calc", |x: i64, y: String, z: i64| {
/// Ok(x + y.len() as i64 + z)
/// });
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_3<
A: Variant + Clone,
B: Variant + Clone,
@ -306,6 +476,18 @@ impl Module {
/// returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: String, z: i64| {
/// *x += y.len() as i64 + z; Ok(*x)
/// });
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_3_mut<
A: Variant + Clone,
B: Variant + Clone,
@ -334,6 +516,16 @@ impl Module {
///
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
/// It is also returned by the `set_fn_XXX` calls.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn get_fn(&self, hash: u64) -> Option<&Box<FnAny>> {
self.functions.get(&hash).map(|v| v.as_ref())
}
@ -366,7 +558,16 @@ impl Module {
})?)
}
/// Get a script-defined function.
/// Get the script-defined functions.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// assert_eq!(module.get_fn_lib().len(), 0);
/// ```
pub fn get_fn_lib(&self) -> &FunctionsLib {
&self.fn_lib
}
@ -383,73 +584,29 @@ impl Module {
.fn_lib
.get_function(name, args))
}
}
/// Re-export module resolvers.
pub mod resolvers {
pub use super::file::FileModuleResolver;
pub use super::stat::StaticModuleResolver;
}
/// Script file-based module resolver.
#[cfg(not(feature = "no_std"))]
mod file {
use super::*;
use crate::stdlib::path::PathBuf;
/// A module resolution service that loads module script files from the file system.
/// Create a new `Module` by evaluating an `AST`.
///
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
/// allow specification of a base directory with module path used as a relative path offset
/// to the base directory. The script file is then forced to be in a specified extension
/// (default `.rhai`).
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct FileModuleResolver {
path: PathBuf,
extension: String,
}
impl FileModuleResolver {
/// Create a new `FileModuleResolver` with a specific base path.
pub fn new_with_path(path: PathBuf) -> Self {
Self::new_with_path_and_extension(path, "rhai".to_string())
}
/// Create a new `FileModuleResolver` with a specific base path and file extension.
/// # Examples
///
/// The default extension is `.rhai`.
pub fn new_with_path_and_extension(path: PathBuf, extension: String) -> Self {
Self { path, extension }
}
/// Create a new `FileModuleResolver` with the current directory as base path.
pub fn new() -> Self {
Default::default()
}
}
impl ModuleResolver for FileModuleResolver {
fn resolve(
&self,
engine: &Engine,
path: &str,
pos: Position,
) -> Result<Module, Box<EvalAltResult>> {
// Construct the script file path
let mut file_path = self.path.clone();
file_path.push(path);
file_path.set_extension(&self.extension); // Force extension
// Compile it
let ast = engine
.compile_file(file_path)
.map_err(|err| EvalAltResult::set_position(err, pos))?;
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// use rhai::{Engine, Module};
///
/// let engine = Engine::new();
/// let ast = engine.compile("let answer = 42;")?;
/// let module = Module::eval_ast_as_new(&ast, &engine)?;
/// assert!(module.contains_var("answer"));
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// # Ok(())
/// # }
/// ```
pub fn eval_ast_as_new(ast: &AST, engine: &Engine) -> FuncReturn<Self> {
// Use new scope
let mut scope = Scope::new();
// Run the script
engine
.eval_ast_with_scope_raw(&mut scope, &ast)
.map_err(|err| EvalAltResult::set_position(err, pos))?;
engine.eval_ast_with_scope_raw(&mut scope, &ast)?;
// Create new module
let mut module = Module::new();
@ -478,6 +635,134 @@ mod file {
Ok(module)
}
}
/// Re-export module resolvers.
pub mod resolvers {
#[cfg(not(feature = "no_std"))]
pub use super::file::FileModuleResolver;
pub use super::stat::StaticModuleResolver;
}
/// Script file-based module resolver.
#[cfg(not(feature = "no_std"))]
mod file {
use super::*;
use crate::stdlib::path::PathBuf;
/// A module resolution service that loads module script files from the file system.
///
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
/// allow specification of a base directory with module path used as a relative path offset
/// to the base directory. The script file is then forced to be in a specified extension
/// (default `.rhai`).
///
/// # Examples
///
/// ```
/// use rhai::Engine;
/// use rhai::module_resolvers::FileModuleResolver;
///
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
/// // with file extension '.x'.
/// let resolver = FileModuleResolver::new_with_path_and_extension("./scripts", "x");
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct FileModuleResolver {
path: PathBuf,
extension: String,
}
impl FileModuleResolver {
/// Create a new `FileModuleResolver` with a specific base path.
///
/// # Examples
///
/// ```
/// use rhai::Engine;
/// use rhai::module_resolvers::FileModuleResolver;
///
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
/// // with file extension '.rhai' (the default).
/// let resolver = FileModuleResolver::new_with_path("./scripts");
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
pub fn new_with_path<P: Into<PathBuf>>(path: P) -> Self {
Self::new_with_path_and_extension(path, "rhai")
}
/// Create a new `FileModuleResolver` with a specific base path and file extension.
///
/// The default extension is `.rhai`.
///
/// # Examples
///
/// ```
/// use rhai::Engine;
/// use rhai::module_resolvers::FileModuleResolver;
///
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
/// // with file extension '.x'.
/// let resolver = FileModuleResolver::new_with_path_and_extension("./scripts", "x");
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
pub fn new_with_path_and_extension<P: Into<PathBuf>, E: Into<String>>(
path: P,
extension: E,
) -> Self {
Self {
path: path.into(),
extension: extension.into(),
}
}
/// Create a new `FileModuleResolver` with the current directory as base path.
///
/// # Examples
///
/// ```
/// use rhai::Engine;
/// use rhai::module_resolvers::FileModuleResolver;
///
/// // Create a new 'FileModuleResolver' loading scripts from the current directory
/// // with file extension '.rhai' (the default).
/// let resolver = FileModuleResolver::new();
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
pub fn new() -> Self {
Default::default()
}
}
impl ModuleResolver for FileModuleResolver {
fn resolve(
&self,
engine: &Engine,
path: &str,
pos: Position,
) -> Result<Module, Box<EvalAltResult>> {
// Construct the script file path
let mut file_path = self.path.clone();
file_path.push(path);
file_path.set_extension(&self.extension); // Force extension
// Compile it
let ast = engine
.compile_file(file_path)
.map_err(|err| EvalAltResult::set_position(err, pos))?;
Module::eval_ast_as_new(&ast, engine)
.map_err(|err| EvalAltResult::set_position(err, pos))
}
}
}
/// Static module resolver.
@ -485,11 +770,41 @@ mod stat {
use super::*;
/// A module resolution service that serves modules added into it.
///
/// # Examples
///
/// ```
/// use rhai::{Engine, Module};
/// use rhai::module_resolvers::StaticModuleResolver;
///
/// let mut resolver = StaticModuleResolver::new();
///
/// let module = Module::new();
/// resolver.insert("hello".to_string(), module);
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
#[derive(Debug, Clone, Default)]
pub struct StaticModuleResolver(HashMap<String, Module>);
impl StaticModuleResolver {
/// Create a new `StaticModuleResolver`.
///
/// # Examples
///
/// ```
/// use rhai::{Engine, Module};
/// use rhai::module_resolvers::StaticModuleResolver;
///
/// let mut resolver = StaticModuleResolver::new();
///
/// let module = Module::new();
/// resolver.insert("hello".to_string(), module);
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
pub fn new() -> Self {
Default::default()
}

View File

@ -711,11 +711,16 @@ pub fn optimize_into_ast(
#[cfg(feature = "no_optimize")]
const level: OptimizationLevel = OptimizationLevel::None;
#[cfg(not(feature = "no_function"))]
let fn_lib: Vec<_> = functions
.iter()
.map(|fn_def| (fn_def.name.as_str(), fn_def.params.len()))
.collect();
#[cfg(feature = "no_function")]
const fn_lib: &[(&str, usize)] = &[];
#[cfg(not(feature = "no_function"))]
let lib = FunctionsLib::from_vec(
functions
.iter()
@ -745,6 +750,9 @@ pub fn optimize_into_ast(
.collect(),
);
#[cfg(feature = "no_function")]
let lib: FunctionsLib = Default::default();
AST::new(
match level {
OptimizationLevel::None => statements,

View File

@ -1,3 +1,5 @@
#![cfg(not(feature = "no_index"))]
use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut};
use crate::any::{Dynamic, Variant};

View File

@ -1,3 +1,5 @@
#![cfg(not(feature = "no_object"))]
use super::{reg_binary, reg_binary_mut, reg_unary_mut};
use crate::any::Dynamic;

View File

@ -1,10 +1,16 @@
use super::{reg_binary, reg_binary_mut, reg_none, reg_unary, reg_unary_mut};
use crate::def_package;
use crate::engine::{Array, Map, FUNC_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT};
use crate::engine::{FUNC_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT};
use crate::fn_register::map_dynamic as map;
use crate::parser::INT;
#[cfg(not(feature = "no_index"))]
use crate::engine::Array;
#[cfg(not(feature = "no_object"))]
use crate::engine::Map;
use crate::stdlib::{
fmt::{Debug, Display},
format,
@ -18,6 +24,7 @@ fn to_debug<T: Debug>(x: &mut T) -> String {
fn to_string<T: Display>(x: &mut T) -> String {
format!("{}", x)
}
#[cfg(not(feature = "no_object"))]
fn format_map(x: &mut Map) -> String {
format!("#{:?}", x)
}

View File

@ -1,10 +1,12 @@
use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut};
use crate::def_package;
use crate::engine::Array;
use crate::fn_register::map_dynamic as map;
use crate::parser::INT;
#[cfg(not(feature = "no_index"))]
use crate::engine::Array;
use crate::stdlib::{
fmt::Display,
format,

View File

@ -39,6 +39,7 @@ pub type INT = i32;
/// The system floating-point type.
///
/// Not available under the `no_float` feature.
#[cfg(not(feature = "no_float"))]
pub type FLOAT = f64;
type PERR = ParseErrorType;
@ -143,13 +144,11 @@ impl AST {
(true, true) => vec![],
};
#[cfg(feature = "sync")]
return Self(ast, Arc::new(functions.merge(other.1.as_ref())));
#[cfg(not(feature = "sync"))]
return Self(ast, Rc::new(functions.merge(other.1.as_ref())));
Self::new(ast, functions.merge(other.1.as_ref()))
}
/// Clear all function definitions in the `AST`.
#[cfg(not(feature = "no_function"))]
pub fn clear_functions(&mut self) {
#[cfg(feature = "sync")]
{
@ -162,6 +161,7 @@ impl AST {
}
/// Clear all statements in the `AST`, leaving only function definitions.
#[cfg(not(feature = "no_function"))]
pub fn retain_functions(&mut self) {
self.0 = vec![];
}
@ -351,6 +351,7 @@ pub enum Expr {
/// Integer constant.
IntegerConstant(INT, Position),
/// Floating-point constant.
#[cfg(not(feature = "no_float"))]
FloatConstant(FLOAT, Position),
/// Character constant.
CharConstant(char, Position),
@ -413,6 +414,7 @@ impl Expr {
Self::False(_) => false.into(),
Self::Unit(_) => ().into(),
#[cfg(not(feature = "no_index"))]
Self::Array(items, _) if items.iter().all(Self::is_constant) => {
Dynamic(Union::Array(Box::new(
items
@ -422,6 +424,7 @@ impl Expr {
)))
}
#[cfg(not(feature = "no_object"))]
Self::Map(items, _) if items.iter().all(|(_, v, _)| v.is_constant()) => {
Dynamic(Union::Map(Box::new(
items
@ -442,8 +445,10 @@ impl Expr {
/// Panics when the expression is not constant.
pub fn get_constant_str(&self) -> String {
match self {
Self::IntegerConstant(i, _) => i.to_string(),
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(f, _) => f.to_string(),
Self::IntegerConstant(i, _) => i.to_string(),
Self::CharConstant(c, _) => c.to_string(),
Self::StringConstant(_, _) => "string".to_string(),
Self::True(_) => "true".to_string(),
@ -459,8 +464,10 @@ impl Expr {
/// Get the `Position` of the expression.
pub fn position(&self) -> Position {
match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, pos) => *pos,
Self::IntegerConstant(_, pos)
| Self::FloatConstant(_, pos)
| Self::CharConstant(_, pos)
| Self::StringConstant(_, pos)
| Self::Array(_, pos)
@ -485,8 +492,10 @@ impl Expr {
/// Get the `Position` of the expression.
pub(crate) fn set_position(mut self, new_pos: Position) -> Self {
match &mut self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, pos) => *pos = new_pos,
Self::IntegerConstant(_, pos)
| Self::FloatConstant(_, pos)
| Self::CharConstant(_, pos)
| Self::StringConstant(_, pos)
| Self::Array(_, pos)
@ -531,8 +540,10 @@ impl Expr {
/// Is the expression a constant?
pub fn is_constant(&self) -> bool {
match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, _) => true,
Self::IntegerConstant(_, _)
| Self::FloatConstant(_, _)
| Self::CharConstant(_, _)
| Self::StringConstant(_, _)
| Self::True(_)
@ -559,8 +570,10 @@ impl Expr {
/// Is a particular token allowed as a postfix operator to this expression?
pub fn is_valid_postfix(&self, token: &Token) -> bool {
match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, _) => false,
Self::IntegerConstant(_, _)
| Self::FloatConstant(_, _)
| Self::CharConstant(_, _)
| Self::In(_, _, _)
| Self::And(_, _, _)
@ -764,8 +777,15 @@ fn parse_index_chain<'a>(
.into_err(*pos))
}
Expr::FloatConstant(_, pos)
| Expr::CharConstant(_, pos)
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(_, pos) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(pos))
}
Expr::CharConstant(_, pos)
| Expr::Assignment(_, _, pos)
| Expr::And(_, _, pos)
| Expr::Or(_, _, pos)
@ -792,8 +812,16 @@ fn parse_index_chain<'a>(
)
.into_err(*pos))
}
Expr::FloatConstant(_, pos)
| Expr::CharConstant(_, pos)
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(_, pos) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(pos))
}
Expr::CharConstant(_, pos)
| Expr::Assignment(_, _, pos)
| Expr::And(_, _, pos)
| Expr::Or(_, _, pos)
@ -811,6 +839,7 @@ fn parse_index_chain<'a>(
},
// lhs[float]
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(_, pos) => {
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a float".into(),
@ -1095,6 +1124,7 @@ fn parse_primary<'a>(
}
}
// Indexing
#[cfg(not(feature = "no_index"))]
(expr, Token::LeftBracket) => {
parse_index_chain(input, stack, expr, token_pos, allow_stmt_expr)?
}
@ -1280,7 +1310,6 @@ fn make_dot_expr(
fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, Box<ParseError>> {
match (&lhs, &rhs) {
(_, Expr::IntegerConstant(_, pos))
| (_, Expr::FloatConstant(_, pos))
| (_, Expr::And(_, _, pos))
| (_, Expr::Or(_, _, pos))
| (_, Expr::In(_, _, pos))
@ -1294,11 +1323,20 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, Box<Pars
.into_err(*pos))
}
#[cfg(not(feature = "no_float"))]
(_, Expr::FloatConstant(_, pos)) => {
return Err(PERR::MalformedInExpr(
"'in' expression expects a string, array or object map".into(),
)
.into_err(*pos))
}
// "xxx" in "xxxx", 'x' in "xxxx" - OK!
(Expr::StringConstant(_, _), Expr::StringConstant(_, _))
| (Expr::CharConstant(_, _), Expr::StringConstant(_, _)) => (),
// 123.456 in "xxxx"
#[cfg(not(feature = "no_float"))]
(Expr::FloatConstant(_, pos), Expr::StringConstant(_, _)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not a float".into(),
@ -1352,6 +1390,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, Box<Pars
| (Expr::CharConstant(_, _), Expr::Map(_, _)) => (),
// 123.456 in #{...}
#[cfg(not(feature = "no_float"))]
(Expr::FloatConstant(_, pos), Expr::Map(_, _)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not a float".into(),
@ -1975,6 +2014,7 @@ fn parse_stmt<'a>(
Token::Let => parse_let(input, stack, ScopeEntryType::Normal, allow_stmt_expr),
Token::Const => parse_let(input, stack, ScopeEntryType::Constant, allow_stmt_expr),
#[cfg(not(feature = "no_module"))]
Token::Import => parse_import(input, stack, allow_stmt_expr),
_ => parse_expr_stmt(input, stack, allow_stmt_expr),
@ -2105,13 +2145,15 @@ fn parse_global_level<'a>(
while !input.peek().unwrap().0.is_eof() {
// Collect all the function definitions
#[cfg(not(feature = "no_function"))]
{
if let (Token::Fn, _) = input.peek().unwrap() {
let mut stack = Stack::new();
let f = parse_fn(input, &mut stack, true)?;
functions.insert(calc_fn_def(&f.name, f.params.len()), f);
continue;
}
}
// Actual statement
let stmt = parse_stmt(input, &mut stack, false, true)?;

View File

@ -1,10 +1,12 @@
//! Module that defines the `Scope` type representing a function call-stack scope.
use crate::any::{Dynamic, Union, Variant};
use crate::module::Module;
use crate::parser::{map_dynamic_to_expr, Expr};
use crate::token::Position;
#[cfg(not(feature = "no_module"))]
use crate::module::Module;
use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec, vec::Vec};
/// Type of an entry in the Scope.
@ -172,6 +174,7 @@ impl<'a> Scope<'a> {
/// Add (push) a new module to the Scope.
///
/// Modules are used for accessing member variables, functions and plugins under a namespace.
#[cfg(not(feature = "no_module"))]
pub fn push_module<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Module) {
self.push_dynamic_value(
name,
@ -340,6 +343,7 @@ impl<'a> Scope<'a> {
}
/// Find a module in the Scope, starting from the last entry.
#[cfg(not(feature = "no_module"))]
pub fn find_module(&mut self, name: &str) -> Option<&mut Module> {
let index = self.get_module_index(name)?;
self.get_mut(index).0.downcast_mut::<Module>()