Complete StaticVec implementation.
This commit is contained in:
parent
a2c50879fe
commit
8b5550eeb6
@ -2288,8 +2288,9 @@ engine.set_max_operations(0); // allow unlimited operations
|
|||||||
```
|
```
|
||||||
|
|
||||||
The concept of one single _operation_ in Rhai is volatile - it roughly equals one expression node,
|
The concept of one single _operation_ in Rhai is volatile - it roughly equals one expression node,
|
||||||
one statement, one iteration of a loop, or one function call etc. with sub-expressions and statement
|
loading a variable/constant, one operator call, one complete statement, one iteration of a loop,
|
||||||
blocks executed inside these contexts accumulated on top.
|
or one function call etc. with sub-expressions and statement blocks executed inside these contexts accumulated on top.
|
||||||
|
A good rule-of-thumb is that one simple non-trivial expression consumes on average 5-10 operations.
|
||||||
|
|
||||||
One _operation_ can take an unspecified amount of time and CPU cycles, depending on the particular operation
|
One _operation_ can take an unspecified amount of time and CPU cycles, depending on the particular operation
|
||||||
involved. For example, loading a constant consumes very few CPU cycles, while calling an external Rust function,
|
involved. For example, loading a constant consumes very few CPU cycles, while calling an external Rust function,
|
||||||
|
@ -652,7 +652,10 @@ impl Engine {
|
|||||||
let scripts = [script];
|
let scripts = [script];
|
||||||
let stream = lex(&scripts);
|
let stream = lex(&scripts);
|
||||||
|
|
||||||
parse_global_expr(&mut stream.peekable(), self, scope, self.optimization_level)
|
{
|
||||||
|
let mut peekable = stream.peekable();
|
||||||
|
parse_global_expr(&mut peekable, self, scope, self.optimization_level)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a script file.
|
/// Evaluate a script file.
|
||||||
@ -748,11 +751,10 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
script: &str,
|
script: &str,
|
||||||
) -> Result<T, Box<EvalAltResult>> {
|
) -> Result<T, Box<EvalAltResult>> {
|
||||||
// Since the AST will be thrown away afterwards, don't bother to optimize it
|
|
||||||
let ast = self.compile_with_scope_and_optimization_level(
|
let ast = self.compile_with_scope_and_optimization_level(
|
||||||
scope,
|
scope,
|
||||||
&[script],
|
&[script],
|
||||||
OptimizationLevel::None,
|
self.optimization_level,
|
||||||
)?;
|
)?;
|
||||||
self.eval_ast_with_scope(scope, &ast)
|
self.eval_ast_with_scope(scope, &ast)
|
||||||
}
|
}
|
||||||
|
@ -119,11 +119,11 @@ impl Target<'_> {
|
|||||||
.map_err(|_| EvalAltResult::ErrorCharMismatch(pos))?;
|
.map_err(|_| EvalAltResult::ErrorCharMismatch(pos))?;
|
||||||
|
|
||||||
let mut chars: StaticVec<char> = s.chars().collect();
|
let mut chars: StaticVec<char> = s.chars().collect();
|
||||||
let ch = *chars.get_ref(*index);
|
let ch = chars[*index];
|
||||||
|
|
||||||
// See if changed - if so, update the String
|
// See if changed - if so, update the String
|
||||||
if ch != new_ch {
|
if ch != new_ch {
|
||||||
*chars.get_mut(*index) = new_ch;
|
chars[*index] = new_ch;
|
||||||
s.clear();
|
s.clear();
|
||||||
chars.iter().for_each(|&ch| s.push(ch));
|
chars.iter().for_each(|&ch| s.push(ch));
|
||||||
}
|
}
|
||||||
@ -190,7 +190,7 @@ impl<'a> State<'a> {
|
|||||||
}
|
}
|
||||||
/// Get a script-defined function definition from the `State`.
|
/// Get a script-defined function definition from the `State`.
|
||||||
pub fn get_function(&self, hash: u64) -> Option<&FnDef> {
|
pub fn get_function(&self, hash: u64) -> Option<&FnDef> {
|
||||||
self.fn_lib.get(&hash).map(|f| f.as_ref())
|
self.fn_lib.get(&hash).map(|fn_def| fn_def.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,7 +463,7 @@ fn search_scope<'a>(
|
|||||||
.downcast_mut::<Module>()
|
.downcast_mut::<Module>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
} else {
|
} else {
|
||||||
let (id, root_pos) = modules.get_ref(0);
|
let (id, root_pos) = modules.get(0);
|
||||||
|
|
||||||
scope.find_module(id).ok_or_else(|| {
|
scope.find_module(id).ok_or_else(|| {
|
||||||
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
|
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
|
||||||
@ -736,8 +736,6 @@ impl Engine {
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(Dynamic, State<'s>), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, State<'s>), Box<EvalAltResult>> {
|
||||||
self.inc_operations(&mut state, pos)?;
|
|
||||||
|
|
||||||
let orig_scope_level = state.scope_level;
|
let orig_scope_level = state.scope_level;
|
||||||
state.scope_level += 1;
|
state.scope_level += 1;
|
||||||
|
|
||||||
@ -1120,6 +1118,7 @@ impl Engine {
|
|||||||
let index = if state.always_search { None } else { *index };
|
let index = if state.always_search { None } else { *index };
|
||||||
let mod_and_hash = modules.as_ref().map(|m| (m, *hash_var));
|
let mod_and_hash = modules.as_ref().map(|m| (m, *hash_var));
|
||||||
let (target, typ) = search_scope(scope, &name, mod_and_hash, index, *pos)?;
|
let (target, typ) = search_scope(scope, &name, mod_and_hash, index, *pos)?;
|
||||||
|
self.inc_operations(state, *pos)?;
|
||||||
|
|
||||||
// Constants cannot be modified
|
// Constants cannot be modified
|
||||||
match typ {
|
match typ {
|
||||||
@ -1171,6 +1170,8 @@ impl Engine {
|
|||||||
size: usize,
|
size: usize,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
|
self.inc_operations(state, expr.position())?;
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
Expr::FnCall(x) if x.1.is_none() => {
|
Expr::FnCall(x) if x.1.is_none() => {
|
||||||
let arg_values =
|
let arg_values =
|
||||||
@ -1328,13 +1329,13 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Dynamic(Union::Map(rhs_value)) => match lhs_value {
|
Dynamic(Union::Map(rhs_value)) => match lhs_value {
|
||||||
// Only allows String or char
|
// Only allows String or char
|
||||||
Dynamic(Union::Str(s)) => Ok(rhs_value.contains_key(s.as_ref()).into()),
|
Dynamic(Union::Str(s)) => Ok(rhs_value.contains_key(s.as_str()).into()),
|
||||||
Dynamic(Union::Char(c)) => Ok(rhs_value.contains_key(&c.to_string()).into()),
|
Dynamic(Union::Char(c)) => Ok(rhs_value.contains_key(&c.to_string()).into()),
|
||||||
_ => Err(Box::new(EvalAltResult::ErrorInExpr(lhs.position()))),
|
_ => Err(Box::new(EvalAltResult::ErrorInExpr(lhs.position()))),
|
||||||
},
|
},
|
||||||
Dynamic(Union::Str(rhs_value)) => match lhs_value {
|
Dynamic(Union::Str(rhs_value)) => match lhs_value {
|
||||||
// Only allows String or char
|
// Only allows String or char
|
||||||
Dynamic(Union::Str(s)) => Ok(rhs_value.contains(s.as_ref()).into()),
|
Dynamic(Union::Str(s)) => Ok(rhs_value.contains(s.as_str()).into()),
|
||||||
Dynamic(Union::Char(c)) => Ok(rhs_value.contains(c).into()),
|
Dynamic(Union::Char(c)) => Ok(rhs_value.contains(c).into()),
|
||||||
_ => Err(Box::new(EvalAltResult::ErrorInExpr(lhs.position()))),
|
_ => Err(Box::new(EvalAltResult::ErrorInExpr(lhs.position()))),
|
||||||
},
|
},
|
||||||
@ -1382,6 +1383,8 @@ impl Engine {
|
|||||||
let index = if state.always_search { None } else { *index };
|
let index = if state.always_search { None } else { *index };
|
||||||
let mod_and_hash = modules.as_ref().map(|m| (m, *hash_var));
|
let mod_and_hash = modules.as_ref().map(|m| (m, *hash_var));
|
||||||
let (lhs_ptr, typ) = search_scope(scope, name, mod_and_hash, index, *pos)?;
|
let (lhs_ptr, typ) = search_scope(scope, name, mod_and_hash, index, *pos)?;
|
||||||
|
self.inc_operations(state, *pos)?;
|
||||||
|
|
||||||
match typ {
|
match typ {
|
||||||
ScopeEntryType::Constant => Err(Box::new(
|
ScopeEntryType::Constant => Err(Box::new(
|
||||||
EvalAltResult::ErrorAssignmentToConstant(name.clone(), *pos),
|
EvalAltResult::ErrorAssignmentToConstant(name.clone(), *pos),
|
||||||
@ -1465,13 +1468,13 @@ impl Engine {
|
|||||||
|
|
||||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||||
|
|
||||||
if name == KEYWORD_EVAL && args.len() == 1 && args.get_ref(0).is::<String>() {
|
if name == KEYWORD_EVAL && args.len() == 1 && args.get(0).is::<String>() {
|
||||||
let hash_fn = calc_fn_hash(empty(), name, once(TypeId::of::<String>()));
|
let hash_fn = calc_fn_hash(empty(), name, once(TypeId::of::<String>()));
|
||||||
|
|
||||||
if !self.has_override(state, (hash_fn, *hash_fn_def)) {
|
if !self.has_override(state, (hash_fn, *hash_fn_def)) {
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
let pos = args_expr.get_ref(0).position();
|
let pos = args_expr.get(0).position();
|
||||||
|
|
||||||
// Evaluate the text string as a script
|
// Evaluate the text string as a script
|
||||||
let result = self.eval_script_expr(scope, state, args.pop(), pos);
|
let result = self.eval_script_expr(scope, state, args.pop(), pos);
|
||||||
@ -1505,7 +1508,7 @@ impl Engine {
|
|||||||
|
|
||||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||||
|
|
||||||
let (id, root_pos) = modules.get_ref(0); // First module
|
let (id, root_pos) = modules.get(0); // First module
|
||||||
|
|
||||||
let module = if let Some(index) = modules.index() {
|
let module = if let Some(index) = modules.index() {
|
||||||
scope
|
scope
|
||||||
@ -1663,9 +1666,7 @@ impl Engine {
|
|||||||
|
|
||||||
match self.eval_expr(scope, state, expr, level)?.as_bool() {
|
match self.eval_expr(scope, state, expr, level)?.as_bool() {
|
||||||
Ok(true) => match self.eval_stmt(scope, state, body, level) {
|
Ok(true) => match self.eval_stmt(scope, state, body, level) {
|
||||||
Ok(_) => {
|
Ok(_) => (),
|
||||||
self.inc_operations(state, body.position())?;
|
|
||||||
}
|
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
||||||
EvalAltResult::ErrorLoopBreak(true, _) => return Ok(Default::default()),
|
EvalAltResult::ErrorLoopBreak(true, _) => return Ok(Default::default()),
|
||||||
@ -1682,9 +1683,7 @@ impl Engine {
|
|||||||
// Loop statement
|
// Loop statement
|
||||||
Stmt::Loop(body) => loop {
|
Stmt::Loop(body) => loop {
|
||||||
match self.eval_stmt(scope, state, body, level) {
|
match self.eval_stmt(scope, state, body, level) {
|
||||||
Ok(_) => {
|
Ok(_) => (),
|
||||||
self.inc_operations(state, body.position())?;
|
|
||||||
}
|
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
||||||
EvalAltResult::ErrorLoopBreak(true, _) => return Ok(Default::default()),
|
EvalAltResult::ErrorLoopBreak(true, _) => return Ok(Default::default()),
|
||||||
@ -1773,7 +1772,6 @@ impl Engine {
|
|||||||
let val = self.eval_expr(scope, state, expr.as_ref().unwrap(), level)?;
|
let val = self.eval_expr(scope, state, expr.as_ref().unwrap(), level)?;
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
||||||
scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false);
|
scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false);
|
||||||
self.inc_operations(state, *pos)?;
|
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1781,7 +1779,6 @@ impl Engine {
|
|||||||
let ((var_name, pos), _) = x.as_ref();
|
let ((var_name, pos), _) = x.as_ref();
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
||||||
scope.push(var_name, ());
|
scope.push(var_name, ());
|
||||||
self.inc_operations(state, *pos)?;
|
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1791,7 +1788,6 @@ impl Engine {
|
|||||||
let val = self.eval_expr(scope, state, &expr, level)?;
|
let val = self.eval_expr(scope, state, &expr, level)?;
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
||||||
scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true);
|
scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true);
|
||||||
self.inc_operations(state, *pos)?;
|
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1818,7 +1814,7 @@ impl Engine {
|
|||||||
.eval_expr(scope, state, &expr, level)?
|
.eval_expr(scope, state, &expr, level)?
|
||||||
.try_cast::<String>()
|
.try_cast::<String>()
|
||||||
{
|
{
|
||||||
if let Some(resolver) = self.module_resolver.as_ref() {
|
if let Some(resolver) = &self.module_resolver {
|
||||||
// Use an empty scope to create a module
|
// Use an empty scope to create a module
|
||||||
let module =
|
let module =
|
||||||
resolver.resolve(self, Scope::new(), &path, expr.position())?;
|
resolver.resolve(self, Scope::new(), &path, expr.position())?;
|
||||||
@ -1827,7 +1823,6 @@ impl Engine {
|
|||||||
scope.push_module(mod_name, module);
|
scope.push_module(mod_name, module);
|
||||||
|
|
||||||
state.modules += 1;
|
state.modules += 1;
|
||||||
self.inc_operations(state, *pos)?;
|
|
||||||
|
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
} else {
|
} else {
|
||||||
@ -1884,7 +1879,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Report progress - only in steps
|
// Report progress - only in steps
|
||||||
if let Some(progress) = self.progress.as_ref() {
|
if let Some(progress) = &self.progress {
|
||||||
if !progress(state.operations) {
|
if !progress(state.operations) {
|
||||||
// Terminate script if progress returns false
|
// Terminate script if progress returns false
|
||||||
return Err(Box::new(EvalAltResult::ErrorTerminated(pos)));
|
return Err(Box::new(EvalAltResult::ErrorTerminated(pos)));
|
||||||
|
@ -51,7 +51,7 @@ pub struct Module {
|
|||||||
all_variables: HashMap<u64, Dynamic>,
|
all_variables: HashMap<u64, Dynamic>,
|
||||||
|
|
||||||
/// External Rust functions.
|
/// External Rust functions.
|
||||||
functions: HashMap<u64, (String, FnAccess, Vec<TypeId>, SharedNativeFunction)>,
|
functions: HashMap<u64, (String, FnAccess, StaticVec<TypeId>, SharedNativeFunction)>,
|
||||||
|
|
||||||
/// Flattened collection of all external Rust functions, including those in sub-modules.
|
/// Flattened collection of all external Rust functions, including those in sub-modules.
|
||||||
all_functions: HashMap<u64, SharedNativeFunction>,
|
all_functions: HashMap<u64, SharedNativeFunction>,
|
||||||
@ -292,8 +292,9 @@ impl Module {
|
|||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
let func = Arc::new(f);
|
let func = Arc::new(f);
|
||||||
|
|
||||||
self.functions
|
let params = params.into_iter().cloned().collect();
|
||||||
.insert(hash_fn, (name, access, params.to_vec(), func));
|
|
||||||
|
self.functions.insert(hash_fn, (name, access, params, func));
|
||||||
|
|
||||||
hash_fn
|
hash_fn
|
||||||
}
|
}
|
||||||
@ -616,13 +617,13 @@ impl Module {
|
|||||||
pub(crate) fn index_all_sub_modules(&mut self) {
|
pub(crate) fn index_all_sub_modules(&mut self) {
|
||||||
// Collect a particular module.
|
// Collect a particular module.
|
||||||
fn index_module<'a>(
|
fn index_module<'a>(
|
||||||
module: &'a mut Module,
|
module: &'a Module,
|
||||||
qualifiers: &mut Vec<&'a str>,
|
qualifiers: &mut Vec<&'a str>,
|
||||||
variables: &mut Vec<(u64, Dynamic)>,
|
variables: &mut Vec<(u64, Dynamic)>,
|
||||||
functions: &mut Vec<(u64, SharedNativeFunction)>,
|
functions: &mut Vec<(u64, SharedNativeFunction)>,
|
||||||
fn_lib: &mut Vec<(u64, SharedFnDef)>,
|
fn_lib: &mut Vec<(u64, SharedFnDef)>,
|
||||||
) {
|
) {
|
||||||
for (name, m) in module.modules.iter_mut() {
|
for (name, m) in &module.modules {
|
||||||
// Index all the sub-modules first.
|
// Index all the sub-modules first.
|
||||||
qualifiers.push(name);
|
qualifiers.push(name);
|
||||||
index_module(m, qualifiers, variables, functions, fn_lib);
|
index_module(m, qualifiers, variables, functions, fn_lib);
|
||||||
@ -630,7 +631,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Index all variables
|
// Index all variables
|
||||||
for (var_name, value) in module.variables.iter() {
|
for (var_name, value) in &module.variables {
|
||||||
// Qualifiers + variable name
|
// Qualifiers + variable name
|
||||||
let hash_var = calc_fn_hash(qualifiers.iter().map(|&v| v), var_name, empty());
|
let hash_var = calc_fn_hash(qualifiers.iter().map(|&v| v), var_name, empty());
|
||||||
variables.push((hash_var, value.clone()));
|
variables.push((hash_var, value.clone()));
|
||||||
|
@ -236,24 +236,24 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
|||||||
// import expr as id;
|
// import expr as id;
|
||||||
Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1))),
|
Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1))),
|
||||||
// { block }
|
// { block }
|
||||||
Stmt::Block(mut x) => {
|
Stmt::Block(x) => {
|
||||||
let orig_len = x.0.len(); // Original number of statements in the block, for change detection
|
let orig_len = x.0.len(); // Original number of statements in the block, for change detection
|
||||||
let orig_constants_len = state.constants.len(); // Original number of constants in the state, for restore later
|
let orig_constants_len = state.constants.len(); // Original number of constants in the state, for restore later
|
||||||
let pos = x.1;
|
let pos = x.1;
|
||||||
|
|
||||||
// Optimize each statement in the block
|
// Optimize each statement in the block
|
||||||
let mut result: Vec<_> =
|
let mut result: Vec<_> =
|
||||||
x.0.iter_mut()
|
x.0.into_iter()
|
||||||
.map(|stmt| match stmt {
|
.map(|stmt| match stmt {
|
||||||
// Add constant into the state
|
// Add constant into the state
|
||||||
Stmt::Const(v) => {
|
Stmt::Const(v) => {
|
||||||
let ((name, pos), expr) = v.as_mut();
|
let ((name, pos), expr) = *v;
|
||||||
state.push_constant(name, mem::take(expr));
|
state.push_constant(&name, expr);
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Stmt::Noop(*pos) // No need to keep constants
|
Stmt::Noop(pos) // No need to keep constants
|
||||||
}
|
}
|
||||||
// Optimize the statement
|
// Optimize the statement
|
||||||
_ => optimize_stmt(mem::take(stmt), state, preserve_result),
|
_ => optimize_stmt(stmt, state, preserve_result),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -399,14 +399,14 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(x) => match (x.0, x.1) {
|
Expr::Dot(x) => match (x.0, x.1) {
|
||||||
// map.string
|
// map.string
|
||||||
(Expr::Map(mut m), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
(Expr::Map(m), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
let ((prop, _, _), _) = p.as_ref();
|
let ((prop, _, _), _) = p.as_ref();
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let pos = m.1;
|
let pos = m.1;
|
||||||
m.0.iter_mut().find(|((name, _), _)| name == prop)
|
m.0.into_iter().find(|((name, _), _)| name == prop)
|
||||||
.map(|(_, expr)| mem::take(expr).set_position(pos))
|
.map(|(_, expr)| expr.set_position(pos))
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
@ -423,16 +423,16 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
// Array literal where everything is pure - promote the indexed item.
|
// Array literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
a.0.get(i.0 as usize).set_position(a.1)
|
a.0.take(i.0 as usize).set_position(a.1)
|
||||||
}
|
}
|
||||||
// map[string]
|
// map[string]
|
||||||
(Expr::Map(mut m), Expr::StringConstant(s)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
(Expr::Map(m), Expr::StringConstant(s)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let pos = m.1;
|
let pos = m.1;
|
||||||
m.0.iter_mut().find(|((name, _), _)| name == &s.0)
|
m.0.into_iter().find(|((name, _), _)| name == &s.0)
|
||||||
.map(|(_, expr)| mem::take(expr).set_position(pos))
|
.map(|(_, expr)| expr.set_position(pos))
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
// string[int]
|
// string[int]
|
||||||
@ -446,13 +446,13 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
},
|
},
|
||||||
// [ items .. ]
|
// [ items .. ]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Array(mut a) => Expr::Array(Box::new((a.0
|
Expr::Array(a) => Expr::Array(Box::new((a.0
|
||||||
.iter_mut().map(|expr| optimize_expr(mem::take(expr), state))
|
.into_iter().map(|expr| optimize_expr(expr, state))
|
||||||
.collect(), a.1))),
|
.collect(), a.1))),
|
||||||
// [ items .. ]
|
// [ items .. ]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Map(mut m) => Expr::Map(Box::new((m.0
|
Expr::Map(m) => Expr::Map(Box::new((m.0
|
||||||
.iter_mut().map(|((key, pos), expr)| ((mem::take(key), *pos), optimize_expr(mem::take(expr), state)))
|
.into_iter().map(|((key, pos), expr)| ((key, pos), optimize_expr(expr, state)))
|
||||||
.collect(), m.1))),
|
.collect(), m.1))),
|
||||||
// lhs in rhs
|
// lhs in rhs
|
||||||
Expr::In(x) => match (x.0, x.1) {
|
Expr::In(x) => match (x.0, x.1) {
|
||||||
@ -532,7 +532,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
|
|
||||||
// Do not call some special keywords
|
// Do not call some special keywords
|
||||||
Expr::FnCall(mut x) if DONT_EVAL_KEYWORDS.contains(&(x.0).0.as_ref())=> {
|
Expr::FnCall(mut x) if DONT_EVAL_KEYWORDS.contains(&(x.0).0.as_ref())=> {
|
||||||
x.3 = x.3.iter_mut().map(|a| optimize_expr(mem::take(a), state)).collect();
|
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||||
Expr::FnCall(x)
|
Expr::FnCall(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,12 +547,12 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
if state.fn_lib.iter().find(|(id, len)| *id == name && *len == args.len()).is_some() {
|
if state.fn_lib.iter().find(|(id, len)| *id == name && *len == args.len()).is_some() {
|
||||||
// A script-defined function overrides the built-in function - do not make the call
|
// A script-defined function overrides the built-in function - do not make the call
|
||||||
x.3 = x.3.iter_mut().map(|a| optimize_expr(mem::take(a), state)).collect();
|
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||||
return Expr::FnCall(x);
|
return Expr::FnCall(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect();
|
let mut arg_values: StaticVec<_> = args.iter().map(Expr::get_constant_value).collect();
|
||||||
let mut call_args: Vec<_> = arg_values.iter_mut().collect();
|
let mut call_args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||||
|
|
||||||
// Save the typename of the first argument if it is `type_of()`
|
// Save the typename of the first argument if it is `type_of()`
|
||||||
// This is to avoid `call_args` being passed into the closure
|
// This is to avoid `call_args` being passed into the closure
|
||||||
@ -562,7 +562,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
""
|
""
|
||||||
};
|
};
|
||||||
|
|
||||||
call_fn(&state.engine.packages, &state.engine.global_module, name, &mut call_args, *pos).ok()
|
call_fn(&state.engine.packages, &state.engine.global_module, name, call_args.as_mut(), *pos).ok()
|
||||||
.and_then(|result|
|
.and_then(|result|
|
||||||
result.or_else(|| {
|
result.or_else(|| {
|
||||||
if !arg_for_type_of.is_empty() {
|
if !arg_for_type_of.is_empty() {
|
||||||
@ -579,14 +579,14 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
})
|
})
|
||||||
).unwrap_or_else(|| {
|
).unwrap_or_else(|| {
|
||||||
// Optimize function call arguments
|
// Optimize function call arguments
|
||||||
x.3 = x.3.iter_mut().map(|a| optimize_expr(mem::take(a), state)).collect();
|
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||||
Expr::FnCall(x)
|
Expr::FnCall(x)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// id(args ..) -> optimize function call arguments
|
// id(args ..) -> optimize function call arguments
|
||||||
Expr::FnCall(mut x) => {
|
Expr::FnCall(mut x) => {
|
||||||
x.3 = x.3.iter_mut().map(|a| optimize_expr(mem::take(a), state)).collect();
|
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||||
Expr::FnCall(x)
|
Expr::FnCall(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -702,11 +702,14 @@ pub fn optimize_into_ast(
|
|||||||
const level: OptimizationLevel = OptimizationLevel::None;
|
const level: OptimizationLevel = OptimizationLevel::None;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let fn_lib: Vec<_> = functions
|
let fn_lib_values: StaticVec<_> = functions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|fn_def| (fn_def.name.as_str(), fn_def.params.len()))
|
.map(|fn_def| (fn_def.name.as_str(), fn_def.params.len()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
let fn_lib = fn_lib_values.as_ref();
|
||||||
|
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
const fn_lib: &[(&str, usize)] = &[];
|
const fn_lib: &[(&str, usize)] = &[];
|
||||||
|
|
||||||
@ -716,7 +719,7 @@ pub fn optimize_into_ast(
|
|||||||
let pos = fn_def.body.position();
|
let pos = fn_def.body.position();
|
||||||
|
|
||||||
// Optimize the function body
|
// Optimize the function body
|
||||||
let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), &fn_lib, level);
|
let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), fn_lib, level);
|
||||||
|
|
||||||
// {} -> Noop
|
// {} -> Noop
|
||||||
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
||||||
@ -742,7 +745,7 @@ pub fn optimize_into_ast(
|
|||||||
match level {
|
match level {
|
||||||
OptimizationLevel::None => statements,
|
OptimizationLevel::None => statements,
|
||||||
OptimizationLevel::Simple | OptimizationLevel::Full => {
|
OptimizationLevel::Simple | OptimizationLevel::Full => {
|
||||||
optimize(statements, engine, &scope, &fn_lib, level)
|
optimize(statements, engine, &scope, fn_lib, level)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
lib,
|
lib,
|
||||||
|
@ -330,7 +330,7 @@ impl Stmt {
|
|||||||
Stmt::Loop(x) => x.position(),
|
Stmt::Loop(x) => x.position(),
|
||||||
Stmt::For(x) => x.2.position(),
|
Stmt::For(x) => x.2.position(),
|
||||||
Stmt::Import(x) => (x.1).1,
|
Stmt::Import(x) => (x.1).1,
|
||||||
Stmt::Export(x) => (x.get_ref(0).0).1,
|
Stmt::Export(x) => (x.get(0).0).1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -745,7 +745,7 @@ fn parse_call_expr<'a>(
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let hash_fn_def = {
|
let hash_fn_def = {
|
||||||
if let Some(modules) = modules.as_mut() {
|
if let Some(modules) = modules.as_mut() {
|
||||||
modules.set_index(stack.find_module(&modules.get_ref(0).0));
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
||||||
|
|
||||||
// Rust functions are indexed in two steps:
|
// Rust functions are indexed in two steps:
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
@ -786,7 +786,7 @@ fn parse_call_expr<'a>(
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let hash_fn_def = {
|
let hash_fn_def = {
|
||||||
if let Some(modules) = modules.as_mut() {
|
if let Some(modules) = modules.as_mut() {
|
||||||
modules.set_index(stack.find_module(&modules.get_ref(0).0));
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
||||||
|
|
||||||
// Rust functions are indexed in two steps:
|
// Rust functions are indexed in two steps:
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
@ -1251,7 +1251,7 @@ fn parse_primary<'a>(
|
|||||||
|
|
||||||
// Qualifiers + variable name
|
// Qualifiers + variable name
|
||||||
*hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, empty());
|
*hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, empty());
|
||||||
modules.set_index(stack.find_module(&modules.get_ref(0).0));
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -1471,7 +1471,7 @@ fn make_dot_expr(
|
|||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
unreachable!();
|
unreachable!();
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
return Err(PERR::PropertyExpected.into_err(x.1.unwrap().get_ref(0).1));
|
return Err(PERR::PropertyExpected.into_err(x.1.unwrap().get(0).1));
|
||||||
}
|
}
|
||||||
// lhs.dot_lhs.dot_rhs
|
// lhs.dot_lhs.dot_rhs
|
||||||
(lhs, Expr::Dot(x)) => {
|
(lhs, Expr::Dot(x)) => {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use crate::error::LexError;
|
use crate::error::LexError;
|
||||||
use crate::parser::INT;
|
use crate::parser::INT;
|
||||||
|
use crate::utils::StaticVec;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::parser::FLOAT;
|
use crate::parser::FLOAT;
|
||||||
@ -425,7 +426,7 @@ pub struct TokenIterator<'a> {
|
|||||||
/// Current position.
|
/// Current position.
|
||||||
pos: Position,
|
pos: Position,
|
||||||
/// The input character streams.
|
/// The input character streams.
|
||||||
streams: Vec<Peekable<Chars<'a>>>,
|
streams: StaticVec<Peekable<Chars<'a>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TokenIterator<'a> {
|
impl<'a> TokenIterator<'a> {
|
||||||
|
@ -61,8 +61,3 @@ pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str, state: &State) -> Cow<'s
|
|||||||
name.to_string().into()
|
name.to_string().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide a type instance that is uninitialized.
|
|
||||||
pub fn unsafe_uninit<T>() -> T {
|
|
||||||
unsafe { mem::MaybeUninit::uninit().assume_init() }
|
|
||||||
}
|
|
||||||
|
360
src/utils.rs
360
src/utils.rs
@ -2,9 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! # Safety
|
//! # Safety
|
||||||
//!
|
//!
|
||||||
//! The `StaticVec` type has some `unsafe` blocks.
|
//! The `StaticVec` type has some `unsafe` blocks to handle conversions between `MaybeUninit` and regular types.
|
||||||
|
|
||||||
use crate::r#unsafe::unsafe_uninit;
|
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
@ -12,7 +10,8 @@ use crate::stdlib::{
|
|||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
iter::FromIterator,
|
iter::FromIterator,
|
||||||
mem,
|
mem,
|
||||||
ops::Drop,
|
mem::MaybeUninit,
|
||||||
|
ops::{Drop, Index, IndexMut},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -52,13 +51,41 @@ pub fn calc_fn_spec<'a>(
|
|||||||
s.finish()
|
s.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_STATIC_VEC: usize = 4;
|
/// A type to hold a number of values in static storage for no-allocation, quick access.
|
||||||
|
/// If too many items are stored, it converts into using a `Vec`.
|
||||||
/// A type to hold a number of values in static storage for speed, and any spill-overs in a `Vec`.
|
|
||||||
///
|
///
|
||||||
/// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate.
|
/// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate.
|
||||||
/// This simplified implementation here is to avoid pulling in another crate.
|
/// This simplified implementation here is to avoid pulling in another crate.
|
||||||
///
|
///
|
||||||
|
/// # Implementation
|
||||||
|
///
|
||||||
|
/// A `StaticVec` holds data in _either one_ of two storages: 1) a fixed-size array of `MAX_STATIC_VEC`
|
||||||
|
/// items, and 2) a dynamic `Vec`. At any time, either one of them (or both) must be empty, depending on the
|
||||||
|
/// total number of items.
|
||||||
|
///
|
||||||
|
/// There is a `len` field containing the total number of items held by the `StaticVec`.
|
||||||
|
///
|
||||||
|
/// The fixed-size array (`list`) is not initialized (i.e. initialized with `MaybeUninit::uninit()`).
|
||||||
|
///
|
||||||
|
/// When `len <= MAX_STATIC_VEC`, all elements are stored in the fixed-size array.
|
||||||
|
/// Array slots `>= len` are `MaybeUninit::uninit()` while slots `< len` are considered actual data.
|
||||||
|
/// In this scenario, the `Vec` (`more`) is empty.
|
||||||
|
///
|
||||||
|
/// As soon as we try to push a new item into the `StaticVec` that makes the total number exceed
|
||||||
|
/// `MAX_STATIC_VEC`, all the items in the fixed-sized array are taken out, replaced with
|
||||||
|
/// `MaybeUninit::uninit()` (via `mem::replace`) and pushed into the `Vec`.
|
||||||
|
/// Then the new item is added to the `Vec`.
|
||||||
|
///
|
||||||
|
/// Therefore, if `len > MAX_STATIC_VEC`, then the fixed-size array (`list`) is considered
|
||||||
|
/// empty and uninitialized while all data resides in the `Vec` (`more`).
|
||||||
|
///
|
||||||
|
/// When popping an item off of the `StaticVec`, the reverse is true. When `len = MAX_STATIC_VEC + 1`,
|
||||||
|
/// after popping the item, all the items residing in the `Vec` are moved back to the fixed-size array (`list`).
|
||||||
|
/// The `Vec` will then be empty.
|
||||||
|
///
|
||||||
|
/// Therefore, if `len <= MAX_STATIC_VEC`, data is in the fixed-size array (`list`).
|
||||||
|
/// Otherwise, data is in the `Vec` (`more`).
|
||||||
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This type uses some unsafe code (mainly for uninitialized/unused array slots) for efficiency.
|
/// This type uses some unsafe code (mainly for uninitialized/unused array slots) for efficiency.
|
||||||
@ -67,12 +94,16 @@ const MAX_STATIC_VEC: usize = 4;
|
|||||||
pub struct StaticVec<T> {
|
pub struct StaticVec<T> {
|
||||||
/// Total number of values held.
|
/// Total number of values held.
|
||||||
len: usize,
|
len: usize,
|
||||||
/// Static storage. 4 slots should be enough for most cases - i.e. four items of fast, no-allocation access.
|
/// Fixed-size storage for fast, no-allocation access.
|
||||||
list: [mem::MaybeUninit<T>; MAX_STATIC_VEC],
|
list: [MaybeUninit<T>; MAX_STATIC_VEC],
|
||||||
/// Dynamic storage. For spill-overs.
|
/// Dynamic storage. For spill-overs.
|
||||||
more: Vec<T>,
|
more: Vec<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Maximum slots of fixed-size storage for a `StaticVec`.
|
||||||
|
/// 4 slots should be enough for most cases.
|
||||||
|
const MAX_STATIC_VEC: usize = 4;
|
||||||
|
|
||||||
impl<T> Drop for StaticVec<T> {
|
impl<T> Drop for StaticVec<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.clear();
|
self.clear();
|
||||||
@ -83,7 +114,7 @@ impl<T> Default for StaticVec<T> {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
len: 0,
|
len: 0,
|
||||||
list: unsafe_uninit(),
|
list: unsafe { mem::MaybeUninit::uninit().assume_init() },
|
||||||
more: Vec::new(),
|
more: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,9 +122,18 @@ impl<T> Default for StaticVec<T> {
|
|||||||
|
|
||||||
impl<T: PartialEq> PartialEq for StaticVec<T> {
|
impl<T: PartialEq> PartialEq for StaticVec<T> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.len == other.len
|
if self.len != other.len || self.more != other.more {
|
||||||
//&& self.list[0..self.len] == other.list[0..self.len]
|
return false;
|
||||||
&& self.more == other.more
|
}
|
||||||
|
|
||||||
|
if self.len > MAX_STATIC_VEC {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
mem::transmute::<_, &[T; MAX_STATIC_VEC]>(&self.list)
|
||||||
|
== mem::transmute::<_, &[T; MAX_STATIC_VEC]>(&other.list)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,10 +142,10 @@ impl<T: Clone> Clone for StaticVec<T> {
|
|||||||
let mut value: Self = Default::default();
|
let mut value: Self = Default::default();
|
||||||
value.len = self.len;
|
value.len = self.len;
|
||||||
|
|
||||||
if self.len <= self.list.len() {
|
if self.is_fixed_storage() {
|
||||||
for x in 0..self.len {
|
for x in 0..self.len {
|
||||||
let item: &T = unsafe { mem::transmute(self.list.get(x).unwrap()) };
|
let item: &T = unsafe { mem::transmute(self.list.get(x).unwrap()) };
|
||||||
value.list[x] = mem::MaybeUninit::new(item.clone());
|
value.list[x] = MaybeUninit::new(item.clone());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
value.more = self.more.clone();
|
value.more = self.more.clone();
|
||||||
@ -130,72 +170,118 @@ impl<T> FromIterator<T> for StaticVec<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> StaticVec<T> {
|
impl<T> StaticVec<T> {
|
||||||
fn extract(value: mem::MaybeUninit<T>) -> T {
|
|
||||||
unsafe { value.assume_init() }
|
|
||||||
}
|
|
||||||
/// Create a new `StaticVec`.
|
/// Create a new `StaticVec`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
/// Empty the `StaticVec`.
|
/// Empty the `StaticVec`.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
if self.len <= self.list.len() {
|
if self.is_fixed_storage() {
|
||||||
for x in 0..self.len {
|
for x in 0..self.len {
|
||||||
Self::extract(mem::replace(
|
self.extract_from_list(x);
|
||||||
self.list.get_mut(x).unwrap(),
|
|
||||||
mem::MaybeUninit::uninit(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.more.clear();
|
self.more.clear();
|
||||||
}
|
}
|
||||||
self.len = 0;
|
self.len = 0;
|
||||||
}
|
}
|
||||||
|
/// Extract a `MaybeUninit` into a concrete initialized type.
|
||||||
|
fn extract(value: MaybeUninit<T>) -> T {
|
||||||
|
unsafe { value.assume_init() }
|
||||||
|
}
|
||||||
|
/// Extract an item from the fixed-size array, replacing it with `MaybeUninit::uninit()`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if fixed-size storage is not used, or if the `index` is out of bounds.
|
||||||
|
fn extract_from_list(&mut self, index: usize) -> T {
|
||||||
|
if !self.is_fixed_storage() {
|
||||||
|
panic!("not fixed storage in StaticVec");
|
||||||
|
}
|
||||||
|
if index >= self.len {
|
||||||
|
panic!("index OOB in StaticVec");
|
||||||
|
}
|
||||||
|
Self::extract(mem::replace(
|
||||||
|
self.list.get_mut(index).unwrap(),
|
||||||
|
MaybeUninit::uninit(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
/// Set an item into the fixed-size array.
|
||||||
|
/// If `drop` is `true`, the original value is extracted then automatically dropped.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if fixed-size storage is not used, or if the `index` is out of bounds.
|
||||||
|
fn set_into_list(&mut self, index: usize, value: T, drop: bool) {
|
||||||
|
if !self.is_fixed_storage() {
|
||||||
|
panic!("not fixed storage in StaticVec");
|
||||||
|
}
|
||||||
|
// Allow setting at most one slot to the right
|
||||||
|
if index > self.len {
|
||||||
|
panic!("index OOB in StaticVec");
|
||||||
|
}
|
||||||
|
let temp = mem::replace(self.list.get_mut(index).unwrap(), MaybeUninit::new(value));
|
||||||
|
if drop {
|
||||||
|
// Extract the original value - which will drop it automatically
|
||||||
|
Self::extract(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Move item in the fixed-size array into the `Vec`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if fixed-size storage is not used, or if the fixed-size storage is not full.
|
||||||
|
fn move_fixed_into_vec(&mut self, num: usize) {
|
||||||
|
if !self.is_fixed_storage() {
|
||||||
|
panic!("not fixed storage in StaticVec");
|
||||||
|
}
|
||||||
|
if self.len != num {
|
||||||
|
panic!("fixed storage is not full in StaticVec");
|
||||||
|
}
|
||||||
|
self.more.extend(
|
||||||
|
self.list
|
||||||
|
.iter_mut()
|
||||||
|
.take(num)
|
||||||
|
.map(|v| mem::replace(v, MaybeUninit::uninit()))
|
||||||
|
.map(Self::extract),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/// Is data stored in fixed-size storage?
|
||||||
|
fn is_fixed_storage(&self) -> bool {
|
||||||
|
self.len <= MAX_STATIC_VEC
|
||||||
|
}
|
||||||
/// Push a new value to the end of this `StaticVec`.
|
/// Push a new value to the end of this `StaticVec`.
|
||||||
pub fn push<X: Into<T>>(&mut self, value: X) {
|
pub fn push<X: Into<T>>(&mut self, value: X) {
|
||||||
if self.len == self.list.len() {
|
if self.len == MAX_STATIC_VEC {
|
||||||
// Move the fixed list to the Vec
|
self.move_fixed_into_vec(MAX_STATIC_VEC);
|
||||||
self.more.extend(
|
|
||||||
self.list
|
|
||||||
.iter_mut()
|
|
||||||
.map(|v| mem::replace(v, mem::MaybeUninit::uninit()))
|
|
||||||
.map(Self::extract),
|
|
||||||
);
|
|
||||||
self.more.push(value.into());
|
self.more.push(value.into());
|
||||||
} else if self.len < self.list.len() {
|
} else if self.is_fixed_storage() {
|
||||||
mem::replace(
|
self.set_into_list(self.len, value.into(), false);
|
||||||
self.list.get_mut(self.len).unwrap(),
|
|
||||||
mem::MaybeUninit::new(value.into()),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
self.more.push(value.into());
|
self.more.push(value.into());
|
||||||
}
|
}
|
||||||
self.len += 1;
|
self.len += 1;
|
||||||
}
|
}
|
||||||
/// Insert a new value to this `StaticVec` at a particular position.
|
/// Insert a new value to this `StaticVec` at a particular position.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `index` is out of bounds.
|
||||||
pub fn insert<X: Into<T>>(&mut self, index: usize, value: X) {
|
pub fn insert<X: Into<T>>(&mut self, index: usize, value: X) {
|
||||||
if index > self.len {
|
if index > self.len {
|
||||||
panic!("index OOB in StaticVec");
|
panic!("index OOB in StaticVec");
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.len == self.list.len() {
|
if self.len == MAX_STATIC_VEC {
|
||||||
// Move the fixed list to the Vec
|
self.move_fixed_into_vec(MAX_STATIC_VEC);
|
||||||
self.more.extend(
|
|
||||||
self.list
|
|
||||||
.iter_mut()
|
|
||||||
.map(|v| mem::replace(v, mem::MaybeUninit::uninit()))
|
|
||||||
.map(Self::extract),
|
|
||||||
);
|
|
||||||
self.more.insert(index, value.into());
|
self.more.insert(index, value.into());
|
||||||
} else if self.len < self.list.len() {
|
} else if self.is_fixed_storage() {
|
||||||
|
// Move all items one slot to the right
|
||||||
for x in (index..self.len).rev() {
|
for x in (index..self.len).rev() {
|
||||||
let temp = mem::replace(self.list.get_mut(x).unwrap(), mem::MaybeUninit::uninit());
|
let orig_value = self.extract_from_list(x);
|
||||||
mem::replace(self.list.get_mut(x + 1).unwrap(), temp);
|
self.set_into_list(x + 1, orig_value, false);
|
||||||
}
|
}
|
||||||
mem::replace(
|
self.set_into_list(index, value.into(), false);
|
||||||
self.list.get_mut(index).unwrap(),
|
|
||||||
mem::MaybeUninit::new(value.into()),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
self.more.insert(index, value.into());
|
self.more.insert(index, value.into());
|
||||||
}
|
}
|
||||||
@ -207,29 +293,62 @@ impl<T> StaticVec<T> {
|
|||||||
///
|
///
|
||||||
/// Panics if the `StaticVec` is empty.
|
/// Panics if the `StaticVec` is empty.
|
||||||
pub fn pop(&mut self) -> T {
|
pub fn pop(&mut self) -> T {
|
||||||
if self.len <= 0 {
|
if self.is_empty() {
|
||||||
panic!("nothing to pop!");
|
panic!("nothing to pop!");
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = if self.len <= self.list.len() {
|
let result = if self.is_fixed_storage() {
|
||||||
Self::extract(mem::replace(
|
self.extract_from_list(self.len - 1)
|
||||||
self.list.get_mut(self.len - 1).unwrap(),
|
|
||||||
mem::MaybeUninit::uninit(),
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
let r = self.more.pop().unwrap();
|
let value = self.more.pop().unwrap();
|
||||||
|
|
||||||
// Move back to the fixed list
|
// Move back to the fixed list
|
||||||
if self.more.len() == self.list.len() {
|
if self.more.len() == MAX_STATIC_VEC {
|
||||||
for index in (0..self.list.len()).rev() {
|
for index in (0..MAX_STATIC_VEC).rev() {
|
||||||
mem::replace(
|
let item = self.more.pop().unwrap();
|
||||||
self.list.get_mut(index).unwrap(),
|
self.set_into_list(index, item, false);
|
||||||
mem::MaybeUninit::new(self.more.pop().unwrap()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r
|
value
|
||||||
|
};
|
||||||
|
|
||||||
|
self.len -= 1;
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
/// Remove a value from this `StaticVec` at a particular position.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `index` is out of bounds.
|
||||||
|
pub fn remove(&mut self, index: usize) -> T {
|
||||||
|
if index >= self.len {
|
||||||
|
panic!("index OOB in StaticVec");
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = if self.is_fixed_storage() {
|
||||||
|
let value = self.extract_from_list(index);
|
||||||
|
|
||||||
|
// Move all items one slot to the left
|
||||||
|
for x in index..self.len - 1 {
|
||||||
|
let orig_value = self.extract_from_list(x + 1);
|
||||||
|
self.set_into_list(x, orig_value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
let value = self.more.remove(index);
|
||||||
|
|
||||||
|
// Move back to the fixed list
|
||||||
|
if self.more.len() == MAX_STATIC_VEC {
|
||||||
|
for index in (0..MAX_STATIC_VEC).rev() {
|
||||||
|
let item = self.more.pop().unwrap();
|
||||||
|
self.set_into_list(index, item, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value
|
||||||
};
|
};
|
||||||
|
|
||||||
self.len -= 1;
|
self.len -= 1;
|
||||||
@ -248,15 +367,15 @@ impl<T> StaticVec<T> {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the index is out of bounds.
|
/// Panics if `index` is out of bounds.
|
||||||
pub fn get_ref(&self, index: usize) -> &T {
|
pub fn get(&self, index: usize) -> &T {
|
||||||
if index >= self.len {
|
if index >= self.len {
|
||||||
panic!("index OOB in StaticVec");
|
panic!("index OOB in StaticVec");
|
||||||
}
|
}
|
||||||
|
|
||||||
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
|
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
|
||||||
|
|
||||||
if self.len <= list.len() {
|
if self.is_fixed_storage() {
|
||||||
list.get(index).unwrap()
|
list.get(index).unwrap()
|
||||||
} else {
|
} else {
|
||||||
self.more.get(index).unwrap()
|
self.more.get(index).unwrap()
|
||||||
@ -266,7 +385,7 @@ impl<T> StaticVec<T> {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the index is out of bounds.
|
/// Panics if `index` is out of bounds.
|
||||||
pub fn get_mut(&mut self, index: usize) -> &mut T {
|
pub fn get_mut(&mut self, index: usize) -> &mut T {
|
||||||
if index >= self.len {
|
if index >= self.len {
|
||||||
panic!("index OOB in StaticVec");
|
panic!("index OOB in StaticVec");
|
||||||
@ -274,7 +393,7 @@ impl<T> StaticVec<T> {
|
|||||||
|
|
||||||
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
|
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
|
||||||
|
|
||||||
if self.len <= list.len() {
|
if self.is_fixed_storage() {
|
||||||
list.get_mut(index).unwrap()
|
list.get_mut(index).unwrap()
|
||||||
} else {
|
} else {
|
||||||
self.more.get_mut(index).unwrap()
|
self.more.get_mut(index).unwrap()
|
||||||
@ -284,7 +403,7 @@ impl<T> StaticVec<T> {
|
|||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||||
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
|
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
|
||||||
|
|
||||||
if self.len <= list.len() {
|
if self.is_fixed_storage() {
|
||||||
list[..self.len].iter()
|
list[..self.len].iter()
|
||||||
} else {
|
} else {
|
||||||
self.more.iter()
|
self.more.iter()
|
||||||
@ -294,7 +413,7 @@ impl<T> StaticVec<T> {
|
|||||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
||||||
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
|
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
|
||||||
|
|
||||||
if self.len <= list.len() {
|
if self.is_fixed_storage() {
|
||||||
list[..self.len].iter_mut()
|
list[..self.len].iter_mut()
|
||||||
} else {
|
} else {
|
||||||
self.more.iter_mut()
|
self.more.iter_mut()
|
||||||
@ -302,18 +421,66 @@ impl<T> StaticVec<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> StaticVec<T> {
|
||||||
|
/// Get a mutable iterator to entries in the `StaticVec`.
|
||||||
|
pub fn into_iter(mut self) -> Box<dyn Iterator<Item = T>> {
|
||||||
|
if self.is_fixed_storage() {
|
||||||
|
let mut it = FixedStorageIterator {
|
||||||
|
data: unsafe { mem::MaybeUninit::uninit().assume_init() },
|
||||||
|
index: 0,
|
||||||
|
limit: self.len,
|
||||||
|
};
|
||||||
|
|
||||||
|
for x in 0..self.len {
|
||||||
|
it.data[x] = mem::replace(self.list.get_mut(x).unwrap(), MaybeUninit::uninit());
|
||||||
|
}
|
||||||
|
self.len = 0;
|
||||||
|
|
||||||
|
Box::new(it)
|
||||||
|
} else {
|
||||||
|
Box::new(Vec::from(self).into_iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator that takes control of the fixed-size storage of a `StaticVec` and returns its values.
|
||||||
|
struct FixedStorageIterator<T> {
|
||||||
|
data: [MaybeUninit<T>; MAX_STATIC_VEC],
|
||||||
|
index: usize,
|
||||||
|
limit: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Iterator for FixedStorageIterator<T> {
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.index >= self.limit {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.index += 1;
|
||||||
|
|
||||||
|
let value = mem::replace(
|
||||||
|
self.data.get_mut(self.index - 1).unwrap(),
|
||||||
|
MaybeUninit::uninit(),
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe { Some(value.assume_init()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Default> StaticVec<T> {
|
impl<T: Default> StaticVec<T> {
|
||||||
/// Get the item at a particular index, replacing it with the default.
|
/// Get the item at a particular index, replacing it with the default.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the index is out of bounds.
|
/// Panics if `index` is out of bounds.
|
||||||
pub fn get(&mut self, index: usize) -> T {
|
pub fn take(&mut self, index: usize) -> T {
|
||||||
if index >= self.len {
|
if index >= self.len {
|
||||||
panic!("index OOB in StaticVec");
|
panic!("index OOB in StaticVec");
|
||||||
}
|
}
|
||||||
|
|
||||||
mem::take(if self.len <= self.list.len() {
|
mem::take(if self.is_fixed_storage() {
|
||||||
unsafe { mem::transmute(self.list.get_mut(index).unwrap()) }
|
unsafe { mem::transmute(self.list.get_mut(index).unwrap()) }
|
||||||
} else {
|
} else {
|
||||||
self.more.get_mut(index).unwrap()
|
self.more.get_mut(index).unwrap()
|
||||||
@ -333,7 +500,7 @@ impl<T> AsRef<[T]> for StaticVec<T> {
|
|||||||
fn as_ref(&self) -> &[T] {
|
fn as_ref(&self) -> &[T] {
|
||||||
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
|
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
|
||||||
|
|
||||||
if self.len <= list.len() {
|
if self.is_fixed_storage() {
|
||||||
&list[..self.len]
|
&list[..self.len]
|
||||||
} else {
|
} else {
|
||||||
&self.more[..]
|
&self.more[..]
|
||||||
@ -345,7 +512,7 @@ impl<T> AsMut<[T]> for StaticVec<T> {
|
|||||||
fn as_mut(&mut self) -> &mut [T] {
|
fn as_mut(&mut self) -> &mut [T] {
|
||||||
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
|
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
|
||||||
|
|
||||||
if self.len <= list.len() {
|
if self.is_fixed_storage() {
|
||||||
&mut list[..self.len]
|
&mut list[..self.len]
|
||||||
} else {
|
} else {
|
||||||
&mut self.more[..]
|
&mut self.more[..]
|
||||||
@ -353,20 +520,30 @@ impl<T> AsMut<[T]> for StaticVec<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Index<usize> for StaticVec<T> {
|
||||||
|
type Output = T;
|
||||||
|
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
self.get(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IndexMut<usize> for StaticVec<T> {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
self.get_mut(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> From<StaticVec<T>> for Vec<T> {
|
impl<T> From<StaticVec<T>> for Vec<T> {
|
||||||
fn from(mut value: StaticVec<T>) -> Self {
|
fn from(mut value: StaticVec<T>) -> Self {
|
||||||
if value.len <= value.list.len() {
|
if value.len <= MAX_STATIC_VEC {
|
||||||
value
|
value.move_fixed_into_vec(value.len);
|
||||||
.list
|
|
||||||
.iter_mut()
|
|
||||||
.map(|v| mem::replace(v, mem::MaybeUninit::uninit()))
|
|
||||||
.map(StaticVec::extract)
|
|
||||||
.collect()
|
|
||||||
} else {
|
|
||||||
let mut arr = Self::new();
|
|
||||||
arr.append(&mut value.more);
|
|
||||||
arr
|
|
||||||
}
|
}
|
||||||
|
value.len = 0;
|
||||||
|
|
||||||
|
let mut arr = Self::new();
|
||||||
|
arr.append(&mut value.more);
|
||||||
|
arr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,12 +552,9 @@ impl<T> From<Vec<T>> for StaticVec<T> {
|
|||||||
let mut arr: Self = Default::default();
|
let mut arr: Self = Default::default();
|
||||||
arr.len = value.len();
|
arr.len = value.len();
|
||||||
|
|
||||||
if arr.len <= arr.list.len() {
|
if arr.len <= MAX_STATIC_VEC {
|
||||||
for x in (0..arr.len).rev() {
|
for x in (0..arr.len).rev() {
|
||||||
mem::replace(
|
arr.set_into_list(x, value.pop().unwrap(), false);
|
||||||
arr.list.get_mut(x).unwrap(),
|
|
||||||
mem::MaybeUninit::new(value.pop().unwrap()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
arr.more = value;
|
arr.more = value;
|
||||||
|
@ -84,28 +84,39 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.eval::<()>(
|
.eval::<INT>(
|
||||||
r#"
|
r#"
|
||||||
|
let x = 0;
|
||||||
|
|
||||||
for x in range(0, 10) {
|
for x in range(0, 10) {
|
||||||
import "hello" as h;
|
import "hello" as h;
|
||||||
|
x += h::answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorTooManyModules(_)
|
EvalAltResult::ErrorTooManyModules(_)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.eval::<()>(
|
.eval::<INT>(
|
||||||
r#"
|
r#"
|
||||||
|
let x = 0;
|
||||||
|
|
||||||
fn foo() {
|
fn foo() {
|
||||||
import "hello" as h;
|
import "hello" as h;
|
||||||
|
x += h::answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
for x in range(0, 10) {
|
for x in range(0, 10) {
|
||||||
foo();
|
foo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
@ -114,6 +125,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
engine.set_max_modules(0);
|
engine.set_max_modules(0);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
engine.eval::<()>(
|
engine.eval::<()>(
|
||||||
r#"
|
r#"
|
||||||
fn foo() {
|
fn foo() {
|
||||||
|
@ -43,20 +43,40 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
engine.eval::<()>(
|
engine.eval::<()>(
|
||||||
r#"
|
r#"
|
||||||
|
print("Test1");
|
||||||
|
let x = 0;
|
||||||
|
|
||||||
|
while x < 28 {
|
||||||
|
print(x);
|
||||||
|
x += 1;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
engine.eval::<()>(
|
||||||
|
r#"
|
||||||
|
print("Test2");
|
||||||
fn inc(x) { x + 1 }
|
fn inc(x) { x + 1 }
|
||||||
let x = 0;
|
let x = 0;
|
||||||
while x < 20 { x = inc(x); }
|
while x < 20 { x = inc(x); }
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.eval::<()>(
|
.eval::<()>(
|
||||||
r#"
|
r#"
|
||||||
|
print("Test3");
|
||||||
fn inc(x) { x + 1 }
|
fn inc(x) { x + 1 }
|
||||||
let x = 0;
|
let x = 0;
|
||||||
while x < 1000 { x = inc(x); }
|
|
||||||
"#
|
while x < 28 {
|
||||||
|
print(x);
|
||||||
|
x = inc(x);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorTooManyOperations(_)
|
EvalAltResult::ErrorTooManyOperations(_)
|
||||||
|
Loading…
Reference in New Issue
Block a user