From 65529d28ef54f7974fb1508f596dbf99b7910822 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 12 Dec 2021 18:09:42 +0800 Subject: [PATCH 01/97] Bump fixes version. --- .github/workflows/build.yml | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a3359626..5242963f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ on: push: branches: - master - - v1.2-fixes + - v1.3-fixes pull_request: {} jobs: diff --git a/Cargo.toml b/Cargo.toml index b2364997..cb0a838c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "codegen"] [package] name = "rhai" -version = "1.3.0" +version = "1.3.1" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"] description = "Embedded scripting for Rust" From 93e3c4c10ada0de13ca294cf0b7d2e1b2c715352 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 13 Dec 2021 09:40:43 +0800 Subject: [PATCH 02/97] Compact BLOB display. --- CHANGELOG.md | 9 +++++++++ src/types/dynamic.rs | 11 ++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e07fe18..c0b20648 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ Rhai Release Notes ================== +Version 1.3.1 +============= + +Enhancements +------------ + +* `BLOB`'s are refined to display in a more compact hex format. + + Version 1.3.0 ============= diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 9f142049..314dbc4b 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -689,7 +689,16 @@ impl fmt::Debug for Dynamic { #[cfg(not(feature = "no_index"))] Union::Array(ref value, _, _) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_index"))] - Union::Blob(ref value, _, _) => fmt::Debug::fmt(value, f), + Union::Blob(ref value, _, _) => { + f.write_str("[")?; + value.iter().enumerate().try_for_each(|(i, v)| { + if i > 0 && i % 8 == 0 { + f.write_str(" ")?; + } + write!(f, "{:02x}", v) + })?; + f.write_str("]") + } #[cfg(not(feature = "no_object"))] Union::Map(ref value, _, _) => { f.write_str("#")?; From 59a9ff0b0fc7d11e4650e86676e1d21a3ad57834 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 15 Dec 2021 14:18:03 +0800 Subject: [PATCH 03/97] Fix range precedences and add comparison operators. --- src/func/builtin.rs | 80 +++++++++++++++++++++++++++++--------- src/packages/iter_basic.rs | 22 +++++++---- src/tokenizer.rs | 4 +- 3 files changed, 78 insertions(+), 28 deletions(-) diff --git a/src/func/builtin.rs b/src/func/builtin.rs index e2bb1c74..a257e2fd 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -2,7 +2,9 @@ use super::call::FnCallArgs; use crate::engine::OP_CONTAINS; -use crate::{Dynamic, ImmutableString, NativeCallContext, RhaiResult, INT}; +use crate::{ + Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult, INT, +}; use std::any::TypeId; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -55,24 +57,6 @@ pub fn get_builtin_binary_op_fn( let type1 = x.type_id(); let type2 = y.type_id(); - // One of the operands is a custom type, so it is never built-in - if x.is_variant() || y.is_variant() { - return if is_numeric(type1) && is_numeric(type2) { - // Disallow comparisons between different numeric types - None - } else if type1 != type2 { - // If the types are not the same, default to not compare - match op { - "!=" => Some(|_, _| Ok(Dynamic::TRUE)), - "==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), - _ => None, - } - } else { - // Disallow comparisons between the same type - None - }; - } - let types_pair = (type1, type2); macro_rules! impl_op { @@ -301,6 +285,64 @@ pub fn get_builtin_binary_op_fn( }; } + // Non-compatible ranges + if types_pair + == ( + TypeId::of::(), + TypeId::of::(), + ) + || types_pair + == ( + TypeId::of::(), + TypeId::of::(), + ) + { + return match op { + "!=" => Some(|_, _| Ok(Dynamic::TRUE)), + "==" => Some(|_, _| Ok(Dynamic::FALSE)), + _ => None, + }; + } + + // Handle ranges here because ranges are implemented as custom type + if type1 == TypeId::of::() { + if type1 == type2 { + return match op { + "==" => Some(impl_op!(ExclusiveRange == ExclusiveRange)), + "!=" => Some(impl_op!(ExclusiveRange != ExclusiveRange)), + _ => None, + }; + } + } + + if type1 == TypeId::of::() { + if type1 == type2 { + return match op { + "==" => Some(impl_op!(InclusiveRange == InclusiveRange)), + "!=" => Some(impl_op!(InclusiveRange != InclusiveRange)), + _ => None, + }; + } + } + + // One of the operands is a custom type, so it is never built-in + if x.is_variant() || y.is_variant() { + return if is_numeric(type1) && is_numeric(type2) { + // Disallow comparisons between different numeric types + None + } else if type1 != type2 { + // If the types are not the same, default to not compare + match op { + "!=" => Some(|_, _| Ok(Dynamic::TRUE)), + "==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), + _ => None, + } + } else { + // Disallow comparisons between the same type + None + }; + } + // Default comparison operators for different types if type2 != type1 { return match op { diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 1b4e1e97..c5a0941b 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -530,15 +530,15 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { #[export_module] mod range_functions { #[rhai_fn(get = "start", name = "start", pure)] - pub fn range_start(range: &mut ExclusiveRange) -> INT { + pub fn start(range: &mut ExclusiveRange) -> INT { range.start } #[rhai_fn(get = "end", name = "end", pure)] - pub fn range_end(range: &mut ExclusiveRange) -> INT { + pub fn end(range: &mut ExclusiveRange) -> INT { range.end } #[rhai_fn(name = "contains", pure)] - pub fn range_contains(range: &mut ExclusiveRange, value: INT) -> bool { + pub fn contains(range: &mut ExclusiveRange, value: INT) -> bool { range.contains(&value) } #[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)] @@ -546,20 +546,28 @@ mod range_functions { let _range = range; false } + #[rhai_fn(get = "is_empty", name = "is_empty", pure)] + pub fn is_empty(range: &mut ExclusiveRange) -> bool { + range.is_empty() + } #[rhai_fn(get = "start", name = "start", pure)] - pub fn range_inclusive_start(range: &mut InclusiveRange) -> INT { + pub fn start_inclusive(range: &mut InclusiveRange) -> INT { *range.start() } #[rhai_fn(get = "end", name = "end", pure)] - pub fn range_inclusive_end(range: &mut InclusiveRange) -> INT { + pub fn end_inclusive(range: &mut InclusiveRange) -> INT { *range.end() } #[rhai_fn(name = "contains", pure)] - pub fn range_inclusive_contains(range: &mut InclusiveRange, value: INT) -> bool { + pub fn contains_inclusive(range: &mut InclusiveRange, value: INT) -> bool { range.contains(&value) } + #[rhai_fn(get = "is_empty", name = "is_empty", pure)] + pub fn is_empty_inclusive(range: &mut InclusiveRange) -> bool { + range.is_empty() + } #[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)] - pub fn range_inclusive_is_inclusive(range: &mut InclusiveRange) -> bool { + pub fn is_inclusive_inclusive(range: &mut InclusiveRange) -> bool { let _range = range; true } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 9e93f582..4a3ab161 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -878,8 +878,6 @@ impl Token { | LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign | ModuloAssign => 0, - ExclusiveRange | InclusiveRange => 10, - Or | XOr | Pipe => 30, And | Ampersand => 60, @@ -890,6 +888,8 @@ impl Token { LessThan | LessThanEqualsTo | GreaterThan | GreaterThanEqualsTo => 130, + ExclusiveRange | InclusiveRange => 140, + Plus | Minus => 150, Divide | Multiply | Modulo => 180, From abbfa3777d649bfe8fc8a40b9b9a3a52c4c8dd1e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 15 Dec 2021 22:12:51 +0800 Subject: [PATCH 04/97] Add more range API's. --- CHANGELOG.md | 1 + src/ast.rs | 6 ++++++ src/optimizer.rs | 41 +++++++++++++------------------------- src/packages/iter_basic.rs | 12 ++++++++++- src/tokenizer.rs | 10 ++++++++++ 5 files changed, 42 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64a35df2..ac70de93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Version 1.4.0 ============= This version adds support for integer _ranges_ via the `..` and `..=` operators. +Many standard API's are extended with range parameters where appropriate. New features ------------ diff --git a/src/ast.rs b/src/ast.rs index dc87ec79..8d7e19f1 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1057,6 +1057,12 @@ impl StmtBlock { pub fn len(&self) -> usize { self.0.len() } + /// Get the statements of this statements block. + #[inline(always)] + #[must_use] + pub fn statements(&self) -> &[Stmt] { + &self.0 + } /// Get the position (location of the beginning `{`) of this statements block. #[inline(always)] #[must_use] diff --git a/src/optimizer.rs b/src/optimizer.rs index c1170a35..304d4cbd 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -471,11 +471,6 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b // switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def } optimize_expr(&mut condition, state, false); - let def_pos = if x.1.position().is_none() { - *pos - } else { - x.1.position() - }; let def_stmt = optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false); @@ -483,16 +478,16 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b condition, Box::new(( mem::take(&mut block.1), - Stmt::Block(def_stmt.into_boxed_slice(), def_pos).into(), + Stmt::Block(def_stmt.into_boxed_slice(), x.1.position().or_else(*pos)) + .into(), )), match_expr.position(), ); } else { // Promote the matched case - let new_pos = block.1.position(); let statements = optimize_stmt_block(mem::take(&mut *block.1), state, true, true, false); - *stmt = Stmt::Block(statements.into_boxed_slice(), new_pos); + *stmt = Stmt::Block(statements.into_boxed_slice(), block.1.position()); } state.set_dirty(); @@ -519,29 +514,27 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b // switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def } optimize_expr(&mut condition, state, false); - let def_block = mem::take(&mut *x.1); - let def_stmt = optimize_stmt_block(def_block, state, true, true, false); - let def_pos = if x.1.position().is_none() { - *pos - } else { - x.1.position() - }; - + let def_stmt = + optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false); *stmt = Stmt::If( condition, Box::new(( mem::take(stmt_block), - Stmt::Block(def_stmt.into_boxed_slice(), def_pos).into(), + Stmt::Block( + def_stmt.into_boxed_slice(), + x.1.position().or_else(*pos), + ) + .into(), )), match_expr.position(), ); } else { // Promote the matched case - let new_pos = stmt_block.position(); let statements = mem::take(&mut **stmt_block); let statements = optimize_stmt_block(statements, state, true, true, false); - *stmt = Stmt::Block(statements.into_boxed_slice(), new_pos); + *stmt = + Stmt::Block(statements.into_boxed_slice(), stmt_block.position()); } state.set_dirty(); @@ -551,10 +544,9 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b // Multiple ranges - clear the table and just keep the right ranges if !table.is_empty() { state.set_dirty(); + table.clear(); } - table.clear(); - let old_ranges_len = ranges.len(); ranges.retain(|&mut (start, end, inclusive, _, _)| { @@ -585,13 +577,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b // Promote the default case state.set_dirty(); - let def_pos = if x.1.position().is_none() { - *pos - } else { - x.1.position() - }; let def_stmt = optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false); - *stmt = Stmt::Block(def_stmt.into_boxed_slice(), def_pos); + *stmt = Stmt::Block(def_stmt.into_boxed_slice(), x.1.position().or_else(*pos)); } // switch Stmt::Switch(match_expr, x, _) => { diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index c5a0941b..75538f63 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -542,10 +542,15 @@ mod range_functions { range.contains(&value) } #[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)] - pub fn range_is_inclusive(range: &mut ExclusiveRange) -> bool { + pub fn is_inclusive(range: &mut ExclusiveRange) -> bool { let _range = range; false } + #[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)] + pub fn is_exclusive(range: &mut ExclusiveRange) -> bool { + let _range = range; + true + } #[rhai_fn(get = "is_empty", name = "is_empty", pure)] pub fn is_empty(range: &mut ExclusiveRange) -> bool { range.is_empty() @@ -571,4 +576,9 @@ mod range_functions { let _range = range; true } + #[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)] + pub fn is_exclusive_inclusive(range: &mut InclusiveRange) -> bool { + let _range = range; + false + } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 4a3ab161..b49cbd68 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -213,6 +213,16 @@ impl Position { #[cfg(feature = "no_position")] return true; } + /// Returns an fallback [`Position`] if it is [`NONE`][Position::NONE]? + #[inline] + #[must_use] + pub const fn or_else(self, pos: Self) -> Self { + if self.is_none() { + pos + } else { + self + } + } /// Print this [`Position`] for debug purposes. #[inline] pub(crate) fn debug_print(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { From 2adb4ba4a8acf4a225269a3a0997a2bb507794e4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 15 Dec 2021 23:21:05 +0800 Subject: [PATCH 05/97] Simplify deref_mut to &mut *. --- src/engine.rs | 2 +- src/optimizer.rs | 167 ++++++++++++--------------------------- src/types/parse_error.rs | 4 +- 3 files changed, 54 insertions(+), 119 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 2795bf0c..145ced34 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -756,7 +756,7 @@ impl DerefMut for Target<'_> { match self { Self::RefMut(r) => *r, #[cfg(not(feature = "no_closure"))] - Self::LockGuard((r, _)) => r.deref_mut(), + Self::LockGuard((r, _)) => &mut *r, Self::TempValue(ref mut r) => r, #[cfg(not(feature = "no_index"))] Self::Bit(_, ref mut r, _) diff --git a/src/optimizer.rs b/src/optimizer.rs index 304d4cbd..1526da0b 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -16,7 +16,6 @@ use std::{ convert::TryFrom, hash::{Hash, Hasher}, mem, - ops::DerefMut, }; /// Level of optimization performed. @@ -389,8 +388,10 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b let value = mem::take(&mut x2.args[1]); if let Expr::Stack(slot, pos) = value { - let value = mem::take(x2.constants.get_mut(slot).expect("valid slot")); - x.2 = Expr::from_dynamic(value, pos); + x.2 = Expr::from_dynamic( + mem::take(x2.constants.get_mut(slot).expect("valid slot")), + pos, + ); } else { x.2 = value; } @@ -432,28 +433,28 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b // if false { if_block } else { else_block } -> else_block Stmt::If(Expr::BoolConstant(false, _), x, _) => { state.set_dirty(); - let else_block = mem::take(&mut *x.1); - *stmt = match optimize_stmt_block(else_block, state, preserve_result, true, false) { - statements if statements.is_empty() => Stmt::Noop(x.1.position()), - statements => Stmt::Block(statements.into_boxed_slice(), x.1.position()), - } + *stmt = + match optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false) + { + statements if statements.is_empty() => Stmt::Noop(x.1.position()), + statements => Stmt::Block(statements.into_boxed_slice(), x.1.position()), + } } // if true { if_block } else { else_block } -> if_block Stmt::If(Expr::BoolConstant(true, _), x, _) => { state.set_dirty(); - let if_block = mem::take(&mut *x.0); - *stmt = match optimize_stmt_block(if_block, state, preserve_result, true, false) { - statements if statements.is_empty() => Stmt::Noop(x.0.position()), - statements => Stmt::Block(statements.into_boxed_slice(), x.0.position()), - } + *stmt = + match optimize_stmt_block(mem::take(&mut *x.0), state, preserve_result, true, false) + { + statements if statements.is_empty() => Stmt::Noop(x.0.position()), + statements => Stmt::Block(statements.into_boxed_slice(), x.0.position()), + } } // if expr { if_block } else { else_block } Stmt::If(condition, x, _) => { optimize_expr(condition, state, false); - let if_block = mem::take(x.0.deref_mut()); - *x.0 = optimize_stmt_block(if_block, state, preserve_result, true, false); - let else_block = mem::take(x.1.deref_mut()); - *x.1 = optimize_stmt_block(else_block, state, preserve_result, true, false); + *x.0 = optimize_stmt_block(mem::take(&mut *x.0), state, preserve_result, true, false); + *x.1 = optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false); } // switch const { ... } @@ -584,7 +585,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b Stmt::Switch(match_expr, x, _) => { optimize_expr(match_expr, state, false); x.0.values_mut().for_each(|block| { - let statements = mem::take(block.1.deref_mut()); + let statements = mem::take(&mut *block.1); *block.1 = optimize_stmt_block(statements, state, preserve_result, true, false); if let Some(mut condition) = mem::take(&mut block.0) { @@ -605,8 +606,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b x.0.remove(&key); } - let def_block = mem::take(x.1.deref_mut()); - *x.1 = optimize_stmt_block(def_block, state, preserve_result, true, false); + *x.1 = optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false); } // while false { block } -> Noop @@ -620,8 +620,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b if let Expr::BoolConstant(true, pos) = condition { *condition = Expr::Unit(*pos); } - let block = mem::take(body.as_mut().deref_mut()); - *body.as_mut().deref_mut() = optimize_stmt_block(block, state, false, true, false); + ***body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false); if body.len() == 1 { match body[0] { @@ -648,24 +647,21 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b if *x == options.contains(AST_OPTION_NEGATED) => { state.set_dirty(); - let block_pos = body.position(); - let block = mem::take(body.as_mut().deref_mut()); *stmt = Stmt::Block( - optimize_stmt_block(block, state, false, true, false).into_boxed_slice(), - block_pos, + optimize_stmt_block(mem::take(&mut **body), state, false, true, false) + .into_boxed_slice(), + body.position(), ); } // do { block } while|until expr Stmt::Do(body, condition, _, _) => { optimize_expr(condition, state, false); - let block = mem::take(body.as_mut().deref_mut()); - *body.as_mut().deref_mut() = optimize_stmt_block(block, state, false, true, false); + ***body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false); } // for id in expr { block } Stmt::For(iterable, x, _) => { optimize_expr(iterable, state, false); - let body = mem::take(x.2.deref_mut()); - *x.2 = optimize_stmt_block(body, state, false, true, false); + *x.2 = optimize_stmt_block(mem::take(&mut *x.2), state, false, true, false); } // let id = expr; Stmt::Var(expr, _, options, _) if !options.contains(AST_OPTION_CONSTANT) => { @@ -696,19 +692,16 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b Stmt::TryCatch(x, _) if x.0.iter().all(Stmt::is_pure) => { // If try block is pure, there will never be any exceptions state.set_dirty(); - let try_pos = x.0.position(); - let try_block = mem::take(&mut *x.0); *stmt = Stmt::Block( - optimize_stmt_block(try_block, state, false, true, false).into_boxed_slice(), - try_pos, + optimize_stmt_block(mem::take(&mut *x.0), state, false, true, false) + .into_boxed_slice(), + x.0.position(), ); } // try { try_block } catch ( var ) { catch_block } Stmt::TryCatch(x, _) => { - let try_block = mem::take(x.0.deref_mut()); - *x.0 = optimize_stmt_block(try_block, state, false, true, false); - let catch_block = mem::take(x.2.deref_mut()); - *x.2 = optimize_stmt_block(catch_block, state, false, true, false); + *x.0 = optimize_stmt_block(mem::take(&mut *x.0), state, false, true, false); + *x.2 = optimize_stmt_block(mem::take(&mut *x.2), state, false, true, false); } // func(...) Stmt::Expr(expr @ Expr::FnCall(_, _)) => { @@ -729,7 +722,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b // {...}; Stmt::Expr(Expr::Stmt(x)) => { state.set_dirty(); - *stmt = mem::take(x.as_mut()).into(); + *stmt = mem::take(&mut **x).into(); } // expr; Stmt::Expr(expr) => optimize_expr(expr, state, false), @@ -757,8 +750,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { Expr::Stmt(x) if x.is_empty() => { state.set_dirty(); *expr = Expr::Unit(x.position()) } // { stmt; ... } - do not count promotion as dirty because it gets turned back into an array Expr::Stmt(x) => { - *x.as_mut().deref_mut() = - optimize_stmt_block(mem::take(x.as_mut().deref_mut()), state, true, true, false); + ***x = optimize_stmt_block(mem::take(&mut **x), state, true, true, false); // { Stmt(Expr) } - promote match x.as_mut().as_mut() { @@ -792,9 +784,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { #[cfg(not(feature = "no_index"))] Expr::Index(x, _, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) { // array[int] - (Expr::Array(a, pos), Expr::IntegerConstant(i, _)) - if *i >= 0 && (*i as usize) < a.len() && a.iter().all(Expr::is_pure) => - { + (Expr::Array(a, pos), Expr::IntegerConstant(i, _)) if *i >= 0 && (*i as usize) < a.len() && a.iter().all(Expr::is_pure) => { // Array literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); @@ -803,9 +793,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { *expr = result; } // array[-int] - (Expr::Array(a, pos), Expr::IntegerConstant(i, _)) - if *i < 0 && i.checked_abs().map(|n| n as usize <= a.len()).unwrap_or(false) && a.iter().all(Expr::is_pure) => - { + (Expr::Array(a, pos), Expr::IntegerConstant(i, _)) if *i < 0 && i.checked_abs().map(|n| n as usize <= a.len()).unwrap_or(false) && a.iter().all(Expr::is_pure) => { // Array literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); @@ -872,39 +860,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { let mut n = 0; // Merge consecutive strings - while n < x.len()-1 { + while n < x.len() - 1 { match (mem::take(&mut x[n]), mem::take(&mut x[n+1])) { - (Expr::StringConstant(mut s1, pos), Expr::StringConstant(s2, _)) => { - s1 += s2; - x[n] = Expr::StringConstant(s1, pos); - x.remove(n+1); - state.set_dirty(); - } - (expr1, Expr::Unit(_)) => { - x[n] = expr1; - x.remove(n+1); - state.set_dirty(); - } - (Expr::Unit(_), expr2) => { - x[n+1] = expr2; - x.remove(n); - state.set_dirty(); - } - (expr1, Expr::StringConstant(s, _)) if s.is_empty() => { - x[n] = expr1; - x.remove(n+1); - state.set_dirty(); - } - (Expr::StringConstant(s, _), expr2) if s.is_empty()=> { - x[n+1] = expr2; - x.remove(n); - state.set_dirty(); - } - (expr1, expr2) => { - x[n] = expr1; - x[n+1] = expr2; - n += 1; - } + (Expr::StringConstant(mut s1, pos), Expr::StringConstant(s2, _)) => { s1 += s2; x[n] = Expr::StringConstant(s1, pos); x.remove(n+1); state.set_dirty(); } + (expr1, Expr::Unit(_)) => { x[n] = expr1; x.remove(n+1); state.set_dirty(); } + (Expr::Unit(_), expr2) => { x[n+1] = expr2; x.remove(n); state.set_dirty(); } + (expr1, Expr::StringConstant(s, _)) if s.is_empty() => { x[n] = expr1; x.remove(n+1); state.set_dirty(); } + (Expr::StringConstant(s, _), expr2) if s.is_empty()=> { x[n+1] = expr2; x.remove(n); state.set_dirty(); } + (expr1, expr2) => { x[n] = expr1; x[n+1] = expr2; n += 1; } } } @@ -931,52 +894,24 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { // lhs && rhs Expr::And(x, _) => match (&mut x.lhs, &mut x.rhs) { // true && rhs -> rhs - (Expr::BoolConstant(true, _), rhs) => { - state.set_dirty(); - optimize_expr(rhs, state, false); - *expr = mem::take(rhs); - } + (Expr::BoolConstant(true, _), rhs) => { state.set_dirty(); optimize_expr(rhs, state, false); *expr = mem::take(rhs); } // false && rhs -> false - (Expr::BoolConstant(false, pos), _) => { - state.set_dirty(); - *expr = Expr::BoolConstant(false, *pos); - } + (Expr::BoolConstant(false, pos), _) => { state.set_dirty(); *expr = Expr::BoolConstant(false, *pos); } // lhs && true -> lhs - (lhs, Expr::BoolConstant(true, _)) => { - state.set_dirty(); - optimize_expr(lhs, state, false); - *expr = mem::take(lhs); - } + (lhs, Expr::BoolConstant(true, _)) => { state.set_dirty(); optimize_expr(lhs, state, false); *expr = mem::take(lhs); } // lhs && rhs - (lhs, rhs) => { - optimize_expr(lhs, state, false); - optimize_expr(rhs, state, false); - } + (lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, false); } }, // lhs || rhs Expr::Or(ref mut x, _) => match (&mut x.lhs, &mut x.rhs) { // false || rhs -> rhs - (Expr::BoolConstant(false, _), rhs) => { - state.set_dirty(); - optimize_expr(rhs, state, false); - *expr = mem::take(rhs); - } + (Expr::BoolConstant(false, _), rhs) => { state.set_dirty(); optimize_expr(rhs, state, false); *expr = mem::take(rhs); } // true || rhs -> true - (Expr::BoolConstant(true, pos), _) => { - state.set_dirty(); - *expr = Expr::BoolConstant(true, *pos); - } + (Expr::BoolConstant(true, pos), _) => { state.set_dirty(); *expr = Expr::BoolConstant(true, *pos); } // lhs || false - (lhs, Expr::BoolConstant(false, _)) => { - state.set_dirty(); - optimize_expr(lhs, state, false); - *expr = mem::take(lhs); - } + (lhs, Expr::BoolConstant(false, _)) => { state.set_dirty(); optimize_expr(lhs, state, false); *expr = mem::take(lhs); } // lhs || rhs - (lhs, rhs) => { - optimize_expr(lhs, state, false); - optimize_expr(rhs, state, false); - } + (lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, false); } }, // eval! @@ -1222,7 +1157,7 @@ pub fn optimize_into_ast( let mut fn_def = crate::func::native::shared_take_or_clone(fn_def); // Optimize the function body - let body = mem::take(fn_def.body.deref_mut()); + let body = mem::take(&mut *fn_def.body); *fn_def.body = optimize_top_level(body, engine, scope, lib2, optimization_level); diff --git a/src/types/parse_error.rs b/src/types/parse_error.rs index d814aa3a..6a84389b 100644 --- a/src/types/parse_error.rs +++ b/src/types/parse_error.rs @@ -108,9 +108,9 @@ pub enum ParseErrorType { DuplicatedSwitchCase, /// A variable name is duplicated. Wrapped value is the variable name. DuplicatedVariable(String), - /// An integer case of a `switch` statement is after a range case. + /// An integer case of a `switch` statement is in an appropriate place. WrongSwitchIntegerCase, - /// The default case of a `switch` statement is not the last. + /// The default case of a `switch` statement is in an appropriate place. WrongSwitchDefaultCase, /// The case condition of a `switch` statement is not appropriate. WrongSwitchCaseCondition, From 224aa0ea68a89fab94c3f67c6d6462f685e8b0a1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 16 Dec 2021 16:06:44 +0800 Subject: [PATCH 06/97] Allow custom syntax inside binary operator expressions. --- CHANGELOG.md | 5 +++++ README.md | 2 +- src/parser.rs | 38 ++++++++++++++++++-------------------- src/tokenizer.rs | 28 ++++++++++++---------------- tests/custom_syntax.rs | 34 ++++++++++++++++++++++++++++++++++ tests/serde.rs | 1 + 6 files changed, 71 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0b20648..8c60ac64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Rhai Release Notes Version 1.3.1 ============= +Bug fixes +--------- + +* Custom syntax now works properly inside binary expressions. + Enhancements ------------ diff --git a/README.md b/README.md index 07a221a4..bfda54e5 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ The [`scripts`](https://github.com/rhaiscript/rhai/tree/master/scripts) subdirec Below is the standard _Fibonacci_ example for scripting languages: -```js +```ts // This Rhai script calculates the n-th Fibonacci number using a // really dumb algorithm to test the speed of the scripting engine. diff --git a/src/parser.rs b/src/parser.rs index 25bc5272..600f0090 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1550,9 +1550,26 @@ fn parse_unary( settings.ensure_level_within_max_limit(state.max_expr_depth)?; let (token, token_pos) = input.peek().expect(NEVER_ENDS); + let token_pos = *token_pos; let mut settings = settings; - settings.pos = *token_pos; + settings.pos = token_pos; + + // Check if it is a custom syntax. + if !state.engine.custom_syntax.is_empty() { + match token { + Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key) => { + if let Some((key, syntax)) = state.engine.custom_syntax.get_key_value(key.as_ref()) + { + input.next().expect(NEVER_ENDS); + return parse_custom_syntax( + input, state, lib, settings, key, syntax, token_pos, + ); + } + } + _ => (), + } + } match token { // -expr @@ -2251,25 +2268,6 @@ fn parse_expr( let mut settings = settings; settings.pos = input.peek().expect(NEVER_ENDS).1; - // Check if it is a custom syntax. - if !state.engine.custom_syntax.is_empty() { - let (token, pos) = input.peek().expect(NEVER_ENDS); - let token_pos = *pos; - - match token { - Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key) => { - if let Some((key, syntax)) = state.engine.custom_syntax.get_key_value(key.as_ref()) - { - input.next().expect(NEVER_ENDS); - return parse_custom_syntax( - input, state, lib, settings, key, syntax, token_pos, - ); - } - } - _ => (), - } - } - // Parse expression normally. let lhs = parse_unary(input, state, lib, settings.level_up())?; parse_binary_op( diff --git a/src/tokenizer.rs b/src/tokenizer.rs index c039a8dc..e5a4905d 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -863,11 +863,6 @@ impl Token { use Token::*; Precedence::new(match self { - // Assignments are not considered expressions - set to zero - Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | PowerOfAssign - | LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign - | ModuloAssign => 0, - Or | XOr | Pipe => 30, And | Ampersand => 60, @@ -886,8 +881,6 @@ impl Token { LeftShift | RightShift => 210, - Period => 240, - _ => 0, }) } @@ -898,14 +891,6 @@ impl Token { use Token::*; match self { - // Assignments bind to the right - Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | PowerOfAssign - | LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign - | ModuloAssign => true, - - // Property access binds to the right - Period => true, - // Exponentiation binds to the right PowerOf => true, @@ -1662,6 +1647,17 @@ fn get_next_token_inner( // Shebang ('#', '!') => return Some((Token::Reserved("#!".into()), start_pos)), + ('#', ' ') => { + eat_next(stream, pos); + let token = if stream.peek_next() == Some('{') { + eat_next(stream, pos); + "# {" + } else { + "#" + }; + return Some((Token::Reserved(token.into()), start_pos)); + } + ('#', _) => return Some((Token::Reserved("#".into()), start_pos)), // Operators @@ -2182,7 +2178,7 @@ impl<'a> Iterator for TokenIterator<'a> { ("(*", false) | ("*)", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(), "'(* .. *)' is not a valid comment format. This is not Pascal! Should it be '/* .. */'?".to_string(), )), - ("#", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(), + ("# {", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(), "'#' is not a valid symbol. Should it be '#{'?".to_string(), )), // Reserved keyword/operator that is custom. diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index f381bdf0..cbc9962a 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -239,3 +239,37 @@ fn test_custom_syntax_raw() -> Result<(), Box> { Ok(()) } + +#[test] +fn test_custom_syntax_raw2() -> Result<(), Box> { + let mut engine = Engine::new(); + + engine + .register_custom_operator("#", 255)? + .register_custom_syntax_raw( + "#", + |symbols, lookahead| match symbols.len() { + 1 if lookahead == "-" => Ok(Some("$symbol$".into())), + 1 => Ok(Some("$int$".into())), + 2 if symbols[1] == "-" => Ok(Some("$int$".into())), + 2 => Ok(None), + 3 => Ok(None), + _ => unreachable!(), + }, + false, + move |_, inputs| { + let id = if inputs.len() == 2 { + -inputs[1].get_literal_value::().unwrap() + } else { + inputs[0].get_literal_value::().unwrap() + }; + Ok(id.into()) + }, + ); + + assert_eq!(engine.eval::("let x = 41; x + #1")?, 42); + assert_eq!(engine.eval::("#42/2")?, 21); + assert_eq!(engine.eval::("#-1")?, -1); + + Ok(()) +} diff --git a/tests/serde.rs b/tests/serde.rs index 25929ac5..b1b76e3a 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -12,6 +12,7 @@ use rhai::Array; use rhai::Map; #[cfg(not(feature = "no_float"))] use rhai::FLOAT; +#[cfg(not(feature = "no_float"))] #[cfg(feature = "decimal")] use rust_decimal::Decimal; From fbc2b1f13d4fb66cb19e8997d4d4b7b799b5e60f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 16 Dec 2021 18:01:49 +0800 Subject: [PATCH 07/97] Allow non-custom reserved symbols in custom syntax. --- CHANGELOG.md | 5 ++++ src/api/deprecated.rs | 20 ++++++++++++-- src/custom_syntax.rs | 15 ++++++++--- src/parser.rs | 19 ++++---------- src/tokenizer.rs | 11 +++----- src/types/parse_error.rs | 6 +++-- tests/custom_syntax.rs | 57 ++++++++++++++++++++-------------------- tests/tokens.rs | 4 +-- 8 files changed, 77 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0986d3b1..0305f73b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ New features * Added support for integer _ranges_ via the `..` and `..=` operators. +Deprecated API's +---------------- + +* `Expression::get_variable_name` is deprecated in favor of the new `Expression::get_string_value`. + Version 1.3.1 ============= diff --git a/src/api/deprecated.rs b/src/api/deprecated.rs index b315e636..9dbc3b5f 100644 --- a/src/api/deprecated.rs +++ b/src/api/deprecated.rs @@ -1,8 +1,8 @@ //! Module containing all deprecated API that will be removed in the next major version. use crate::{ - Dynamic, Engine, EvalAltResult, FnPtr, ImmutableString, NativeCallContext, RhaiResult, Scope, - AST, + Dynamic, Engine, EvalAltResult, Expression, FnPtr, ImmutableString, NativeCallContext, + RhaiResult, Scope, AST, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -304,3 +304,19 @@ impl FnPtr { self.call_raw(context, this_ptr, arg_values) } } + +impl Expression<'_> { + /// If this expression is a variable name, return it. Otherwise [`None`]. + /// + /// # Deprecated + /// + /// This method is deprecated. Use [`get_string_value`][Expression::get_string_value] instead. + /// + /// This method will be removed in the next major version. + #[deprecated(since = "1.4.0", note = "use `get_string_value` instead")] + #[inline(always)] + #[must_use] + pub fn get_variable_name(&self) -> Option<&str> { + self.get_string_value() + } +} diff --git a/src/custom_syntax.rs b/src/custom_syntax.rs index 56e96c5f..f718b0e3 100644 --- a/src/custom_syntax.rs +++ b/src/custom_syntax.rs @@ -65,11 +65,17 @@ impl<'a> From<&'a Expr> for Expression<'a> { } impl Expression<'_> { - /// If this expression is a variable name, return it. Otherwise [`None`]. + /// Get the value of this expression if it is a variable name or a string constant. + /// + /// Returns [`None`] also if the constant is not of the specified type. #[inline(always)] #[must_use] - pub fn get_variable_name(&self) -> Option<&str> { - self.0.get_variable_name(true) + pub fn get_string_value(&self) -> Option<&str> { + match self.0 { + Expr::Variable(_, _, x) if x.1.is_none() => Some(x.2.as_str()), + Expr::StringConstant(x, _) => Some(x.as_str()), + _ => None, + } } /// Get the position of this expression. #[inline(always)] @@ -109,6 +115,9 @@ impl Expression<'_> { if TypeId::of::() == TypeId::of::() { return match self.0 { Expr::StringConstant(x, _) => unsafe_try_cast(x.clone()).ok(), + Expr::Variable(_, _, x) => { + unsafe_try_cast(Into::::into(&x.2)).ok() + } _ => None, }; } diff --git a/src/parser.rs b/src/parser.rs index 757001fb..73b0e2d4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1401,10 +1401,7 @@ fn parse_primary( let msg = format!("'{}' can only be used in functions", s); return Err(LexError::ImproperSymbol(s.to_string(), msg).into_err(settings.pos)); } - _ if is_valid_identifier(s.chars()) => { - return Err(PERR::Reserved(s.to_string()).into_err(settings.pos)) - } - _ => return Err(LexError::UnexpectedInput(s.to_string()).into_err(settings.pos)), + _ => return Err(PERR::Reserved(s.to_string()).into_err(settings.pos)), } } @@ -1698,12 +1695,12 @@ fn make_assignment_stmt( ref e => Some(e.position()), }, Expr::Index(x, term, _) | Expr::Dot(x, term, _) => match x.lhs { - Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), + Expr::Property(_) => unreachable!("unexpected `Expr::Property` in indexing"), _ if !term => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _, _))), _ => None, }, Expr::Property(_) if parent_is_dot => None, - Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), + Expr::Property(_) => unreachable!("unexpected `Expr::Property` in indexing"), e if parent_is_dot => Some(e.position()), _ => None, } @@ -2277,15 +2274,9 @@ fn parse_expr( settings.pos = input.peek().expect(NEVER_ENDS).1; // Parse expression normally. + let precedence = Precedence::new(1); let lhs = parse_unary(input, state, lib, settings.level_up())?; - parse_binary_op( - input, - state, - lib, - Precedence::new(1), - lhs, - settings.level_up(), - ) + parse_binary_op(input, state, lib, precedence, lhs, settings.level_up()) } /// Parse an if statement. diff --git a/src/tokenizer.rs b/src/tokenizer.rs index d00aa2b5..127bf18f 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -2213,14 +2213,9 @@ impl<'a> Iterator for TokenIterator<'a> { )), // Reserved keyword/operator that is custom. (_, true) => Token::Custom(s), - // Reserved operator that is not custom. - (token, false) if !is_valid_identifier(token.chars()) => { - let msg = format!("'{}' is a reserved symbol", token); - Token::LexError(LERR::ImproperSymbol(s.to_string(), msg)) - }, // Reserved keyword that is not custom and disabled. (token, false) if self.engine.disabled_symbols.contains(token) => { - let msg = format!("reserved symbol '{}' is disabled", token); + let msg = format!("reserved {} '{}' is disabled", if is_valid_identifier(token.chars()) { "keyword"} else {"symbol"}, token); Token::LexError(LERR::ImproperSymbol(s.to_string(), msg)) }, // Reserved keyword/operator that is not custom. @@ -2230,14 +2225,14 @@ impl<'a> Iterator for TokenIterator<'a> { Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.contains_key(&*s) => { (Token::Custom(s), pos) } - // Custom standard keyword/symbol - must be disabled + // Custom keyword/symbol - must be disabled Some((token, pos)) if self.engine.custom_keywords.contains_key(&*token.syntax()) => { if self.engine.disabled_symbols.contains(&*token.syntax()) { // Disabled standard keyword/symbol (Token::Custom(token.syntax().into()), pos) } else { // Active standard keyword - should never be a custom keyword! - unreachable!("{:?} is an active keyword", token) + unreachable!("`{:?}` is an active keyword", token) } } // Disabled symbol diff --git a/src/types/parse_error.rs b/src/types/parse_error.rs index 6a84389b..b1c839f9 100644 --- a/src/types/parse_error.rs +++ b/src/types/parse_error.rs @@ -1,5 +1,6 @@ //! Module containing error definitions for the parsing process. +use crate::tokenizer::is_valid_identifier; use crate::{EvalAltResult, Position}; #[cfg(feature = "no_std")] use core_error::Error; @@ -120,7 +121,7 @@ pub enum ParseErrorType { PropertyExpected, /// Missing a variable name after the `let`, `const`, `for` or `catch` keywords. VariableExpected, - /// An identifier is a reserved keyword. + /// An identifier is a reserved symbol. Reserved(String), /// An expression is of the wrong type. /// Wrapped values are the type requested and type of the actual result. @@ -260,7 +261,8 @@ impl fmt::Display for ParseErrorType { }, Self::LiteralTooLarge(typ, max) => write!(f, "{} exceeds the maximum limit ({})", typ, max), - Self::Reserved(s) => write!(f, "'{}' is a reserved keyword", s), + Self::Reserved(s) if is_valid_identifier(s.chars()) => write!(f, "'{}' is a reserved keyword", s), + Self::Reserved(s) => write!(f, "'{}' is a reserved symbol", s), Self::UnexpectedEOF => f.write_str("Script is incomplete"), Self::WrongSwitchIntegerCase => f.write_str("Integer switch case cannot follow a range case"), Self::WrongSwitchDefaultCase => f.write_str("Default switch case must be the last"), diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index cbc9962a..9ef86f40 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -25,13 +25,13 @@ fn test_custom_syntax() -> Result<(), Box> { ], true, |context, inputs| { - let var_name = inputs[0].get_variable_name().unwrap().to_string(); + let var_name = inputs[0].get_string_value().unwrap(); let op = inputs[1].get_literal_value::().unwrap(); let max = inputs[2].get_literal_value::().unwrap(); let stmt = &inputs[3]; let condition = &inputs[4]; - context.scope_mut().push(var_name.clone(), 0 as INT); + context.scope_mut().push(var_name.to_string(), 0 as INT); let mut count: INT = 0; @@ -151,14 +151,14 @@ fn test_custom_syntax() -> Result<(), Box> { &["var", "$ident$", "=", "$expr$"], true, |context, inputs| { - let var_name = inputs[0].get_variable_name().unwrap().to_string(); + let var_name = inputs[0].get_string_value().unwrap(); let expr = &inputs[1]; // Evaluate the expression let value = context.eval_expression_tree(expr)?; - if !context.scope().is_constant(&var_name).unwrap_or(false) { - context.scope_mut().set_value(var_name, value); + if !context.scope().is_constant(var_name).unwrap_or(false) { + context.scope_mut().set_value(var_name.to_string(), value); Ok(Dynamic::UNIT) } else { Err(format!("variable {} is constant", var_name).into()) @@ -206,7 +206,7 @@ fn test_custom_syntax_raw() -> Result<(), Box> { |context, inputs| { context.scope_mut().push("foo", 999 as INT); - Ok(match inputs[0].get_variable_name().unwrap() { + Ok(match inputs[0].get_string_value().unwrap() { "world" if inputs .last() @@ -244,32 +244,31 @@ fn test_custom_syntax_raw() -> Result<(), Box> { fn test_custom_syntax_raw2() -> Result<(), Box> { let mut engine = Engine::new(); - engine - .register_custom_operator("#", 255)? - .register_custom_syntax_raw( - "#", - |symbols, lookahead| match symbols.len() { - 1 if lookahead == "-" => Ok(Some("$symbol$".into())), - 1 => Ok(Some("$int$".into())), - 2 if symbols[1] == "-" => Ok(Some("$int$".into())), - 2 => Ok(None), - 3 => Ok(None), - _ => unreachable!(), - }, - false, - move |_, inputs| { - let id = if inputs.len() == 2 { - -inputs[1].get_literal_value::().unwrap() - } else { - inputs[0].get_literal_value::().unwrap() - }; - Ok(id.into()) - }, - ); + engine.register_custom_syntax_raw( + "#", + |symbols, lookahead| match symbols.len() { + 1 if lookahead == "-" => Ok(Some("$symbol$".into())), + 1 => Ok(Some("$int$".into())), + 2 if symbols[1] == "-" => Ok(Some("$int$".into())), + 2 => Ok(None), + 3 => Ok(None), + _ => unreachable!(), + }, + false, + move |_, inputs| { + let id = if inputs.len() == 2 { + -inputs[1].get_literal_value::().unwrap() + } else { + inputs[0].get_literal_value::().unwrap() + }; + Ok(id.into()) + }, + ); + assert_eq!(engine.eval::("#-1")?, -1); assert_eq!(engine.eval::("let x = 41; x + #1")?, 42); assert_eq!(engine.eval::("#42/2")?, 21); - assert_eq!(engine.eval::("#-1")?, -1); + assert_eq!(engine.eval::("sign(#1)")?, 1); Ok(()) } diff --git a/tests/tokens.rs b/tests/tokens.rs index 3da33e39..392f8953 100644 --- a/tests/tokens.rs +++ b/tests/tokens.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, LexError, ParseErrorType, INT}; +use rhai::{Engine, EvalAltResult, ParseErrorType, INT}; #[test] fn test_tokens_disabled() { @@ -26,7 +26,7 @@ fn test_tokens_disabled() { assert!(matches!( *engine.compile("let x = += 0;").expect_err("should error").0, - ParseErrorType::BadInput(LexError::UnexpectedInput(err)) if err == "+=" + ParseErrorType::Reserved(err) if err == "+=" )); } From 10fa6844c486ec447db700ae3db29704382c8e61 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 16 Dec 2021 22:40:10 +0800 Subject: [PATCH 08/97] Fix custom syntax with method calls. --- CHANGELOG.md | 2 +- src/ast.rs | 5 ++-- src/parser.rs | 56 +++++++++++++++++++++++++----------------- tests/custom_syntax.rs | 1 + 4 files changed, 37 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c60ac64..d7a27f27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ Version 1.3.1 Bug fixes --------- -* Custom syntax now works properly inside binary expressions. +* Custom syntax now works properly inside binary expressions and with method calls. Enhancements ------------ diff --git a/src/ast.rs b/src/ast.rs index 15b9ba10..b47829e3 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -2421,7 +2421,8 @@ impl Expr { | Self::Dot(_, _, _) | Self::Index(_, _, _) | Self::Array(_, _) - | Self::Map(_, _) => match token { + | Self::Map(_, _) + | Self::Custom(_, _) => match token { #[cfg(not(feature = "no_index"))] Token::LeftBracket => true, _ => false, @@ -2443,8 +2444,6 @@ impl Expr { _ => false, }, - Self::Custom(_, _) => false, - Self::Stack(_, _) => false, } } diff --git a/src/parser.rs b/src/parser.rs index 600f0090..b06a2995 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1135,7 +1135,7 @@ fn parse_primary( let mut settings = settings; settings.pos = *token_pos; - let mut root_expr = match token { + let root_expr = match token { Token::EOF => return Err(PERR::UnexpectedEOF.into_err(settings.pos)), Token::IntegerConstant(_) @@ -1294,6 +1294,20 @@ fn parse_primary( #[cfg(not(feature = "no_object"))] Token::MapStart => parse_map_literal(input, state, lib, settings.level_up())?, + // Custom syntax. + Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key) + if state.engine.custom_syntax.contains_key(&**key) => + { + let (key, syntax) = state + .engine + .custom_syntax + .get_key_value(&**key) + .expect("exists"); + let (_, pos) = input.next().expect(NEVER_ENDS); + let settings2 = settings.level_up(); + parse_custom_syntax(input, state, lib, settings2, key, syntax, pos)? + } + // Identifier Token::Identifier(_) => { let s = match input.next().expect(NEVER_ENDS) { @@ -1398,18 +1412,31 @@ fn parse_primary( } }; + parse_postfix(input, state, lib, root_expr, settings) +} + +/// Tail processing of all possible postfix operators of a primary expression. +fn parse_postfix( + input: &mut TokenStream, + state: &mut ParseState, + lib: &mut FunctionsLib, + mut lhs: Expr, + settings: ParseSettings, +) -> Result { + let mut settings = settings; + // Tail processing all possible postfix operators loop { let (tail_token, _) = input.peek().expect(NEVER_ENDS); - if !root_expr.is_valid_postfix(tail_token) { + if !lhs.is_valid_postfix(tail_token) { break; } let (tail_token, tail_pos) = input.next().expect(NEVER_ENDS); settings.pos = tail_pos; - root_expr = match (root_expr, tail_token) { + lhs = match (lhs, tail_token) { // Qualified function call with ! (Expr::Variable(_, _, x), Token::Bang) if x.1.is_some() => { return if !match_token(input, Token::LeftParen).0 { @@ -1503,7 +1530,7 @@ fn parse_primary( } // Cache the hash key for namespace-qualified variables - let namespaced_variable = match root_expr { + let namespaced_variable = match lhs { Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()), Expr::Index(ref mut x, _, _) | Expr::Dot(ref mut x, _, _) => match x.lhs { Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()), @@ -1536,7 +1563,7 @@ fn parse_primary( } // Make sure identifiers are valid - Ok(root_expr) + Ok(lhs) } /// Parse a potential unary operator. @@ -1550,26 +1577,9 @@ fn parse_unary( settings.ensure_level_within_max_limit(state.max_expr_depth)?; let (token, token_pos) = input.peek().expect(NEVER_ENDS); - let token_pos = *token_pos; let mut settings = settings; - settings.pos = token_pos; - - // Check if it is a custom syntax. - if !state.engine.custom_syntax.is_empty() { - match token { - Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key) => { - if let Some((key, syntax)) = state.engine.custom_syntax.get_key_value(key.as_ref()) - { - input.next().expect(NEVER_ENDS); - return parse_custom_syntax( - input, state, lib, settings, key, syntax, token_pos, - ); - } - } - _ => (), - } - } + settings.pos = *token_pos; match token { // -expr diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index cbc9962a..d2bb257a 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -268,6 +268,7 @@ fn test_custom_syntax_raw2() -> Result<(), Box> { ); assert_eq!(engine.eval::("let x = 41; x + #1")?, 42); + assert_eq!(engine.eval::("#-42.abs()")?, 42); assert_eq!(engine.eval::("#42/2")?, 21); assert_eq!(engine.eval::("#-1")?, -1); From c7ec27acc76cac01baedf16561043084952bbe2c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 17 Dec 2021 16:07:13 +0800 Subject: [PATCH 09/97] Split AST into separate files. --- src/api/mod.rs | 6 +- src/ast.rs | 2590 ----------------------------------------- src/ast/ast.rs | 861 ++++++++++++++ src/ast/expr.rs | 835 +++++++++++++ src/ast/flags.rs | 169 +++ src/ast/ident.rs | 37 + src/ast/mod.rs | 19 + src/ast/script_fn.rs | 128 ++ src/ast/stmt.rs | 625 ++++++++++ src/func/call.rs | 6 +- src/func/native.rs | 2 +- src/func/script.rs | 2 +- src/module/mod.rs | 4 +- src/serde/metadata.rs | 19 +- src/tokenizer.rs | 2 +- tests/call_fn.rs | 2 +- 16 files changed, 2693 insertions(+), 2614 deletions(-) delete mode 100644 src/ast.rs create mode 100644 src/ast/ast.rs create mode 100644 src/ast/expr.rs create mode 100644 src/ast/flags.rs create mode 100644 src/ast/ident.rs create mode 100644 src/ast/mod.rs create mode 100644 src/ast/script_fn.rs create mode 100644 src/ast/stmt.rs diff --git a/src/api/mod.rs b/src/api/mod.rs index a8a2a3c3..7accbaf6 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -83,17 +83,15 @@ impl Engine { .map(|f| { f.func .get_script_fn_def() - .expect("scripted function") + .expect("script-defined function") .clone() }) .collect(); - let statements = std::mem::take(ast.statements_mut()); - crate::optimizer::optimize_into_ast( self, scope, - statements, + ast.take_statements(), #[cfg(not(feature = "no_function"))] lib, optimization_level, diff --git a/src/ast.rs b/src/ast.rs deleted file mode 100644 index d0c7a68a..00000000 --- a/src/ast.rs +++ /dev/null @@ -1,2590 +0,0 @@ -//! Module defining the AST (abstract syntax tree). - -use crate::calc_fn_hash; -use crate::engine::{OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE}; -use crate::func::hashing::ALT_ZERO_HASH; -use crate::module::NamespaceRef; -use crate::tokenizer::Token; -use crate::types::dynamic::Union; -use crate::{ - Dynamic, FnNamespace, Identifier, ImmutableString, Module, Position, Shared, StaticVec, INT, -}; -#[cfg(feature = "no_std")] -use std::prelude::v1::*; -use std::{ - collections::BTreeMap, - fmt, - hash::Hash, - mem, - num::{NonZeroU8, NonZeroUsize}, - ops::{ - Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, DerefMut, Not, Sub, - SubAssign, - }, -}; - -#[cfg(not(feature = "no_float"))] -use std::str::FromStr; - -#[cfg(not(feature = "no_float"))] -use num_traits::Float; - -/// A type representing the access mode of a function. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub enum FnAccess { - /// Public function. - Public, - /// Private function. - Private, -} - -/// _(internals)_ A type containing information on a scripted function. -/// Exported under the `internals` feature only. -#[derive(Debug, Clone)] -pub struct ScriptFnDef { - /// Function body. - pub body: StmtBlock, - /// Encapsulated running environment, if any. - pub lib: Option>, - /// Encapsulated imported modules. - /// - /// Not available under `no_module`. - #[cfg(not(feature = "no_module"))] - pub mods: crate::engine::Imports, - /// Function name. - pub name: Identifier, - /// Function access mode. - pub access: FnAccess, - /// Names of function parameters. - pub params: StaticVec, - /// _(metadata)_ Function doc-comments (if any). - /// Exported under the `metadata` feature only. - #[cfg(feature = "metadata")] - pub comments: Option]>>, -} - -impl fmt::Display for ScriptFnDef { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}{}({})", - match self.access { - FnAccess::Public => "", - FnAccess::Private => "private ", - }, - self.name, - self.params - .iter() - .map(|s| s.as_str()) - .collect::>() - .join(", ") - ) - } -} - -/// A type containing the metadata of a script-defined function. -/// -/// Not available under `no_function`. -/// -/// Created by [`AST::iter_functions`]. -#[cfg(not(feature = "no_function"))] -#[derive(Debug, Eq, PartialEq, Clone, Hash)] -pub struct ScriptFnMetadata<'a> { - /// _(metadata)_ Function doc-comments (if any). - /// Exported under the `metadata` feature only. - /// - /// Block doc-comments are kept in a single string slice with line-breaks within. - /// - /// Line doc-comments are kept in one string slice per line without the termination line-break. - /// - /// Leading white-spaces are stripped, and each string slice always starts with the corresponding - /// doc-comment leader: `///` or `/**`. - #[cfg(feature = "metadata")] - pub comments: Vec<&'a str>, - /// Function access mode. - pub access: FnAccess, - /// Function name. - pub name: &'a str, - /// Function parameters (if any). - pub params: Vec<&'a str>, -} - -#[cfg(not(feature = "no_function"))] -impl fmt::Display for ScriptFnMetadata<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}{}({})", - match self.access { - FnAccess::Public => "", - FnAccess::Private => "private ", - }, - self.name, - self.params - .iter() - .cloned() - .collect::>() - .join(", ") - ) - } -} - -#[cfg(not(feature = "no_function"))] -impl<'a> From<&'a ScriptFnDef> for ScriptFnMetadata<'a> { - #[inline] - fn from(value: &'a ScriptFnDef) -> Self { - Self { - #[cfg(not(feature = "no_function"))] - #[cfg(feature = "metadata")] - comments: value - .comments - .as_ref() - .map_or_else(|| Vec::new(), |v| v.iter().map(Box::as_ref).collect()), - access: value.access, - name: &value.name, - params: value.params.iter().map(|s| s.as_str()).collect(), - } - } -} - -#[cfg(not(feature = "no_function"))] -impl std::cmp::PartialOrd for ScriptFnMetadata<'_> { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -#[cfg(not(feature = "no_function"))] -impl std::cmp::Ord for ScriptFnMetadata<'_> { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - match self.name.cmp(other.name) { - std::cmp::Ordering::Equal => self.params.len().cmp(&other.params.len()), - cmp => cmp, - } - } -} - -/// Compiled AST (abstract syntax tree) of a Rhai script. -/// -/// # Thread Safety -/// -/// Currently, [`AST`] is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. -#[derive(Debug, Clone)] -pub struct AST { - /// Source of the [`AST`]. - source: Option, - /// Global statements. - body: StmtBlock, - /// Script-defined functions. - #[cfg(not(feature = "no_function"))] - functions: Shared, - /// Embedded module resolver, if any. - #[cfg(not(feature = "no_module"))] - resolver: Option>, -} - -impl Default for AST { - #[inline(always)] - fn default() -> Self { - Self::empty() - } -} - -impl AST { - /// Create a new [`AST`]. - #[cfg(not(feature = "internals"))] - #[inline(always)] - #[must_use] - pub(crate) fn new( - statements: impl IntoIterator, - #[cfg(not(feature = "no_function"))] functions: impl Into>, - ) -> Self { - Self { - source: None, - body: StmtBlock::new(statements, Position::NONE), - #[cfg(not(feature = "no_function"))] - functions: functions.into(), - #[cfg(not(feature = "no_module"))] - resolver: None, - } - } - /// _(internals)_ Create a new [`AST`]. - /// Exported under the `internals` feature only. - #[cfg(feature = "internals")] - #[inline(always)] - #[must_use] - pub fn new( - statements: impl IntoIterator, - #[cfg(not(feature = "no_function"))] functions: impl Into>, - ) -> Self { - Self { - source: None, - body: StmtBlock::new(statements, Position::NONE), - #[cfg(not(feature = "no_function"))] - functions: functions.into(), - #[cfg(not(feature = "no_module"))] - resolver: None, - } - } - /// Create a new [`AST`] with a source name. - #[cfg(not(feature = "internals"))] - #[inline(always)] - #[must_use] - pub(crate) fn new_with_source( - statements: impl IntoIterator, - #[cfg(not(feature = "no_function"))] functions: impl Into>, - source: impl Into, - ) -> Self { - let mut ast = Self::new( - statements, - #[cfg(not(feature = "no_function"))] - functions, - ); - ast.set_source(source); - ast - } - /// _(internals)_ Create a new [`AST`] with a source name. - /// Exported under the `internals` feature only. - #[cfg(feature = "internals")] - #[inline(always)] - #[must_use] - pub fn new_with_source( - statements: impl IntoIterator, - #[cfg(not(feature = "no_function"))] functions: impl Into>, - source: impl Into, - ) -> Self { - let mut ast = Self::new( - statements, - #[cfg(not(feature = "no_function"))] - functions, - ); - ast.set_source(source); - ast - } - /// Create an empty [`AST`]. - #[inline] - #[must_use] - pub fn empty() -> Self { - Self { - source: None, - body: StmtBlock::NONE, - #[cfg(not(feature = "no_function"))] - functions: Module::new().into(), - #[cfg(not(feature = "no_module"))] - resolver: None, - } - } - /// Get the source, if any. - #[inline(always)] - #[must_use] - pub fn source(&self) -> Option<&str> { - self.source.as_ref().map(|s| s.as_str()) - } - /// Get a reference to the source. - #[inline(always)] - #[must_use] - pub(crate) fn source_raw(&self) -> Option<&Identifier> { - self.source.as_ref() - } - /// Set the source. - #[inline] - pub fn set_source(&mut self, source: impl Into) -> &mut Self { - let source = source.into(); - #[cfg(not(feature = "no_function"))] - Shared::get_mut(&mut self.functions) - .as_mut() - .map(|m| m.set_id(source.clone())); - self.source = Some(source); - self - } - /// Clear the source. - #[inline(always)] - pub fn clear_source(&mut self) -> &mut Self { - self.source = None; - self - } - /// Get the statements. - #[cfg(not(feature = "internals"))] - #[inline(always)] - #[must_use] - pub(crate) fn statements(&self) -> &[Stmt] { - &self.body.0 - } - /// _(internals)_ Get the statements. - /// Exported under the `internals` feature only. - #[cfg(feature = "internals")] - #[inline(always)] - #[must_use] - pub fn statements(&self) -> &[Stmt] { - &self.body.0 - } - /// Get a mutable reference to the statements. - #[allow(dead_code)] - #[inline(always)] - #[must_use] - pub(crate) fn statements_mut(&mut self) -> &mut StaticVec { - &mut self.body.0 - } - /// Does this [`AST`] contain script-defined functions? - /// - /// Not available under `no_function`. - #[cfg(not(feature = "no_function"))] - #[inline(always)] - #[must_use] - pub fn has_functions(&self) -> bool { - !self.functions.is_empty() - } - /// Get the internal shared [`Module`] containing all script-defined functions. - #[cfg(not(feature = "internals"))] - #[cfg(not(feature = "no_function"))] - #[inline(always)] - #[must_use] - pub(crate) fn shared_lib(&self) -> &Shared { - &self.functions - } - /// _(internals)_ Get the internal shared [`Module`] containing all script-defined functions. - /// Exported under the `internals` feature only. - /// - /// Not available under `no_function`. - #[cfg(feature = "internals")] - #[cfg(not(feature = "no_function"))] - #[inline(always)] - #[must_use] - pub fn shared_lib(&self) -> &Shared { - &self.functions - } - /// Get the embedded [module resolver][`ModuleResolver`]. - #[cfg(not(feature = "internals"))] - #[cfg(not(feature = "no_module"))] - #[inline(always)] - #[must_use] - pub(crate) fn resolver( - &self, - ) -> Option<&Shared> { - self.resolver.as_ref() - } - /// _(internals)_ Get the embedded [module resolver][crate::ModuleResolver]. - /// Exported under the `internals` feature only. - /// - /// Not available under `no_module`. - #[cfg(feature = "internals")] - #[cfg(not(feature = "no_module"))] - #[inline(always)] - #[must_use] - pub fn resolver(&self) -> Option<&Shared> { - self.resolver.as_ref() - } - /// Set the embedded [module resolver][`ModuleResolver`]. - #[cfg(not(feature = "no_module"))] - #[inline(always)] - pub(crate) fn set_resolver( - &mut self, - resolver: impl Into>, - ) -> &mut Self { - self.resolver = Some(resolver.into()); - self - } - /// Clone the [`AST`]'s functions into a new [`AST`]. - /// No statements are cloned. - /// - /// Not available under `no_function`. - /// - /// This operation is cheap because functions are shared. - #[cfg(not(feature = "no_function"))] - #[inline(always)] - #[must_use] - pub fn clone_functions_only(&self) -> Self { - self.clone_functions_only_filtered(|_, _, _, _, _| true) - } - /// Clone the [`AST`]'s functions into a new [`AST`] based on a filter predicate. - /// No statements are cloned. - /// - /// Not available under `no_function`. - /// - /// This operation is cheap because functions are shared. - #[cfg(not(feature = "no_function"))] - #[inline] - #[must_use] - pub fn clone_functions_only_filtered( - &self, - filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, - ) -> Self { - let mut functions = Module::new(); - functions.merge_filtered(&self.functions, &filter); - Self { - source: self.source.clone(), - body: StmtBlock::NONE, - functions: functions.into(), - #[cfg(not(feature = "no_module"))] - resolver: self.resolver.clone(), - } - } - /// Clone the [`AST`]'s script statements into a new [`AST`]. - /// No functions are cloned. - #[inline(always)] - #[must_use] - pub fn clone_statements_only(&self) -> Self { - Self { - source: self.source.clone(), - body: self.body.clone(), - #[cfg(not(feature = "no_function"))] - functions: Module::new().into(), - #[cfg(not(feature = "no_module"))] - resolver: self.resolver.clone(), - } - } - /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, - /// version is returned. - /// - /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. - /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. - /// Of course, if the first [`AST`] uses a `return` statement at the end, then - /// the second [`AST`] will essentially be dead code. - /// - /// All script-defined functions in the second [`AST`] overwrite similarly-named functions - /// in the first [`AST`] with the same number of parameters. - /// - /// # Example - /// - /// ``` - /// # fn main() -> Result<(), Box> { - /// # #[cfg(not(feature = "no_function"))] - /// # { - /// use rhai::Engine; - /// - /// let engine = Engine::new(); - /// - /// let ast1 = engine.compile(" - /// fn foo(x) { 42 + x } - /// foo(1) - /// ")?; - /// - /// let ast2 = engine.compile(r#" - /// fn foo(n) { `hello${n}` } - /// foo("!") - /// "#)?; - /// - /// let ast = ast1.merge(&ast2); // Merge 'ast2' into 'ast1' - /// - /// // Notice that using the '+' operator also works: - /// // let ast = &ast1 + &ast2; - /// - /// // 'ast' is essentially: - /// // - /// // fn foo(n) { `hello${n}` } // <- definition of first 'foo' is overwritten - /// // foo(1) // <- notice this will be "hello1" instead of 43, - /// // // but it is no longer the return value - /// // foo("!") // returns "hello!" - /// - /// // Evaluate it - /// assert_eq!(engine.eval_ast::(&ast)?, "hello!"); - /// # } - /// # Ok(()) - /// # } - /// ``` - #[inline(always)] - #[must_use] - pub fn merge(&self, other: &Self) -> Self { - self.merge_filtered_impl(other, |_, _, _, _, _| true) - } - /// Combine one [`AST`] with another. The second [`AST`] is consumed. - /// - /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. - /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. - /// Of course, if the first [`AST`] uses a `return` statement at the end, then - /// the second [`AST`] will essentially be dead code. - /// - /// All script-defined functions in the second [`AST`] overwrite similarly-named functions - /// in the first [`AST`] with the same number of parameters. - /// - /// # Example - /// - /// ``` - /// # fn main() -> Result<(), Box> { - /// # #[cfg(not(feature = "no_function"))] - /// # { - /// use rhai::Engine; - /// - /// let engine = Engine::new(); - /// - /// let mut ast1 = engine.compile(" - /// fn foo(x) { 42 + x } - /// foo(1) - /// ")?; - /// - /// let ast2 = engine.compile(r#" - /// fn foo(n) { `hello${n}` } - /// foo("!") - /// "#)?; - /// - /// ast1.combine(ast2); // Combine 'ast2' into 'ast1' - /// - /// // Notice that using the '+=' operator also works: - /// // ast1 += ast2; - /// - /// // 'ast1' is essentially: - /// // - /// // fn foo(n) { `hello${n}` } // <- definition of first 'foo' is overwritten - /// // foo(1) // <- notice this will be "hello1" instead of 43, - /// // // but it is no longer the return value - /// // foo("!") // returns "hello!" - /// - /// // Evaluate it - /// assert_eq!(engine.eval_ast::(&ast1)?, "hello!"); - /// # } - /// # Ok(()) - /// # } - /// ``` - #[inline(always)] - pub fn combine(&mut self, other: Self) -> &mut Self { - self.combine_filtered_impl(other, |_, _, _, _, _| true) - } - /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version - /// is returned. - /// - /// Not available under `no_function`. - /// - /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. - /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. - /// Of course, if the first [`AST`] uses a `return` statement at the end, then - /// the second [`AST`] will essentially be dead code. - /// - /// All script-defined functions in the second [`AST`] are first selected based on a filter - /// predicate, then overwrite similarly-named functions in the first [`AST`] with the - /// same number of parameters. - /// - /// # Example - /// - /// ``` - /// # fn main() -> Result<(), Box> { - /// use rhai::Engine; - /// - /// let engine = Engine::new(); - /// - /// let ast1 = engine.compile(" - /// fn foo(x) { 42 + x } - /// foo(1) - /// ")?; - /// - /// let ast2 = engine.compile(r#" - /// fn foo(n) { `hello${n}` } - /// fn error() { 0 } - /// foo("!") - /// "#)?; - /// - /// // Merge 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' - /// let ast = ast1.merge_filtered(&ast2, |_, _, script, name, params| - /// script && name == "error" && params == 0); - /// - /// // 'ast' is essentially: - /// // - /// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten - /// // // because 'ast2::foo' is filtered away - /// // foo(1) // <- notice this will be 43 instead of "hello1", - /// // // but it is no longer the return value - /// // fn error() { 0 } // <- this function passes the filter and is merged - /// // foo("!") // <- returns "42!" - /// - /// // Evaluate it - /// assert_eq!(engine.eval_ast::(&ast)?, "42!"); - /// # Ok(()) - /// # } - /// ``` - #[cfg(not(feature = "no_function"))] - #[inline(always)] - #[must_use] - pub fn merge_filtered( - &self, - other: &Self, - filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, - ) -> Self { - self.merge_filtered_impl(other, filter) - } - /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version - /// is returned. - #[inline] - #[must_use] - fn merge_filtered_impl( - &self, - other: &Self, - _filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, - ) -> Self { - let merged = match (self.body.is_empty(), other.body.is_empty()) { - (false, false) => { - let mut body = self.body.clone(); - body.0.extend(other.body.0.iter().cloned()); - body - } - (false, true) => self.body.clone(), - (true, false) => other.body.clone(), - (true, true) => StmtBlock::NONE, - }; - - let source = other.source.clone().or_else(|| self.source.clone()); - - #[cfg(not(feature = "no_function"))] - let functions = { - let mut functions = self.functions.as_ref().clone(); - functions.merge_filtered(&other.functions, &_filter); - functions - }; - - if let Some(source) = source { - Self::new_with_source( - merged.0, - #[cfg(not(feature = "no_function"))] - functions, - source, - ) - } else { - Self::new( - merged.0, - #[cfg(not(feature = "no_function"))] - functions, - ) - } - } - /// Combine one [`AST`] with another. The second [`AST`] is consumed. - /// - /// Not available under `no_function`. - /// - /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. - /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. - /// Of course, if the first [`AST`] uses a `return` statement at the end, then - /// the second [`AST`] will essentially be dead code. - /// - /// All script-defined functions in the second [`AST`] are first selected based on a filter - /// predicate, then overwrite similarly-named functions in the first [`AST`] with the - /// same number of parameters. - /// - /// # Example - /// - /// ``` - /// # fn main() -> Result<(), Box> { - /// use rhai::Engine; - /// - /// let engine = Engine::new(); - /// - /// let mut ast1 = engine.compile(" - /// fn foo(x) { 42 + x } - /// foo(1) - /// ")?; - /// - /// let ast2 = engine.compile(r#" - /// fn foo(n) { `hello${n}` } - /// fn error() { 0 } - /// foo("!") - /// "#)?; - /// - /// // Combine 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' - /// ast1.combine_filtered(ast2, |_, _, script, name, params| - /// script && name == "error" && params == 0); - /// - /// // 'ast1' is essentially: - /// // - /// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten - /// // // because 'ast2::foo' is filtered away - /// // foo(1) // <- notice this will be 43 instead of "hello1", - /// // // but it is no longer the return value - /// // fn error() { 0 } // <- this function passes the filter and is merged - /// // foo("!") // <- returns "42!" - /// - /// // Evaluate it - /// assert_eq!(engine.eval_ast::(&ast1)?, "42!"); - /// # Ok(()) - /// # } - /// ``` - #[cfg(not(feature = "no_function"))] - #[inline(always)] - pub fn combine_filtered( - &mut self, - other: Self, - filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, - ) -> &mut Self { - self.combine_filtered_impl(other, filter) - } - /// Combine one [`AST`] with another. The second [`AST`] is consumed. - #[inline] - fn combine_filtered_impl( - &mut self, - other: Self, - _filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, - ) -> &mut Self { - self.body.0.extend(other.body.0.into_iter()); - - #[cfg(not(feature = "no_function"))] - if !other.functions.is_empty() { - crate::func::native::shared_make_mut(&mut self.functions) - .merge_filtered(&other.functions, &_filter); - } - self - } - /// Filter out the functions, retaining only some based on a filter predicate. - /// - /// Not available under `no_function`. - /// - /// # Example - /// - /// ``` - /// # fn main() -> Result<(), Box> { - /// # #[cfg(not(feature = "no_function"))] - /// # { - /// use rhai::Engine; - /// - /// let engine = Engine::new(); - /// - /// let mut ast = engine.compile(r#" - /// fn foo(n) { n + 1 } - /// fn bar() { print("hello"); } - /// "#)?; - /// - /// // Remove all functions except 'foo(_)' - /// ast.retain_functions(|_, _, name, params| name == "foo" && params == 1); - /// # } - /// # Ok(()) - /// # } - /// ``` - #[cfg(not(feature = "no_function"))] - #[inline] - pub fn retain_functions( - &mut self, - filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool, - ) -> &mut Self { - if !self.functions.is_empty() { - crate::func::native::shared_make_mut(&mut self.functions) - .retain_script_functions(filter); - } - self - } - /// Iterate through all function definitions. - /// - /// Not available under `no_function`. - #[cfg(not(feature = "no_function"))] - #[allow(dead_code)] - #[inline] - pub(crate) fn iter_fn_def(&self) -> impl Iterator { - self.functions - .iter_script_fn() - .map(|(_, _, _, _, fn_def)| fn_def.as_ref()) - } - /// Iterate through all function definitions. - /// - /// Not available under `no_function`. - #[cfg(not(feature = "no_function"))] - #[inline] - pub fn iter_functions<'a>(&'a self) -> impl Iterator + 'a { - self.functions - .iter_script_fn() - .map(|(_, _, _, _, fn_def)| fn_def.as_ref().into()) - } - /// Clear all function definitions in the [`AST`]. - /// - /// Not available under `no_function`. - #[cfg(not(feature = "no_function"))] - #[inline(always)] - pub fn clear_functions(&mut self) -> &mut Self { - self.functions = Module::new().into(); - self - } - /// Clear all statements in the [`AST`], leaving only function definitions. - #[inline(always)] - pub fn clear_statements(&mut self) -> &mut Self { - self.body = StmtBlock::NONE; - self - } - /// Extract all top-level literal constant and/or variable definitions. - /// This is useful for extracting all global constants from a script without actually running it. - /// - /// A literal constant/variable definition takes the form of: - /// `const VAR = `_value_`;` and `let VAR = `_value_`;` - /// where _value_ is a literal expression or will be optimized into a literal. - /// - /// # Example - /// - /// ``` - /// # fn main() -> Result<(), Box> { - /// use rhai::{Engine, Scope}; - /// - /// let engine = Engine::new(); - /// - /// let ast = engine.compile( - /// " - /// const A = 40 + 2; // constant that optimizes into a literal - /// let b = 123; // literal variable - /// const B = b * A; // non-literal constant - /// const C = 999; // literal constant - /// b = A + C; // expression - /// - /// { // <- new block scope - /// const Z = 0; // <- literal constant not at top-level - /// } - /// ")?; - /// - /// let mut iter = ast.iter_literal_variables(true, false) - /// .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap())); - /// - /// # #[cfg(not(feature = "no_optimize"))] - /// assert_eq!(iter.next(), Some(("A", true, 42))); - /// assert_eq!(iter.next(), Some(("C", true, 999))); - /// assert_eq!(iter.next(), None); - /// - /// let mut iter = ast.iter_literal_variables(false, true) - /// .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap())); - /// - /// assert_eq!(iter.next(), Some(("b", false, 123))); - /// assert_eq!(iter.next(), None); - /// - /// let mut iter = ast.iter_literal_variables(true, true) - /// .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap())); - /// - /// # #[cfg(not(feature = "no_optimize"))] - /// assert_eq!(iter.next(), Some(("A", true, 42))); - /// assert_eq!(iter.next(), Some(("b", false, 123))); - /// assert_eq!(iter.next(), Some(("C", true, 999))); - /// assert_eq!(iter.next(), None); - /// - /// let scope: Scope = ast.iter_literal_variables(true, false).collect(); - /// - /// # #[cfg(not(feature = "no_optimize"))] - /// assert_eq!(scope.len(), 2); - /// - /// Ok(()) - /// # } - /// ``` - pub fn iter_literal_variables( - &self, - include_constants: bool, - include_variables: bool, - ) -> impl Iterator { - self.statements().iter().filter_map(move |stmt| match stmt { - Stmt::Var(expr, name, options, _) - if options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT) && include_constants - || !options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT) - && include_variables => - { - if let Some(value) = expr.get_literal_value() { - Some(( - name.as_str(), - options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT), - value, - )) - } else { - None - } - } - _ => None, - }) - } - /// Recursively walk the [`AST`], including function bodies (if any). - /// Return `false` from the callback to terminate the walk. - #[cfg(not(feature = "internals"))] - #[cfg(not(feature = "no_module"))] - #[inline] - pub(crate) fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool { - let path = &mut Vec::new(); - - for stmt in self.statements() { - if !stmt.walk(path, on_node) { - return false; - } - } - #[cfg(not(feature = "no_function"))] - for stmt in self.iter_fn_def().flat_map(|f| f.body.0.iter()) { - if !stmt.walk(path, on_node) { - return false; - } - } - - true - } - /// _(internals)_ Recursively walk the [`AST`], including function bodies (if any). - /// Return `false` from the callback to terminate the walk. - /// Exported under the `internals` feature only. - #[cfg(feature = "internals")] - #[inline] - pub fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool { - let path = &mut Vec::new(); - - for stmt in self.statements() { - if !stmt.walk(path, on_node) { - return false; - } - } - #[cfg(not(feature = "no_function"))] - for stmt in self.iter_fn_def().flat_map(|f| f.body.0.iter()) { - if !stmt.walk(path, on_node) { - return false; - } - } - - true - } -} - -impl> Add for &AST { - type Output = AST; - - #[inline(always)] - fn add(self, rhs: A) -> Self::Output { - self.merge(rhs.as_ref()) - } -} - -impl> AddAssign for AST { - #[inline(always)] - fn add_assign(&mut self, rhs: A) { - self.combine(rhs.into()); - } -} - -impl AsRef<[Stmt]> for AST { - #[inline(always)] - fn as_ref(&self) -> &[Stmt] { - self.statements() - } -} - -#[cfg(not(feature = "no_function"))] -impl AsRef for AST { - #[inline(always)] - fn as_ref(&self) -> &Module { - self.shared_lib().as_ref() - } -} - -#[cfg(not(feature = "no_function"))] -impl AsRef> for AST { - #[inline(always)] - fn as_ref(&self) -> &Shared { - self.shared_lib() - } -} - -/// _(internals)_ An identifier containing a name and a [position][Position]. -/// Exported under the `internals` feature only. -#[derive(Clone, Eq, PartialEq, Hash)] -pub struct Ident { - /// Identifier name. - pub name: Identifier, - /// Position. - pub pos: Position, -} - -impl fmt::Debug for Ident { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.name)?; - self.pos.debug_print(f) - } -} - -impl AsRef for Ident { - #[inline(always)] - fn as_ref(&self) -> &str { - self.name.as_ref() - } -} - -impl Ident { - #[inline(always)] - pub fn as_str(&self) -> &str { - self.name.as_str() - } -} - -/// _(internals)_ An [`AST`] node, consisting of either an [`Expr`] or a [`Stmt`]. -/// Exported under the `internals` feature only. -#[derive(Debug, Clone, Hash)] -pub enum ASTNode<'a> { - /// A statement ([`Stmt`]). - Stmt(&'a Stmt), - /// An expression ([`Expr`]). - Expr(&'a Expr), -} - -impl<'a> From<&'a Stmt> for ASTNode<'a> { - fn from(stmt: &'a Stmt) -> Self { - Self::Stmt(stmt) - } -} - -impl<'a> From<&'a Expr> for ASTNode<'a> { - fn from(expr: &'a Expr) -> Self { - Self::Expr(expr) - } -} - -impl ASTNode<'_> { - /// Get the [`Position`] of this [`ASTNode`]. - pub const fn position(&self) -> Position { - match self { - ASTNode::Stmt(stmt) => stmt.position(), - ASTNode::Expr(expr) => expr.position(), - } - } -} - -/// _(internals)_ A scoped block of statements. -/// Exported under the `internals` feature only. -#[derive(Clone, Hash, Default)] -pub struct StmtBlock(StaticVec, Position); - -impl StmtBlock { - /// A [`StmtBlock`] that does not exist. - pub const NONE: Self = Self::empty(Position::NONE); - - /// Create a new [`StmtBlock`]. - #[must_use] - pub fn new(statements: impl IntoIterator, pos: Position) -> Self { - let mut statements: StaticVec<_> = statements.into_iter().collect(); - statements.shrink_to_fit(); - Self(statements, pos) - } - /// Create an empty [`StmtBlock`]. - #[inline(always)] - #[must_use] - pub const fn empty(pos: Position) -> Self { - Self(StaticVec::new_const(), pos) - } - /// Is this statements block empty? - #[inline(always)] - #[must_use] - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - /// Number of statements in this statements block. - #[inline(always)] - #[must_use] - pub fn len(&self) -> usize { - self.0.len() - } - /// Get the statements of this statements block. - #[inline(always)] - #[must_use] - pub fn statements(&self) -> &[Stmt] { - &self.0 - } - /// Get the position (location of the beginning `{`) of this statements block. - #[inline(always)] - #[must_use] - pub const fn position(&self) -> Position { - self.1 - } -} - -impl Deref for StmtBlock { - type Target = StaticVec; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for StmtBlock { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl fmt::Debug for StmtBlock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Block")?; - fmt::Debug::fmt(&self.0, f)?; - self.1.debug_print(f) - } -} - -impl From for Stmt { - #[inline] - fn from(block: StmtBlock) -> Self { - let block_pos = block.position(); - Self::Block(block.0.into_boxed_slice(), block_pos) - } -} - -/// A type that holds a configuration option with bit-flags. -/// Exported under the `internals` feature only. -#[derive(PartialEq, Eq, Copy, Clone, Hash, Default)] -pub struct OptionFlags(u8); - -impl OptionFlags { - /// Does this [`OptionFlags`] contain a particular option flag? - #[inline(always)] - #[must_use] - pub const fn contains(self, flag: Self) -> bool { - self.0 & flag.0 != 0 - } -} - -impl Not for OptionFlags { - type Output = Self; - - /// Return the negation of the [`OptionFlags`]. - #[inline(always)] - fn not(self) -> Self::Output { - Self(!self.0) & AST_OPTION_FLAGS::AST_OPTION_ALL - } -} - -impl Add for OptionFlags { - type Output = Self; - - /// Return the union of two [`OptionFlags`]. - #[inline(always)] - fn add(self, rhs: Self) -> Self::Output { - Self(self.0 | rhs.0) - } -} - -impl AddAssign for OptionFlags { - /// Add the option flags in one [`OptionFlags`] to another. - #[inline(always)] - fn add_assign(&mut self, rhs: Self) { - self.0 |= rhs.0 - } -} - -impl BitOr for OptionFlags { - type Output = Self; - - /// Return the union of two [`OptionFlags`]. - #[inline(always)] - fn bitor(self, rhs: Self) -> Self::Output { - Self(self.0 | rhs.0) - } -} - -impl BitOrAssign for OptionFlags { - /// Add the option flags in one [`OptionFlags`] to another. - #[inline(always)] - fn bitor_assign(&mut self, rhs: Self) { - self.0 |= rhs.0 - } -} - -impl Sub for OptionFlags { - type Output = Self; - - /// Return the difference of two [`OptionFlags`]. - #[inline(always)] - fn sub(self, rhs: Self) -> Self::Output { - Self(self.0 & !rhs.0) - } -} - -impl SubAssign for OptionFlags { - /// Remove the option flags in one [`OptionFlags`] from another. - #[inline(always)] - fn sub_assign(&mut self, rhs: Self) { - self.0 &= !rhs.0 - } -} - -impl BitAnd for OptionFlags { - type Output = Self; - - /// Return the intersection of two [`OptionFlags`]. - #[inline(always)] - fn bitand(self, rhs: Self) -> Self::Output { - Self(self.0 & !rhs.0) - } -} - -impl BitAndAssign for OptionFlags { - /// Keep only the intersection of one [`OptionFlags`] with another. - #[inline(always)] - fn bitand_assign(&mut self, rhs: Self) { - self.0 &= !rhs.0 - } -} - -/// Option bit-flags for [`AST`] nodes. -#[allow(non_snake_case)] -pub mod AST_OPTION_FLAGS { - use super::OptionFlags; - - /// _(internals)_ No options for the [`AST`][crate::AST] node. - /// Exported under the `internals` feature only. - pub const AST_OPTION_NONE: OptionFlags = OptionFlags(0b0000_0000); - /// _(internals)_ The [`AST`][crate::AST] node is constant. - /// Exported under the `internals` feature only. - pub const AST_OPTION_CONSTANT: OptionFlags = OptionFlags(0b0000_0001); - /// _(internals)_ The [`AST`][crate::AST] node is public. - /// Exported under the `internals` feature only. - pub const AST_OPTION_PUBLIC: OptionFlags = OptionFlags(0b0000_0010); - /// _(internals)_ The [`AST`][crate::AST] node is in negated mode. - /// Exported under the `internals` feature only. - pub const AST_OPTION_NEGATED: OptionFlags = OptionFlags(0b0000_0100); - /// _(internals)_ The [`AST`][crate::AST] node breaks out of normal control flow. - /// Exported under the `internals` feature only. - pub const AST_OPTION_BREAK_OUT: OptionFlags = OptionFlags(0b0000_1000); - /// _(internals)_ Mask of all options. - /// Exported under the `internals` feature only. - pub(crate) const AST_OPTION_ALL: OptionFlags = OptionFlags( - AST_OPTION_CONSTANT.0 | AST_OPTION_PUBLIC.0 | AST_OPTION_NEGATED.0 | AST_OPTION_BREAK_OUT.0, - ); - - impl std::fmt::Debug for OptionFlags { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - fn write_option( - options: &OptionFlags, - f: &mut std::fmt::Formatter<'_>, - num_flags: &mut usize, - flag: OptionFlags, - name: &str, - ) -> std::fmt::Result { - if options.contains(flag) { - if *num_flags > 0 { - f.write_str("+")?; - } - f.write_str(name)?; - *num_flags += 1; - } - Ok(()) - } - - let num_flags = &mut 0; - - f.write_str("(")?; - write_option(self, f, num_flags, AST_OPTION_CONSTANT, "Constant")?; - write_option(self, f, num_flags, AST_OPTION_PUBLIC, "Public")?; - write_option(self, f, num_flags, AST_OPTION_NEGATED, "Negated")?; - write_option(self, f, num_flags, AST_OPTION_BREAK_OUT, "Break")?; - f.write_str(")")?; - - Ok(()) - } - } -} - -/// _(internals)_ A statement. -/// Exported under the `internals` feature only. -#[derive(Debug, Clone, Hash)] -pub enum Stmt { - /// No-op. - Noop(Position), - /// `if` expr `{` stmt `}` `else` `{` stmt `}` - If(Expr, Box<(StmtBlock, StmtBlock)>, Position), - /// `switch` expr `if` condition `{` literal or _ `=>` stmt `,` ... `}` - Switch( - Expr, - Box<( - BTreeMap, StmtBlock)>>, - StmtBlock, - StaticVec<(INT, INT, bool, Option, StmtBlock)>, - )>, - Position, - ), - /// `while` expr `{` stmt `}` | `loop` `{` stmt `}` - /// - /// If the guard expression is [`UNIT`][Expr::Unit], then it is a `loop` statement. - While(Expr, Box, Position), - /// `do` `{` stmt `}` `while`|`until` expr - /// - /// ### Option Flags - /// - /// * [`AST_OPTION_NONE`][AST_OPTION_FLAGS::AST_OPTION_NONE] = `while` - /// * [`AST_OPTION_NEGATED`][AST_OPTION_FLAGS::AST_OPTION_NEGATED] = `until` - Do(Box, Expr, OptionFlags, Position), - /// `for` `(` id `,` counter `)` `in` expr `{` stmt `}` - For(Expr, Box<(Ident, Option, StmtBlock)>, Position), - /// \[`export`\] `let`|`const` id `=` expr - /// - /// ### Option Flags - /// - /// * [`AST_OPTION_PUBLIC`][AST_OPTION_FLAGS::AST_OPTION_PUBLIC] = `export` - /// * [`AST_OPTION_CONSTANT`][AST_OPTION_FLAGS::AST_OPTION_CONSTANT] = `const` - Var(Expr, Box, OptionFlags, Position), - /// expr op`=` expr - Assignment(Box<(Expr, Option>, Expr)>, Position), - /// func `(` expr `,` ... `)` - /// - /// Note - this is a duplicate of [`Expr::FnCall`] to cover the very common pattern of a single - /// function call forming one statement. - FnCall(Box, Position), - /// `{` stmt`;` ... `}` - Block(Box<[Stmt]>, Position), - /// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}` - TryCatch(Box<(StmtBlock, Option, StmtBlock)>, Position), - /// [expression][Expr] - Expr(Expr), - /// `continue`/`break` - /// - /// ### Option Flags - /// - /// * [`AST_OPTION_NONE`][AST_OPTION_FLAGS::AST_OPTION_NONE] = `continue` - /// * [`AST_OPTION_BREAK_OUT`][AST_OPTION_FLAGS::AST_OPTION_BREAK_OUT] = `break` - BreakLoop(OptionFlags, Position), - /// `return`/`throw` - /// - /// ### Option Flags - /// - /// * [`AST_OPTION_NONE`][AST_OPTION_FLAGS::AST_OPTION_NONE] = `return` - /// * [`AST_OPTION_BREAK_OUT`][AST_OPTION_FLAGS::AST_OPTION_BREAK_OUT] = `throw` - Return(OptionFlags, Option, Position), - /// `import` expr `as` var - /// - /// Not available under `no_module`. - #[cfg(not(feature = "no_module"))] - Import(Expr, Option>, Position), - /// `export` var `as` var `,` ... - /// - /// Not available under `no_module`. - #[cfg(not(feature = "no_module"))] - Export(Box<[(Ident, Ident)]>, Position), - /// Convert a variable to shared. - /// - /// Not available under `no_closure`. - /// - /// # Notes - /// - /// This variant does not map to any language structure. It is currently only used only to - /// convert a normal variable into a shared variable when the variable is _captured_ by a closure. - #[cfg(not(feature = "no_closure"))] - Share(Identifier), -} - -impl Default for Stmt { - #[inline(always)] - fn default() -> Self { - Self::Noop(Position::NONE) - } -} - -impl From for StmtBlock { - #[inline] - fn from(stmt: Stmt) -> Self { - match stmt { - Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos), - Stmt::Noop(pos) => Self(StaticVec::new_const(), pos), - _ => { - let pos = stmt.position(); - Self(vec![stmt].into(), pos) - } - } - } -} - -impl Stmt { - /// Is this statement [`Noop`][Stmt::Noop]? - #[inline(always)] - #[must_use] - pub const fn is_noop(&self) -> bool { - matches!(self, Self::Noop(_)) - } - /// Get the [position][Position] of this statement. - #[must_use] - pub const fn position(&self) -> Position { - match self { - Self::Noop(pos) - | Self::BreakLoop(_, pos) - | Self::Block(_, pos) - | Self::Assignment(_, pos) - | Self::FnCall(_, pos) - | Self::If(_, _, pos) - | Self::Switch(_, _, pos) - | Self::While(_, _, pos) - | Self::Do(_, _, _, pos) - | Self::For(_, _, pos) - | Self::Return(_, _, pos) - | Self::Var(_, _, _, pos) - | Self::TryCatch(_, pos) => *pos, - - Self::Expr(x) => x.position(), - - #[cfg(not(feature = "no_module"))] - Self::Import(_, _, pos) => *pos, - #[cfg(not(feature = "no_module"))] - Self::Export(_, pos) => *pos, - - #[cfg(not(feature = "no_closure"))] - Self::Share(_) => Position::NONE, - } - } - /// Override the [position][Position] of this statement. - pub fn set_position(&mut self, new_pos: Position) -> &mut Self { - match self { - Self::Noop(pos) - | Self::BreakLoop(_, pos) - | Self::Block(_, pos) - | Self::Assignment(_, pos) - | Self::FnCall(_, pos) - | Self::If(_, _, pos) - | Self::Switch(_, _, pos) - | Self::While(_, _, pos) - | Self::Do(_, _, _, pos) - | Self::For(_, _, pos) - | Self::Return(_, _, pos) - | Self::Var(_, _, _, pos) - | Self::TryCatch(_, pos) => *pos = new_pos, - - Self::Expr(x) => { - x.set_position(new_pos); - } - - #[cfg(not(feature = "no_module"))] - Self::Import(_, _, pos) => *pos = new_pos, - #[cfg(not(feature = "no_module"))] - Self::Export(_, pos) => *pos = new_pos, - - #[cfg(not(feature = "no_closure"))] - Self::Share(_) => (), - } - - self - } - /// Does this statement return a value? - #[must_use] - pub const fn returns_value(&self) -> bool { - match self { - Self::If(_, _, _) - | Self::Switch(_, _, _) - | Self::Block(_, _) - | Self::Expr(_) - | Self::FnCall(_, _) => true, - - Self::Noop(_) - | Self::While(_, _, _) - | Self::Do(_, _, _, _) - | Self::For(_, _, _) - | Self::TryCatch(_, _) => false, - - Self::Var(_, _, _, _) - | Self::Assignment(_, _) - | Self::BreakLoop(_, _) - | Self::Return(_, _, _) => false, - - #[cfg(not(feature = "no_module"))] - Self::Import(_, _, _) | Self::Export(_, _) => false, - - #[cfg(not(feature = "no_closure"))] - Self::Share(_) => false, - } - } - /// Is this statement self-terminated (i.e. no need for a semicolon terminator)? - #[must_use] - pub const fn is_self_terminated(&self) -> bool { - match self { - Self::If(_, _, _) - | Self::Switch(_, _, _) - | Self::While(_, _, _) - | Self::For(_, _, _) - | Self::Block(_, _) - | Self::TryCatch(_, _) => true, - - // A No-op requires a semicolon in order to know it is an empty statement! - Self::Noop(_) => false, - - Self::Expr(Expr::Custom(x, _)) if x.is_self_terminated() => true, - - Self::Var(_, _, _, _) - | Self::Assignment(_, _) - | Self::Expr(_) - | Self::FnCall(_, _) - | Self::Do(_, _, _, _) - | Self::BreakLoop(_, _) - | Self::Return(_, _, _) => false, - - #[cfg(not(feature = "no_module"))] - Self::Import(_, _, _) | Self::Export(_, _) => false, - - #[cfg(not(feature = "no_closure"))] - Self::Share(_) => false, - } - } - /// Is this statement _pure_? - /// - /// A pure statement has no side effects. - #[must_use] - pub fn is_pure(&self) -> bool { - match self { - Self::Noop(_) => true, - Self::Expr(expr) => expr.is_pure(), - Self::If(condition, x, _) => { - condition.is_pure() - && (x.0).0.iter().all(Stmt::is_pure) - && (x.1).0.iter().all(Stmt::is_pure) - } - Self::Switch(expr, x, _) => { - expr.is_pure() - && x.0.values().all(|block| { - block.0.as_ref().map(Expr::is_pure).unwrap_or(true) - && (block.1).0.iter().all(Stmt::is_pure) - }) - && (x.2).iter().all(|(_, _, _, condition, stmt)| { - condition.as_ref().map(Expr::is_pure).unwrap_or(true) - && stmt.0.iter().all(Stmt::is_pure) - }) - && (x.1).0.iter().all(Stmt::is_pure) - } - - // Loops that exit can be pure because it can never be infinite. - Self::While(Expr::BoolConstant(false, _), _, _) => true, - Self::Do(body, Expr::BoolConstant(x, _), options, _) - if *x == options.contains(AST_OPTION_FLAGS::AST_OPTION_NEGATED) => - { - body.iter().all(Stmt::is_pure) - } - - // Loops are never pure since they can be infinite - and that's a side effect. - Self::While(_, _, _) | Self::Do(_, _, _, _) => false, - - // For loops can be pure because if the iterable is pure, it is finite, - // so infinite loops can never occur. - Self::For(iterable, x, _) => iterable.is_pure() && (x.2).0.iter().all(Stmt::is_pure), - - Self::Var(_, _, _, _) | Self::Assignment(_, _) | Self::FnCall(_, _) => false, - Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), - Self::BreakLoop(_, _) | Self::Return(_, _, _) => false, - Self::TryCatch(x, _) => { - (x.0).0.iter().all(Stmt::is_pure) && (x.2).0.iter().all(Stmt::is_pure) - } - - #[cfg(not(feature = "no_module"))] - Self::Import(_, _, _) => false, - #[cfg(not(feature = "no_module"))] - Self::Export(_, _) => false, - - #[cfg(not(feature = "no_closure"))] - Self::Share(_) => false, - } - } - /// Is this statement _pure_ within the containing block? - /// - /// An internally pure statement only has side effects that disappear outside the block. - /// - /// Currently only variable definitions (i.e. `let` and `const`) and `import`/`export` - /// statements are internally pure. - #[inline] - #[must_use] - pub fn is_internally_pure(&self) -> bool { - match self { - Self::Var(expr, _, _, _) => expr.is_pure(), - - #[cfg(not(feature = "no_module"))] - Self::Import(expr, _, _) => expr.is_pure(), - #[cfg(not(feature = "no_module"))] - Self::Export(_, _) => true, - - _ => self.is_pure(), - } - } - /// Does this statement break the current control flow through the containing block? - /// - /// Currently this is only true for `return`, `throw`, `break` and `continue`. - /// - /// All statements following this statement will essentially be dead code. - #[inline] - #[must_use] - pub const fn is_control_flow_break(&self) -> bool { - match self { - Self::Return(_, _, _) | Self::BreakLoop(_, _) => true, - _ => false, - } - } - /// Recursively walk this statement. - /// Return `false` from the callback to terminate the walk. - pub fn walk<'a>( - &'a self, - path: &mut Vec>, - on_node: &mut impl FnMut(&[ASTNode]) -> bool, - ) -> bool { - // Push the current node onto the path - path.push(self.into()); - - if !on_node(path) { - return false; - } - - match self { - Self::Var(e, _, _, _) => { - if !e.walk(path, on_node) { - return false; - } - } - Self::If(e, x, _) => { - if !e.walk(path, on_node) { - return false; - } - for s in &(x.0).0 { - if !s.walk(path, on_node) { - return false; - } - } - for s in &(x.1).0 { - if !s.walk(path, on_node) { - return false; - } - } - } - Self::Switch(e, x, _) => { - if !e.walk(path, on_node) { - return false; - } - for b in x.0.values() { - if !b.0.as_ref().map(|e| e.walk(path, on_node)).unwrap_or(true) { - return false; - } - for s in &(b.1).0 { - if !s.walk(path, on_node) { - return false; - } - } - } - for (_, _, _, c, stmt) in &x.2 { - if !c.as_ref().map(|e| e.walk(path, on_node)).unwrap_or(true) { - return false; - } - for s in &stmt.0 { - if !s.walk(path, on_node) { - return false; - } - } - } - for s in &(x.1).0 { - if !s.walk(path, on_node) { - return false; - } - } - } - Self::While(e, s, _) | Self::Do(s, e, _, _) => { - if !e.walk(path, on_node) { - return false; - } - for s in &s.0 { - if !s.walk(path, on_node) { - return false; - } - } - } - Self::For(e, x, _) => { - if !e.walk(path, on_node) { - return false; - } - for s in &(x.2).0 { - if !s.walk(path, on_node) { - return false; - } - } - } - Self::Assignment(x, _) => { - if !x.0.walk(path, on_node) { - return false; - } - if !x.2.walk(path, on_node) { - return false; - } - } - Self::FnCall(x, _) => { - for s in &x.args { - if !s.walk(path, on_node) { - return false; - } - } - } - Self::Block(x, _) => { - for s in x.iter() { - if !s.walk(path, on_node) { - return false; - } - } - } - Self::TryCatch(x, _) => { - for s in &(x.0).0 { - if !s.walk(path, on_node) { - return false; - } - } - for s in &(x.2).0 { - if !s.walk(path, on_node) { - return false; - } - } - } - Self::Expr(e) | Self::Return(_, Some(e), _) => { - if !e.walk(path, on_node) { - return false; - } - } - #[cfg(not(feature = "no_module"))] - Self::Import(e, _, _) => { - if !e.walk(path, on_node) { - return false; - } - } - _ => (), - } - - path.pop().expect("contains current node"); - - true - } -} - -/// _(internals)_ A custom syntax expression. -/// Exported under the `internals` feature only. -#[derive(Debug, Clone, Hash)] -pub struct CustomExpr { - /// List of keywords. - pub inputs: StaticVec, - /// List of tokens actually parsed. - pub tokens: StaticVec, - /// Is the current [`Scope`][crate::Scope] possibly modified by this custom statement - /// (e.g. introducing a new variable)? - pub scope_may_be_changed: bool, - /// Is this custom syntax self-terminated? - pub self_terminated: bool, -} - -impl CustomExpr { - /// Is this custom syntax self-terminated (i.e. no need for a semicolon terminator)? - /// - /// A self-terminated custom syntax always ends in `$block$`, `}` or `;` - #[must_use] - #[inline(always)] - pub const fn is_self_terminated(&self) -> bool { - self.self_terminated - } -} - -/// _(internals)_ A binary expression. -/// Exported under the `internals` feature only. -#[derive(Debug, Clone, Hash)] -pub struct BinaryExpr { - /// LHS expression. - pub lhs: Expr, - /// RHS expression. - pub rhs: Expr, -} - -/// _(internals)_ An op-assignment operator. -/// Exported under the `internals` feature only. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub struct OpAssignment<'a> { - /// Hash of the op-assignment call. - pub hash_op_assign: u64, - /// Hash of the underlying operator call (for fallback). - pub hash_op: u64, - /// Op-assignment operator. - pub op: &'a str, -} - -impl OpAssignment<'_> { - /// Create a new [`OpAssignment`]. - /// - /// # Panics - /// - /// Panics if the operator name is not an op-assignment operator. - #[must_use] - pub fn new(op: Token) -> Self { - let op_raw = op - .map_op_assignment() - .expect("op-assignment") - .literal_syntax(); - let op_assignment = op.literal_syntax(); - - Self { - hash_op_assign: calc_fn_hash(op_assignment, 2), - hash_op: calc_fn_hash(op_raw, 2), - op: op_assignment, - } - } -} - -/// _(internals)_ A set of function call hashes. Exported under the `internals` feature only. -/// -/// Two separate hashes are pre-calculated because of the following patterns: -/// -/// ```ignore -/// func(a, b, c); // Native: func(a, b, c) - 3 parameters -/// // Script: func(a, b, c) - 3 parameters -/// -/// a.func(b, c); // Native: func(&mut a, b, c) - 3 parameters -/// // Script: func(b, c) - 2 parameters -/// ``` -/// -/// For normal function calls, the native hash equals the script hash. -/// -/// For method-style calls, the script hash contains one fewer parameter. -/// -/// Function call hashes are used in the following manner: -/// -/// * First, the script hash is tried, which contains only the called function's name plus the -/// number of parameters. -/// -/// * Next, the actual types of arguments are hashed and _combined_ with the native hash, which is -/// then used to search for a native function. In other words, a complete native function call -/// hash always contains the called function's name plus the types of the arguments. This is due -/// to possible function overloading for different parameter types. -#[derive(Clone, Copy, Eq, PartialEq, Hash, Default)] -pub struct FnCallHashes { - /// Pre-calculated hash for a script-defined function (zero if native functions only). - #[cfg(not(feature = "no_function"))] - pub script: u64, - /// Pre-calculated hash for a native Rust function with no parameter types. - pub native: u64, -} - -impl fmt::Debug for FnCallHashes { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[cfg(not(feature = "no_function"))] - if self.script != 0 { - return if self.script == self.native { - fmt::Debug::fmt(&self.native, f) - } else { - write!(f, "({}, {})", self.script, self.native) - }; - } - - write!(f, "{} (native only)", self.native) - } -} - -impl From for FnCallHashes { - #[inline(always)] - fn from(hash: u64) -> Self { - let hash = if hash == 0 { ALT_ZERO_HASH } else { hash }; - - Self { - #[cfg(not(feature = "no_function"))] - script: hash, - native: hash, - } - } -} - -impl FnCallHashes { - /// Create a [`FnCallHashes`] with only the native Rust hash. - #[inline(always)] - #[must_use] - pub const fn from_native(hash: u64) -> Self { - Self { - #[cfg(not(feature = "no_function"))] - script: 0, - native: if hash == 0 { ALT_ZERO_HASH } else { hash }, - } - } - /// Create a [`FnCallHashes`] with both native Rust and script function hashes. - #[inline(always)] - #[must_use] - pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self { - Self { - #[cfg(not(feature = "no_function"))] - script: if script == 0 { ALT_ZERO_HASH } else { script }, - native: if native == 0 { ALT_ZERO_HASH } else { native }, - } - } - /// Is this [`FnCallHashes`] native Rust only? - #[inline(always)] - #[must_use] - pub const fn is_native_only(&self) -> bool { - #[cfg(not(feature = "no_function"))] - return self.script == 0; - - #[cfg(feature = "no_function")] - return true; - } -} - -/// _(internals)_ A function call. -/// Exported under the `internals` feature only. -#[derive(Debug, Clone, Default, Hash)] -pub struct FnCallExpr { - /// Namespace of the function, if any. - pub namespace: Option, - /// Function name. - pub name: Identifier, - /// Pre-calculated hashes. - pub hashes: FnCallHashes, - /// List of function call argument expressions. - pub args: StaticVec, - /// List of function call arguments that are constants. - /// - /// Any arguments in `args` that is [`Expr::Stack`] indexes into this - /// array to find the constant for use as its argument value. - /// - /// # Notes - /// - /// Constant arguments are very common in function calls, and keeping each constant in - /// an [`Expr::DynamicConstant`] involves an additional allocation. Keeping the constant - /// values in an inlined array avoids these extra allocations. - pub constants: StaticVec, - /// Does this function call capture the parent scope? - pub capture_parent_scope: bool, -} - -impl FnCallExpr { - /// Does this function call contain a qualified namespace? - #[inline(always)] - #[must_use] - pub const fn is_qualified(&self) -> bool { - self.namespace.is_some() - } - /// Convert this into an [`Expr::FnCall`]. - #[inline(always)] - #[must_use] - pub fn into_fn_call_expr(self, pos: Position) -> Expr { - Expr::FnCall(self.into(), pos) - } -} - -/// A type that wraps a floating-point number and implements [`Hash`]. -/// -/// Not available under `no_float`. -#[cfg(not(feature = "no_float"))] -#[derive(Clone, Copy, PartialEq, PartialOrd)] -pub struct FloatWrapper(F); - -#[cfg(not(feature = "no_float"))] -impl Hash for FloatWrapper { - #[inline(always)] - fn hash(&self, state: &mut H) { - self.0.to_ne_bytes().hash(state); - } -} - -#[cfg(not(feature = "no_float"))] -impl AsRef for FloatWrapper { - #[inline(always)] - fn as_ref(&self) -> &F { - &self.0 - } -} - -#[cfg(not(feature = "no_float"))] -impl AsMut for FloatWrapper { - #[inline(always)] - fn as_mut(&mut self) -> &mut F { - &mut self.0 - } -} - -#[cfg(not(feature = "no_float"))] -impl std::ops::Deref for FloatWrapper { - type Target = F; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -#[cfg(not(feature = "no_float"))] -impl std::ops::DerefMut for FloatWrapper { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -#[cfg(not(feature = "no_float"))] -impl fmt::Debug for FloatWrapper { - #[inline(always)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } -} - -#[cfg(not(feature = "no_float"))] -impl> fmt::Display for FloatWrapper { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let abs = self.0.abs(); - if abs.is_zero() { - f.write_str("0.0") - } else if abs > Self::MAX_NATURAL_FLOAT_FOR_DISPLAY.into() - || abs < Self::MIN_NATURAL_FLOAT_FOR_DISPLAY.into() - { - write!(f, "{:e}", self.0) - } else { - fmt::Display::fmt(&self.0, f)?; - if abs.fract().is_zero() { - f.write_str(".0")?; - } - Ok(()) - } - } -} - -#[cfg(not(feature = "no_float"))] -impl From for FloatWrapper { - #[inline(always)] - fn from(value: F) -> Self { - Self::new(value) - } -} - -#[cfg(not(feature = "no_float"))] -impl FromStr for FloatWrapper { - type Err = ::Err; - - #[inline] - fn from_str(s: &str) -> Result { - F::from_str(s).map(Into::::into) - } -} - -#[cfg(not(feature = "no_float"))] -impl FloatWrapper { - /// Maximum floating-point number for natural display before switching to scientific notation. - pub const MAX_NATURAL_FLOAT_FOR_DISPLAY: f32 = 10000000000000.0; - - /// Minimum floating-point number for natural display before switching to scientific notation. - pub const MIN_NATURAL_FLOAT_FOR_DISPLAY: f32 = 0.0000000000001; - - /// Create a new [`FloatWrapper`]. - #[inline(always)] - #[must_use] - pub fn new(value: F) -> Self { - Self(value) - } -} - -#[cfg(not(feature = "no_float"))] -impl FloatWrapper { - /// Create a new [`FloatWrapper`]. - #[inline(always)] - #[must_use] - pub const fn new_const(value: crate::FLOAT) -> Self { - Self(value) - } -} - -/// _(internals)_ An expression sub-tree. -/// Exported under the `internals` feature only. -#[derive(Clone, Hash)] -pub enum Expr { - /// Dynamic constant. - /// - /// Used to hold complex constants such as [`Array`][crate::Array] or [`Map`][crate::Map] for quick cloning. - /// Primitive data types should use the appropriate variants to avoid an allocation. - DynamicConstant(Box, Position), - /// Boolean constant. - BoolConstant(bool, Position), - /// Integer constant. - IntegerConstant(INT, Position), - /// Floating-point constant. - /// - /// Not available under `no_float`. - #[cfg(not(feature = "no_float"))] - FloatConstant(FloatWrapper, Position), - /// Character constant. - CharConstant(char, Position), - /// [String][ImmutableString] constant. - StringConstant(ImmutableString, Position), - /// An interpolated [string][ImmutableString]. - InterpolatedString(Box>, Position), - /// [ expr, ... ] - Array(Box>, Position), - /// #{ name:expr, ... } - Map( - Box<(StaticVec<(Ident, Expr)>, BTreeMap)>, - Position, - ), - /// () - Unit(Position), - /// Variable access - optional short index, position, (optional index, optional (hash, modules), variable name) - /// - /// The short index is [`u8`] which is used when the index is <= 255, which should be the vast - /// majority of cases (unless there are more than 255 variables defined!). - /// This is to avoid reading a pointer redirection during each variable access. - Variable( - Option, - Position, - Box<( - Option, - Option<(NamespaceRef, u64)>, - Identifier, - )>, - ), - /// Property access - ((getter, hash), (setter, hash), prop) - Property( - Box<( - (Identifier, u64), - (Identifier, u64), - (ImmutableString, Position), - )>, - ), - /// Stack slot for function calls. See [`FnCallExpr`] for more details. - /// - /// This variant does not map to any language structure. It is used in function calls with - /// constant arguments where the `usize` number indexes into an array containing a list of - /// constant arguments for the function call. - Stack(usize, Position), - /// { [statement][Stmt] ... } - Stmt(Box), - /// func `(` expr `,` ... `)` - FnCall(Box, Position), - /// lhs `.` rhs - bool variable is a dummy - Dot(Box, bool, Position), - /// expr `[` expr `]` - boolean indicates whether the dotting/indexing chain stops - Index(Box, bool, Position), - /// lhs `&&` rhs - And(Box, Position), - /// lhs `||` rhs - Or(Box, Position), - /// Custom syntax - Custom(Box, Position), -} - -impl Default for Expr { - #[inline(always)] - fn default() -> Self { - Self::Unit(Position::NONE) - } -} - -impl fmt::Debug for Expr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut display_pos = self.position(); - - match self { - Self::DynamicConstant(value, _) => write!(f, "{:?}", value), - Self::BoolConstant(value, _) => write!(f, "{:?}", value), - Self::IntegerConstant(value, _) => write!(f, "{:?}", value), - #[cfg(not(feature = "no_float"))] - Self::FloatConstant(value, _) => write!(f, "{:?}", value), - Self::CharConstant(value, _) => write!(f, "{:?}", value), - Self::StringConstant(value, _) => write!(f, "{:?}", value), - Self::Unit(_) => f.write_str("()"), - - Self::InterpolatedString(x, _) => { - f.write_str("InterpolatedString")?; - return f.debug_list().entries(x.iter()).finish(); - } - Self::Array(x, _) => { - f.write_str("Array")?; - f.debug_list().entries(x.iter()).finish() - } - Self::Map(x, _) => { - f.write_str("Map")?; - f.debug_map() - .entries(x.0.iter().map(|(k, v)| (k, v))) - .finish() - } - Self::Variable(i, _, x) => { - f.write_str("Variable(")?; - if let Some((_, ref namespace)) = x.1 { - write!(f, "{}{}", namespace, Token::DoubleColon.literal_syntax())? - } - f.write_str(&x.2)?; - if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) { - write!(f, " #{}", n)? - } - f.write_str(")") - } - Self::Property(x) => write!(f, "Property({})", (x.2).0), - Self::Stack(x, _) => write!(f, "StackSlot({})", x), - Self::Stmt(x) => { - f.write_str("ExprStmtBlock")?; - f.debug_list().entries(x.0.iter()).finish() - } - Self::FnCall(x, _) => { - let mut ff = f.debug_struct("FnCall"); - x.namespace.as_ref().map(|ns| ff.field("namespace", ns)); - ff.field("name", &x.name) - .field("hash", &x.hashes) - .field("args", &x.args); - if !x.constants.is_empty() { - ff.field("constants", &x.constants); - } - if x.capture_parent_scope { - ff.field("capture_parent_scope", &x.capture_parent_scope); - } - ff.finish() - } - Self::Index(x, term, pos) => { - display_pos = *pos; - - f.debug_struct("Index") - .field("lhs", &x.lhs) - .field("rhs", &x.rhs) - .field("terminate", term) - .finish() - } - Self::Dot(x, _, pos) | Self::And(x, pos) | Self::Or(x, pos) => { - let op_name = match self { - Self::Dot(_, _, _) => "Dot", - Self::And(_, _) => "And", - Self::Or(_, _) => "Or", - _ => unreachable!(), - }; - - display_pos = *pos; - - f.debug_struct(op_name) - .field("lhs", &x.lhs) - .field("rhs", &x.rhs) - .finish() - } - Self::Custom(x, _) => f.debug_tuple("Custom").field(x).finish(), - }?; - - display_pos.debug_print(f) - } -} - -impl Expr { - /// Get the [`Dynamic`] value of a literal constant expression. - /// - /// Returns [`None`] if the expression is not a literal constant. - #[inline] - #[must_use] - pub fn get_literal_value(&self) -> Option { - Some(match self { - Self::DynamicConstant(x, _) => x.as_ref().clone(), - Self::IntegerConstant(x, _) => (*x).into(), - #[cfg(not(feature = "no_float"))] - Self::FloatConstant(x, _) => (*x).into(), - Self::CharConstant(x, _) => (*x).into(), - Self::StringConstant(x, _) => x.clone().into(), - Self::BoolConstant(x, _) => (*x).into(), - Self::Unit(_) => Dynamic::UNIT, - - #[cfg(not(feature = "no_index"))] - Self::Array(x, _) if self.is_constant() => { - let mut arr = crate::Array::with_capacity(x.len()); - arr.extend( - x.iter() - .map(|v| v.get_literal_value().expect("constant value")), - ); - Dynamic::from_array(arr) - } - - #[cfg(not(feature = "no_object"))] - Self::Map(x, _) if self.is_constant() => { - Dynamic::from_map(x.0.iter().fold(x.1.clone(), |mut map, (k, v)| { - let value_ref = map.get_mut(k.name.as_str()).expect("contains all keys"); - *value_ref = v.get_literal_value().expect("constant value"); - map - })) - } - - // Binary operators - Self::FnCall(x, _) if x.args.len() == 2 => match x.name.as_str() { - // x..y - OP_EXCLUSIVE_RANGE => { - if let Expr::IntegerConstant(ref start, _) = x.args[0] { - if let Expr::IntegerConstant(ref end, _) = x.args[1] { - (*start..*end).into() - } else { - return None; - } - } else { - return None; - } - } - // x..=y - OP_INCLUSIVE_RANGE => { - if let Expr::IntegerConstant(ref start, _) = x.args[0] { - if let Expr::IntegerConstant(ref end, _) = x.args[1] { - (*start..=*end).into() - } else { - return None; - } - } else { - return None; - } - } - _ => return None, - }, - - _ => return None, - }) - } - /// Create an [`Expr`] from a [`Dynamic`] value. - #[inline] - #[must_use] - pub fn from_dynamic(value: Dynamic, pos: Position) -> Self { - match value.0 { - Union::Unit(_, _, _) => Self::Unit(pos), - Union::Bool(b, _, _) => Self::BoolConstant(b, pos), - Union::Str(s, _, _) => Self::StringConstant(s, pos), - Union::Char(c, _, _) => Self::CharConstant(c, pos), - Union::Int(i, _, _) => Self::IntegerConstant(i, pos), - - #[cfg(feature = "decimal")] - Union::Decimal(value, _, _) => Self::DynamicConstant(Box::new((*value).into()), pos), - - #[cfg(not(feature = "no_float"))] - Union::Float(f, _, _) => Self::FloatConstant(f, pos), - - #[cfg(not(feature = "no_index"))] - Union::Array(a, _, _) => Self::DynamicConstant(Box::new((*a).into()), pos), - - #[cfg(not(feature = "no_object"))] - Union::Map(m, _, _) => Self::DynamicConstant(Box::new((*m).into()), pos), - - _ => Self::DynamicConstant(value.into(), pos), - } - } - /// Is the expression a simple variable access? - #[inline] - #[must_use] - pub(crate) const fn is_variable_access(&self, non_qualified: bool) -> bool { - match self { - Self::Variable(_, _, x) => !non_qualified || x.1.is_none(), - _ => false, - } - } - /// Return the variable name if the expression a simple variable access. - #[inline] - #[must_use] - pub(crate) fn get_variable_name(&self, non_qualified: bool) -> Option<&str> { - match self { - Self::Variable(_, _, x) if !non_qualified || x.1.is_none() => Some(x.2.as_str()), - _ => None, - } - } - /// Get the [position][Position] of the expression. - #[inline] - #[must_use] - pub const fn position(&self) -> Position { - match self { - #[cfg(not(feature = "no_float"))] - Self::FloatConstant(_, pos) => *pos, - - Self::DynamicConstant(_, pos) - | Self::BoolConstant(_, pos) - | Self::IntegerConstant(_, pos) - | Self::CharConstant(_, pos) - | Self::Unit(pos) - | Self::StringConstant(_, pos) - | Self::Array(_, pos) - | Self::Map(_, pos) - | Self::Variable(_, pos, _) - | Self::Stack(_, pos) - | Self::FnCall(_, pos) - | Self::Custom(_, pos) - | Self::InterpolatedString(_, pos) => *pos, - - Self::Property(x) => (x.2).1, - Self::Stmt(x) => x.1, - - Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _, _) | Self::Index(x, _, _) => { - x.lhs.position() - } - } - } - /// Override the [position][Position] of the expression. - #[inline] - pub fn set_position(&mut self, new_pos: Position) -> &mut Self { - match self { - #[cfg(not(feature = "no_float"))] - Self::FloatConstant(_, pos) => *pos = new_pos, - - Self::DynamicConstant(_, pos) - | Self::BoolConstant(_, pos) - | Self::IntegerConstant(_, pos) - | Self::CharConstant(_, pos) - | Self::Unit(pos) - | Self::StringConstant(_, pos) - | Self::Array(_, pos) - | Self::Map(_, pos) - | Self::And(_, pos) - | Self::Or(_, pos) - | Self::Dot(_, _, pos) - | Self::Index(_, _, pos) - | Self::Variable(_, pos, _) - | Self::Stack(_, pos) - | Self::FnCall(_, pos) - | Self::Custom(_, pos) - | Self::InterpolatedString(_, pos) => *pos = new_pos, - - Self::Property(x) => (x.2).1 = new_pos, - Self::Stmt(x) => x.1 = new_pos, - } - - self - } - /// Is the expression pure? - /// - /// A pure expression has no side effects. - #[inline] - #[must_use] - pub fn is_pure(&self) -> bool { - match self { - Self::InterpolatedString(x, _) | Self::Array(x, _) => x.iter().all(Self::is_pure), - - Self::Map(x, _) => x.0.iter().map(|(_, v)| v).all(Self::is_pure), - - Self::And(x, _) | Self::Or(x, _) => x.lhs.is_pure() && x.rhs.is_pure(), - - Self::Stmt(x) => x.0.iter().all(Stmt::is_pure), - - Self::Variable(_, _, _) | Self::Stack(_, _) => true, - - _ => self.is_constant(), - } - } - /// Is the expression the unit `()` literal? - #[inline(always)] - #[must_use] - pub const fn is_unit(&self) -> bool { - matches!(self, Self::Unit(_)) - } - /// Is the expression a constant? - #[inline] - #[must_use] - pub fn is_constant(&self) -> bool { - match self { - #[cfg(not(feature = "no_float"))] - Self::FloatConstant(_, _) => true, - - Self::DynamicConstant(_, _) - | Self::BoolConstant(_, _) - | Self::IntegerConstant(_, _) - | Self::CharConstant(_, _) - | Self::StringConstant(_, _) - | Self::Unit(_) - | Self::Stack(_, _) => true, - - Self::InterpolatedString(x, _) | Self::Array(x, _) => x.iter().all(Self::is_constant), - - Self::Map(x, _) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant), - - _ => false, - } - } - /// Is a particular [token][Token] allowed as a postfix operator to this expression? - #[inline] - #[must_use] - pub const fn is_valid_postfix(&self, token: &Token) -> bool { - match token { - #[cfg(not(feature = "no_object"))] - Token::Period => return true, - _ => (), - } - - match self { - #[cfg(not(feature = "no_float"))] - Self::FloatConstant(_, _) => false, - - Self::DynamicConstant(_, _) - | Self::BoolConstant(_, _) - | Self::CharConstant(_, _) - | Self::And(_, _) - | Self::Or(_, _) - | Self::Unit(_) => false, - - Self::IntegerConstant(_, _) - | Self::StringConstant(_, _) - | Self::InterpolatedString(_, _) - | Self::FnCall(_, _) - | Self::Stmt(_) - | Self::Dot(_, _, _) - | Self::Index(_, _, _) - | Self::Array(_, _) - | Self::Map(_, _) - | Self::Custom(_, _) => match token { - #[cfg(not(feature = "no_index"))] - Token::LeftBracket => true, - _ => false, - }, - - Self::Variable(_, _, _) => match token { - #[cfg(not(feature = "no_index"))] - Token::LeftBracket => true, - Token::LeftParen => true, - Token::Bang => true, - Token::DoubleColon => true, - _ => false, - }, - - Self::Property(_) => match token { - #[cfg(not(feature = "no_index"))] - Token::LeftBracket => true, - Token::LeftParen => true, - _ => false, - }, - - Self::Stack(_, _) => false, - } - } - /// Recursively walk this expression. - /// Return `false` from the callback to terminate the walk. - pub fn walk<'a>( - &'a self, - path: &mut Vec>, - on_node: &mut impl FnMut(&[ASTNode]) -> bool, - ) -> bool { - // Push the current node onto the path - path.push(self.into()); - - if !on_node(path) { - return false; - } - - match self { - Self::Stmt(x) => { - for s in &x.0 { - if !s.walk(path, on_node) { - return false; - } - } - } - Self::InterpolatedString(x, _) | Self::Array(x, _) => { - for e in x.as_ref() { - if !e.walk(path, on_node) { - return false; - } - } - } - Self::Map(x, _) => { - for (_, e) in &x.0 { - if !e.walk(path, on_node) { - return false; - } - } - } - Self::Index(x, _, _) | Self::Dot(x, _, _) | Expr::And(x, _) | Expr::Or(x, _) => { - if !x.lhs.walk(path, on_node) { - return false; - } - if !x.rhs.walk(path, on_node) { - return false; - } - } - Self::FnCall(x, _) => { - for e in &x.args { - if !e.walk(path, on_node) { - return false; - } - } - } - Self::Custom(x, _) => { - for e in &x.inputs { - if !e.walk(path, on_node) { - return false; - } - } - } - _ => (), - } - - path.pop().expect("contains current node"); - - true - } -} - -impl AST { - /// _(internals)_ Get the internal [`Module`] containing all script-defined functions. - /// Exported under the `internals` feature only. - /// - /// Not available under `no_function`. - /// - /// # Deprecated - /// - /// This method is deprecated. Use [`shared_lib`][AST::shared_lib] instead. - /// - /// This method will be removed in the next major version. - #[deprecated(since = "1.3.0", note = "use `shared_lib` instead")] - #[cfg(feature = "internals")] - #[cfg(not(feature = "no_function"))] - #[inline(always)] - #[must_use] - pub fn lib(&self) -> &crate::Module { - &self.functions - } -} diff --git a/src/ast/ast.rs b/src/ast/ast.rs new file mode 100644 index 00000000..82ca8087 --- /dev/null +++ b/src/ast/ast.rs @@ -0,0 +1,861 @@ +//! Module defining the AST (abstract syntax tree). + +use super::{Expr, FnAccess, Stmt, StmtBlock, AST_OPTION_FLAGS}; +use crate::{Dynamic, FnNamespace, Identifier, Position, StaticVec}; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; +use std::{ + hash::Hash, + ops::{Add, AddAssign}, +}; + +/// Compiled AST (abstract syntax tree) of a Rhai script. +/// +/// # Thread Safety +/// +/// Currently, [`AST`] is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. +#[derive(Debug, Clone)] +pub struct AST { + /// Source of the [`AST`]. + source: Option, + /// Global statements. + body: StmtBlock, + /// Script-defined functions. + #[cfg(not(feature = "no_function"))] + functions: crate::Shared, + /// Embedded module resolver, if any. + #[cfg(not(feature = "no_module"))] + resolver: Option>, +} + +impl Default for AST { + #[inline(always)] + fn default() -> Self { + Self::empty() + } +} + +impl AST { + /// Create a new [`AST`]. + #[cfg(not(feature = "internals"))] + #[inline(always)] + #[must_use] + pub(crate) fn new( + statements: impl IntoIterator, + #[cfg(not(feature = "no_function"))] functions: impl Into>, + ) -> Self { + Self { + source: None, + body: StmtBlock::new(statements, Position::NONE), + #[cfg(not(feature = "no_function"))] + functions: functions.into(), + #[cfg(not(feature = "no_module"))] + resolver: None, + } + } + /// _(internals)_ Create a new [`AST`]. + /// Exported under the `internals` feature only. + #[cfg(feature = "internals")] + #[inline(always)] + #[must_use] + pub fn new( + statements: impl IntoIterator, + #[cfg(not(feature = "no_function"))] functions: impl Into>, + ) -> Self { + Self { + source: None, + body: StmtBlock::new(statements, Position::NONE), + #[cfg(not(feature = "no_function"))] + functions: functions.into(), + #[cfg(not(feature = "no_module"))] + resolver: None, + } + } + /// Create a new [`AST`] with a source name. + #[cfg(not(feature = "internals"))] + #[inline(always)] + #[must_use] + pub(crate) fn new_with_source( + statements: impl IntoIterator, + #[cfg(not(feature = "no_function"))] functions: impl Into>, + source: impl Into, + ) -> Self { + let mut ast = Self::new( + statements, + #[cfg(not(feature = "no_function"))] + functions, + ); + ast.set_source(source); + ast + } + /// _(internals)_ Create a new [`AST`] with a source name. + /// Exported under the `internals` feature only. + #[cfg(feature = "internals")] + #[inline(always)] + #[must_use] + pub fn new_with_source( + statements: impl IntoIterator, + #[cfg(not(feature = "no_function"))] functions: impl Into>, + source: impl Into, + ) -> Self { + let mut ast = Self::new( + statements, + #[cfg(not(feature = "no_function"))] + functions, + ); + ast.set_source(source); + ast + } + /// Create an empty [`AST`]. + #[inline] + #[must_use] + pub fn empty() -> Self { + Self { + source: None, + body: StmtBlock::NONE, + #[cfg(not(feature = "no_function"))] + functions: crate::Module::new().into(), + #[cfg(not(feature = "no_module"))] + resolver: None, + } + } + /// Get the source, if any. + #[inline(always)] + #[must_use] + pub fn source(&self) -> Option<&str> { + self.source.as_ref().map(|s| s.as_str()) + } + /// Get a reference to the source. + #[inline(always)] + #[must_use] + pub(crate) fn source_raw(&self) -> Option<&Identifier> { + self.source.as_ref() + } + /// Set the source. + #[inline] + pub fn set_source(&mut self, source: impl Into) -> &mut Self { + let source = source.into(); + #[cfg(not(feature = "no_function"))] + crate::Shared::get_mut(&mut self.functions) + .as_mut() + .map(|m| m.set_id(source.clone())); + self.source = Some(source); + self + } + /// Clear the source. + #[inline(always)] + pub fn clear_source(&mut self) -> &mut Self { + self.source = None; + self + } + /// Get the statements. + #[cfg(not(feature = "internals"))] + #[inline(always)] + #[must_use] + pub(crate) fn statements(&self) -> &[Stmt] { + self.body.statements() + } + /// _(internals)_ Get the statements. + /// Exported under the `internals` feature only. + #[cfg(feature = "internals")] + #[inline(always)] + #[must_use] + pub fn statements(&self) -> &[Stmt] { + self.body.statements() + } + /// Extract the statements. + #[allow(dead_code)] + #[inline(always)] + #[must_use] + pub(crate) fn take_statements(&mut self) -> StaticVec { + self.body.take_statements() + } + /// Does this [`AST`] contain script-defined functions? + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] + #[inline(always)] + #[must_use] + pub fn has_functions(&self) -> bool { + !self.functions.is_empty() + } + /// Get the internal shared [`Module`][crate::Module] containing all script-defined functions. + #[cfg(not(feature = "internals"))] + #[cfg(not(feature = "no_function"))] + #[inline(always)] + #[must_use] + pub(crate) fn shared_lib(&self) -> &crate::Shared { + &self.functions + } + /// _(internals)_ Get the internal shared [`Module`][crate::Module] containing all script-defined functions. + /// Exported under the `internals` feature only. + /// + /// Not available under `no_function`. + #[cfg(feature = "internals")] + #[cfg(not(feature = "no_function"))] + #[inline(always)] + #[must_use] + pub fn shared_lib(&self) -> &crate::Shared { + &self.functions + } + /// Get the embedded [module resolver][`ModuleResolver`]. + #[cfg(not(feature = "internals"))] + #[cfg(not(feature = "no_module"))] + #[inline(always)] + #[must_use] + pub(crate) fn resolver( + &self, + ) -> Option<&crate::Shared> { + self.resolver.as_ref() + } + /// _(internals)_ Get the embedded [module resolver][crate::ModuleResolver]. + /// Exported under the `internals` feature only. + /// + /// Not available under `no_module`. + #[cfg(feature = "internals")] + #[cfg(not(feature = "no_module"))] + #[inline(always)] + #[must_use] + pub fn resolver( + &self, + ) -> Option<&crate::Shared> { + self.resolver.as_ref() + } + /// Set the embedded [module resolver][`ModuleResolver`]. + #[cfg(not(feature = "no_module"))] + #[inline(always)] + pub(crate) fn set_resolver( + &mut self, + resolver: impl Into>, + ) -> &mut Self { + self.resolver = Some(resolver.into()); + self + } + /// Clone the [`AST`]'s functions into a new [`AST`]. + /// No statements are cloned. + /// + /// Not available under `no_function`. + /// + /// This operation is cheap because functions are shared. + #[cfg(not(feature = "no_function"))] + #[inline(always)] + #[must_use] + pub fn clone_functions_only(&self) -> Self { + self.clone_functions_only_filtered(|_, _, _, _, _| true) + } + /// Clone the [`AST`]'s functions into a new [`AST`] based on a filter predicate. + /// No statements are cloned. + /// + /// Not available under `no_function`. + /// + /// This operation is cheap because functions are shared. + #[cfg(not(feature = "no_function"))] + #[inline] + #[must_use] + pub fn clone_functions_only_filtered( + &self, + filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, + ) -> Self { + let mut functions = crate::Module::new(); + functions.merge_filtered(&self.functions, &filter); + Self { + source: self.source.clone(), + body: StmtBlock::NONE, + functions: functions.into(), + #[cfg(not(feature = "no_module"))] + resolver: self.resolver.clone(), + } + } + /// Clone the [`AST`]'s script statements into a new [`AST`]. + /// No functions are cloned. + #[inline(always)] + #[must_use] + pub fn clone_statements_only(&self) -> Self { + Self { + source: self.source.clone(), + body: self.body.clone(), + #[cfg(not(feature = "no_function"))] + functions: crate::Module::new().into(), + #[cfg(not(feature = "no_module"))] + resolver: self.resolver.clone(), + } + } + /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, + /// version is returned. + /// + /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. + /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. + /// Of course, if the first [`AST`] uses a `return` statement at the end, then + /// the second [`AST`] will essentially be dead code. + /// + /// All script-defined functions in the second [`AST`] overwrite similarly-named functions + /// in the first [`AST`] with the same number of parameters. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// # #[cfg(not(feature = "no_function"))] + /// # { + /// use rhai::Engine; + /// + /// let engine = Engine::new(); + /// + /// let ast1 = engine.compile(" + /// fn foo(x) { 42 + x } + /// foo(1) + /// ")?; + /// + /// let ast2 = engine.compile(r#" + /// fn foo(n) { `hello${n}` } + /// foo("!") + /// "#)?; + /// + /// let ast = ast1.merge(&ast2); // Merge 'ast2' into 'ast1' + /// + /// // Notice that using the '+' operator also works: + /// // let ast = &ast1 + &ast2; + /// + /// // 'ast' is essentially: + /// // + /// // fn foo(n) { `hello${n}` } // <- definition of first 'foo' is overwritten + /// // foo(1) // <- notice this will be "hello1" instead of 43, + /// // // but it is no longer the return value + /// // foo("!") // returns "hello!" + /// + /// // Evaluate it + /// assert_eq!(engine.eval_ast::(&ast)?, "hello!"); + /// # } + /// # Ok(()) + /// # } + /// ``` + #[inline(always)] + #[must_use] + pub fn merge(&self, other: &Self) -> Self { + self.merge_filtered_impl(other, |_, _, _, _, _| true) + } + /// Combine one [`AST`] with another. The second [`AST`] is consumed. + /// + /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. + /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. + /// Of course, if the first [`AST`] uses a `return` statement at the end, then + /// the second [`AST`] will essentially be dead code. + /// + /// All script-defined functions in the second [`AST`] overwrite similarly-named functions + /// in the first [`AST`] with the same number of parameters. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// # #[cfg(not(feature = "no_function"))] + /// # { + /// use rhai::Engine; + /// + /// let engine = Engine::new(); + /// + /// let mut ast1 = engine.compile(" + /// fn foo(x) { 42 + x } + /// foo(1) + /// ")?; + /// + /// let ast2 = engine.compile(r#" + /// fn foo(n) { `hello${n}` } + /// foo("!") + /// "#)?; + /// + /// ast1.combine(ast2); // Combine 'ast2' into 'ast1' + /// + /// // Notice that using the '+=' operator also works: + /// // ast1 += ast2; + /// + /// // 'ast1' is essentially: + /// // + /// // fn foo(n) { `hello${n}` } // <- definition of first 'foo' is overwritten + /// // foo(1) // <- notice this will be "hello1" instead of 43, + /// // // but it is no longer the return value + /// // foo("!") // returns "hello!" + /// + /// // Evaluate it + /// assert_eq!(engine.eval_ast::(&ast1)?, "hello!"); + /// # } + /// # Ok(()) + /// # } + /// ``` + #[inline(always)] + pub fn combine(&mut self, other: Self) -> &mut Self { + self.combine_filtered_impl(other, |_, _, _, _, _| true) + } + /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version + /// is returned. + /// + /// Not available under `no_function`. + /// + /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. + /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. + /// Of course, if the first [`AST`] uses a `return` statement at the end, then + /// the second [`AST`] will essentially be dead code. + /// + /// All script-defined functions in the second [`AST`] are first selected based on a filter + /// predicate, then overwrite similarly-named functions in the first [`AST`] with the + /// same number of parameters. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// let engine = Engine::new(); + /// + /// let ast1 = engine.compile(" + /// fn foo(x) { 42 + x } + /// foo(1) + /// ")?; + /// + /// let ast2 = engine.compile(r#" + /// fn foo(n) { `hello${n}` } + /// fn error() { 0 } + /// foo("!") + /// "#)?; + /// + /// // Merge 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' + /// let ast = ast1.merge_filtered(&ast2, |_, _, script, name, params| + /// script && name == "error" && params == 0); + /// + /// // 'ast' is essentially: + /// // + /// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten + /// // // because 'ast2::foo' is filtered away + /// // foo(1) // <- notice this will be 43 instead of "hello1", + /// // // but it is no longer the return value + /// // fn error() { 0 } // <- this function passes the filter and is merged + /// // foo("!") // <- returns "42!" + /// + /// // Evaluate it + /// assert_eq!(engine.eval_ast::(&ast)?, "42!"); + /// # Ok(()) + /// # } + /// ``` + #[cfg(not(feature = "no_function"))] + #[inline(always)] + #[must_use] + pub fn merge_filtered( + &self, + other: &Self, + filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, + ) -> Self { + self.merge_filtered_impl(other, filter) + } + /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version + /// is returned. + #[inline] + #[must_use] + fn merge_filtered_impl( + &self, + other: &Self, + _filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, + ) -> Self { + let merged = match (self.body.is_empty(), other.body.is_empty()) { + (false, false) => { + let mut body = self.body.clone(); + body.extend(other.body.iter().cloned()); + body + } + (false, true) => self.body.clone(), + (true, false) => other.body.clone(), + (true, true) => StmtBlock::NONE, + }; + + let source = other.source.clone().or_else(|| self.source.clone()); + + #[cfg(not(feature = "no_function"))] + let functions = { + let mut functions = self.functions.as_ref().clone(); + functions.merge_filtered(&other.functions, &_filter); + functions + }; + + if let Some(source) = source { + Self::new_with_source( + merged, + #[cfg(not(feature = "no_function"))] + functions, + source, + ) + } else { + Self::new( + merged, + #[cfg(not(feature = "no_function"))] + functions, + ) + } + } + /// Combine one [`AST`] with another. The second [`AST`] is consumed. + /// + /// Not available under `no_function`. + /// + /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. + /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. + /// Of course, if the first [`AST`] uses a `return` statement at the end, then + /// the second [`AST`] will essentially be dead code. + /// + /// All script-defined functions in the second [`AST`] are first selected based on a filter + /// predicate, then overwrite similarly-named functions in the first [`AST`] with the + /// same number of parameters. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// let engine = Engine::new(); + /// + /// let mut ast1 = engine.compile(" + /// fn foo(x) { 42 + x } + /// foo(1) + /// ")?; + /// + /// let ast2 = engine.compile(r#" + /// fn foo(n) { `hello${n}` } + /// fn error() { 0 } + /// foo("!") + /// "#)?; + /// + /// // Combine 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' + /// ast1.combine_filtered(ast2, |_, _, script, name, params| + /// script && name == "error" && params == 0); + /// + /// // 'ast1' is essentially: + /// // + /// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten + /// // // because 'ast2::foo' is filtered away + /// // foo(1) // <- notice this will be 43 instead of "hello1", + /// // // but it is no longer the return value + /// // fn error() { 0 } // <- this function passes the filter and is merged + /// // foo("!") // <- returns "42!" + /// + /// // Evaluate it + /// assert_eq!(engine.eval_ast::(&ast1)?, "42!"); + /// # Ok(()) + /// # } + /// ``` + #[cfg(not(feature = "no_function"))] + #[inline(always)] + pub fn combine_filtered( + &mut self, + other: Self, + filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, + ) -> &mut Self { + self.combine_filtered_impl(other, filter) + } + /// Combine one [`AST`] with another. The second [`AST`] is consumed. + #[inline] + fn combine_filtered_impl( + &mut self, + other: Self, + _filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, + ) -> &mut Self { + self.body.extend(other.body.into_iter()); + + #[cfg(not(feature = "no_function"))] + if !other.functions.is_empty() { + crate::func::native::shared_make_mut(&mut self.functions) + .merge_filtered(&other.functions, &_filter); + } + self + } + /// Filter out the functions, retaining only some based on a filter predicate. + /// + /// Not available under `no_function`. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// # #[cfg(not(feature = "no_function"))] + /// # { + /// use rhai::Engine; + /// + /// let engine = Engine::new(); + /// + /// let mut ast = engine.compile(r#" + /// fn foo(n) { n + 1 } + /// fn bar() { print("hello"); } + /// "#)?; + /// + /// // Remove all functions except 'foo(_)' + /// ast.retain_functions(|_, _, name, params| name == "foo" && params == 1); + /// # } + /// # Ok(()) + /// # } + /// ``` + #[cfg(not(feature = "no_function"))] + #[inline] + pub fn retain_functions( + &mut self, + filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool, + ) -> &mut Self { + if !self.functions.is_empty() { + crate::func::native::shared_make_mut(&mut self.functions) + .retain_script_functions(filter); + } + self + } + /// Iterate through all function definitions. + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] + #[allow(dead_code)] + #[inline] + pub(crate) fn iter_fn_def(&self) -> impl Iterator { + self.functions + .iter_script_fn() + .map(|(_, _, _, _, fn_def)| fn_def.as_ref()) + } + /// Iterate through all function definitions. + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] + #[inline] + pub fn iter_functions<'a>(&'a self) -> impl Iterator + 'a { + self.functions + .iter_script_fn() + .map(|(_, _, _, _, fn_def)| fn_def.as_ref().into()) + } + /// Clear all function definitions in the [`AST`]. + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] + #[inline(always)] + pub fn clear_functions(&mut self) -> &mut Self { + self.functions = crate::Module::new().into(); + self + } + /// Clear all statements in the [`AST`], leaving only function definitions. + #[inline(always)] + pub fn clear_statements(&mut self) -> &mut Self { + self.body = StmtBlock::NONE; + self + } + /// Extract all top-level literal constant and/or variable definitions. + /// This is useful for extracting all global constants from a script without actually running it. + /// + /// A literal constant/variable definition takes the form of: + /// `const VAR = `_value_`;` and `let VAR = `_value_`;` + /// where _value_ is a literal expression or will be optimized into a literal. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// use rhai::{Engine, Scope}; + /// + /// let engine = Engine::new(); + /// + /// let ast = engine.compile( + /// " + /// const A = 40 + 2; // constant that optimizes into a literal + /// let b = 123; // literal variable + /// const B = b * A; // non-literal constant + /// const C = 999; // literal constant + /// b = A + C; // expression + /// + /// { // <- new block scope + /// const Z = 0; // <- literal constant not at top-level + /// } + /// ")?; + /// + /// let mut iter = ast.iter_literal_variables(true, false) + /// .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap())); + /// + /// # #[cfg(not(feature = "no_optimize"))] + /// assert_eq!(iter.next(), Some(("A", true, 42))); + /// assert_eq!(iter.next(), Some(("C", true, 999))); + /// assert_eq!(iter.next(), None); + /// + /// let mut iter = ast.iter_literal_variables(false, true) + /// .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap())); + /// + /// assert_eq!(iter.next(), Some(("b", false, 123))); + /// assert_eq!(iter.next(), None); + /// + /// let mut iter = ast.iter_literal_variables(true, true) + /// .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap())); + /// + /// # #[cfg(not(feature = "no_optimize"))] + /// assert_eq!(iter.next(), Some(("A", true, 42))); + /// assert_eq!(iter.next(), Some(("b", false, 123))); + /// assert_eq!(iter.next(), Some(("C", true, 999))); + /// assert_eq!(iter.next(), None); + /// + /// let scope: Scope = ast.iter_literal_variables(true, false).collect(); + /// + /// # #[cfg(not(feature = "no_optimize"))] + /// assert_eq!(scope.len(), 2); + /// + /// Ok(()) + /// # } + /// ``` + pub fn iter_literal_variables( + &self, + include_constants: bool, + include_variables: bool, + ) -> impl Iterator { + self.statements().iter().filter_map(move |stmt| match stmt { + Stmt::Var(expr, name, options, _) + if options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT) && include_constants + || !options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT) + && include_variables => + { + if let Some(value) = expr.get_literal_value() { + Some(( + name.as_str(), + options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT), + value, + )) + } else { + None + } + } + _ => None, + }) + } + /// Recursively walk the [`AST`], including function bodies (if any). + /// Return `false` from the callback to terminate the walk. + #[cfg(not(feature = "internals"))] + #[cfg(not(feature = "no_module"))] + #[inline] + pub(crate) fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool { + let path = &mut Vec::new(); + + for stmt in self.statements() { + if !stmt.walk(path, on_node) { + return false; + } + } + #[cfg(not(feature = "no_function"))] + for stmt in self.iter_fn_def().flat_map(|f| f.body.iter()) { + if !stmt.walk(path, on_node) { + return false; + } + } + + true + } + /// _(internals)_ Recursively walk the [`AST`], including function bodies (if any). + /// Return `false` from the callback to terminate the walk. + /// Exported under the `internals` feature only. + #[cfg(feature = "internals")] + #[inline] + pub fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool { + let path = &mut Vec::new(); + + for stmt in self.statements() { + if !stmt.walk(path, on_node) { + return false; + } + } + #[cfg(not(feature = "no_function"))] + for stmt in self.iter_fn_def().flat_map(|f| f.body.iter()) { + if !stmt.walk(path, on_node) { + return false; + } + } + + true + } +} + +impl> Add for &AST { + type Output = AST; + + #[inline(always)] + fn add(self, rhs: A) -> Self::Output { + self.merge(rhs.as_ref()) + } +} + +impl> AddAssign for AST { + #[inline(always)] + fn add_assign(&mut self, rhs: A) { + self.combine(rhs.into()); + } +} + +impl AsRef<[Stmt]> for AST { + #[inline(always)] + fn as_ref(&self) -> &[Stmt] { + self.statements() + } +} + +#[cfg(not(feature = "no_function"))] +impl AsRef for AST { + #[inline(always)] + fn as_ref(&self) -> &crate::Module { + self.shared_lib().as_ref() + } +} + +#[cfg(not(feature = "no_function"))] +impl AsRef> for AST { + #[inline(always)] + fn as_ref(&self) -> &crate::Shared { + self.shared_lib() + } +} + +/// _(internals)_ An [`AST`] node, consisting of either an [`Expr`] or a [`Stmt`]. +/// Exported under the `internals` feature only. +#[derive(Debug, Clone, Hash)] +pub enum ASTNode<'a> { + /// A statement ([`Stmt`]). + Stmt(&'a Stmt), + /// An expression ([`Expr`]). + Expr(&'a Expr), +} + +impl<'a> From<&'a Stmt> for ASTNode<'a> { + fn from(stmt: &'a Stmt) -> Self { + Self::Stmt(stmt) + } +} + +impl<'a> From<&'a Expr> for ASTNode<'a> { + fn from(expr: &'a Expr) -> Self { + Self::Expr(expr) + } +} + +impl ASTNode<'_> { + /// Get the [`Position`] of this [`ASTNode`]. + pub const fn position(&self) -> Position { + match self { + ASTNode::Stmt(stmt) => stmt.position(), + ASTNode::Expr(expr) => expr.position(), + } + } +} + +impl AST { + /// _(internals)_ Get the internal [`Module`][crate::Module] containing all script-defined functions. + /// Exported under the `internals` feature only. + /// + /// Not available under `no_function`. + /// + /// # Deprecated + /// + /// This method is deprecated. Use [`shared_lib`][AST::shared_lib] instead. + /// + /// This method will be removed in the next major version. + #[deprecated(since = "1.3.0", note = "use `shared_lib` instead")] + #[cfg(feature = "internals")] + #[cfg(not(feature = "no_function"))] + #[inline(always)] + #[must_use] + pub fn lib(&self) -> &crate::Module { + &self.functions + } +} diff --git a/src/ast/expr.rs b/src/ast/expr.rs new file mode 100644 index 00000000..66aba2f7 --- /dev/null +++ b/src/ast/expr.rs @@ -0,0 +1,835 @@ +//! Module defining script expressions. + +use super::{ASTNode, Ident, Stmt, StmtBlock}; +use crate::engine::{OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE}; +use crate::func::hashing::ALT_ZERO_HASH; +use crate::module::NamespaceRef; +use crate::tokenizer::Token; +use crate::types::dynamic::Union; +use crate::{Dynamic, Identifier, ImmutableString, Position, StaticVec, INT}; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; +use std::{ + collections::BTreeMap, + fmt, + hash::Hash, + num::{NonZeroU8, NonZeroUsize}, +}; + +#[cfg(not(feature = "no_float"))] +use std::str::FromStr; + +#[cfg(not(feature = "no_float"))] +use num_traits::Float; + +/// _(internals)_ A binary expression. +/// Exported under the `internals` feature only. +#[derive(Debug, Clone, Hash)] +pub struct BinaryExpr { + /// LHS expression. + pub lhs: Expr, + /// RHS expression. + pub rhs: Expr, +} + +/// _(internals)_ A custom syntax expression. +/// Exported under the `internals` feature only. +#[derive(Debug, Clone, Hash)] +pub struct CustomExpr { + /// List of keywords. + pub inputs: StaticVec, + /// List of tokens actually parsed. + pub tokens: StaticVec, + /// Is the current [`Scope`][crate::Scope] possibly modified by this custom statement + /// (e.g. introducing a new variable)? + pub scope_may_be_changed: bool, + /// Is this custom syntax self-terminated? + pub self_terminated: bool, +} + +impl CustomExpr { + /// Is this custom syntax self-terminated (i.e. no need for a semicolon terminator)? + /// + /// A self-terminated custom syntax always ends in `$block$`, `}` or `;` + #[must_use] + #[inline(always)] + pub const fn is_self_terminated(&self) -> bool { + self.self_terminated + } +} + +/// _(internals)_ A set of function call hashes. Exported under the `internals` feature only. +/// +/// Two separate hashes are pre-calculated because of the following patterns: +/// +/// ```ignore +/// func(a, b, c); // Native: func(a, b, c) - 3 parameters +/// // Script: func(a, b, c) - 3 parameters +/// +/// a.func(b, c); // Native: func(&mut a, b, c) - 3 parameters +/// // Script: func(b, c) - 2 parameters +/// ``` +/// +/// For normal function calls, the native hash equals the script hash. +/// +/// For method-style calls, the script hash contains one fewer parameter. +/// +/// Function call hashes are used in the following manner: +/// +/// * First, the script hash is tried, which contains only the called function's name plus the +/// number of parameters. +/// +/// * Next, the actual types of arguments are hashed and _combined_ with the native hash, which is +/// then used to search for a native function. In other words, a complete native function call +/// hash always contains the called function's name plus the types of the arguments. This is due +/// to possible function overloading for different parameter types. +#[derive(Clone, Copy, Eq, PartialEq, Hash, Default)] +pub struct FnCallHashes { + /// Pre-calculated hash for a script-defined function (zero if native functions only). + #[cfg(not(feature = "no_function"))] + pub script: u64, + /// Pre-calculated hash for a native Rust function with no parameter types. + pub native: u64, +} + +impl fmt::Debug for FnCallHashes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + #[cfg(not(feature = "no_function"))] + if self.script != 0 { + return if self.script == self.native { + fmt::Debug::fmt(&self.native, f) + } else { + write!(f, "({}, {})", self.script, self.native) + }; + } + + write!(f, "{} (native only)", self.native) + } +} + +impl From for FnCallHashes { + #[inline(always)] + fn from(hash: u64) -> Self { + let hash = if hash == 0 { ALT_ZERO_HASH } else { hash }; + + Self { + #[cfg(not(feature = "no_function"))] + script: hash, + native: hash, + } + } +} + +impl FnCallHashes { + /// Create a [`FnCallHashes`] with only the native Rust hash. + #[inline(always)] + #[must_use] + pub const fn from_native(hash: u64) -> Self { + Self { + #[cfg(not(feature = "no_function"))] + script: 0, + native: if hash == 0 { ALT_ZERO_HASH } else { hash }, + } + } + /// Create a [`FnCallHashes`] with both native Rust and script function hashes. + #[inline(always)] + #[must_use] + pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self { + Self { + #[cfg(not(feature = "no_function"))] + script: if script == 0 { ALT_ZERO_HASH } else { script }, + native: if native == 0 { ALT_ZERO_HASH } else { native }, + } + } + /// Is this [`FnCallHashes`] native Rust only? + #[inline(always)] + #[must_use] + pub const fn is_native_only(&self) -> bool { + #[cfg(not(feature = "no_function"))] + return self.script == 0; + + #[cfg(feature = "no_function")] + return true; + } +} + +/// _(internals)_ A function call. +/// Exported under the `internals` feature only. +#[derive(Debug, Clone, Default, Hash)] +pub struct FnCallExpr { + /// Namespace of the function, if any. + pub namespace: Option, + /// Function name. + pub name: Identifier, + /// Pre-calculated hashes. + pub hashes: FnCallHashes, + /// List of function call argument expressions. + pub args: StaticVec, + /// List of function call arguments that are constants. + /// + /// Any arguments in `args` that is [`Expr::Stack`] indexes into this + /// array to find the constant for use as its argument value. + /// + /// # Notes + /// + /// Constant arguments are very common in function calls, and keeping each constant in + /// an [`Expr::DynamicConstant`] involves an additional allocation. Keeping the constant + /// values in an inlined array avoids these extra allocations. + pub constants: StaticVec, + /// Does this function call capture the parent scope? + pub capture_parent_scope: bool, +} + +impl FnCallExpr { + /// Does this function call contain a qualified namespace? + #[inline(always)] + #[must_use] + pub const fn is_qualified(&self) -> bool { + self.namespace.is_some() + } + /// Convert this into an [`Expr::FnCall`]. + #[inline(always)] + #[must_use] + pub fn into_fn_call_expr(self, pos: Position) -> Expr { + Expr::FnCall(self.into(), pos) + } +} + +/// A type that wraps a floating-point number and implements [`Hash`]. +/// +/// Not available under `no_float`. +#[cfg(not(feature = "no_float"))] +#[derive(Clone, Copy, PartialEq, PartialOrd)] +pub struct FloatWrapper(F); + +#[cfg(not(feature = "no_float"))] +impl Hash for FloatWrapper { + #[inline(always)] + fn hash(&self, state: &mut H) { + self.0.to_ne_bytes().hash(state); + } +} + +#[cfg(not(feature = "no_float"))] +impl AsRef for FloatWrapper { + #[inline(always)] + fn as_ref(&self) -> &F { + &self.0 + } +} + +#[cfg(not(feature = "no_float"))] +impl AsMut for FloatWrapper { + #[inline(always)] + fn as_mut(&mut self) -> &mut F { + &mut self.0 + } +} + +#[cfg(not(feature = "no_float"))] +impl std::ops::Deref for FloatWrapper { + type Target = F; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[cfg(not(feature = "no_float"))] +impl std::ops::DerefMut for FloatWrapper { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[cfg(not(feature = "no_float"))] +impl fmt::Debug for FloatWrapper { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +#[cfg(not(feature = "no_float"))] +impl> fmt::Display for FloatWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let abs = self.0.abs(); + if abs.is_zero() { + f.write_str("0.0") + } else if abs > Self::MAX_NATURAL_FLOAT_FOR_DISPLAY.into() + || abs < Self::MIN_NATURAL_FLOAT_FOR_DISPLAY.into() + { + write!(f, "{:e}", self.0) + } else { + fmt::Display::fmt(&self.0, f)?; + if abs.fract().is_zero() { + f.write_str(".0")?; + } + Ok(()) + } + } +} + +#[cfg(not(feature = "no_float"))] +impl From for FloatWrapper { + #[inline(always)] + fn from(value: F) -> Self { + Self::new(value) + } +} + +#[cfg(not(feature = "no_float"))] +impl FromStr for FloatWrapper { + type Err = ::Err; + + #[inline] + fn from_str(s: &str) -> Result { + F::from_str(s).map(Into::::into) + } +} + +#[cfg(not(feature = "no_float"))] +impl FloatWrapper { + /// Maximum floating-point number for natural display before switching to scientific notation. + pub const MAX_NATURAL_FLOAT_FOR_DISPLAY: f32 = 10000000000000.0; + + /// Minimum floating-point number for natural display before switching to scientific notation. + pub const MIN_NATURAL_FLOAT_FOR_DISPLAY: f32 = 0.0000000000001; + + /// Create a new [`FloatWrapper`]. + #[inline(always)] + #[must_use] + pub fn new(value: F) -> Self { + Self(value) + } +} + +#[cfg(not(feature = "no_float"))] +impl FloatWrapper { + /// Create a new [`FloatWrapper`]. + #[inline(always)] + #[must_use] + pub const fn new_const(value: crate::FLOAT) -> Self { + Self(value) + } +} + +/// _(internals)_ An expression sub-tree. +/// Exported under the `internals` feature only. +#[derive(Clone, Hash)] +pub enum Expr { + /// Dynamic constant. + /// + /// Used to hold complex constants such as [`Array`][crate::Array] or [`Map`][crate::Map] for quick cloning. + /// Primitive data types should use the appropriate variants to avoid an allocation. + DynamicConstant(Box, Position), + /// Boolean constant. + BoolConstant(bool, Position), + /// Integer constant. + IntegerConstant(INT, Position), + /// Floating-point constant. + /// + /// Not available under `no_float`. + #[cfg(not(feature = "no_float"))] + FloatConstant(FloatWrapper, Position), + /// Character constant. + CharConstant(char, Position), + /// [String][ImmutableString] constant. + StringConstant(ImmutableString, Position), + /// An interpolated [string][ImmutableString]. + InterpolatedString(Box>, Position), + /// [ expr, ... ] + Array(Box>, Position), + /// #{ name:expr, ... } + Map( + Box<(StaticVec<(Ident, Expr)>, BTreeMap)>, + Position, + ), + /// () + Unit(Position), + /// Variable access - optional short index, position, (optional index, optional (hash, modules), variable name) + /// + /// The short index is [`u8`] which is used when the index is <= 255, which should be the vast + /// majority of cases (unless there are more than 255 variables defined!). + /// This is to avoid reading a pointer redirection during each variable access. + Variable( + Option, + Position, + Box<( + Option, + Option<(NamespaceRef, u64)>, + Identifier, + )>, + ), + /// Property access - ((getter, hash), (setter, hash), prop) + Property( + Box<( + (Identifier, u64), + (Identifier, u64), + (ImmutableString, Position), + )>, + ), + /// Stack slot for function calls. See [`FnCallExpr`] for more details. + /// + /// This variant does not map to any language structure. It is used in function calls with + /// constant arguments where the `usize` number indexes into an array containing a list of + /// constant arguments for the function call. + Stack(usize, Position), + /// { [statement][Stmt] ... } + Stmt(Box), + /// func `(` expr `,` ... `)` + FnCall(Box, Position), + /// lhs `.` rhs - bool variable is a dummy + Dot(Box, bool, Position), + /// expr `[` expr `]` - boolean indicates whether the dotting/indexing chain stops + Index(Box, bool, Position), + /// lhs `&&` rhs + And(Box, Position), + /// lhs `||` rhs + Or(Box, Position), + /// Custom syntax + Custom(Box, Position), +} + +impl Default for Expr { + #[inline(always)] + fn default() -> Self { + Self::Unit(Position::NONE) + } +} + +impl fmt::Debug for Expr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut display_pos = self.position(); + + match self { + Self::DynamicConstant(value, _) => write!(f, "{:?}", value), + Self::BoolConstant(value, _) => write!(f, "{:?}", value), + Self::IntegerConstant(value, _) => write!(f, "{:?}", value), + #[cfg(not(feature = "no_float"))] + Self::FloatConstant(value, _) => write!(f, "{:?}", value), + Self::CharConstant(value, _) => write!(f, "{:?}", value), + Self::StringConstant(value, _) => write!(f, "{:?}", value), + Self::Unit(_) => f.write_str("()"), + + Self::InterpolatedString(x, _) => { + f.write_str("InterpolatedString")?; + return f.debug_list().entries(x.iter()).finish(); + } + Self::Array(x, _) => { + f.write_str("Array")?; + f.debug_list().entries(x.iter()).finish() + } + Self::Map(x, _) => { + f.write_str("Map")?; + f.debug_map() + .entries(x.0.iter().map(|(k, v)| (k, v))) + .finish() + } + Self::Variable(i, _, x) => { + f.write_str("Variable(")?; + if let Some((_, ref namespace)) = x.1 { + write!(f, "{}{}", namespace, Token::DoubleColon.literal_syntax())? + } + f.write_str(&x.2)?; + if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) { + write!(f, " #{}", n)? + } + f.write_str(")") + } + Self::Property(x) => write!(f, "Property({})", (x.2).0), + Self::Stack(x, _) => write!(f, "StackSlot({})", x), + Self::Stmt(x) => { + f.write_str("ExprStmtBlock")?; + f.debug_list().entries(x.iter()).finish() + } + Self::FnCall(x, _) => { + let mut ff = f.debug_struct("FnCall"); + x.namespace.as_ref().map(|ns| ff.field("namespace", ns)); + ff.field("name", &x.name) + .field("hash", &x.hashes) + .field("args", &x.args); + if !x.constants.is_empty() { + ff.field("constants", &x.constants); + } + if x.capture_parent_scope { + ff.field("capture_parent_scope", &x.capture_parent_scope); + } + ff.finish() + } + Self::Index(x, term, pos) => { + display_pos = *pos; + + f.debug_struct("Index") + .field("lhs", &x.lhs) + .field("rhs", &x.rhs) + .field("terminate", term) + .finish() + } + Self::Dot(x, _, pos) | Self::And(x, pos) | Self::Or(x, pos) => { + let op_name = match self { + Self::Dot(_, _, _) => "Dot", + Self::And(_, _) => "And", + Self::Or(_, _) => "Or", + _ => unreachable!(), + }; + + display_pos = *pos; + + f.debug_struct(op_name) + .field("lhs", &x.lhs) + .field("rhs", &x.rhs) + .finish() + } + Self::Custom(x, _) => f.debug_tuple("Custom").field(x).finish(), + }?; + + display_pos.debug_print(f) + } +} + +impl Expr { + /// Get the [`Dynamic`] value of a literal constant expression. + /// + /// Returns [`None`] if the expression is not a literal constant. + #[inline] + #[must_use] + pub fn get_literal_value(&self) -> Option { + Some(match self { + Self::DynamicConstant(x, _) => x.as_ref().clone(), + Self::IntegerConstant(x, _) => (*x).into(), + #[cfg(not(feature = "no_float"))] + Self::FloatConstant(x, _) => (*x).into(), + Self::CharConstant(x, _) => (*x).into(), + Self::StringConstant(x, _) => x.clone().into(), + Self::BoolConstant(x, _) => (*x).into(), + Self::Unit(_) => Dynamic::UNIT, + + #[cfg(not(feature = "no_index"))] + Self::Array(x, _) if self.is_constant() => { + let mut arr = crate::Array::with_capacity(x.len()); + arr.extend( + x.iter() + .map(|v| v.get_literal_value().expect("constant value")), + ); + Dynamic::from_array(arr) + } + + #[cfg(not(feature = "no_object"))] + Self::Map(x, _) if self.is_constant() => { + Dynamic::from_map(x.0.iter().fold(x.1.clone(), |mut map, (k, v)| { + let value_ref = map.get_mut(k.name.as_str()).expect("contains all keys"); + *value_ref = v.get_literal_value().expect("constant value"); + map + })) + } + + // Binary operators + Self::FnCall(x, _) if x.args.len() == 2 => match x.name.as_str() { + // x..y + OP_EXCLUSIVE_RANGE => { + if let Expr::IntegerConstant(ref start, _) = x.args[0] { + if let Expr::IntegerConstant(ref end, _) = x.args[1] { + (*start..*end).into() + } else { + return None; + } + } else { + return None; + } + } + // x..=y + OP_INCLUSIVE_RANGE => { + if let Expr::IntegerConstant(ref start, _) = x.args[0] { + if let Expr::IntegerConstant(ref end, _) = x.args[1] { + (*start..=*end).into() + } else { + return None; + } + } else { + return None; + } + } + _ => return None, + }, + + _ => return None, + }) + } + /// Create an [`Expr`] from a [`Dynamic`] value. + #[inline] + #[must_use] + pub fn from_dynamic(value: Dynamic, pos: Position) -> Self { + match value.0 { + Union::Unit(_, _, _) => Self::Unit(pos), + Union::Bool(b, _, _) => Self::BoolConstant(b, pos), + Union::Str(s, _, _) => Self::StringConstant(s, pos), + Union::Char(c, _, _) => Self::CharConstant(c, pos), + Union::Int(i, _, _) => Self::IntegerConstant(i, pos), + + #[cfg(feature = "decimal")] + Union::Decimal(value, _, _) => Self::DynamicConstant(Box::new((*value).into()), pos), + + #[cfg(not(feature = "no_float"))] + Union::Float(f, _, _) => Self::FloatConstant(f, pos), + + #[cfg(not(feature = "no_index"))] + Union::Array(a, _, _) => Self::DynamicConstant(Box::new((*a).into()), pos), + + #[cfg(not(feature = "no_object"))] + Union::Map(m, _, _) => Self::DynamicConstant(Box::new((*m).into()), pos), + + _ => Self::DynamicConstant(value.into(), pos), + } + } + /// Is the expression a simple variable access? + #[inline] + #[must_use] + pub(crate) const fn is_variable_access(&self, non_qualified: bool) -> bool { + match self { + Self::Variable(_, _, x) => !non_qualified || x.1.is_none(), + _ => false, + } + } + /// Return the variable name if the expression a simple variable access. + #[inline] + #[must_use] + pub(crate) fn get_variable_name(&self, non_qualified: bool) -> Option<&str> { + match self { + Self::Variable(_, _, x) if !non_qualified || x.1.is_none() => Some(x.2.as_str()), + _ => None, + } + } + /// Get the [position][Position] of the expression. + #[inline] + #[must_use] + pub const fn position(&self) -> Position { + match self { + #[cfg(not(feature = "no_float"))] + Self::FloatConstant(_, pos) => *pos, + + Self::DynamicConstant(_, pos) + | Self::BoolConstant(_, pos) + | Self::IntegerConstant(_, pos) + | Self::CharConstant(_, pos) + | Self::Unit(pos) + | Self::StringConstant(_, pos) + | Self::Array(_, pos) + | Self::Map(_, pos) + | Self::Variable(_, pos, _) + | Self::Stack(_, pos) + | Self::FnCall(_, pos) + | Self::Custom(_, pos) + | Self::InterpolatedString(_, pos) => *pos, + + Self::Property(x) => (x.2).1, + Self::Stmt(x) => x.position(), + + Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _, _) | Self::Index(x, _, _) => { + x.lhs.position() + } + } + } + /// Override the [position][Position] of the expression. + #[inline] + pub fn set_position(&mut self, new_pos: Position) -> &mut Self { + match self { + #[cfg(not(feature = "no_float"))] + Self::FloatConstant(_, pos) => *pos = new_pos, + + Self::DynamicConstant(_, pos) + | Self::BoolConstant(_, pos) + | Self::IntegerConstant(_, pos) + | Self::CharConstant(_, pos) + | Self::Unit(pos) + | Self::StringConstant(_, pos) + | Self::Array(_, pos) + | Self::Map(_, pos) + | Self::And(_, pos) + | Self::Or(_, pos) + | Self::Dot(_, _, pos) + | Self::Index(_, _, pos) + | Self::Variable(_, pos, _) + | Self::Stack(_, pos) + | Self::FnCall(_, pos) + | Self::Custom(_, pos) + | Self::InterpolatedString(_, pos) => *pos = new_pos, + + Self::Property(x) => (x.2).1 = new_pos, + Self::Stmt(x) => x.set_position(new_pos), + } + + self + } + /// Is the expression pure? + /// + /// A pure expression has no side effects. + #[inline] + #[must_use] + pub fn is_pure(&self) -> bool { + match self { + Self::InterpolatedString(x, _) | Self::Array(x, _) => x.iter().all(Self::is_pure), + + Self::Map(x, _) => x.0.iter().map(|(_, v)| v).all(Self::is_pure), + + Self::And(x, _) | Self::Or(x, _) => x.lhs.is_pure() && x.rhs.is_pure(), + + Self::Stmt(x) => x.iter().all(Stmt::is_pure), + + Self::Variable(_, _, _) | Self::Stack(_, _) => true, + + _ => self.is_constant(), + } + } + /// Is the expression the unit `()` literal? + #[inline(always)] + #[must_use] + pub const fn is_unit(&self) -> bool { + matches!(self, Self::Unit(_)) + } + /// Is the expression a constant? + #[inline] + #[must_use] + pub fn is_constant(&self) -> bool { + match self { + #[cfg(not(feature = "no_float"))] + Self::FloatConstant(_, _) => true, + + Self::DynamicConstant(_, _) + | Self::BoolConstant(_, _) + | Self::IntegerConstant(_, _) + | Self::CharConstant(_, _) + | Self::StringConstant(_, _) + | Self::Unit(_) + | Self::Stack(_, _) => true, + + Self::InterpolatedString(x, _) | Self::Array(x, _) => x.iter().all(Self::is_constant), + + Self::Map(x, _) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant), + + _ => false, + } + } + /// Is a particular [token][Token] allowed as a postfix operator to this expression? + #[inline] + #[must_use] + pub const fn is_valid_postfix(&self, token: &Token) -> bool { + match token { + #[cfg(not(feature = "no_object"))] + Token::Period => return true, + _ => (), + } + + match self { + #[cfg(not(feature = "no_float"))] + Self::FloatConstant(_, _) => false, + + Self::DynamicConstant(_, _) + | Self::BoolConstant(_, _) + | Self::CharConstant(_, _) + | Self::And(_, _) + | Self::Or(_, _) + | Self::Unit(_) => false, + + Self::IntegerConstant(_, _) + | Self::StringConstant(_, _) + | Self::InterpolatedString(_, _) + | Self::FnCall(_, _) + | Self::Stmt(_) + | Self::Dot(_, _, _) + | Self::Index(_, _, _) + | Self::Array(_, _) + | Self::Map(_, _) + | Self::Custom(_, _) => match token { + #[cfg(not(feature = "no_index"))] + Token::LeftBracket => true, + _ => false, + }, + + Self::Variable(_, _, _) => match token { + #[cfg(not(feature = "no_index"))] + Token::LeftBracket => true, + Token::LeftParen => true, + Token::Bang => true, + Token::DoubleColon => true, + _ => false, + }, + + Self::Property(_) => match token { + #[cfg(not(feature = "no_index"))] + Token::LeftBracket => true, + Token::LeftParen => true, + _ => false, + }, + + Self::Stack(_, _) => false, + } + } + /// Recursively walk this expression. + /// Return `false` from the callback to terminate the walk. + pub fn walk<'a>( + &'a self, + path: &mut Vec>, + on_node: &mut impl FnMut(&[ASTNode]) -> bool, + ) -> bool { + // Push the current node onto the path + path.push(self.into()); + + if !on_node(path) { + return false; + } + + match self { + Self::Stmt(x) => { + for s in x.iter() { + if !s.walk(path, on_node) { + return false; + } + } + } + Self::InterpolatedString(x, _) | Self::Array(x, _) => { + for e in x.as_ref() { + if !e.walk(path, on_node) { + return false; + } + } + } + Self::Map(x, _) => { + for (_, e) in &x.0 { + if !e.walk(path, on_node) { + return false; + } + } + } + Self::Index(x, _, _) | Self::Dot(x, _, _) | Expr::And(x, _) | Expr::Or(x, _) => { + if !x.lhs.walk(path, on_node) { + return false; + } + if !x.rhs.walk(path, on_node) { + return false; + } + } + Self::FnCall(x, _) => { + for e in &x.args { + if !e.walk(path, on_node) { + return false; + } + } + } + Self::Custom(x, _) => { + for e in &x.inputs { + if !e.walk(path, on_node) { + return false; + } + } + } + _ => (), + } + + path.pop().expect("contains current node"); + + true + } +} diff --git a/src/ast/flags.rs b/src/ast/flags.rs new file mode 100644 index 00000000..4b5bc19d --- /dev/null +++ b/src/ast/flags.rs @@ -0,0 +1,169 @@ +//! Module defining script options. + +/// A type representing the access mode of a function. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub enum FnAccess { + /// Public function. + Public, + /// Private function. + Private, +} + +use std::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Not, Sub, SubAssign}; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; + +/// A type that holds a configuration option with bit-flags. +/// Exported under the `internals` feature only. +#[derive(PartialEq, Eq, Copy, Clone, Hash, Default)] +pub struct OptionFlags(u8); + +impl OptionFlags { + /// Does this [`OptionFlags`] contain a particular option flag? + #[inline(always)] + #[must_use] + pub const fn contains(self, flag: Self) -> bool { + self.0 & flag.0 != 0 + } +} + +impl Not for OptionFlags { + type Output = Self; + + /// Return the negation of the [`OptionFlags`]. + #[inline(always)] + fn not(self) -> Self::Output { + Self(!self.0) & AST_OPTION_FLAGS::AST_OPTION_ALL + } +} + +impl Add for OptionFlags { + type Output = Self; + + /// Return the union of two [`OptionFlags`]. + #[inline(always)] + fn add(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +impl AddAssign for OptionFlags { + /// Add the option flags in one [`OptionFlags`] to another. + #[inline(always)] + fn add_assign(&mut self, rhs: Self) { + self.0 |= rhs.0 + } +} + +impl BitOr for OptionFlags { + type Output = Self; + + /// Return the union of two [`OptionFlags`]. + #[inline(always)] + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +impl BitOrAssign for OptionFlags { + /// Add the option flags in one [`OptionFlags`] to another. + #[inline(always)] + fn bitor_assign(&mut self, rhs: Self) { + self.0 |= rhs.0 + } +} + +impl Sub for OptionFlags { + type Output = Self; + + /// Return the difference of two [`OptionFlags`]. + #[inline(always)] + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 & !rhs.0) + } +} + +impl SubAssign for OptionFlags { + /// Remove the option flags in one [`OptionFlags`] from another. + #[inline(always)] + fn sub_assign(&mut self, rhs: Self) { + self.0 &= !rhs.0 + } +} + +impl BitAnd for OptionFlags { + type Output = Self; + + /// Return the intersection of two [`OptionFlags`]. + #[inline(always)] + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & !rhs.0) + } +} + +impl BitAndAssign for OptionFlags { + /// Keep only the intersection of one [`OptionFlags`] with another. + #[inline(always)] + fn bitand_assign(&mut self, rhs: Self) { + self.0 &= !rhs.0 + } +} + +/// Option bit-flags for [`AST`][super::AST] nodes. +#[allow(non_snake_case)] +pub mod AST_OPTION_FLAGS { + use super::OptionFlags; + + /// _(internals)_ No options for the [`AST`][crate::AST] node. + /// Exported under the `internals` feature only. + pub const AST_OPTION_NONE: OptionFlags = OptionFlags(0b0000_0000); + /// _(internals)_ The [`AST`][crate::AST] node is constant. + /// Exported under the `internals` feature only. + pub const AST_OPTION_CONSTANT: OptionFlags = OptionFlags(0b0000_0001); + /// _(internals)_ The [`AST`][crate::AST] node is public. + /// Exported under the `internals` feature only. + pub const AST_OPTION_PUBLIC: OptionFlags = OptionFlags(0b0000_0010); + /// _(internals)_ The [`AST`][crate::AST] node is in negated mode. + /// Exported under the `internals` feature only. + pub const AST_OPTION_NEGATED: OptionFlags = OptionFlags(0b0000_0100); + /// _(internals)_ The [`AST`][crate::AST] node breaks out of normal control flow. + /// Exported under the `internals` feature only. + pub const AST_OPTION_BREAK_OUT: OptionFlags = OptionFlags(0b0000_1000); + /// _(internals)_ Mask of all options. + /// Exported under the `internals` feature only. + pub(crate) const AST_OPTION_ALL: OptionFlags = OptionFlags( + AST_OPTION_CONSTANT.0 | AST_OPTION_PUBLIC.0 | AST_OPTION_NEGATED.0 | AST_OPTION_BREAK_OUT.0, + ); + + impl std::fmt::Debug for OptionFlags { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn write_option( + options: &OptionFlags, + f: &mut std::fmt::Formatter<'_>, + num_flags: &mut usize, + flag: OptionFlags, + name: &str, + ) -> std::fmt::Result { + if options.contains(flag) { + if *num_flags > 0 { + f.write_str("+")?; + } + f.write_str(name)?; + *num_flags += 1; + } + Ok(()) + } + + let num_flags = &mut 0; + + f.write_str("(")?; + write_option(self, f, num_flags, AST_OPTION_CONSTANT, "Constant")?; + write_option(self, f, num_flags, AST_OPTION_PUBLIC, "Public")?; + write_option(self, f, num_flags, AST_OPTION_NEGATED, "Negated")?; + write_option(self, f, num_flags, AST_OPTION_BREAK_OUT, "Break")?; + f.write_str(")")?; + + Ok(()) + } + } +} diff --git a/src/ast/ident.rs b/src/ast/ident.rs new file mode 100644 index 00000000..4a19878d --- /dev/null +++ b/src/ast/ident.rs @@ -0,0 +1,37 @@ +//! Module defining script identifiers. + +use crate::{Identifier, Position}; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; +use std::{fmt, hash::Hash}; + +/// _(internals)_ An identifier containing a name and a [position][Position]. +/// Exported under the `internals` feature only. +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct Ident { + /// Identifier name. + pub name: Identifier, + /// Position. + pub pos: Position, +} + +impl fmt::Debug for Ident { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.name)?; + self.pos.debug_print(f) + } +} + +impl AsRef for Ident { + #[inline(always)] + fn as_ref(&self) -> &str { + self.name.as_ref() + } +} + +impl Ident { + #[inline(always)] + pub fn as_str(&self) -> &str { + self.name.as_str() + } +} diff --git a/src/ast/mod.rs b/src/ast/mod.rs new file mode 100644 index 00000000..0bb45b1e --- /dev/null +++ b/src/ast/mod.rs @@ -0,0 +1,19 @@ +//! Module defining the AST (abstract syntax tree). + +pub mod ast; +pub mod expr; +pub mod flags; +pub mod ident; +pub mod script_fn; +pub mod stmt; + +pub use ast::{ASTNode, AST}; +pub use expr::{BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnCallHashes}; +pub use flags::{FnAccess, OptionFlags, AST_OPTION_FLAGS}; +pub use ident::Ident; +#[cfg(not(feature = "no_function"))] +pub use script_fn::{ScriptFnDef, ScriptFnMetadata}; +pub use stmt::{OpAssignment, Stmt, StmtBlock}; + +#[cfg(feature = "no_function")] +pub struct ScriptFnDef; diff --git a/src/ast/script_fn.rs b/src/ast/script_fn.rs new file mode 100644 index 00000000..c0bd55ca --- /dev/null +++ b/src/ast/script_fn.rs @@ -0,0 +1,128 @@ +//! Module defining script-defined functions. +#![cfg(not(feature = "no_function"))] + +use super::{FnAccess, StmtBlock}; +use crate::{Identifier, Module, Shared, StaticVec}; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; +use std::{fmt, hash::Hash}; + +/// _(internals)_ A type containing information on a script-defined function. +/// Exported under the `internals` feature only. +#[derive(Debug, Clone)] +pub struct ScriptFnDef { + /// Function body. + pub body: StmtBlock, + /// Encapsulated running environment, if any. + pub lib: Option>, + /// Encapsulated imported modules. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] + pub mods: crate::engine::Imports, + /// Function name. + pub name: Identifier, + /// Function access mode. + pub access: FnAccess, + /// Names of function parameters. + pub params: StaticVec, + /// _(metadata)_ Function doc-comments (if any). + /// Exported under the `metadata` feature only. + #[cfg(feature = "metadata")] + pub comments: Option]>>, +} + +impl fmt::Display for ScriptFnDef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}{}({})", + match self.access { + FnAccess::Public => "", + FnAccess::Private => "private ", + }, + self.name, + self.params + .iter() + .map(|s| s.as_str()) + .collect::>() + .join(", ") + ) + } +} + +/// A type containing the metadata of a script-defined function. +/// +/// Not available under `no_function`. +/// +/// Created by [`AST::iter_functions`][super::AST::iter_functions]. +#[derive(Debug, Eq, PartialEq, Clone, Hash)] +pub struct ScriptFnMetadata<'a> { + /// _(metadata)_ Function doc-comments (if any). + /// Exported under the `metadata` feature only. + /// + /// Block doc-comments are kept in a single string slice with line-breaks within. + /// + /// Line doc-comments are kept in one string slice per line without the termination line-break. + /// + /// Leading white-spaces are stripped, and each string slice always starts with the corresponding + /// doc-comment leader: `///` or `/**`. + #[cfg(feature = "metadata")] + pub comments: Vec<&'a str>, + /// Function access mode. + pub access: FnAccess, + /// Function name. + pub name: &'a str, + /// Function parameters (if any). + pub params: Vec<&'a str>, +} + +impl fmt::Display for ScriptFnMetadata<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}{}({})", + match self.access { + FnAccess::Public => "", + FnAccess::Private => "private ", + }, + self.name, + self.params + .iter() + .cloned() + .collect::>() + .join(", ") + ) + } +} + +impl<'a> From<&'a ScriptFnDef> for ScriptFnMetadata<'a> { + #[inline] + fn from(value: &'a ScriptFnDef) -> Self { + Self { + #[cfg(feature = "metadata")] + comments: value + .comments + .as_ref() + .map_or_else(|| Vec::new(), |v| v.iter().map(Box::as_ref).collect()), + access: value.access, + name: &value.name, + params: value.params.iter().map(|s| s.as_str()).collect(), + } + } +} + +impl std::cmp::PartialOrd for ScriptFnMetadata<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl std::cmp::Ord for ScriptFnMetadata<'_> { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + match self.name.cmp(other.name) { + std::cmp::Ordering::Equal => self.params.len().cmp(&other.params.len()), + cmp => cmp, + } + } +} diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs new file mode 100644 index 00000000..6cb2d6a5 --- /dev/null +++ b/src/ast/stmt.rs @@ -0,0 +1,625 @@ +//! Module defining script statements. + +use super::{ASTNode, Expr, FnCallExpr, Ident, OptionFlags, AST_OPTION_FLAGS}; +use crate::tokenizer::Token; +use crate::{calc_fn_hash, Position, StaticVec, INT}; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; +use std::{ + collections::BTreeMap, + fmt, + hash::Hash, + mem, + ops::{Deref, DerefMut}, +}; + +/// _(internals)_ An op-assignment operator. +/// Exported under the `internals` feature only. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct OpAssignment<'a> { + /// Hash of the op-assignment call. + pub hash_op_assign: u64, + /// Hash of the underlying operator call (for fallback). + pub hash_op: u64, + /// Op-assignment operator. + pub op: &'a str, +} + +impl OpAssignment<'_> { + /// Create a new [`OpAssignment`]. + /// + /// # Panics + /// + /// Panics if the operator name is not an op-assignment operator. + #[must_use] + pub fn new(op: Token) -> Self { + let op_raw = op + .map_op_assignment() + .expect("op-assignment") + .literal_syntax(); + let op_assignment = op.literal_syntax(); + + Self { + hash_op_assign: calc_fn_hash(op_assignment, 2), + hash_op: calc_fn_hash(op_raw, 2), + op: op_assignment, + } + } +} + +/// _(internals)_ A scoped block of statements. +/// Exported under the `internals` feature only. +#[derive(Clone, Hash, Default)] +pub struct StmtBlock(StaticVec, Position); + +impl StmtBlock { + /// A [`StmtBlock`] that does not exist. + pub const NONE: Self = Self::empty(Position::NONE); + + /// Create a new [`StmtBlock`]. + #[must_use] + pub fn new(statements: impl IntoIterator, pos: Position) -> Self { + let mut statements: StaticVec<_> = statements.into_iter().collect(); + statements.shrink_to_fit(); + Self(statements, pos) + } + /// Create an empty [`StmtBlock`]. + #[inline(always)] + #[must_use] + pub const fn empty(pos: Position) -> Self { + Self(StaticVec::new_const(), pos) + } + /// Is this statements block empty? + #[inline(always)] + #[must_use] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + /// Number of statements in this statements block. + #[inline(always)] + #[must_use] + pub fn len(&self) -> usize { + self.0.len() + } + /// Get the statements of this statements block. + #[inline(always)] + #[must_use] + pub fn statements(&self) -> &[Stmt] { + &self.0 + } + /// Extract the statements. + #[inline(always)] + #[must_use] + pub(crate) fn take_statements(&mut self) -> StaticVec { + mem::take(&mut self.0) + } + /// Get an iterator over the statements of this statements block. + #[inline(always)] + #[must_use] + pub fn iter(&self) -> impl Iterator { + self.0.iter() + } + /// Get the position (location of the beginning `{`) of this statements block. + #[inline(always)] + #[must_use] + pub const fn position(&self) -> Position { + self.1 + } + /// Set the position (location of the beginning `{`) of this statements block. + #[inline(always)] + pub fn set_position(&mut self, pos: Position) { + self.1 = pos; + } +} + +impl Deref for StmtBlock { + type Target = StaticVec; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for StmtBlock { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl fmt::Debug for StmtBlock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Block")?; + fmt::Debug::fmt(&self.0, f)?; + self.1.debug_print(f) + } +} + +impl From for Stmt { + #[inline(always)] + fn from(block: StmtBlock) -> Self { + Self::Block(block.0.into_boxed_slice(), block.1) + } +} + +impl IntoIterator for StmtBlock { + type Item = Stmt; + type IntoIter = smallvec::IntoIter<[Stmt; 3]>; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl Extend for StmtBlock { + #[inline(always)] + fn extend>(&mut self, iter: T) { + self.0.extend(iter) + } +} + +/// _(internals)_ A statement. +/// Exported under the `internals` feature only. +#[derive(Debug, Clone, Hash)] +pub enum Stmt { + /// No-op. + Noop(Position), + /// `if` expr `{` stmt `}` `else` `{` stmt `}` + If(Expr, Box<(StmtBlock, StmtBlock)>, Position), + /// `switch` expr `if` condition `{` literal or _ `=>` stmt `,` ... `}` + Switch( + Expr, + Box<( + BTreeMap, StmtBlock)>>, + StmtBlock, + StaticVec<(INT, INT, bool, Option, StmtBlock)>, + )>, + Position, + ), + /// `while` expr `{` stmt `}` | `loop` `{` stmt `}` + /// + /// If the guard expression is [`UNIT`][Expr::Unit], then it is a `loop` statement. + While(Expr, Box, Position), + /// `do` `{` stmt `}` `while`|`until` expr + /// + /// ### Option Flags + /// + /// * [`AST_OPTION_NONE`][AST_OPTION_FLAGS::AST_OPTION_NONE] = `while` + /// * [`AST_OPTION_NEGATED`][AST_OPTION_FLAGS::AST_OPTION_NEGATED] = `until` + Do(Box, Expr, OptionFlags, Position), + /// `for` `(` id `,` counter `)` `in` expr `{` stmt `}` + For(Expr, Box<(Ident, Option, StmtBlock)>, Position), + /// \[`export`\] `let`|`const` id `=` expr + /// + /// ### Option Flags + /// + /// * [`AST_OPTION_PUBLIC`][AST_OPTION_FLAGS::AST_OPTION_PUBLIC] = `export` + /// * [`AST_OPTION_CONSTANT`][AST_OPTION_FLAGS::AST_OPTION_CONSTANT] = `const` + Var(Expr, Box, OptionFlags, Position), + /// expr op`=` expr + Assignment(Box<(Expr, Option>, Expr)>, Position), + /// func `(` expr `,` ... `)` + /// + /// Note - this is a duplicate of [`Expr::FnCall`] to cover the very common pattern of a single + /// function call forming one statement. + FnCall(Box, Position), + /// `{` stmt`;` ... `}` + Block(Box<[Stmt]>, Position), + /// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}` + TryCatch(Box<(StmtBlock, Option, StmtBlock)>, Position), + /// [expression][Expr] + Expr(Expr), + /// `continue`/`break` + /// + /// ### Option Flags + /// + /// * [`AST_OPTION_NONE`][AST_OPTION_FLAGS::AST_OPTION_NONE] = `continue` + /// * [`AST_OPTION_BREAK_OUT`][AST_OPTION_FLAGS::AST_OPTION_BREAK_OUT] = `break` + BreakLoop(OptionFlags, Position), + /// `return`/`throw` + /// + /// ### Option Flags + /// + /// * [`AST_OPTION_NONE`][AST_OPTION_FLAGS::AST_OPTION_NONE] = `return` + /// * [`AST_OPTION_BREAK_OUT`][AST_OPTION_FLAGS::AST_OPTION_BREAK_OUT] = `throw` + Return(OptionFlags, Option, Position), + /// `import` expr `as` var + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] + Import(Expr, Option>, Position), + /// `export` var `as` var `,` ... + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] + Export(Box<[(Ident, Ident)]>, Position), + /// Convert a variable to shared. + /// + /// Not available under `no_closure`. + /// + /// # Notes + /// + /// This variant does not map to any language structure. It is currently only used only to + /// convert a normal variable into a shared variable when the variable is _captured_ by a closure. + #[cfg(not(feature = "no_closure"))] + Share(crate::Identifier), +} + +impl Default for Stmt { + #[inline(always)] + fn default() -> Self { + Self::Noop(Position::NONE) + } +} + +impl From for StmtBlock { + #[inline] + fn from(stmt: Stmt) -> Self { + match stmt { + Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos), + Stmt::Noop(pos) => Self(StaticVec::new_const(), pos), + _ => { + let pos = stmt.position(); + Self(vec![stmt].into(), pos) + } + } + } +} + +impl Stmt { + /// Is this statement [`Noop`][Stmt::Noop]? + #[inline(always)] + #[must_use] + pub const fn is_noop(&self) -> bool { + matches!(self, Self::Noop(_)) + } + /// Get the [position][Position] of this statement. + #[must_use] + pub const fn position(&self) -> Position { + match self { + Self::Noop(pos) + | Self::BreakLoop(_, pos) + | Self::Block(_, pos) + | Self::Assignment(_, pos) + | Self::FnCall(_, pos) + | Self::If(_, _, pos) + | Self::Switch(_, _, pos) + | Self::While(_, _, pos) + | Self::Do(_, _, _, pos) + | Self::For(_, _, pos) + | Self::Return(_, _, pos) + | Self::Var(_, _, _, pos) + | Self::TryCatch(_, pos) => *pos, + + Self::Expr(x) => x.position(), + + #[cfg(not(feature = "no_module"))] + Self::Import(_, _, pos) => *pos, + #[cfg(not(feature = "no_module"))] + Self::Export(_, pos) => *pos, + + #[cfg(not(feature = "no_closure"))] + Self::Share(_) => Position::NONE, + } + } + /// Override the [position][Position] of this statement. + pub fn set_position(&mut self, new_pos: Position) -> &mut Self { + match self { + Self::Noop(pos) + | Self::BreakLoop(_, pos) + | Self::Block(_, pos) + | Self::Assignment(_, pos) + | Self::FnCall(_, pos) + | Self::If(_, _, pos) + | Self::Switch(_, _, pos) + | Self::While(_, _, pos) + | Self::Do(_, _, _, pos) + | Self::For(_, _, pos) + | Self::Return(_, _, pos) + | Self::Var(_, _, _, pos) + | Self::TryCatch(_, pos) => *pos = new_pos, + + Self::Expr(x) => { + x.set_position(new_pos); + } + + #[cfg(not(feature = "no_module"))] + Self::Import(_, _, pos) => *pos = new_pos, + #[cfg(not(feature = "no_module"))] + Self::Export(_, pos) => *pos = new_pos, + + #[cfg(not(feature = "no_closure"))] + Self::Share(_) => (), + } + + self + } + /// Does this statement return a value? + #[must_use] + pub const fn returns_value(&self) -> bool { + match self { + Self::If(_, _, _) + | Self::Switch(_, _, _) + | Self::Block(_, _) + | Self::Expr(_) + | Self::FnCall(_, _) => true, + + Self::Noop(_) + | Self::While(_, _, _) + | Self::Do(_, _, _, _) + | Self::For(_, _, _) + | Self::TryCatch(_, _) => false, + + Self::Var(_, _, _, _) + | Self::Assignment(_, _) + | Self::BreakLoop(_, _) + | Self::Return(_, _, _) => false, + + #[cfg(not(feature = "no_module"))] + Self::Import(_, _, _) | Self::Export(_, _) => false, + + #[cfg(not(feature = "no_closure"))] + Self::Share(_) => false, + } + } + /// Is this statement self-terminated (i.e. no need for a semicolon terminator)? + #[must_use] + pub const fn is_self_terminated(&self) -> bool { + match self { + Self::If(_, _, _) + | Self::Switch(_, _, _) + | Self::While(_, _, _) + | Self::For(_, _, _) + | Self::Block(_, _) + | Self::TryCatch(_, _) => true, + + // A No-op requires a semicolon in order to know it is an empty statement! + Self::Noop(_) => false, + + Self::Expr(Expr::Custom(x, _)) if x.is_self_terminated() => true, + + Self::Var(_, _, _, _) + | Self::Assignment(_, _) + | Self::Expr(_) + | Self::FnCall(_, _) + | Self::Do(_, _, _, _) + | Self::BreakLoop(_, _) + | Self::Return(_, _, _) => false, + + #[cfg(not(feature = "no_module"))] + Self::Import(_, _, _) | Self::Export(_, _) => false, + + #[cfg(not(feature = "no_closure"))] + Self::Share(_) => false, + } + } + /// Is this statement _pure_? + /// + /// A pure statement has no side effects. + #[must_use] + pub fn is_pure(&self) -> bool { + match self { + Self::Noop(_) => true, + Self::Expr(expr) => expr.is_pure(), + Self::If(condition, x, _) => { + condition.is_pure() + && (x.0).0.iter().all(Stmt::is_pure) + && (x.1).0.iter().all(Stmt::is_pure) + } + Self::Switch(expr, x, _) => { + expr.is_pure() + && x.0.values().all(|block| { + block.0.as_ref().map(Expr::is_pure).unwrap_or(true) + && (block.1).0.iter().all(Stmt::is_pure) + }) + && (x.2).iter().all(|(_, _, _, condition, stmt)| { + condition.as_ref().map(Expr::is_pure).unwrap_or(true) + && stmt.0.iter().all(Stmt::is_pure) + }) + && (x.1).0.iter().all(Stmt::is_pure) + } + + // Loops that exit can be pure because it can never be infinite. + Self::While(Expr::BoolConstant(false, _), _, _) => true, + Self::Do(body, Expr::BoolConstant(x, _), options, _) + if *x == options.contains(AST_OPTION_FLAGS::AST_OPTION_NEGATED) => + { + body.iter().all(Stmt::is_pure) + } + + // Loops are never pure since they can be infinite - and that's a side effect. + Self::While(_, _, _) | Self::Do(_, _, _, _) => false, + + // For loops can be pure because if the iterable is pure, it is finite, + // so infinite loops can never occur. + Self::For(iterable, x, _) => iterable.is_pure() && (x.2).0.iter().all(Stmt::is_pure), + + Self::Var(_, _, _, _) | Self::Assignment(_, _) | Self::FnCall(_, _) => false, + Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), + Self::BreakLoop(_, _) | Self::Return(_, _, _) => false, + Self::TryCatch(x, _) => { + (x.0).0.iter().all(Stmt::is_pure) && (x.2).0.iter().all(Stmt::is_pure) + } + + #[cfg(not(feature = "no_module"))] + Self::Import(_, _, _) => false, + #[cfg(not(feature = "no_module"))] + Self::Export(_, _) => false, + + #[cfg(not(feature = "no_closure"))] + Self::Share(_) => false, + } + } + /// Is this statement _pure_ within the containing block? + /// + /// An internally pure statement only has side effects that disappear outside the block. + /// + /// Currently only variable definitions (i.e. `let` and `const`) and `import`/`export` + /// statements are internally pure. + #[inline] + #[must_use] + pub fn is_internally_pure(&self) -> bool { + match self { + Self::Var(expr, _, _, _) => expr.is_pure(), + + #[cfg(not(feature = "no_module"))] + Self::Import(expr, _, _) => expr.is_pure(), + #[cfg(not(feature = "no_module"))] + Self::Export(_, _) => true, + + _ => self.is_pure(), + } + } + /// Does this statement break the current control flow through the containing block? + /// + /// Currently this is only true for `return`, `throw`, `break` and `continue`. + /// + /// All statements following this statement will essentially be dead code. + #[inline] + #[must_use] + pub const fn is_control_flow_break(&self) -> bool { + match self { + Self::Return(_, _, _) | Self::BreakLoop(_, _) => true, + _ => false, + } + } + /// Recursively walk this statement. + /// Return `false` from the callback to terminate the walk. + pub fn walk<'a>( + &'a self, + path: &mut Vec>, + on_node: &mut impl FnMut(&[ASTNode]) -> bool, + ) -> bool { + // Push the current node onto the path + path.push(self.into()); + + if !on_node(path) { + return false; + } + + match self { + Self::Var(e, _, _, _) => { + if !e.walk(path, on_node) { + return false; + } + } + Self::If(e, x, _) => { + if !e.walk(path, on_node) { + return false; + } + for s in &(x.0).0 { + if !s.walk(path, on_node) { + return false; + } + } + for s in &(x.1).0 { + if !s.walk(path, on_node) { + return false; + } + } + } + Self::Switch(e, x, _) => { + if !e.walk(path, on_node) { + return false; + } + for b in x.0.values() { + if !b.0.as_ref().map(|e| e.walk(path, on_node)).unwrap_or(true) { + return false; + } + for s in &(b.1).0 { + if !s.walk(path, on_node) { + return false; + } + } + } + for (_, _, _, c, stmt) in &x.2 { + if !c.as_ref().map(|e| e.walk(path, on_node)).unwrap_or(true) { + return false; + } + for s in &stmt.0 { + if !s.walk(path, on_node) { + return false; + } + } + } + for s in &(x.1).0 { + if !s.walk(path, on_node) { + return false; + } + } + } + Self::While(e, s, _) | Self::Do(s, e, _, _) => { + if !e.walk(path, on_node) { + return false; + } + for s in &s.0 { + if !s.walk(path, on_node) { + return false; + } + } + } + Self::For(e, x, _) => { + if !e.walk(path, on_node) { + return false; + } + for s in &(x.2).0 { + if !s.walk(path, on_node) { + return false; + } + } + } + Self::Assignment(x, _) => { + if !x.0.walk(path, on_node) { + return false; + } + if !x.2.walk(path, on_node) { + return false; + } + } + Self::FnCall(x, _) => { + for s in &x.args { + if !s.walk(path, on_node) { + return false; + } + } + } + Self::Block(x, _) => { + for s in x.iter() { + if !s.walk(path, on_node) { + return false; + } + } + } + Self::TryCatch(x, _) => { + for s in &(x.0).0 { + if !s.walk(path, on_node) { + return false; + } + } + for s in &(x.2).0 { + if !s.walk(path, on_node) { + return false; + } + } + } + Self::Expr(e) | Self::Return(_, Some(e), _) => { + if !e.walk(path, on_node) { + return false; + } + } + #[cfg(not(feature = "no_module"))] + Self::Import(e, _, _) => { + if !e.walk(path, on_node) { + return false; + } + } + _ => (), + } + + path.pop().expect("contains current node"); + + true + } +} diff --git a/src/func/call.rs b/src/func/call.rs index 7db44596..f1cf279b 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -561,7 +561,7 @@ impl Engine { _ => (), } - // Scripted function call? + // Script-defined function call? #[cfg(not(feature = "no_function"))] if let Some(FnResolutionCacheEntry { func, source }) = self .resolve_fn(mods, state, lib, fn_name, hashes.script, None, false, false) @@ -570,7 +570,7 @@ impl Engine { // Script function call assert!(func.is_script()); - let func = func.get_script_fn_def().expect("scripted function"); + let func = func.get_script_fn_def().expect("script-defined function"); if func.body.is_empty() { return Ok((Dynamic::UNIT, false)); @@ -1271,7 +1271,7 @@ impl Engine { match func { #[cfg(not(feature = "no_function"))] Some(f) if f.is_script() => { - let fn_def = f.get_script_fn_def().expect("scripted function"); + let fn_def = f.get_script_fn_def().expect("script-defined function"); if fn_def.body.is_empty() { Ok(Dynamic::UNIT) diff --git a/src/func/native.rs b/src/func/native.rs index e5cf5b8e..b31c1327 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -476,7 +476,7 @@ impl CallableFunction { Self::Script(_) => false, } } - /// Is this a Rhai-scripted function? + /// Is this a script-defined function? #[inline] #[must_use] pub const fn is_script(&self) -> bool { diff --git a/src/func/script.rs b/src/func/script.rs index 5e32bfcd..383dd400 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -163,7 +163,7 @@ impl Engine { result } - // Does a scripted function exist? + // Does a script-defined function exist? #[must_use] pub(crate) fn has_script_fn( &self, diff --git a/src/module/mod.rs b/src/module/mod.rs index 8152f0a9..4e7bc7a8 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1328,7 +1328,7 @@ impl Module { f.access, f.name.as_str(), f.params, - f.func.get_script_fn_def().expect("scripted function"), + f.func.get_script_fn_def().expect("script-defined function"), ) }) } @@ -1459,7 +1459,7 @@ impl Module { let mut func = f .func .get_script_fn_def() - .expect("scripted function") + .expect("script-defined function") .as_ref() .clone(); func.lib = Some(ast.shared_lib().clone()); diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index 6470e528..df6364fe 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -163,18 +163,15 @@ impl From<&crate::module::FuncInfo> for FnMetadata { signature: info.gen_signature(), doc_comments: if info.func.is_script() { #[cfg(feature = "no_function")] - { - unreachable!("scripted functions should not exist under no_function") - } + unreachable!("script-defined functions should not exist under no_function"); + #[cfg(not(feature = "no_function"))] - { - info.func - .get_script_fn_def() - .expect("scripted function") - .comments - .as_ref() - .map_or_else(|| Vec::new(), |v| v.to_vec()) - } + info.func + .get_script_fn_def() + .expect("script-defined function") + .comments + .as_ref() + .map_or_else(|| Vec::new(), |v| v.to_vec()) } else { Vec::new() }, diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 127bf18f..69d0788a 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -2042,7 +2042,7 @@ pub fn is_valid_identifier(name: impl Iterator) -> bool { first_alphabetic } -/// Is a text string a valid scripted function name? +/// Is a text string a valid script-defined function name? #[inline(always)] #[must_use] pub fn is_valid_function_name(name: impl AsRef) -> bool { diff --git a/tests/call_fn.rs b/tests/call_fn.rs index 2542a0e4..aa098fb2 100644 --- a/tests/call_fn.rs +++ b/tests/call_fn.rs @@ -296,7 +296,7 @@ fn test_call_fn_events() -> Result<(), Box> { "end" => engine.call_fn(scope, ast, "end", (event_data,)).unwrap(), // The 'update' event maps to function 'update'. - // This event provides a default implementation when the scripted function is not found. + // This event provides a default implementation when the script-defined function is not found. "update" => engine .call_fn(scope, ast, "update", (event_data,)) .or_else(|err| match *err { From 42638db0fb4924f761f4b98a2a8de6de2c4a894a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 17 Dec 2021 16:15:30 +0800 Subject: [PATCH 10/97] Move Engine default limits. --- src/api/limits.rs | 34 +++++++++++++++++++++++++++++++--- src/engine.rs | 26 -------------------------- src/func/call.rs | 2 +- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/api/limits.rs b/src/api/limits.rs index 7d9fd36c..ec609944 100644 --- a/src/api/limits.rs +++ b/src/api/limits.rs @@ -6,6 +6,34 @@ use std::num::{NonZeroU64, NonZeroUsize}; #[cfg(feature = "no_std")] use std::prelude::v1::*; +pub mod defaults { + #[cfg(not(feature = "unchecked"))] + #[cfg(debug_assertions)] + #[cfg(not(feature = "no_function"))] + pub const MAX_CALL_STACK_DEPTH: usize = 8; + #[cfg(not(feature = "unchecked"))] + #[cfg(debug_assertions)] + pub const MAX_EXPR_DEPTH: usize = 32; + #[cfg(not(feature = "unchecked"))] + #[cfg(not(feature = "no_function"))] + #[cfg(debug_assertions)] + pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16; + + #[cfg(not(feature = "unchecked"))] + #[cfg(not(debug_assertions))] + #[cfg(not(feature = "no_function"))] + pub const MAX_CALL_STACK_DEPTH: usize = 64; + #[cfg(not(feature = "unchecked"))] + #[cfg(not(debug_assertions))] + pub const MAX_EXPR_DEPTH: usize = 64; + #[cfg(not(feature = "unchecked"))] + #[cfg(not(feature = "no_function"))] + #[cfg(not(debug_assertions))] + pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32; + + pub const MAX_DYNAMIC_PARAMETERS: usize = 16; +} + /// A type containing all the limits imposed by the [`Engine`]. /// /// Not available under `unchecked`. @@ -56,10 +84,10 @@ impl Limits { pub const fn new() -> Self { Self { #[cfg(not(feature = "no_function"))] - max_call_stack_depth: crate::engine::MAX_CALL_STACK_DEPTH, - max_expr_depth: NonZeroUsize::new(crate::engine::MAX_EXPR_DEPTH), + max_call_stack_depth: defaults::MAX_CALL_STACK_DEPTH, + max_expr_depth: NonZeroUsize::new(defaults::MAX_EXPR_DEPTH), #[cfg(not(feature = "no_function"))] - max_function_expr_depth: NonZeroUsize::new(crate::engine::MAX_FUNCTION_EXPR_DEPTH), + max_function_expr_depth: NonZeroUsize::new(defaults::MAX_FUNCTION_EXPR_DEPTH), max_operations: None, #[cfg(not(feature = "no_module"))] max_modules: usize::MAX, diff --git a/src/engine.rs b/src/engine.rs index 145ced34..49ea0512 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -307,32 +307,6 @@ impl fmt::Debug for Imports { } } -#[cfg(not(feature = "unchecked"))] -#[cfg(debug_assertions)] -#[cfg(not(feature = "no_function"))] -pub const MAX_CALL_STACK_DEPTH: usize = 8; -#[cfg(not(feature = "unchecked"))] -#[cfg(debug_assertions)] -pub const MAX_EXPR_DEPTH: usize = 32; -#[cfg(not(feature = "unchecked"))] -#[cfg(not(feature = "no_function"))] -#[cfg(debug_assertions)] -pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16; - -#[cfg(not(feature = "unchecked"))] -#[cfg(not(debug_assertions))] -#[cfg(not(feature = "no_function"))] -pub const MAX_CALL_STACK_DEPTH: usize = 64; -#[cfg(not(feature = "unchecked"))] -#[cfg(not(debug_assertions))] -pub const MAX_EXPR_DEPTH: usize = 64; -#[cfg(not(feature = "unchecked"))] -#[cfg(not(feature = "no_function"))] -#[cfg(not(debug_assertions))] -pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32; - -pub const MAX_DYNAMIC_PARAMETERS: usize = 16; - pub const KEYWORD_PRINT: &str = "print"; pub const KEYWORD_DEBUG: &str = "debug"; pub const KEYWORD_TYPE_OF: &str = "type_of"; diff --git a/src/func/call.rs b/src/func/call.rs index f1cf279b..f78dd894 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -2,11 +2,11 @@ use super::native::{CallableFunction, FnAny}; use super::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; +use crate::api::limits::defaults::MAX_DYNAMIC_PARAMETERS; use crate::ast::FnCallHashes; use crate::engine::{ EvalState, FnResolutionCacheEntry, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, - MAX_DYNAMIC_PARAMETERS, }; use crate::module::NamespaceRef; use crate::tokenizer::Token; From 29a9b3878dd08f289741352c954e1a525faf2c22 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 17 Dec 2021 16:32:34 +0800 Subject: [PATCH 11/97] Fix builds. --- src/ast/mod.rs | 5 ++++- src/packages/iter_basic.rs | 8 -------- tests/custom_syntax.rs | 1 + 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 0bb45b1e..3b269a71 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -8,12 +8,15 @@ pub mod script_fn; pub mod stmt; pub use ast::{ASTNode, AST}; -pub use expr::{BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnCallHashes}; +pub use expr::{BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes}; pub use flags::{FnAccess, OptionFlags, AST_OPTION_FLAGS}; pub use ident::Ident; #[cfg(not(feature = "no_function"))] pub use script_fn::{ScriptFnDef, ScriptFnMetadata}; pub use stmt::{OpAssignment, Stmt, StmtBlock}; +#[cfg(not(feature = "no_float"))] +pub use expr::FloatWrapper; + #[cfg(feature = "no_function")] pub struct ScriptFnDef; diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 75538f63..36268851 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -551,10 +551,6 @@ mod range_functions { let _range = range; true } - #[rhai_fn(get = "is_empty", name = "is_empty", pure)] - pub fn is_empty(range: &mut ExclusiveRange) -> bool { - range.is_empty() - } #[rhai_fn(get = "start", name = "start", pure)] pub fn start_inclusive(range: &mut InclusiveRange) -> INT { *range.start() @@ -567,10 +563,6 @@ mod range_functions { pub fn contains_inclusive(range: &mut InclusiveRange, value: INT) -> bool { range.contains(&value) } - #[rhai_fn(get = "is_empty", name = "is_empty", pure)] - pub fn is_empty_inclusive(range: &mut InclusiveRange) -> bool { - range.is_empty() - } #[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)] pub fn is_inclusive_inclusive(range: &mut InclusiveRange) -> bool { let _range = range; diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index ba4c433a..efaf1675 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -267,6 +267,7 @@ fn test_custom_syntax_raw2() -> Result<(), Box> { assert_eq!(engine.eval::("#-1")?, -1); assert_eq!(engine.eval::("let x = 41; x + #1")?, 42); + #[cfg(not(feature = "no_object"))] assert_eq!(engine.eval::("#-42.abs()")?, 42); assert_eq!(engine.eval::("#42/2")?, 21); assert_eq!(engine.eval::("sign(#1)")?, 1); From 8d67c7029422a9d5d7b42ef817286dbc2ed6a62f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 17 Dec 2021 16:55:07 +0800 Subject: [PATCH 12/97] Fix builds. --- src/api/limits.rs | 35 ++++------------------------------- src/api/mod.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/api/limits.rs b/src/api/limits.rs index ec609944..876a5f1b 100644 --- a/src/api/limits.rs +++ b/src/api/limits.rs @@ -1,39 +1,12 @@ //! Settings for [`Engine`]'s limitations. #![cfg(not(feature = "unchecked"))] +use super::default_limits; use crate::Engine; use std::num::{NonZeroU64, NonZeroUsize}; #[cfg(feature = "no_std")] use std::prelude::v1::*; -pub mod defaults { - #[cfg(not(feature = "unchecked"))] - #[cfg(debug_assertions)] - #[cfg(not(feature = "no_function"))] - pub const MAX_CALL_STACK_DEPTH: usize = 8; - #[cfg(not(feature = "unchecked"))] - #[cfg(debug_assertions)] - pub const MAX_EXPR_DEPTH: usize = 32; - #[cfg(not(feature = "unchecked"))] - #[cfg(not(feature = "no_function"))] - #[cfg(debug_assertions)] - pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16; - - #[cfg(not(feature = "unchecked"))] - #[cfg(not(debug_assertions))] - #[cfg(not(feature = "no_function"))] - pub const MAX_CALL_STACK_DEPTH: usize = 64; - #[cfg(not(feature = "unchecked"))] - #[cfg(not(debug_assertions))] - pub const MAX_EXPR_DEPTH: usize = 64; - #[cfg(not(feature = "unchecked"))] - #[cfg(not(feature = "no_function"))] - #[cfg(not(debug_assertions))] - pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32; - - pub const MAX_DYNAMIC_PARAMETERS: usize = 16; -} - /// A type containing all the limits imposed by the [`Engine`]. /// /// Not available under `unchecked`. @@ -84,10 +57,10 @@ impl Limits { pub const fn new() -> Self { Self { #[cfg(not(feature = "no_function"))] - max_call_stack_depth: defaults::MAX_CALL_STACK_DEPTH, - max_expr_depth: NonZeroUsize::new(defaults::MAX_EXPR_DEPTH), + max_call_stack_depth: default_limits::MAX_CALL_STACK_DEPTH, + max_expr_depth: NonZeroUsize::new(default_limits::MAX_EXPR_DEPTH), #[cfg(not(feature = "no_function"))] - max_function_expr_depth: NonZeroUsize::new(defaults::MAX_FUNCTION_EXPR_DEPTH), + max_function_expr_depth: NonZeroUsize::new(default_limits::MAX_FUNCTION_EXPR_DEPTH), max_operations: None, #[cfg(not(feature = "no_module"))] max_modules: usize::MAX, diff --git a/src/api/mod.rs b/src/api/mod.rs index 7accbaf6..13c5488a 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -27,6 +27,34 @@ use crate::{Engine, Identifier}; #[cfg(feature = "no_std")] use std::prelude::v1::*; +pub mod default_limits { + #[cfg(not(feature = "unchecked"))] + #[cfg(debug_assertions)] + #[cfg(not(feature = "no_function"))] + pub const MAX_CALL_STACK_DEPTH: usize = 8; + #[cfg(not(feature = "unchecked"))] + #[cfg(debug_assertions)] + pub const MAX_EXPR_DEPTH: usize = 32; + #[cfg(not(feature = "unchecked"))] + #[cfg(not(feature = "no_function"))] + #[cfg(debug_assertions)] + pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16; + + #[cfg(not(feature = "unchecked"))] + #[cfg(not(debug_assertions))] + #[cfg(not(feature = "no_function"))] + pub const MAX_CALL_STACK_DEPTH: usize = 64; + #[cfg(not(feature = "unchecked"))] + #[cfg(not(debug_assertions))] + pub const MAX_EXPR_DEPTH: usize = 64; + #[cfg(not(feature = "unchecked"))] + #[cfg(not(feature = "no_function"))] + #[cfg(not(debug_assertions))] + pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32; + + pub const MAX_DYNAMIC_PARAMETERS: usize = 16; +} + /// Script optimization API. #[cfg(not(feature = "no_optimize"))] impl Engine { From dc6896fbe2e26134b0558bba8c7c2dcb58a408a4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 17 Dec 2021 16:55:24 +0800 Subject: [PATCH 13/97] Add OpAssignment::new_from_token. --- src/ast/stmt.rs | 22 +++++++++++++++------- src/engine.rs | 10 +++++----- src/func/call.rs | 2 +- src/optimizer.rs | 4 +--- src/parser.rs | 2 +- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 6cb2d6a5..c09c3ff5 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -30,19 +30,27 @@ impl OpAssignment<'_> { /// /// # Panics /// - /// Panics if the operator name is not an op-assignment operator. + /// Panics if the name is not an op-assignment operator. #[must_use] - pub fn new(op: Token) -> Self { + #[inline(always)] + pub fn new(name: &str) -> Self { + Self::new_from_token(Token::lookup_from_syntax(name).expect("operator")) + } + /// Create a new [`OpAssignment`] from a [`Token`]. + /// + /// # Panics + /// + /// Panics if the token is not an op-assignment operator. + #[must_use] + pub fn new_from_token(op: Token) -> Self { let op_raw = op .map_op_assignment() - .expect("op-assignment") + .expect("op-assignment operator") .literal_syntax(); - let op_assignment = op.literal_syntax(); - Self { - hash_op_assign: calc_fn_hash(op_assignment, 2), + hash_op_assign: calc_fn_hash(op.literal_syntax(), 2), hash_op: calc_fn_hash(op_raw, 2), - op: op_assignment, + op: op.literal_syntax(), } } } diff --git a/src/engine.rs b/src/engine.rs index 49ea0512..e88e76e3 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -335,20 +335,20 @@ pub const FN_IDX_SET: &str = "index$set$"; pub const FN_ANONYMOUS: &str = "anon$"; /// Standard equality comparison operator. -pub const OP_EQUALS: &str = "=="; +pub const OP_EQUALS: &str = Token::EqualsTo.literal_syntax(); /// Standard method function for containment testing. /// The `in` operator is implemented as a call to this method. pub const OP_CONTAINS: &str = "contains"; /// Standard exclusive range operator. -pub const OP_EXCLUSIVE_RANGE: &str = ".."; +pub const OP_EXCLUSIVE_RANGE: &str = Token::ExclusiveRange.literal_syntax(); /// Standard inclusive range operator. -pub const OP_INCLUSIVE_RANGE: &str = "..="; +pub const OP_INCLUSIVE_RANGE: &str = Token::InclusiveRange.literal_syntax(); /// Standard concatenation operator token. -pub const TOKEN_OP_CONCAT: Token = Token::PlusAssign; +pub const OP_CONCAT: &str = Token::PlusAssign.literal_syntax(); /// Method of chaining. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] @@ -2312,7 +2312,7 @@ impl Engine { mods, state, lib, - Some(OpAssignment::new(TOKEN_OP_CONCAT)), + Some(OpAssignment::new(OP_CONCAT)), pos, &mut (&mut result).into(), ("", Position::NONE), diff --git a/src/func/call.rs b/src/func/call.rs index f78dd894..87478c71 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -2,7 +2,7 @@ use super::native::{CallableFunction, FnAny}; use super::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; -use crate::api::limits::defaults::MAX_DYNAMIC_PARAMETERS; +use crate::api::default_limits::MAX_DYNAMIC_PARAMETERS; use crate::ast::FnCallHashes; use crate::engine::{ EvalState, FnResolutionCacheEntry, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, diff --git a/src/optimizer.rs b/src/optimizer.rs index 1526da0b..2ad5bc47 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -381,9 +381,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b match x.2 { Expr::FnCall(ref mut x2, _) => { state.set_dirty(); - let op = Token::lookup_from_syntax(&x2.name).expect("operator"); - let op_assignment = op.make_op_assignment().expect("operator"); - x.1 = Some(OpAssignment::new(op_assignment)); + x.1 = Some(OpAssignment::new(&x2.name)); let value = mem::take(&mut x2.args[1]); diff --git a/src/parser.rs b/src/parser.rs index d670caf3..2d0601ca 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1716,7 +1716,7 @@ fn make_assignment_stmt( } } - let op_info = op.map(OpAssignment::new); + let op_info = op.map(OpAssignment::new_from_token); match lhs { // const_expr = rhs From 1c1247ac9aba266de5caa1c410dc35881a42bc1f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 12:28:56 +0800 Subject: [PATCH 14/97] Tidy up code. --- src/types/dynamic.rs | 259 +++++++++++++++++++++---------------------- 1 file changed, 124 insertions(+), 135 deletions(-) diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 0c056d81..4fee4279 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -1031,6 +1031,24 @@ impl Dynamic { pub fn from_decimal(value: rust_decimal::Decimal) -> Self { Self(Union::Decimal(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } + /// Create a [`Dynamic`] from an [`Array`][crate::Array]. + #[cfg(not(feature = "no_index"))] + #[inline(always)] + pub fn from_array(array: crate::Array) -> Self { + Self(Union::Array(array.into(), DEFAULT_TAG_VALUE, ReadWrite)) + } + /// Create a [`Dynamic`] from a [`Blob`][crate::Blob]. + #[cfg(not(feature = "no_index"))] + #[inline(always)] + pub fn from_blob(blob: crate::Blob) -> Self { + Self(Union::Blob(blob.into(), DEFAULT_TAG_VALUE, ReadWrite)) + } + /// Create a [`Dynamic`] from a [`Map`][crate::Map]. + #[cfg(not(feature = "no_object"))] + #[inline(always)] + pub fn from_map(map: crate::Map) -> Self { + Self(Union::Map(map.into(), DEFAULT_TAG_VALUE, ReadWrite)) + } /// Create a new [`Dynamic`] from an [`Instant`]. /// /// Not available under `no-std`. @@ -1177,11 +1195,14 @@ impl Dynamic { /// # Notes /// /// Beware that you need to pass in an [`Array`][crate::Array] type for it to be recognized as an [`Array`][crate::Array]. - /// A [`Vec`][Vec] does not get automatically converted to an [`Array`][crate::Array], but will be a generic - /// restricted trait object instead, because [`Vec`][Vec] is not a supported standard type. + /// A [`Vec`][Vec] does not get automatically converted to an [`Array`][crate::Array], but + /// will be a custom type instead (stored as a trait object). Use `Into` to convert a + /// [`Vec`][Vec] into a [`Dynamic`] as an [`Array`][crate::Array] value. /// /// Similarly, passing in a [`HashMap`][std::collections::HashMap] or - /// [`BTreeMap`][std::collections::BTreeMap] will not get a [`Map`][crate::Map] but a trait object. + /// [`BTreeMap`][std::collections::BTreeMap] will not get a [`Map`][crate::Map] + /// but a custom type. Again, use `Into` to get a [`Dynamic`] with a + /// [`Map`][crate::Map] value. /// /// # Examples /// @@ -1256,7 +1277,7 @@ impl Dynamic { #[cfg(not(feature = "no_index"))] { value = match unsafe_try_cast::<_, crate::Blob>(value) { - Ok(blob) => return Self(Union::Blob(Box::new(blob), DEFAULT_TAG_VALUE, ReadWrite)), + Ok(blob) => return blob.into(), Err(value) => value, }; } @@ -1297,8 +1318,7 @@ impl Dynamic { )) } /// Turn the [`Dynamic`] value into a shared [`Dynamic`] value backed by an - /// [`Rc`][std::rc::Rc]`<`[`RefCell`][std::cell::RefCell]`<`[`Dynamic`]`>>` or - /// [`Arc`][std::sync::Arc]`<`[`RwLock`][std::sync::RwLock]`<`[`Dynamic`]`>>` + /// [`Rc>`][std::rc::Rc] or [`Arc>`][std::sync::Arc] /// depending on the `sync` feature. /// /// Not available under `no_closure`. @@ -2030,117 +2050,9 @@ impl Dynamic { _ => Err(self.type_name()), } } -} - -impl From<()> for Dynamic { - #[inline(always)] - fn from(value: ()) -> Self { - Self(Union::Unit(value, DEFAULT_TAG_VALUE, ReadWrite)) - } -} -impl From for Dynamic { - #[inline(always)] - fn from(value: bool) -> Self { - Self(Union::Bool(value, DEFAULT_TAG_VALUE, ReadWrite)) - } -} -impl From for Dynamic { - #[inline(always)] - fn from(value: INT) -> Self { - Self(Union::Int(value, DEFAULT_TAG_VALUE, ReadWrite)) - } -} -#[cfg(not(feature = "no_float"))] -impl From for Dynamic { - #[inline(always)] - fn from(value: crate::FLOAT) -> Self { - Self(Union::Float(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) - } -} -#[cfg(not(feature = "no_float"))] -impl From> for Dynamic { - #[inline(always)] - fn from(value: crate::ast::FloatWrapper) -> Self { - Self(Union::Float(value, DEFAULT_TAG_VALUE, ReadWrite)) - } -} -#[cfg(feature = "decimal")] -impl From for Dynamic { - #[inline(always)] - fn from(value: rust_decimal::Decimal) -> Self { - Self(Union::Decimal(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) - } -} -impl From for Dynamic { - #[inline(always)] - fn from(value: char) -> Self { - Self(Union::Char(value, DEFAULT_TAG_VALUE, ReadWrite)) - } -} -impl> From for Dynamic { - #[inline(always)] - fn from(value: S) -> Self { - Self(Union::Str(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) - } -} -impl From<&ImmutableString> for Dynamic { - #[inline(always)] - fn from(value: &ImmutableString) -> Self { - value.clone().into() - } -} -impl FromStr for Dynamic { - type Err = (); - - fn from_str(value: &str) -> Result { - Ok(Self(Union::Str(value.into(), DEFAULT_TAG_VALUE, ReadWrite))) - } -} -#[cfg(not(feature = "no_index"))] -impl Dynamic { - /// Create a [`Dynamic`] from an [`Array`][crate::Array]. - #[inline(always)] - pub(crate) fn from_array(array: crate::Array) -> Self { - Self(Union::Array(array.into(), DEFAULT_TAG_VALUE, ReadWrite)) - } -} -#[cfg(not(feature = "no_index"))] -impl From> for Dynamic { - #[inline] - fn from(value: Vec) -> Self { - Self(Union::Array( - Box::new(value.into_iter().map(Dynamic::from).collect()), - DEFAULT_TAG_VALUE, - ReadWrite, - )) - } -} -#[cfg(not(feature = "no_index"))] -impl From<&[T]> for Dynamic { - #[inline] - fn from(value: &[T]) -> Self { - Self(Union::Array( - Box::new(value.iter().cloned().map(Dynamic::from).collect()), - DEFAULT_TAG_VALUE, - ReadWrite, - )) - } -} -#[cfg(not(feature = "no_index"))] -impl std::iter::FromIterator for Dynamic { - #[inline] - fn from_iter>(iter: X) -> Self { - Self(Union::Array( - Box::new(iter.into_iter().map(Dynamic::from).collect()), - DEFAULT_TAG_VALUE, - ReadWrite, - )) - } -} -#[cfg(not(feature = "no_index"))] -impl Dynamic { /// Convert the [`Dynamic`] into an [`Array`][crate::Array]. /// Returns the name of the actual type if the cast fails. + #[cfg(not(feature = "no_index"))] #[inline(always)] pub fn into_array(self) -> Result { match self.0 { @@ -2215,16 +2127,9 @@ impl Dynamic { _ => Err(self.type_name()), } } -} -#[cfg(not(feature = "no_index"))] -impl Dynamic { - /// Create a [`Dynamic`] from a [`Vec`]. - #[inline(always)] - pub fn from_blob(blob: crate::Blob) -> Self { - Self(Union::Blob(Box::new(blob), DEFAULT_TAG_VALUE, ReadWrite)) - } - /// Convert the [`Dynamic`] into a [`Vec`]. + /// Convert the [`Dynamic`] into a [`Blob`][crate::Blob]. /// Returns the name of the actual type if the cast fails. + #[cfg(not(feature = "no_index"))] #[inline(always)] pub fn into_blob(self) -> Result { match self.0 { @@ -2245,12 +2150,102 @@ impl Dynamic { } } } -#[cfg(not(feature = "no_object"))] -impl Dynamic { - /// Create a [`Dynamic`] from a [`Map`][crate::Map]. + +impl From<()> for Dynamic { #[inline(always)] - pub(crate) fn from_map(map: crate::Map) -> Self { - Self(Union::Map(map.into(), DEFAULT_TAG_VALUE, ReadWrite)) + fn from(value: ()) -> Self { + Self(Union::Unit(value, DEFAULT_TAG_VALUE, ReadWrite)) + } +} +impl From for Dynamic { + #[inline(always)] + fn from(value: bool) -> Self { + Self(Union::Bool(value, DEFAULT_TAG_VALUE, ReadWrite)) + } +} +impl From for Dynamic { + #[inline(always)] + fn from(value: INT) -> Self { + Self(Union::Int(value, DEFAULT_TAG_VALUE, ReadWrite)) + } +} +#[cfg(not(feature = "no_float"))] +impl From for Dynamic { + #[inline(always)] + fn from(value: crate::FLOAT) -> Self { + Self(Union::Float(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) + } +} +#[cfg(not(feature = "no_float"))] +impl From> for Dynamic { + #[inline(always)] + fn from(value: crate::ast::FloatWrapper) -> Self { + Self(Union::Float(value, DEFAULT_TAG_VALUE, ReadWrite)) + } +} +#[cfg(feature = "decimal")] +impl From for Dynamic { + #[inline(always)] + fn from(value: rust_decimal::Decimal) -> Self { + Self(Union::Decimal(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) + } +} +impl From for Dynamic { + #[inline(always)] + fn from(value: char) -> Self { + Self(Union::Char(value, DEFAULT_TAG_VALUE, ReadWrite)) + } +} +impl> From for Dynamic { + #[inline(always)] + fn from(value: S) -> Self { + Self(Union::Str(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) + } +} +impl From<&ImmutableString> for Dynamic { + #[inline(always)] + fn from(value: &ImmutableString) -> Self { + value.clone().into() + } +} +impl FromStr for Dynamic { + type Err = (); + + fn from_str(value: &str) -> Result { + Ok(Self(Union::Str(value.into(), DEFAULT_TAG_VALUE, ReadWrite))) + } +} +#[cfg(not(feature = "no_index"))] +impl From> for Dynamic { + #[inline] + fn from(value: Vec) -> Self { + Self(Union::Array( + Box::new(value.into_iter().map(Dynamic::from).collect()), + DEFAULT_TAG_VALUE, + ReadWrite, + )) + } +} +#[cfg(not(feature = "no_index"))] +impl From<&[T]> for Dynamic { + #[inline] + fn from(value: &[T]) -> Self { + Self(Union::Array( + Box::new(value.iter().cloned().map(Dynamic::from).collect()), + DEFAULT_TAG_VALUE, + ReadWrite, + )) + } +} +#[cfg(not(feature = "no_index"))] +impl std::iter::FromIterator for Dynamic { + #[inline] + fn from_iter>(iter: X) -> Self { + Self(Union::Array( + Box::new(iter.into_iter().map(Dynamic::from).collect()), + DEFAULT_TAG_VALUE, + ReadWrite, + )) } } #[cfg(not(feature = "no_object"))] @@ -2329,12 +2324,6 @@ impl From for Dynamic { Self(Union::FnPtr(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } -impl From> for Dynamic { - #[inline(always)] - fn from(value: Box) -> Self { - Self(Union::FnPtr(value, DEFAULT_TAG_VALUE, ReadWrite)) - } -} #[cfg(not(feature = "no_std"))] impl From for Dynamic { #[inline(always)] From fc84600b2222b56efc1fd364bb17bbe5dad2cca0 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 12:29:04 +0800 Subject: [PATCH 15/97] Add NativeCallContext::call_fn --- CHANGELOG.md | 5 +++++ src/func/native.rs | 31 +++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1e17785..5fbc5f3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ New features * Added support for integer _ranges_ via the `..` and `..=` operators. +Enhancements +------------ + +* Added `NativeCallContext::call_fn` to easily call a function. + Deprecated API's ---------------- diff --git a/src/func/native.rs b/src/func/native.rs index b31c1327..f23d04b4 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -5,12 +5,14 @@ use crate::ast::{FnAccess, FnCallHashes}; use crate::engine::{EvalState, Imports}; use crate::plugin::PluginFunction; use crate::tokenizer::{Token, TokenizeState}; +use crate::types::dynamic::Variant; use crate::{ - calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, Module, Position, RhaiResult, + calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, FuncArgs, Module, Position, + RhaiResult, StaticVec, }; -use std::fmt; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{any::type_name, fmt}; /// Trait that maps to `Send + Sync` only under the `sync` feature. #[cfg(feature = "sync")] @@ -225,6 +227,31 @@ impl<'a> NativeCallContext<'a> { pub const fn namespaces(&self) -> &[&Module] { self.lib } + /// Call a function inside the call context with the provided arguments. + #[inline] + pub fn call_fn( + &self, + fn_name: impl AsRef, + args: impl FuncArgs, + ) -> Result> { + let mut arg_values = StaticVec::new_const(); + args.parse(&mut arg_values); + + let mut args: StaticVec<_> = arg_values.iter_mut().collect(); + + let result = self.call_fn_raw(fn_name, false, false, &mut args)?; + + let typ = self.engine().map_type_name(result.type_name()); + + result.try_cast().ok_or_else(|| { + EvalAltResult::ErrorMismatchOutputType( + self.engine().map_type_name(type_name::()).into(), + typ.into(), + Position::NONE, + ) + .into() + }) + } /// Call a function inside the call context. /// /// If `is_method_call` is [`true`], the first argument is assumed to be the `this` pointer for From 2b79a654541d3dfcd0b05deed9e84040f59f4885 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 14:50:37 +0800 Subject: [PATCH 16/97] Move range contains into builtin. --- src/func/builtin.rs | 28 ++++++++++++++++++++++------ src/packages/iter_basic.rs | 8 -------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/func/builtin.rs b/src/func/builtin.rs index a257e2fd..aad1f1d7 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -250,7 +250,7 @@ pub fn get_builtin_binary_op_fn( OP_CONTAINS => Some(|_, args| { let s = &*args[0].read_lock::().expect(BUILTIN); let c = args[1].as_char().expect(BUILTIN); - Ok((s.contains(c)).into()) + Ok(s.contains(c).into()) }), _ => None, }; @@ -306,6 +306,16 @@ pub fn get_builtin_binary_op_fn( // Handle ranges here because ranges are implemented as custom type if type1 == TypeId::of::() { + if type2 == TypeId::of::() { + return match op { + OP_CONTAINS => Some(|_, args| { + let range = &*args[0].read_lock::().expect(BUILTIN); + let x = args[1].as_int().expect("`INT`"); + Ok(range.contains(&x).into()) + }), + _ => None, + }; + } if type1 == type2 { return match op { "==" => Some(impl_op!(ExclusiveRange == ExclusiveRange)), @@ -316,6 +326,16 @@ pub fn get_builtin_binary_op_fn( } if type1 == TypeId::of::() { + if type2 == TypeId::of::() { + return match op { + OP_CONTAINS => Some(|_, args| { + let range = &*args[0].read_lock::().expect(BUILTIN); + let x = args[1].as_int().expect("`INT`"); + Ok(range.contains(&x).into()) + }), + _ => None, + }; + } if type1 == type2 { return match op { "==" => Some(impl_op!(InclusiveRange == InclusiveRange)), @@ -433,11 +453,7 @@ pub fn get_builtin_binary_op_fn( ">=" => Some(impl_op!(ImmutableString >= ImmutableString)), "<" => Some(impl_op!(ImmutableString < ImmutableString)), "<=" => Some(impl_op!(ImmutableString <= ImmutableString)), - OP_CONTAINS => Some(|_, args| { - let s1 = &*args[0].read_lock::().expect(BUILTIN); - let s2 = &*args[1].read_lock::().expect(BUILTIN); - Ok((s1.contains(s2.as_str())).into()) - }), + OP_CONTAINS => Some(impl_op!(ImmutableString.contains(ImmutableString.as_str()))), _ => None, }; } diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 36268851..86952e05 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -537,10 +537,6 @@ mod range_functions { pub fn end(range: &mut ExclusiveRange) -> INT { range.end } - #[rhai_fn(name = "contains", pure)] - pub fn contains(range: &mut ExclusiveRange, value: INT) -> bool { - range.contains(&value) - } #[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)] pub fn is_inclusive(range: &mut ExclusiveRange) -> bool { let _range = range; @@ -559,10 +555,6 @@ mod range_functions { pub fn end_inclusive(range: &mut InclusiveRange) -> INT { *range.end() } - #[rhai_fn(name = "contains", pure)] - pub fn contains_inclusive(range: &mut InclusiveRange, value: INT) -> bool { - range.contains(&value) - } #[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)] pub fn is_inclusive_inclusive(range: &mut InclusiveRange) -> bool { let _range = range; From 9cf83606169a737936d8d10a440cdedacf7bed44 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 14:58:02 +0800 Subject: [PATCH 17/97] Move blob functions to builtin. --- src/func/builtin.rs | 24 ++++++++++++++++++++++++ src/packages/blob_basic.rs | 20 -------------------- src/packages/map_basic.rs | 8 -------- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/func/builtin.rs b/src/func/builtin.rs index aad1f1d7..38a73af8 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -274,6 +274,30 @@ pub fn get_builtin_binary_op_fn( }; } + // blob + #[cfg(not(feature = "no_index"))] + if type1 == TypeId::of::() { + use crate::Blob; + + if type2 == TypeId::of::() { + return match op { + OP_CONTAINS => Some(|_, args| { + let range = &*args[0].read_lock::().expect(BUILTIN); + let x = (args[1].as_int().expect("`INT`") & 0x000000ff) as u8; + Ok(range.contains(&x).into()) + }), + _ => None, + }; + } + if type1 == type2 { + return match op { + "==" => Some(impl_op!(Blob == Blob)), + "!=" => Some(impl_op!(Blob != Blob)), + _ => None, + }; + } + } + // map op string #[cfg(not(feature = "no_object"))] if types_pair == (TypeId::of::(), TypeId::of::()) { diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index aea9851c..8d63a2a7 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -366,26 +366,6 @@ mod blob_functions { drained } - pub fn contains(blob: &mut Blob, value: Dynamic) -> bool { - if blob.is_empty() { - return false; - } - - let value = match value.as_int() { - Ok(value) => value as u8, - _ => return false, - }; - - blob.contains(&value) - } - #[rhai_fn(name = "==", pure)] - pub fn equals(blob1: &mut Blob, blob2: Blob) -> bool { - &*blob1 == &blob2 - } - #[rhai_fn(name = "!=", pure)] - pub fn not_equals(blob1: &mut Blob, blob2: Blob) -> bool { - &*blob1 != &blob2 - } #[inline] fn parse_int(blob: &mut Blob, start: INT, len: INT, is_le: bool) -> INT { diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index f8338043..eec7f678 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -17,14 +17,6 @@ def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, { #[export_module] mod map_functions { - #[rhai_fn(name = "has", pure)] - pub fn contains(map: &mut Map, prop: ImmutableString) -> bool { - if map.is_empty() { - false - } else { - map.contains_key(prop.as_str()) - } - } #[rhai_fn(pure)] pub fn len(map: &mut Map) -> INT { map.len() as INT From 82d3375fc0ef9a85b5d5b724871b982c913a27c6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 15:36:30 +0800 Subject: [PATCH 18/97] Fix Dynamic::from for BLOB. --- src/types/dynamic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 4fee4279..2fb3f573 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -1277,7 +1277,7 @@ impl Dynamic { #[cfg(not(feature = "no_index"))] { value = match unsafe_try_cast::<_, crate::Blob>(value) { - Ok(blob) => return blob.into(), + Ok(blob) => return Dynamic::from_blob(blob), // don't use blob.into() because it'll be converted into an Array Err(value) => value, }; } From 0ae4d14a626aa62db4be1012de7a084de9e1aeab Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 15:37:20 +0800 Subject: [PATCH 19/97] Move BLOB concat and push to builtin. --- src/func/builtin.rs | 61 +++++++++++++++++++++++++++++++++++-- src/packages/array_basic.rs | 2 +- src/packages/blob_basic.rs | 17 ++--------- 3 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 38a73af8..4ab2f938 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -282,15 +282,37 @@ pub fn get_builtin_binary_op_fn( if type2 == TypeId::of::() { return match op { OP_CONTAINS => Some(|_, args| { - let range = &*args[0].read_lock::().expect(BUILTIN); + let blob = &*args[0].read_lock::().expect(BUILTIN); let x = (args[1].as_int().expect("`INT`") & 0x000000ff) as u8; - Ok(range.contains(&x).into()) + Ok(blob.contains(&x).into()) }), _ => None, }; } if type1 == type2 { return match op { + "+" => Some(|_, args| { + let blob1 = &*args[0].read_lock::().expect(BUILTIN); + let blob2 = &*args[1].read_lock::().expect(BUILTIN); + let result = if blob1.is_empty() { + if blob2.is_empty() { + Blob::new() + } else { + blob2.clone() + } + } else { + if blob2.is_empty() { + blob1.clone() + } else { + let mut blob = Blob::with_capacity(blob1.len() + blob2.len()); + blob.extend_from_slice(blob1); + blob.extend_from_slice(blob2); + blob + } + }; + + Ok(Dynamic::from_blob(result)) // do not use result.into() because it'll convert into an Array + }), "==" => Some(impl_op!(Blob == Blob)), "!=" => Some(impl_op!(Blob != Blob)), _ => None, @@ -652,6 +674,20 @@ pub fn get_builtin_op_assignment_fn( _ => None, }; } + // Blob op= int + #[cfg(not(feature = "no_index"))] + if types_pair == (TypeId::of::(), TypeId::of::()) { + use crate::Blob; + + return match op { + "+=" => Some(|_, args| { + let x = (args[1].as_int().expect("`INT`") & 0x000000ff) as u8; + let mut blob = args[0].write_lock::().expect(BUILTIN); + Ok(blob.push(x).into()) + }), + _ => None, + }; + } // No built-in op-assignments for different types. if type2 != type1 { @@ -734,5 +770,26 @@ pub fn get_builtin_op_assignment_fn( }; } + #[cfg(not(feature = "no_index"))] + if type1 == TypeId::of::() { + use crate::Blob; + + return match op { + "+=" => Some(|_, args| { + let blob2 = std::mem::take(args[1]).cast::(); + let mut blob1 = args[0].write_lock::().expect(BUILTIN); + if !blob2.is_empty() { + if blob1.is_empty() { + *blob1 = blob2; + } else { + blob1.extend_from_slice(&blob2); + } + } + Ok(Dynamic::UNIT) + }), + _ => None, + }; + } + None } diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 9ceeda20..cc1f68db 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -172,7 +172,7 @@ mod array_functions { .checked_abs() .map_or(0, |n| arr_len - (n as usize).min(arr_len)) } else if start as usize >= array.len() { - array.extend(replace.into_iter()); + array.extend(replace); return; } else { start as usize diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index 8d63a2a7..2adf04f8 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -61,12 +61,12 @@ mod blob_functions { pub fn len(blob: &mut Blob) -> INT { blob.len() as INT } - #[rhai_fn(name = "push", name = "+=")] + #[rhai_fn(name = "push")] pub fn push(blob: &mut Blob, item: INT) { let item = (item & 0x00ff) as u8; blob.push(item); } - #[rhai_fn(name = "append", name = "+=")] + #[rhai_fn(name = "append")] pub fn append(blob: &mut Blob, y: Blob) { if !y.is_empty() { if blob.is_empty() { @@ -76,17 +76,6 @@ mod blob_functions { } } } - #[rhai_fn(name = "+")] - pub fn concat(mut blob: Blob, y: Blob) -> Blob { - if !y.is_empty() { - if blob.is_empty() { - blob = y; - } else { - blob.extend(y); - } - } - blob - } pub fn insert(blob: &mut Blob, position: INT, item: INT) { let item = (item & 0x00ff) as u8; @@ -211,7 +200,7 @@ mod blob_functions { .checked_abs() .map_or(0, |n| blob_len - (n as usize).min(blob_len)) } else if start as usize >= blob_len { - blob.extend(replace.into_iter()); + blob.extend(replace); return; } else { start as usize From 973617108930fd72e3295ffbc6537b7ca9310b8e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 17:07:30 +0800 Subject: [PATCH 20/97] Fix bug in hex parsing for negative numbers. --- CHANGELOG.md | 1 + src/lib.rs | 14 ++++++++++++++ src/packages/math_basic.rs | 18 ++++++++++-------- src/tokenizer.rs | 9 ++++++--- tests/number_literals.rs | 17 +++++++++++++++++ 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7a27f27..59d7bc88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Bug fixes --------- * Custom syntax now works properly inside binary expressions and with method calls. +* Hex numbers with the high-bit set now parse correctly into negative integer numbers. Enhancements ------------ diff --git a/src/lib.rs b/src/lib.rs index 077245c1..639d92de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,6 +98,20 @@ pub type INT = i64; #[cfg(feature = "only_i32")] pub type INT = i32; +/// The system base integer type. It is defined as [`u64`]. +/// +/// If the `only_i32` feature is enabled, this will be [`u32`] instead. +#[cfg(not(feature = "only_i32"))] +#[allow(non_camel_case_types)] +type INT_BASE = u64; +/// The system integer base type. +/// It is defined as [`u32`] since the `only_i32` feature is used. +/// +/// If the `only_i32` feature is not used, this will be `u64` instead. +#[cfg(feature = "only_i32")] +#[allow(non_camel_case_types)] +type INT_BASE = u32; + /// The system floating-point type. It is defined as [`f64`]. /// Not available under `no_float`. /// diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 0479b937..2e9eec92 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use crate::plugin::*; -use crate::{def_package, Position, INT}; +use crate::{def_package, Position, INT, INT_BASE}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -120,13 +120,15 @@ mod int_functions { .into()); } - INT::from_str_radix(string.trim(), radix as u32).map_err(|err| { - EvalAltResult::ErrorArithmetic( - format!("Error parsing integer number '{}': {}", string, err), - Position::NONE, - ) - .into() - }) + INT_BASE::from_str_radix(string.trim(), radix as u32) + .map(|v| v as INT) + .map_err(|err| { + EvalAltResult::ErrorArithmetic( + format!("Error parsing integer number '{}': {}", string, err), + Position::NONE, + ) + .into() + }) } #[rhai_fn(name = "parse_int", return_raw)] pub fn parse_int(string: &str) -> Result> { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index e5a4905d..0be44347 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -5,7 +5,7 @@ use crate::engine::{ KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF, }; use crate::func::native::OnParseTokenCallback; -use crate::{Engine, LexError, StaticVec, INT}; +use crate::{Engine, LexError, StaticVec, INT, INT_BASE}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -1507,7 +1507,8 @@ fn get_next_token_inner( .filter(|&&c| c != NUMBER_SEPARATOR) .collect(); - INT::from_str_radix(&out, radix) + INT_BASE::from_str_radix(&out, radix) + .map(|v| v as INT) .map(Token::IntegerConstant) .unwrap_or_else(|_| { Token::LexError(LERR::MalformedNumber(result.into_iter().collect())) @@ -1515,7 +1516,9 @@ fn get_next_token_inner( } else { let out: String = result.iter().filter(|&&c| c != NUMBER_SEPARATOR).collect(); - let num = INT::from_str(&out).map(Token::IntegerConstant); + let num = INT_BASE::from_str(&out) + .map(|v| v as INT) + .map(Token::IntegerConstant); // If integer parsing is unnecessary, try float instead #[cfg(not(feature = "no_float"))] diff --git a/tests/number_literals.rs b/tests/number_literals.rs index 2e7b6b01..daade83b 100644 --- a/tests/number_literals.rs +++ b/tests/number_literals.rs @@ -27,6 +27,11 @@ fn test_hex_literal() -> Result<(), Box> { assert_eq!(engine.eval::("let x = 0Xf; x")?, 15); assert_eq!(engine.eval::("let x = 0xff; x")?, 255); + #[cfg(not(feature = "only_i32"))] + assert_eq!(engine.eval::("let x = 0xffffffffffffffff; x")?, -1); + #[cfg(feature = "only_i32")] + assert_eq!(engine.eval::("let x = 0xffffffff; x")?, -1); + Ok(()) } @@ -51,6 +56,18 @@ fn test_binary_literal() -> Result<(), Box> { engine.eval::("let x = 0b0011_1100_1010_0101; x")?, 15525 ); + #[cfg(not(feature = "only_i32"))] + assert_eq!( + engine.eval::( + "let x = 0b11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111; x" + )?, + -1 + ); + #[cfg(feature = "only_i32")] + assert_eq!( + engine.eval::("let x = 0b11111111_11111111_11111111_11111111; x")?, + -1 + ); Ok(()) } From 7eda6454a89f7d96b20070cba0b6f1168c27bf2f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 17:47:35 +0800 Subject: [PATCH 21/97] Bug fix. --- src/tokenizer.rs | 4 +--- tests/blobs.rs | 7 +++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 0be44347..ca28add3 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1516,9 +1516,7 @@ fn get_next_token_inner( } else { let out: String = result.iter().filter(|&&c| c != NUMBER_SEPARATOR).collect(); - let num = INT_BASE::from_str(&out) - .map(|v| v as INT) - .map(Token::IntegerConstant); + let num = INT::from_str(&out).map(Token::IntegerConstant); // If integer parsing is unnecessary, try float instead #[cfg(not(feature = "no_float"))] diff --git a/tests/blobs.rs b/tests/blobs.rs index 7036f116..43ba16a1 100644 --- a/tests/blobs.rs +++ b/tests/blobs.rs @@ -136,6 +136,13 @@ fn test_blobs_parse() -> Result<(), Box> { 0x1cf588 ); + assert_eq!( + engine.eval::( + "let x = blob(16, 0); x.write_be(0, 8, 0x1234567890abcdef); x.write_be(8, 8, 0xabcdef1234567890); x" + )?, + vec![0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90] + ); + Ok(()) } From aa8cc8f719656761a791d56ad991bc335460595f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 17:59:32 +0800 Subject: [PATCH 22/97] Fix builds. --- tests/custom_syntax.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index d2bb257a..1d21c315 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -268,6 +268,7 @@ fn test_custom_syntax_raw2() -> Result<(), Box> { ); assert_eq!(engine.eval::("let x = 41; x + #1")?, 42); + #[cfg(not(feature = "no_object"))] assert_eq!(engine.eval::("#-42.abs()")?, 42); assert_eq!(engine.eval::("#42/2")?, 21); assert_eq!(engine.eval::("#-1")?, -1); From ac8324ae41734a825941b3825b0d1261098ff379 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 18:08:48 +0800 Subject: [PATCH 23/97] Fix no_object build. --- tests/blobs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/blobs.rs b/tests/blobs.rs index 43ba16a1..90f8afac 100644 --- a/tests/blobs.rs +++ b/tests/blobs.rs @@ -138,7 +138,7 @@ fn test_blobs_parse() -> Result<(), Box> { assert_eq!( engine.eval::( - "let x = blob(16, 0); x.write_be(0, 8, 0x1234567890abcdef); x.write_be(8, 8, 0xabcdef1234567890); x" + "let x = blob(16, 0); write_be(x, 0, 8, 0x1234567890abcdef); write_be(x, 8, 8, 0xabcdef1234567890); x" )?, vec![0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90] ); From e507dcfcb4e13b883c6266bbd03b30ecbbb0cb07 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 18:44:08 +0800 Subject: [PATCH 24/97] Fix no_index build. --- src/types/dynamic.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 2fb3f573..26251386 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -2074,6 +2074,7 @@ impl Dynamic { } /// Convert the [`Dynamic`] into a [`Vec`]. /// Returns the name of the actual type if any cast fails. + #[cfg(not(feature = "no_index"))] #[inline(always)] pub fn into_typed_array(self) -> Result, &'static str> { match self.0 { @@ -2121,6 +2122,9 @@ impl Dynamic { }) .collect() } + Union::Blob(_, _, _) if TypeId::of::() == TypeId::of::() => { + Ok((*value).clone().cast::>()) + } _ => Err((*value).type_name()), } } From 123012404b47b0c16ae82c6211b23ba1d9a1b981 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 23:03:35 +0800 Subject: [PATCH 25/97] Add Blob::write for strings. --- src/packages/blob_basic.rs | 62 +++++++++++++++++++++++++++++--------- tests/blobs.rs | 20 ++++++++++++ 2 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index 2adf04f8..8e70d94f 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -24,7 +24,7 @@ def_package!(crate:BasicBlobPackage:"Basic BLOB utilities.", lib, { #[export_module] mod blob_functions { - pub fn blob() -> Blob { + pub const fn blob() -> Blob { Blob::new() } #[rhai_fn(name = "blob", return_raw)] @@ -54,7 +54,7 @@ mod blob_functions { } let mut blob = Blob::new(); - blob.resize(len, (value & 0x00ff) as u8); + blob.resize(len, (value & 0x000000ff) as u8); Ok(blob) } #[rhai_fn(name = "len", get = "len", pure)] @@ -63,7 +63,7 @@ mod blob_functions { } #[rhai_fn(name = "push")] pub fn push(blob: &mut Blob, item: INT) { - let item = (item & 0x00ff) as u8; + let item = (item & 0x000000ff) as u8; blob.push(item); } #[rhai_fn(name = "append")] @@ -77,7 +77,7 @@ mod blob_functions { } } pub fn insert(blob: &mut Blob, position: INT, item: INT) { - let item = (item & 0x00ff) as u8; + let item = (item & 0x000000ff) as u8; if blob.is_empty() { blob.push(item); @@ -108,7 +108,7 @@ mod blob_functions { return Ok(()); } - let item = (item & 0x00ff) as u8; + let item = (item & 0x000000ff) as u8; let _ctx = ctx; // Check if blob will be over max size limit @@ -452,13 +452,11 @@ mod blob_functions { let len = usize::min(len, INT_BYTES); - let mut buf = [0_u8; INT_BYTES]; - - buf.copy_from_slice(&if is_le { + let buf = if is_le { value.to_le_bytes() } else { value.to_be_bytes() - }); + }; blob[start..][..len].copy_from_slice(&buf[..len]); } @@ -598,14 +596,11 @@ mod blob_functions { const FLOAT_BYTES: usize = mem::size_of::(); let len = usize::min(len, FLOAT_BYTES); - - let mut buf = [0_u8; FLOAT_BYTES]; - - buf.copy_from_slice(&if is_le { + let buf = if is_le { value.to_le_bytes() } else { value.to_be_bytes() - }); + }; blob[start..][..len].copy_from_slice(&buf[..len]); } @@ -647,4 +642,43 @@ mod blob_functions { pub fn write_be_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) { write_float(blob, start, len, value, false) } + #[rhai_fn(name = "write")] + pub fn write_string(blob: &mut Blob, start: INT, len: INT, string: &str) { + if len <= 0 || blob.is_empty() || string.is_empty() { + return; + } + let blob_len = blob.len(); + + let start = if start < 0 { + start + .checked_abs() + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + } else if start as usize >= blob_len { + return; + } else { + start as usize + }; + + let len = if len as usize > blob_len - start { + blob_len - start + } else { + len as usize + }; + + let len = usize::min(len, string.len()); + + blob[start..][..len].copy_from_slice(string[..len].as_bytes()); + } + #[rhai_fn(name = "write")] + pub fn write_string_range(blob: &mut Blob, range: ExclusiveRange, string: &str) { + let start = INT::max(range.start, 0); + let end = INT::max(range.end, start); + write_string(blob, start, end - start, string) + } + #[rhai_fn(name = "write")] + pub fn write_string_range_inclusive(blob: &mut Blob, range: InclusiveRange, string: &str) { + let start = INT::max(*range.start(), 0); + let end = INT::max(*range.end(), start); + write_string(blob, start, end - start + 1, string) + } } diff --git a/tests/blobs.rs b/tests/blobs.rs index f53958b1..dcca26d3 100644 --- a/tests/blobs.rs +++ b/tests/blobs.rs @@ -169,6 +169,16 @@ fn test_blobs_parse() -> Result<(), Box> { vec![0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90] ); + assert_eq!( + engine.eval::(r#"let x = blob(16, 0); write(x, 0, 14, "hello, world!"); x"#)?, + "hello, world!\0\0\0".as_bytes() + ); + + assert_eq!( + engine.eval::(r#"let x = blob(10, 0); write(x, 3, 5, "hello, world!"); x"#)?, + "\0\0\0hello\0\0".as_bytes() + ); + Ok(()) } @@ -238,5 +248,15 @@ fn test_blobs_parse() -> Result<(), Box> { 0x1cf588 ); + assert_eq!( + engine.eval::(r#"let x = blob(16, 0); write(x, 0, 14, "hello, world!"); x"#)?, + "hello, world!\0\0\0".as_bytes() + ); + + assert_eq!( + engine.eval::(r#"let x = blob(10, 0); write(x, 3, 5, "hello, world!"); x"#)?, + "\0\0\0hello\0\0".as_bytes() + ); + Ok(()) } From 5729f0cdd4f6bbc86b81c9301b8fb29da122c477 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 19 Dec 2021 23:22:14 +0800 Subject: [PATCH 26/97] Add test for call_fn_raw. --- src/api/call_fn.rs | 1 - tests/call_fn.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 6f72e1b7..6ac100b0 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -159,7 +159,6 @@ impl Engine { let orig_scope_len = scope.len(); if eval_ast && !statements.is_empty() { - // Make sure new variables introduced at global level do not _spill_ into the function call self.eval_global_statements(scope, mods, state, statements, &[ast.as_ref()], 0)?; if rewind_scope { diff --git a/tests/call_fn.rs b/tests/call_fn.rs index aa098fb2..0080dfcd 100644 --- a/tests/call_fn.rs +++ b/tests/call_fn.rs @@ -69,6 +69,36 @@ fn test_call_fn() -> Result<(), Box> { Ok(()) } +#[test] +fn test_call_fn_scope() -> Result<(), Box> { + let engine = Engine::new(); + let mut scope = Scope::new(); + + let ast = engine.compile( + " + fn foo(x) { + let hello = 42; + bar + hello + x + } + + let bar = 123; + ", + )?; + + for _ in 0..50 { + assert_eq!( + engine + .call_fn_raw(&mut scope, &ast, true, false, "foo", None, [Dynamic::THREE])? + .as_int()?, + 168 + ); + } + + assert_eq!(scope.len(), 100); + + Ok(()) +} + struct Options { pub foo: bool, pub bar: String, From bca9fe53b0d6194181f23316fe21f40c7aae45f3 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 20 Dec 2021 11:42:39 +0800 Subject: [PATCH 27/97] New syntax for def_package. --- CHANGELOG.md | 2 + src/packages/arithmetic.rs | 51 ++--- src/packages/array_basic.rs | 15 +- src/packages/blob_basic.rs | 15 +- src/packages/fn_basic.rs | 11 +- src/packages/iter_basic.rs | 405 ++++++++++++++++++----------------- src/packages/lang_core.rs | 15 +- src/packages/logic.rs | 51 ++--- src/packages/map_basic.rs | 11 +- src/packages/math_basic.rs | 81 +++---- src/packages/mod.rs | 41 +++- src/packages/pkg_core.rs | 36 ++-- src/packages/pkg_std.rs | 51 ++--- src/packages/string_basic.rs | 13 +- src/packages/string_more.rs | 11 +- src/packages/time_basic.rs | 13 +- 16 files changed, 452 insertions(+), 370 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fc1fe03..46cf10f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,11 +16,13 @@ Enhancements ------------ * Added `NativeCallContext::call_fn` to easily call a function. +* A new syntax is introduced for `def_package!` that will replace the old syntax in future versions. Deprecated API's ---------------- * `Expression::get_variable_name` is deprecated in favor of the new `Expression::get_string_value`. +* The old syntax of `def_package!` is deprecated in favor of the new syntax. Version 1.3.1 diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index 6de5634b..452d0c22 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -179,36 +179,39 @@ macro_rules! reg_functions { )* } } -def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, { - lib.standard = true; +def_package! { + /// Basic arithmetic package. + crate::ArithmeticPackage => |lib| { + lib.standard = true; - combine_with_exported_module!(lib, "int", int_functions); - reg_functions!(lib += signed_basic; INT); + combine_with_exported_module!(lib, "int", int_functions); + reg_functions!(lib += signed_basic; INT); - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_functions!(lib += arith_numbers; i8, u8, i16, u16, i32, u32, u64); - reg_functions!(lib += signed_numbers; i8, i16, i32); - - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] { - reg_functions!(lib += arith_num_128; i128, u128); - reg_functions!(lib += signed_num_128; i128); + reg_functions!(lib += arith_numbers; i8, u8, i16, u16, i32, u32, u64); + reg_functions!(lib += signed_numbers; i8, i16, i32); + + #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + { + reg_functions!(lib += arith_num_128; i128, u128); + reg_functions!(lib += signed_num_128; i128); + } } - } - // Basic arithmetic for floating-point - #[cfg(not(feature = "no_float"))] - { - combine_with_exported_module!(lib, "f32", f32_functions); - combine_with_exported_module!(lib, "f64", f64_functions); - } + // Basic arithmetic for floating-point + #[cfg(not(feature = "no_float"))] + { + combine_with_exported_module!(lib, "f32", f32_functions); + combine_with_exported_module!(lib, "f64", f64_functions); + } - // Decimal functions - #[cfg(feature = "decimal")] - combine_with_exported_module!(lib, "decimal", decimal_functions); -}); + // Decimal functions + #[cfg(feature = "decimal")] + combine_with_exported_module!(lib, "decimal", decimal_functions); + } +} #[export_module] mod int_functions { diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index cc1f68db..8b9476e8 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -11,14 +11,17 @@ use crate::{ use std::prelude::v1::*; use std::{any::TypeId, cmp::Ordering, mem}; -def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, { - lib.standard = true; +def_package! { + /// Package of basic array utilities. + crate::BasicArrayPackage => |lib| { + lib.standard = true; - combine_with_exported_module!(lib, "array", array_functions); + combine_with_exported_module!(lib, "array", array_functions); - // Register array iterator - lib.set_iterable::(); -}); + // Register array iterator + lib.set_iterable::(); + } +} #[export_module] mod array_functions { diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index 8e70d94f..bc4e06cd 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -13,14 +13,17 @@ use std::{any::TypeId, mem}; #[cfg(not(feature = "no_float"))] use crate::FLOAT; -def_package!(crate:BasicBlobPackage:"Basic BLOB utilities.", lib, { - lib.standard = true; +def_package! { + /// Package of basic BLOB utilities. + crate::BasicBlobPackage => |lib| { + lib.standard = true; - combine_with_exported_module!(lib, "blob", blob_functions); + combine_with_exported_module!(lib, "blob", blob_functions); - // Register blob iterator - lib.set_iterable::(); -}); + // Register blob iterator + lib.set_iterable::(); + } +} #[export_module] mod blob_functions { diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 37bb6eea..a727ede7 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -3,11 +3,14 @@ use crate::{def_package, FnPtr, ImmutableString, NativeCallContext}; #[cfg(feature = "no_std")] use std::prelude::v1::*; -def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, { - lib.standard = true; +def_package! { + /// Package of basic function poitner utilities. + crate::BasicFnPackage => |lib| { + lib.standard = true; - combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions); -}); + combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions); + } +} #[export_module] mod fn_ptr_functions { diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 86952e05..bdbcfaf9 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -299,233 +299,236 @@ macro_rules! reg_range { }; } -def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { - lib.standard = true; +def_package! { + /// Package of basic range iterators + crate::BasicIteratorPackage => |lib| { + lib.standard = true; - reg_range!(lib | "range" => INT); + reg_range!(lib | "range" => INT); - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_range!(lib | "range" => i8, u8, i16, u16, i32, u32, i64, u64); + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_range!(lib | "range" => i8, u8, i16, u16, i32, u32, i64, u64); - #[cfg(not(target_arch = "wasm32"))] - reg_range!(lib | "range" => i128, u128); - } - - reg_range!(lib | step "range" => INT); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_range!(lib | step "range" => i8, u8, i16, u16, i32, u32, i64, u64); - - #[cfg(not(target_arch = "wasm32"))] - reg_range!(lib | step "range" => i128, u128); - } - - #[cfg(not(feature = "no_float"))] - { - use crate::FLOAT; - - #[derive(Debug, Clone, Copy, PartialEq)] - struct StepFloatRange(FLOAT, FLOAT, FLOAT); - - impl StepFloatRange { - pub fn new(from: FLOAT, to: FLOAT, step: FLOAT) -> Result> { - #[cfg(not(feature = "unchecked"))] - if step == 0.0 { - return Err(EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), - EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(), - crate::Position::NONE, - ).into()); - } - - Ok(Self(from, to, step)) - } + #[cfg(not(target_arch = "wasm32"))] + reg_range!(lib | "range" => i128, u128); } - impl Iterator for StepFloatRange { - type Item = FLOAT; + reg_range!(lib | step "range" => INT); - fn next(&mut self) -> Option { - if self.0 == self.1 { - None - } else if self.0 < self.1 { - #[cfg(not(feature = "unchecked"))] - if self.2 < 0.0 { - return None; - } + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_range!(lib | step "range" => i8, u8, i16, u16, i32, u32, i64, u64); - let v = self.0; - let n = self.0 + self.2; - - self.0 = if n >= self.1 { self.1 } else { n }; - Some(v) - } else { - #[cfg(not(feature = "unchecked"))] - if self.2 > 0.0 { - return None; - } - - let v = self.0; - let n = self.0 + self.2; - - self.0 = if n <= self.1 { self.1 } else { n }; - Some(v) - } - } + #[cfg(not(target_arch = "wasm32"))] + reg_range!(lib | step "range" => i128, u128); } - impl FusedIterator for StepFloatRange {} + #[cfg(not(feature = "no_float"))] + { + use crate::FLOAT; - lib.set_iterator::(); + #[derive(Debug, Clone, Copy, PartialEq)] + struct StepFloatRange(FLOAT, FLOAT, FLOAT); - let _hash = lib.set_native_fn("range", StepFloatRange::new); - #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator"]); - } - - #[cfg(feature = "decimal")] - { - use rust_decimal::Decimal; - - #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] - struct StepDecimalRange(Decimal, Decimal, Decimal); - - impl StepDecimalRange { - pub fn new(from: Decimal, to: Decimal, step: Decimal) -> Result> { - #[cfg(not(feature = "unchecked"))] - if step.is_zero() { - return Err(EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), - EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(), - crate::Position::NONE, - ).into()); - } - - Ok(Self(from, to, step)) - } - } - - impl Iterator for StepDecimalRange { - type Item = Decimal; - - fn next(&mut self) -> Option { - if self.0 == self.1 { - None - } else if self.0 < self.1 { + impl StepFloatRange { + pub fn new(from: FLOAT, to: FLOAT, step: FLOAT) -> Result> { #[cfg(not(feature = "unchecked"))] - if self.2.is_sign_negative() { - return None; + if step == 0.0 { + return Err(EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), + EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(), + crate::Position::NONE, + ).into()); } - let v = self.0; - let n = self.0 + self.2; - - self.0 = if n >= self.1 { self.1 } else { n }; - Some(v) - } else { - #[cfg(not(feature = "unchecked"))] - if self.2.is_sign_positive() { - return None; - } - - let v = self.0; - let n = self.0 + self.2; - - self.0 = if n <= self.1 { self.1 } else { n }; - Some(v) + Ok(Self(from, to, step)) } } + + impl Iterator for StepFloatRange { + type Item = FLOAT; + + fn next(&mut self) -> Option { + if self.0 == self.1 { + None + } else if self.0 < self.1 { + #[cfg(not(feature = "unchecked"))] + if self.2 < 0.0 { + return None; + } + + let v = self.0; + let n = self.0 + self.2; + + self.0 = if n >= self.1 { self.1 } else { n }; + Some(v) + } else { + #[cfg(not(feature = "unchecked"))] + if self.2 > 0.0 { + return None; + } + + let v = self.0; + let n = self.0 + self.2; + + self.0 = if n <= self.1 { self.1 } else { n }; + Some(v) + } + } + } + + impl FusedIterator for StepFloatRange {} + + lib.set_iterator::(); + + let _hash = lib.set_native_fn("range", StepFloatRange::new); + #[cfg(feature = "metadata")] + lib.update_fn_metadata(_hash, &["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator"]); } - impl FusedIterator for StepDecimalRange {} + #[cfg(feature = "decimal")] + { + use rust_decimal::Decimal; - lib.set_iterator::(); + #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] + struct StepDecimalRange(Decimal, Decimal, Decimal); - let _hash = lib.set_native_fn("range", StepDecimalRange::new); + impl StepDecimalRange { + pub fn new(from: Decimal, to: Decimal, step: Decimal) -> Result> { + #[cfg(not(feature = "unchecked"))] + if step.is_zero() { + return Err(EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), + EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(), + crate::Position::NONE, + ).into()); + } + + Ok(Self(from, to, step)) + } + } + + impl Iterator for StepDecimalRange { + type Item = Decimal; + + fn next(&mut self) -> Option { + if self.0 == self.1 { + None + } else if self.0 < self.1 { + #[cfg(not(feature = "unchecked"))] + if self.2.is_sign_negative() { + return None; + } + + let v = self.0; + let n = self.0 + self.2; + + self.0 = if n >= self.1 { self.1 } else { n }; + Some(v) + } else { + #[cfg(not(feature = "unchecked"))] + if self.2.is_sign_positive() { + return None; + } + + let v = self.0; + let n = self.0 + self.2; + + self.0 = if n <= self.1 { self.1 } else { n }; + Some(v) + } + } + } + + impl FusedIterator for StepDecimalRange {} + + lib.set_iterator::(); + + let _hash = lib.set_native_fn("range", StepDecimalRange::new); + #[cfg(feature = "metadata")] + lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator"]); + } + + // Register string iterator + lib.set_iterator::(); + + let _hash = lib.set_native_fn("chars", |string, range: ExclusiveRange| { + let from = INT::max(range.start, 0); + let to = INT::max(range.end, from); + Ok(CharsStream::new(string, from, to - from)) + }); #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator"]); - } + lib.update_fn_metadata(_hash, &["string: &str", "range: Range", "Iterator"]); - // Register string iterator - lib.set_iterator::(); - - let _hash = lib.set_native_fn("chars", |string, range: ExclusiveRange| { - let from = INT::max(range.start, 0); - let to = INT::max(range.end, from); - Ok(CharsStream::new(string, from, to - from)) - }); - #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["string: &str", "range: Range", "Iterator"]); - - let _hash = lib.set_native_fn("chars", |string, range: InclusiveRange| { - let from = INT::max(*range.start(), 0); - let to = INT::max(*range.end(), from - 1); - Ok(CharsStream::new(string, from, to-from + 1)) - }); - #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["string: &str", "range: RangeInclusive", "Iterator"]); - - let _hash = lib.set_native_fn("chars", |string, from, len| Ok(CharsStream::new(string, from, len))); - #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "len: INT", "Iterator"]); - - let _hash = lib.set_native_fn("chars", |string, from| Ok(CharsStream::new(string, from, INT::MAX))); - #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "Iterator"]); - - let _hash = lib.set_native_fn("chars", |string| Ok(CharsStream::new(string, 0, INT::MAX))); - #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["string: &str", "Iterator"]); - - #[cfg(not(feature = "no_object"))] - { - let _hash = lib.set_getter_fn("chars", |string: &mut ImmutableString| Ok(CharsStream::new(string, 0, INT::MAX))); + let _hash = lib.set_native_fn("chars", |string, range: InclusiveRange| { + let from = INT::max(*range.start(), 0); + let to = INT::max(*range.end(), from - 1); + Ok(CharsStream::new(string, from, to-from + 1)) + }); #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["string: &mut ImmutableString", "Iterator"]); - } + lib.update_fn_metadata(_hash, &["string: &str", "range: RangeInclusive", "Iterator"]); - // Register bit-field iterator - lib.set_iterator::(); - - let _hash = lib.set_native_fn("bits", |value, range: ExclusiveRange| { - let from = INT::max(range.start, 0); - let to = INT::max(range.end, from); - BitRange::new(value, from, to - from) - }); - #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["value: INT", "range: Range", "Iterator"]); - - let _hash = lib.set_native_fn("bits", |value, range: InclusiveRange| { - let from = INT::max(*range.start(), 0); - let to = INT::max(*range.end(), from - 1); - BitRange::new(value, from, to - from + 1) - }); - #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["value: INT", "range: RangeInclusive", "Iterator"]); - - let _hash = lib.set_native_fn("bits", BitRange::new); - #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "len: INT", "Iterator"]); - - let _hash = lib.set_native_fn("bits", |value, from| BitRange::new(value, from, INT::MAX)); - #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "Iterator"]); - - let _hash = lib.set_native_fn("bits", |value| BitRange::new(value, 0, INT::MAX) ); - #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["value: INT", "Iterator"]); - - #[cfg(not(feature = "no_object"))] - { - let _hash = lib.set_getter_fn("bits", |value: &mut INT| BitRange::new(*value, 0, INT::MAX) ); + let _hash = lib.set_native_fn("chars", |string, from, len| Ok(CharsStream::new(string, from, len))); #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["value: &mut INT", "range: Range", "Iterator"]); - } + lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "len: INT", "Iterator"]); - combine_with_exported_module!(lib, "range", range_functions); -}); + let _hash = lib.set_native_fn("chars", |string, from| Ok(CharsStream::new(string, from, INT::MAX))); + #[cfg(feature = "metadata")] + lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "Iterator"]); + + let _hash = lib.set_native_fn("chars", |string| Ok(CharsStream::new(string, 0, INT::MAX))); + #[cfg(feature = "metadata")] + lib.update_fn_metadata(_hash, &["string: &str", "Iterator"]); + + #[cfg(not(feature = "no_object"))] + { + let _hash = lib.set_getter_fn("chars", |string: &mut ImmutableString| Ok(CharsStream::new(string, 0, INT::MAX))); + #[cfg(feature = "metadata")] + lib.update_fn_metadata(_hash, &["string: &mut ImmutableString", "Iterator"]); + } + + // Register bit-field iterator + lib.set_iterator::(); + + let _hash = lib.set_native_fn("bits", |value, range: ExclusiveRange| { + let from = INT::max(range.start, 0); + let to = INT::max(range.end, from); + BitRange::new(value, from, to - from) + }); + #[cfg(feature = "metadata")] + lib.update_fn_metadata(_hash, &["value: INT", "range: Range", "Iterator"]); + + let _hash = lib.set_native_fn("bits", |value, range: InclusiveRange| { + let from = INT::max(*range.start(), 0); + let to = INT::max(*range.end(), from - 1); + BitRange::new(value, from, to - from + 1) + }); + #[cfg(feature = "metadata")] + lib.update_fn_metadata(_hash, &["value: INT", "range: RangeInclusive", "Iterator"]); + + let _hash = lib.set_native_fn("bits", BitRange::new); + #[cfg(feature = "metadata")] + lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "len: INT", "Iterator"]); + + let _hash = lib.set_native_fn("bits", |value, from| BitRange::new(value, from, INT::MAX)); + #[cfg(feature = "metadata")] + lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "Iterator"]); + + let _hash = lib.set_native_fn("bits", |value| BitRange::new(value, 0, INT::MAX) ); + #[cfg(feature = "metadata")] + lib.update_fn_metadata(_hash, &["value: INT", "Iterator"]); + + #[cfg(not(feature = "no_object"))] + { + let _hash = lib.set_getter_fn("bits", |value: &mut INT| BitRange::new(*value, 0, INT::MAX) ); + #[cfg(feature = "metadata")] + lib.update_fn_metadata(_hash, &["value: &mut INT", "range: Range", "Iterator"]); + } + + combine_with_exported_module!(lib, "range", range_functions); + } +} #[export_module] mod range_functions { diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index cab9ebc3..75ac1d8e 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -5,6 +5,15 @@ use crate::{Dynamic, EvalAltResult, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; +def_package! { + /// Package of core language features. + crate::LanguageCorePackage => |lib| { + lib.standard = true; + + combine_with_exported_module!(lib, "language_core", core_functions); + } +} + #[export_module] mod core_functions { #[rhai_fn(name = "tag", get = "tag", pure)] @@ -41,9 +50,3 @@ mod core_functions { } } } - -def_package!(crate:LanguageCorePackage:"Language core functions.", lib, { - lib.standard = true; - - combine_with_exported_module!(lib, "language_core", core_functions); -}); diff --git a/src/packages/logic.rs b/src/packages/logic.rs index d6c5523b..a800425c 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -37,33 +37,36 @@ macro_rules! reg_functions { )* } } -def_package!(crate:LogicPackage:"Logical operators.", lib, { - lib.standard = true; +def_package! { + /// Package of basic logic operators. + crate::LogicPackage => |lib| { + lib.standard = true; - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_functions!(lib += numbers; i8, u8, i16, u16, i32, u32, u64); + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_functions!(lib += numbers; i8, u8, i16, u16, i32, u32, u64); - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] - reg_functions!(lib += num_128; i128, u128); + #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + reg_functions!(lib += num_128; i128, u128); + } + + #[cfg(not(feature = "no_float"))] + { + #[cfg(not(feature = "f32_float"))] + reg_functions!(lib += float; f32); + combine_with_exported_module!(lib, "f32", f32_functions); + + #[cfg(feature = "f32_float")] + reg_functions!(lib += float; f64); + combine_with_exported_module!(lib, "f64", f64_functions); + } + + set_exported_fn!(lib, "!", not); + + combine_with_exported_module!(lib, "bit_field", bit_field_functions); } - - #[cfg(not(feature = "no_float"))] - { - #[cfg(not(feature = "f32_float"))] - reg_functions!(lib += float; f32); - combine_with_exported_module!(lib, "f32", f32_functions); - - #[cfg(feature = "f32_float")] - reg_functions!(lib += float; f64); - combine_with_exported_module!(lib, "f64", f64_functions); - } - - set_exported_fn!(lib, "!", not); - - combine_with_exported_module!(lib, "bit_field", bit_field_functions); -}); +} // Logic operators #[export_fn] diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index eec7f678..7f6c8e2f 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -9,11 +9,14 @@ use std::prelude::v1::*; #[cfg(not(feature = "no_index"))] use crate::Array; -def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, { - lib.standard = true; +def_package! { + /// Package of basic object map utilities. + crate::BasicMapPackage => |lib| { + lib.standard = true; - combine_with_exported_module!(lib, "map", map_functions); -}); + combine_with_exported_module!(lib, "map", map_functions); + } +} #[export_module] mod map_functions { diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 2e9eec92..c596cc62 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -58,55 +58,58 @@ macro_rules! reg_functions { )* } } -def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, { - lib.standard = true; +def_package! { + /// Basic mathematical package. + crate::BasicMathPackage => |lib| { + lib.standard = true; - // Integer functions - combine_with_exported_module!(lib, "int", int_functions); + // Integer functions + combine_with_exported_module!(lib, "int", int_functions); - reg_functions!(lib += basic_to_int::to_int(char)); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_functions!(lib += numbers_to_int::to_int(i8, u8, i16, u16, i32, u32, i64, u64)); - - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] - reg_functions!(lib += num_128_to_int::to_int(i128, u128)); - } - - #[cfg(not(feature = "no_float"))] - { - // Floating point functions - combine_with_exported_module!(lib, "float", float_functions); - - // Trig functions - combine_with_exported_module!(lib, "trig", trig_functions); - - reg_functions!(lib += basic_to_float::to_float(INT)); + reg_functions!(lib += basic_to_int::to_int(char)); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] { - reg_functions!(lib += numbers_to_float::to_float(i8, u8, i16, u16, i32, u32, i64, u32)); + reg_functions!(lib += numbers_to_int::to_int(i8, u8, i16, u16, i32, u32, i64, u64)); #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] - reg_functions!(lib += num_128_to_float::to_float(i128, u128)); + reg_functions!(lib += num_128_to_int::to_int(i128, u128)); + } + + #[cfg(not(feature = "no_float"))] + { + // Floating point functions + combine_with_exported_module!(lib, "float", float_functions); + + // Trig functions + combine_with_exported_module!(lib, "trig", trig_functions); + + reg_functions!(lib += basic_to_float::to_float(INT)); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_functions!(lib += numbers_to_float::to_float(i8, u8, i16, u16, i32, u32, i64, u32)); + + #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + reg_functions!(lib += num_128_to_float::to_float(i128, u128)); + } + } + + // Decimal functions + #[cfg(feature = "decimal")] + { + combine_with_exported_module!(lib, "decimal", decimal_functions); + + reg_functions!(lib += basic_to_decimal::to_decimal(INT)); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + reg_functions!(lib += numbers_to_decimal::to_decimal(i8, u8, i16, u16, i32, u32, i64, u64)); } } - - // Decimal functions - #[cfg(feature = "decimal")] - { - combine_with_exported_module!(lib, "decimal", decimal_functions); - - reg_functions!(lib += basic_to_decimal::to_decimal(INT)); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - reg_functions!(lib += numbers_to_decimal::to_decimal(i8, u8, i16, u16, i32, u32, i64, u64)); - } -}); +} #[export_module] mod int_functions { diff --git a/src/packages/mod.rs b/src/packages/mod.rs index a18a11cf..dffdb85b 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -24,6 +24,7 @@ pub use array_basic::BasicArrayPackage; pub use blob_basic::BasicBlobPackage; pub use fn_basic::BasicFnPackage; pub use iter_basic::BasicIteratorPackage; +pub use lang_core::LanguageCorePackage; pub use logic::LogicPackage; #[cfg(not(feature = "no_object"))] pub use map_basic::BasicMapPackage; @@ -69,8 +70,8 @@ pub trait Package { /// ``` #[macro_export] macro_rules! def_package { - ($root:ident : $package:ident : $comment:expr , $lib:ident , $block:stmt) => { - #[doc=$comment] + ($(#[$outer:meta])* $root:ident :: $package:ident => | $lib:ident | $block:stmt) => { + $(#[$outer])* pub struct $package($root::Shared<$root::Module>); impl $root::packages::Package for $package { @@ -88,6 +89,42 @@ macro_rules! def_package { } } + impl $package { + pub fn new() -> Self { + let mut module = $root::Module::new(); + ::init(&mut module); + module.build_index(); + Self(module.into()) + } + } + }; + ($root:ident : $package:ident : $comment:expr , $lib:ident , $block:stmt) => { + #[deprecated(since = "1.4.0", note = "this is an old syntax of `def_package!` and is deprecated; use the new syntax of `def_package!` instead")] + #[doc=$comment] + /// + /// # Deprecated + /// + /// This old syntax of `def_package!` is deprecated. Use the new syntax instead. + /// + /// This syntax will be removed in the next major version. + pub struct $package($root::Shared<$root::Module>); + + impl $root::packages::Package for $package { + fn as_shared_module(&self) -> $root::Shared<$root::Module> { + #[allow(deprecated)] + self.0.clone() + } + fn init($lib: &mut $root::Module) { + $block + } + } + + impl Default for $package { + fn default() -> Self { + Self::new() + } + } + impl $package { pub fn new() -> Self { let mut module = $root::Module::new(); diff --git a/src/packages/pkg_core.rs b/src/packages/pkg_core.rs index e82e7fc2..dc106dee 100644 --- a/src/packages/pkg_core.rs +++ b/src/packages/pkg_core.rs @@ -1,21 +1,27 @@ -use super::arithmetic::ArithmeticPackage; -use super::fn_basic::BasicFnPackage; -use super::iter_basic::BasicIteratorPackage; -use super::lang_core::LanguageCorePackage; -use super::logic::LogicPackage; -use super::string_basic::BasicStringPackage; #[cfg(feature = "no_std")] use std::prelude::v1::*; use crate::def_package; -def_package!(crate:CorePackage:"_Core_ package containing basic facilities.", lib, { - lib.standard = true; +def_package! { + /// Core package containing basic facilities. + /// + /// # Contents + /// + /// * [`LanguageCorePackage`][super::LanguageCorePackage] + /// * [`ArithmeticPackage`][super::ArithmeticPackage] + /// * [`LogicPackage`][super::LogicPackage] + /// * [`BasicStringPackage`][super::BasicStringPackage] + /// * [`BasicIteratorPackage`][super::BasicIteratorPackage] + /// * [`BasicFnPackage`][super::BasicFnPackage] + crate::CorePackage => |lib| { + lib.standard = true; - LanguageCorePackage::init(lib); - ArithmeticPackage::init(lib); - LogicPackage::init(lib); - BasicStringPackage::init(lib); - BasicIteratorPackage::init(lib); - BasicFnPackage::init(lib); -}); + super::LanguageCorePackage::init(lib); + super::ArithmeticPackage::init(lib); + super::LogicPackage::init(lib); + super::BasicStringPackage::init(lib); + super::BasicIteratorPackage::init(lib); + super::BasicFnPackage::init(lib); + } +} diff --git a/src/packages/pkg_std.rs b/src/packages/pkg_std.rs index 3c881232..012aa7d8 100644 --- a/src/packages/pkg_std.rs +++ b/src/packages/pkg_std.rs @@ -1,32 +1,33 @@ -#[cfg(not(feature = "no_index"))] -use super::array_basic::BasicArrayPackage; -#[cfg(not(feature = "no_index"))] -use super::blob_basic::BasicBlobPackage; -#[cfg(not(feature = "no_object"))] -use super::map_basic::BasicMapPackage; -use super::math_basic::BasicMathPackage; -use super::pkg_core::CorePackage; -use super::string_more::MoreStringPackage; -#[cfg(not(feature = "no_std"))] -use super::time_basic::BasicTimePackage; #[cfg(feature = "no_std")] use std::prelude::v1::*; use crate::def_package; -def_package!(crate:StandardPackage:"_Standard_ package containing all built-in features.", lib, { - lib.standard = true; +def_package! { + /// Standard package containing all built-in features. + /// + /// # Contents + /// + /// * [`CorePackage`][super::CorePackage] + /// * [`BasicMathPackage`][super::BasicMathPackage] + /// * [`BasicArrayPackage`][super::BasicArrayPackage] + /// * [`BasicBlobPackage`][super::BasicBlobPackage] + /// * [`BasicMapPackage`][super::BasicMapPackage] + /// * [`BasicTimePackage`][super::BasicTimePackage] + /// * [`MoreStringPackage`][super::MoreStringPackage] + crate::StandardPackage => |lib| { + lib.standard = true; - CorePackage::init(lib); - BasicMathPackage::init(lib); - #[cfg(not(feature = "no_index"))] - { - BasicArrayPackage::init(lib); - BasicBlobPackage::init(lib); + super::CorePackage::init(lib); + super::BasicMathPackage::init(lib); + #[cfg(not(feature = "no_index"))] + super::BasicArrayPackage::init(lib); + #[cfg(not(feature = "no_index"))] + super::BasicBlobPackage::init(lib); + #[cfg(not(feature = "no_object"))] + super::BasicMapPackage::init(lib); + #[cfg(not(feature = "no_std"))] + super::BasicTimePackage::init(lib); + super::MoreStringPackage::init(lib); } - #[cfg(not(feature = "no_object"))] - BasicMapPackage::init(lib); - #[cfg(not(feature = "no_std"))] - BasicTimePackage::init(lib); - MoreStringPackage::init(lib); -}); +} diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index facc8360..33048147 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -15,12 +15,15 @@ use crate::Map; pub const FUNC_TO_STRING: &str = "to_string"; pub const FUNC_TO_DEBUG: &str = "to_debug"; -def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, { - lib.standard = true; +def_package! { + /// Package of basic string utilities (e.g. printing) + crate::BasicStringPackage => |lib| { + lib.standard = true; - combine_with_exported_module!(lib, "print_debug", print_debug_functions); - combine_with_exported_module!(lib, "number_formatting", number_formatting); -}); + combine_with_exported_module!(lib, "print_debug", print_debug_functions); + combine_with_exported_module!(lib, "number_formatting", number_formatting); + } +} // Register print and debug diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index e95c15e3..c29b4af0 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -8,11 +8,14 @@ use std::{any::TypeId, mem}; use super::string_basic::{print_with_func, FUNC_TO_STRING}; -def_package!(crate:MoreStringPackage:"Additional string utilities, including string building.", lib, { - lib.standard = true; +def_package! { + /// Package of additional string utilities over [`BasicStringPackage`][super::BasicStringPackage] + crate::MoreStringPackage => |lib| { + lib.standard = true; - combine_with_exported_module!(lib, "string", string_functions); -}); + combine_with_exported_module!(lib, "string", string_functions); + } +} #[export_module] mod string_functions { diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index c51e340b..bf6a0d50 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -13,12 +13,15 @@ use std::time::{Duration, Instant}; #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] use instant::{Duration, Instant}; -def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, { - lib.standard = true; +def_package! { + /// Package of basic timing utilities. + crate::BasicTimePackage => |lib| { + lib.standard = true; - // Register date/time functions - combine_with_exported_module!(lib, "time", time_functions); -}); + // Register date/time functions + combine_with_exported_module!(lib, "time", time_functions); + } +} #[export_module] mod time_functions { From 57d7985015318b071a0be6bdf0965e3fbdeb978f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 20 Dec 2021 15:18:44 +0800 Subject: [PATCH 28/97] Expand switch example. --- scripts/for1.rhai | 2 +- scripts/switch.rhai | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/for1.rhai b/scripts/for1.rhai index 1e215da5..4818b202 100644 --- a/scripts/for1.rhai +++ b/scripts/for1.rhai @@ -11,7 +11,7 @@ for (a, i) in arr { if a == 3 { break; } } -//print(a); // <- if you uncomment this line, the script will fail to run +//print(a); // <- if you uncomment this line, the script will fail to compile // because 'a' is not defined here for i in range(5, 0, -1) { // runs from 5 down to 1 diff --git a/scripts/switch.rhai b/scripts/switch.rhai index a252fa66..971ff989 100644 --- a/scripts/switch.rhai +++ b/scripts/switch.rhai @@ -1,11 +1,13 @@ -let arr = [42, 123.456, "hello", true, 'x', 999, 1]; +let arr = [42, 123.456, "hello", true, "hey", 'x', 999, 1, 2, 3, 4]; for item in arr { switch item { 42 => print("The Answer!"), 123.456 => print(`Floating point... ${item}`), "hello" => print(`${item} world!`), - 999 => print(`A number: ${item}`), - _ => print(`Something else: <${item}>`) + 999 => print(`Got 999: ${item}`), + 0..100 if item % 2 == 0 => print(`A small even number: ${item}`), + 0..100 => print(`A small odd number: ${item}`), + _ => print(`Something else: <${item}> is ${type_of(item)}`) } } From b4756b4575d04031f34f608ac72878aeaf2d99f9 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 20 Dec 2021 22:13:00 +0800 Subject: [PATCH 29/97] Move CallableFunction to own file. --- src/func/call.rs | 3 +- src/func/callable_function.rs | 250 ++++++++++++++++++++++++++++++++++ src/func/mod.rs | 6 +- src/func/native.rs | 245 +-------------------------------- src/func/register.rs | 5 +- src/func/script.rs | 2 +- src/types/dynamic.rs | 9 +- 7 files changed, 265 insertions(+), 255 deletions(-) create mode 100644 src/func/callable_function.rs diff --git a/src/func/call.rs b/src/func/call.rs index 87478c71..7e6b1c38 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1,6 +1,7 @@ //! Implement function-calling mechanism for [`Engine`]. -use super::native::{CallableFunction, FnAny}; +use super::callable_function::CallableFunction; +use super::native::FnAny; use super::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; use crate::api::default_limits::MAX_DYNAMIC_PARAMETERS; use crate::ast::FnCallHashes; diff --git a/src/func/callable_function.rs b/src/func/callable_function.rs new file mode 100644 index 00000000..251c4d06 --- /dev/null +++ b/src/func/callable_function.rs @@ -0,0 +1,250 @@ +//! Module defining the standard Rhai function type. + +use super::native::{FnAny, FnPlugin, IteratorFn, SendSync}; +use crate::ast::FnAccess; +use crate::plugin::PluginFunction; +use crate::Shared; +use std::fmt; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; + +/// A type encapsulating a function callable by Rhai. +#[derive(Clone)] +pub enum CallableFunction { + /// A pure native Rust function with all arguments passed by value. + Pure(Shared), + /// A native Rust object method with the first argument passed by reference, + /// and the rest passed by value. + Method(Shared), + /// An iterator function. + Iterator(IteratorFn), + /// A plugin function, + Plugin(Shared), + /// A script-defined function. + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] + Script(Shared), +} + +impl fmt::Debug for CallableFunction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Pure(_) => write!(f, "NativePureFunction"), + Self::Method(_) => write!(f, "NativeMethod"), + Self::Iterator(_) => write!(f, "NativeIterator"), + Self::Plugin(_) => write!(f, "PluginFunction"), + + #[cfg(not(feature = "no_function"))] + Self::Script(fn_def) => fmt::Debug::fmt(fn_def, f), + } + } +} + +impl fmt::Display for CallableFunction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Pure(_) => write!(f, "NativePureFunction"), + Self::Method(_) => write!(f, "NativeMethod"), + Self::Iterator(_) => write!(f, "NativeIterator"), + Self::Plugin(_) => write!(f, "PluginFunction"), + + #[cfg(not(feature = "no_function"))] + CallableFunction::Script(s) => fmt::Display::fmt(s, f), + } + } +} + +impl CallableFunction { + /// Is this a pure native Rust function? + #[inline] + #[must_use] + pub fn is_pure(&self) -> bool { + match self { + Self::Pure(_) => true, + Self::Method(_) | Self::Iterator(_) => false, + + Self::Plugin(p) => !p.is_method_call(), + + #[cfg(not(feature = "no_function"))] + Self::Script(_) => false, + } + } + /// Is this a native Rust method function? + #[inline] + #[must_use] + pub fn is_method(&self) -> bool { + match self { + Self::Method(_) => true, + Self::Pure(_) | Self::Iterator(_) => false, + + Self::Plugin(p) => p.is_method_call(), + + #[cfg(not(feature = "no_function"))] + Self::Script(_) => false, + } + } + /// Is this an iterator function? + #[inline] + #[must_use] + pub const fn is_iter(&self) -> bool { + match self { + Self::Iterator(_) => true, + Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => false, + + #[cfg(not(feature = "no_function"))] + Self::Script(_) => false, + } + } + /// Is this a script-defined function? + #[inline] + #[must_use] + pub const fn is_script(&self) -> bool { + match self { + #[cfg(not(feature = "no_function"))] + Self::Script(_) => true, + + Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => false, + } + } + /// Is this a plugin function? + #[inline] + #[must_use] + pub const fn is_plugin_fn(&self) -> bool { + match self { + Self::Plugin(_) => true, + Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false, + + #[cfg(not(feature = "no_function"))] + Self::Script(_) => false, + } + } + /// Is this a native Rust function? + #[inline] + #[must_use] + pub const fn is_native(&self) -> bool { + match self { + Self::Pure(_) | Self::Method(_) => true, + Self::Plugin(_) => true, + Self::Iterator(_) => true, + + #[cfg(not(feature = "no_function"))] + Self::Script(_) => false, + } + } + /// Get the access mode. + #[inline] + #[must_use] + pub fn access(&self) -> FnAccess { + match self { + Self::Plugin(_) => FnAccess::Public, + Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => FnAccess::Public, + + #[cfg(not(feature = "no_function"))] + Self::Script(f) => f.access, + } + } + /// Get a shared reference to a native Rust function. + #[inline] + #[must_use] + pub fn get_native_fn(&self) -> Option<&Shared> { + match self { + Self::Pure(f) | Self::Method(f) => Some(f), + Self::Iterator(_) | Self::Plugin(_) => None, + + #[cfg(not(feature = "no_function"))] + Self::Script(_) => None, + } + } + /// Get a shared reference to a script-defined function definition. + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] + #[inline] + #[must_use] + pub const fn get_script_fn_def(&self) -> Option<&Shared> { + match self { + Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => None, + Self::Script(f) => Some(f), + } + } + /// Get a reference to an iterator function. + #[inline] + #[must_use] + pub fn get_iter_fn(&self) -> Option { + match self { + Self::Iterator(f) => Some(*f), + Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => None, + + #[cfg(not(feature = "no_function"))] + Self::Script(_) => None, + } + } + /// Get a shared reference to a plugin function. + #[inline] + #[must_use] + pub fn get_plugin_fn(&self) -> Option<&Shared> { + match self { + Self::Plugin(f) => Some(f), + Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => None, + + #[cfg(not(feature = "no_function"))] + Self::Script(_) => None, + } + } + /// Create a new [`CallableFunction::Pure`]. + #[inline(always)] + #[must_use] + pub fn from_pure(func: Box) -> Self { + Self::Pure(func.into()) + } + /// Create a new [`CallableFunction::Method`]. + #[inline(always)] + #[must_use] + pub fn from_method(func: Box) -> Self { + Self::Method(func.into()) + } + /// Create a new [`CallableFunction::Plugin`]. + #[inline(always)] + #[must_use] + pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self { + Self::Plugin((Box::new(func) as Box).into()) + } +} + +impl From for CallableFunction { + #[inline(always)] + fn from(func: IteratorFn) -> Self { + Self::Iterator(func) + } +} + +#[cfg(not(feature = "no_function"))] +impl From for CallableFunction { + #[inline(always)] + fn from(_func: crate::ast::ScriptFnDef) -> Self { + Self::Script(_func.into()) + } +} + +#[cfg(not(feature = "no_function"))] +impl From> for CallableFunction { + #[inline(always)] + fn from(_func: Shared) -> Self { + Self::Script(_func) + } +} + +impl From for CallableFunction { + #[inline(always)] + fn from(func: T) -> Self { + Self::from_plugin(func) + } +} + +impl From> for CallableFunction { + #[inline(always)] + fn from(func: Shared) -> Self { + Self::Plugin(func) + } +} diff --git a/src/func/mod.rs b/src/func/mod.rs index 1d5a0841..dcae0482 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -3,6 +3,7 @@ pub mod args; pub mod builtin; pub mod call; +pub mod callable_function; pub mod func; pub mod hashing; pub mod native; @@ -13,6 +14,7 @@ pub mod script; pub use args::FuncArgs; pub use builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; pub use call::FnCallArgs; +pub use callable_function::CallableFunction; #[cfg(not(feature = "no_function"))] pub use func::Func; pub use hashing::{ @@ -20,8 +22,8 @@ pub use hashing::{ combine_hashes, get_hasher, }; pub use native::{ - shared_make_mut, shared_take, shared_take_or_clone, shared_try_take, shared_write_lock, - CallableFunction, FnAny, FnPlugin, IteratorFn, Locked, NativeCallContext, SendSync, Shared, + shared_make_mut, shared_take, shared_take_or_clone, shared_try_take, shared_write_lock, FnAny, + FnPlugin, IteratorFn, Locked, NativeCallContext, SendSync, Shared, }; pub use plugin::PluginFunction; pub use register::RegisterNativeFunction; diff --git a/src/func/native.rs b/src/func/native.rs index f23d04b4..56a8edfc 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -1,7 +1,7 @@ //! Module defining interfaces to native-Rust functions. use super::call::FnCallArgs; -use crate::ast::{FnAccess, FnCallHashes}; +use crate::ast::FnCallHashes; use crate::engine::{EvalState, Imports}; use crate::plugin::PluginFunction; use crate::tokenizer::{Token, TokenizeState}; @@ -10,9 +10,9 @@ use crate::{ calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, FuncArgs, Module, Position, RhaiResult, StaticVec, }; +use std::any::type_name; #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{any::type_name, fmt}; /// Trait that maps to `Send + Sync` only under the `sync` feature. #[cfg(feature = "sync")] @@ -414,244 +414,3 @@ pub type OnVarCallback = Box< + Sync + 'static, >; - -/// A type encapsulating a function callable by Rhai. -#[derive(Clone)] -pub enum CallableFunction { - /// A pure native Rust function with all arguments passed by value. - Pure(Shared), - /// A native Rust object method with the first argument passed by reference, - /// and the rest passed by value. - Method(Shared), - /// An iterator function. - Iterator(IteratorFn), - /// A plugin function, - Plugin(Shared), - /// A script-defined function. - /// - /// Not available under `no_function`. - #[cfg(not(feature = "no_function"))] - Script(Shared), -} - -impl fmt::Debug for CallableFunction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Pure(_) => write!(f, "NativePureFunction"), - Self::Method(_) => write!(f, "NativeMethod"), - Self::Iterator(_) => write!(f, "NativeIterator"), - Self::Plugin(_) => write!(f, "PluginFunction"), - - #[cfg(not(feature = "no_function"))] - Self::Script(fn_def) => fmt::Debug::fmt(fn_def, f), - } - } -} - -impl fmt::Display for CallableFunction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Pure(_) => write!(f, "NativePureFunction"), - Self::Method(_) => write!(f, "NativeMethod"), - Self::Iterator(_) => write!(f, "NativeIterator"), - Self::Plugin(_) => write!(f, "PluginFunction"), - - #[cfg(not(feature = "no_function"))] - CallableFunction::Script(s) => fmt::Display::fmt(s, f), - } - } -} - -impl CallableFunction { - /// Is this a pure native Rust function? - #[inline] - #[must_use] - pub fn is_pure(&self) -> bool { - match self { - Self::Pure(_) => true, - Self::Method(_) | Self::Iterator(_) => false, - - Self::Plugin(p) => !p.is_method_call(), - - #[cfg(not(feature = "no_function"))] - Self::Script(_) => false, - } - } - /// Is this a native Rust method function? - #[inline] - #[must_use] - pub fn is_method(&self) -> bool { - match self { - Self::Method(_) => true, - Self::Pure(_) | Self::Iterator(_) => false, - - Self::Plugin(p) => p.is_method_call(), - - #[cfg(not(feature = "no_function"))] - Self::Script(_) => false, - } - } - /// Is this an iterator function? - #[inline] - #[must_use] - pub const fn is_iter(&self) -> bool { - match self { - Self::Iterator(_) => true, - Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => false, - - #[cfg(not(feature = "no_function"))] - Self::Script(_) => false, - } - } - /// Is this a script-defined function? - #[inline] - #[must_use] - pub const fn is_script(&self) -> bool { - match self { - #[cfg(not(feature = "no_function"))] - Self::Script(_) => true, - - Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => false, - } - } - /// Is this a plugin function? - #[inline] - #[must_use] - pub const fn is_plugin_fn(&self) -> bool { - match self { - Self::Plugin(_) => true, - Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false, - - #[cfg(not(feature = "no_function"))] - Self::Script(_) => false, - } - } - /// Is this a native Rust function? - #[inline] - #[must_use] - pub const fn is_native(&self) -> bool { - match self { - Self::Pure(_) | Self::Method(_) => true, - Self::Plugin(_) => true, - Self::Iterator(_) => true, - - #[cfg(not(feature = "no_function"))] - Self::Script(_) => false, - } - } - /// Get the access mode. - #[inline] - #[must_use] - pub fn access(&self) -> FnAccess { - match self { - Self::Plugin(_) => FnAccess::Public, - Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => FnAccess::Public, - - #[cfg(not(feature = "no_function"))] - Self::Script(f) => f.access, - } - } - /// Get a shared reference to a native Rust function. - #[inline] - #[must_use] - pub fn get_native_fn(&self) -> Option<&Shared> { - match self { - Self::Pure(f) | Self::Method(f) => Some(f), - Self::Iterator(_) | Self::Plugin(_) => None, - - #[cfg(not(feature = "no_function"))] - Self::Script(_) => None, - } - } - /// Get a shared reference to a script-defined function definition. - /// - /// Not available under `no_function`. - #[cfg(not(feature = "no_function"))] - #[inline] - #[must_use] - pub const fn get_script_fn_def(&self) -> Option<&Shared> { - match self { - Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => None, - Self::Script(f) => Some(f), - } - } - /// Get a reference to an iterator function. - #[inline] - #[must_use] - pub fn get_iter_fn(&self) -> Option { - match self { - Self::Iterator(f) => Some(*f), - Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => None, - - #[cfg(not(feature = "no_function"))] - Self::Script(_) => None, - } - } - /// Get a shared reference to a plugin function. - #[inline] - #[must_use] - pub fn get_plugin_fn(&self) -> Option<&Shared> { - match self { - Self::Plugin(f) => Some(f), - Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => None, - - #[cfg(not(feature = "no_function"))] - Self::Script(_) => None, - } - } - /// Create a new [`CallableFunction::Pure`]. - #[inline(always)] - #[must_use] - pub fn from_pure(func: Box) -> Self { - Self::Pure(func.into()) - } - /// Create a new [`CallableFunction::Method`]. - #[inline(always)] - #[must_use] - pub fn from_method(func: Box) -> Self { - Self::Method(func.into()) - } - /// Create a new [`CallableFunction::Plugin`]. - #[inline(always)] - #[must_use] - pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self { - Self::Plugin((Box::new(func) as Box).into()) - } -} - -impl From for CallableFunction { - #[inline(always)] - fn from(func: IteratorFn) -> Self { - Self::Iterator(func) - } -} - -#[cfg(not(feature = "no_function"))] -impl From for CallableFunction { - #[inline(always)] - fn from(_func: crate::ast::ScriptFnDef) -> Self { - Self::Script(_func.into()) - } -} - -#[cfg(not(feature = "no_function"))] -impl From> for CallableFunction { - #[inline(always)] - fn from(_func: Shared) -> Self { - Self::Script(_func) - } -} - -impl From for CallableFunction { - #[inline(always)] - fn from(func: T) -> Self { - Self::from_plugin(func) - } -} - -impl From> for CallableFunction { - #[inline(always)] - fn from(func: Shared) -> Self { - Self::Plugin(func) - } -} diff --git a/src/func/register.rs b/src/func/register.rs index e317f51b..7230ab02 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -2,8 +2,9 @@ #![allow(non_snake_case)] -use crate::func::call::FnCallArgs; -use crate::func::native::{CallableFunction, FnAny, SendSync}; +use super::call::FnCallArgs; +use super::callable_function::CallableFunction; +use super::native::{FnAny, SendSync}; use crate::r#unsafe::unsafe_try_cast; use crate::tokenizer::Position; use crate::types::dynamic::{DynamicWriteLock, Variant}; diff --git a/src/func/script.rs b/src/func/script.rs index 383dd400..1bb56e3f 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -1,9 +1,9 @@ //! Implement script function-calling mechanism for [`Engine`]. #![cfg(not(feature = "no_function"))] +use super::call::FnCallArgs; use crate::ast::ScriptFnDef; use crate::engine::{EvalState, Imports}; -use crate::func::call::FnCallArgs; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::{Dynamic, Engine, EvalAltResult, Module, Position, RhaiResult, Scope, StaticVec}; use std::mem; diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 26251386..09d24950 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -611,14 +611,11 @@ impl fmt::Display for Dynamic { #[cfg(feature = "decimal")] Union::Decimal(ref value, _, _) => fmt::Display::fmt(value, f), #[cfg(not(feature = "no_index"))] - Union::Array(ref value, _, _) => fmt::Debug::fmt(value, f), + Union::Array(_, _, _) => fmt::Debug::fmt(self, f), #[cfg(not(feature = "no_index"))] - Union::Blob(ref value, _, _) => fmt::Debug::fmt(value, f), + Union::Blob(_, _, _) => fmt::Debug::fmt(self, f), #[cfg(not(feature = "no_object"))] - Union::Map(ref value, _, _) => { - f.write_str("#")?; - fmt::Debug::fmt(value, f) - } + Union::Map(_, _, _) => fmt::Debug::fmt(self, f), Union::FnPtr(ref value, _, _) => fmt::Display::fmt(value, f), #[cfg(not(feature = "no_std"))] Union::TimeStamp(_, _, _) => f.write_str(""), From d7960dfe80f19496f35857d98d261cbd13a0f19f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 21 Dec 2021 13:03:39 +0800 Subject: [PATCH 30/97] Short circuit no_function. --- src/func/callable_function.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/func/callable_function.rs b/src/func/callable_function.rs index 251c4d06..65504c42 100644 --- a/src/func/callable_function.rs +++ b/src/func/callable_function.rs @@ -100,10 +100,12 @@ impl CallableFunction { #[inline] #[must_use] pub const fn is_script(&self) -> bool { - match self { - #[cfg(not(feature = "no_function"))] - Self::Script(_) => true, + #[cfg(feature = "no_function")] + return false; + #[cfg(not(feature = "no_function"))] + match self { + Self::Script(_) => true, Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => false, } } @@ -123,12 +125,14 @@ impl CallableFunction { #[inline] #[must_use] pub const fn is_native(&self) -> bool { + #[cfg(feature = "no_function")] + return true; + + #[cfg(not(feature = "no_function"))] match self { Self::Pure(_) | Self::Method(_) => true, Self::Plugin(_) => true, Self::Iterator(_) => true, - - #[cfg(not(feature = "no_function"))] Self::Script(_) => false, } } @@ -136,11 +140,13 @@ impl CallableFunction { #[inline] #[must_use] pub fn access(&self) -> FnAccess { + #[cfg(feature = "no_function")] + return FnAccess::Public; + + #[cfg(not(feature = "no_function"))] match self { Self::Plugin(_) => FnAccess::Public, Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => FnAccess::Public, - - #[cfg(not(feature = "no_function"))] Self::Script(f) => f.access, } } From f74486f904393082b09767cdd51f7bbd10961cd6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 21 Dec 2021 13:21:29 +0800 Subject: [PATCH 31/97] Separate return type name and use references for serialization. --- src/module/mod.rs | 98 +++++++++++++++++++++++++++++++------------ src/serde/metadata.rs | 93 +++++++++++++++++----------------------- 2 files changed, 109 insertions(+), 82 deletions(-) diff --git a/src/module/mod.rs b/src/module/mod.rs index 4e7bc7a8..7a4a25ae 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -47,9 +47,12 @@ pub struct FuncInfo { pub params: usize, /// Parameter types (if applicable). pub param_types: StaticVec, - /// Parameter names (if available). + /// Parameter names and types (if available). #[cfg(feature = "metadata")] - pub param_names: StaticVec, + pub param_names_and_types: StaticVec, + /// Return type name. + #[cfg(feature = "metadata")] + pub return_type_name: Identifier, } impl FuncInfo { @@ -60,16 +63,21 @@ impl FuncInfo { pub fn gen_signature(&self) -> String { let mut sig = format!("{}(", self.name); - if !self.param_names.is_empty() { - let mut params: StaticVec> = - self.param_names.iter().map(|s| s.as_str().into()).collect(); - let return_type = params.pop().unwrap_or_else(|| "()".into()); + if !self.param_names_and_types.is_empty() { + let params: StaticVec<_> = self + .param_names_and_types + .iter() + .map(|s| s.as_str()) + .collect(); sig.push_str(¶ms.join(", ")); - if &*return_type != "()" { - sig.push_str(") -> "); - sig.push_str(&return_type); - } else { - sig.push(')'); + sig.push_str(")"); + + match self.return_type_name.as_str() { + "" | "()" => (), + ty => { + sig.push_str(" -> "); + sig.push_str(ty); + } } } else { for x in 0..self.params { @@ -82,7 +90,12 @@ impl FuncInfo { if self.func.is_script() { sig.push(')'); } else { - sig.push_str(") -> ?"); + sig.push_str(")"); + + match self.return_type_name.as_str() { + "()" => (), + _ => sig.push_str(" -> ?"), + } } } @@ -457,8 +470,12 @@ impl Module { // None + function name + number of arguments. let num_params = fn_def.params.len(); let hash_script = crate::calc_fn_hash(&fn_def.name, num_params); - let mut param_names = fn_def.params.clone(); - param_names.push("Dynamic".into()); + #[cfg(feature = "metadata")] + let param_names_and_types = fn_def + .params + .iter() + .map(|v| self.identifiers.get(v.as_str())) + .collect(); self.functions.insert( hash_script, FuncInfo { @@ -468,7 +485,9 @@ impl Module { params: num_params, param_types: StaticVec::new_const(), #[cfg(feature = "metadata")] - param_names, + param_names_and_types, + #[cfg(feature = "metadata")] + return_type_name: self.identifiers.get("Dynamic"), func: Into::::into(fn_def).into(), } .into(), @@ -613,13 +632,20 @@ impl Module { #[cfg(feature = "metadata")] #[inline] pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[impl AsRef]) -> &mut Self { - let param_names = arg_names + let mut param_names: StaticVec<_> = arg_names .iter() .map(|name| self.identifiers.get(name.as_ref())) .collect(); if let Some(f) = self.functions.get_mut(&hash_fn) { - f.param_names = param_names; + let (param_names, return_type_name) = if param_names.len() > f.params { + let return_type = param_names.pop().expect("exists"); + (param_names, return_type) + } else { + (param_names, Default::default()) + }; + f.param_names_and_types = param_names; + f.return_type_name = return_type_name; } self @@ -664,6 +690,15 @@ impl Module { /// # WARNING - Low Level API /// /// This function is very low level. + /// + /// ## Parameter Names and Types + /// + /// Each parameter name/type pair should be a single string of the format: `var_name: type`. + /// + /// ## Return Type + /// + /// The _last entry_ in the list should be the _return type_ of the function. + /// In other words, the number of entries should be one larger than the number of parameters. #[inline] pub fn set_fn( &mut self, @@ -686,13 +721,20 @@ impl Module { param_types.shrink_to_fit(); #[cfg(feature = "metadata")] - let mut param_names: StaticVec<_> = _arg_names - .iter() - .flat_map(|&p| p.iter()) - .map(|&arg| self.identifiers.get(arg)) - .collect(); - #[cfg(feature = "metadata")] - param_names.shrink_to_fit(); + let (param_names, return_type_name) = { + let mut names = _arg_names + .iter() + .flat_map(|&p| p.iter()) + .map(|&arg| self.identifiers.get(arg)) + .collect::>(); + let return_type = if names.len() > arg_types.len() { + names.pop().expect("exists") + } else { + Default::default() + }; + names.shrink_to_fit(); + (names, return_type) + }; let hash_fn = calc_native_fn_hash(empty::<&str>(), name.as_ref(), ¶m_types); @@ -705,7 +747,9 @@ impl Module { params: param_types.len(), param_types, #[cfg(feature = "metadata")] - param_names, + param_names_and_types: param_names, + #[cfg(feature = "metadata")] + return_type_name, func: func.into(), } .into(), @@ -1284,8 +1328,8 @@ impl Module { /// Get an iterator to the sub-modules in the [`Module`]. #[inline] - pub fn iter_sub_modules(&self) -> impl Iterator)> { - self.modules.iter().map(|(k, m)| (k.as_str(), m.clone())) + pub fn iter_sub_modules(&self) -> impl Iterator)> { + self.modules.iter().map(|(k, m)| (k.as_str(), m)) } /// Get an iterator to the variables in the [`Module`]. diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index df6364fe..2aa999b7 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -48,33 +48,24 @@ impl From for FnAccess { #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -struct FnParam { - pub name: Box, +struct FnParam<'a> { #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub typ: Option>, + pub name: Option<&'a str>, + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub typ: Option<&'a str>, } -impl PartialOrd for FnParam { +impl PartialOrd for FnParam<'_> { fn partial_cmp(&self, other: &Self) -> Option { Some(match self.name.partial_cmp(&other.name).expect("succeed") { Ordering::Less => Ordering::Less, Ordering::Greater => Ordering::Greater, - Ordering::Equal => match (self.typ.is_none(), other.typ.is_none()) { - (true, true) => Ordering::Equal, - (true, false) => Ordering::Greater, - (false, true) => Ordering::Less, - (false, false) => self - .typ - .as_ref() - .expect("`Some`") - .partial_cmp(other.typ.as_ref().expect("`Some`")) - .expect("succeed"), - }, + Ordering::Equal => self.typ.partial_cmp(other.typ).expect("succeed"), }) } } -impl Ord for FnParam { +impl Ord for FnParam<'_> { fn cmp(&self, other: &Self) -> Ordering { match self.name.cmp(&other.name) { Ordering::Equal => self.typ.cmp(&other.typ), @@ -85,7 +76,7 @@ impl Ord for FnParam { #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -struct FnMetadata { +struct FnMetadata<'a> { pub base_hash: u64, pub full_hash: u64, pub namespace: FnNamespace, @@ -95,21 +86,21 @@ struct FnMetadata { pub typ: FnType, pub num_params: usize, #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub params: Vec, + pub params: Vec>, #[serde(default, skip_serializing_if = "Option::is_none")] - pub return_type: Option>, + pub return_type_name: Option<&'a str>, pub signature: String, #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub doc_comments: Vec>, + pub doc_comments: Vec<&'a str>, } -impl PartialOrd for FnMetadata { +impl PartialOrd for FnMetadata<'_> { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for FnMetadata { +impl Ord for FnMetadata<'_> { fn cmp(&self, other: &Self) -> Ordering { match self.name.cmp(&other.name) { Ordering::Equal => match self.num_params.cmp(&other.num_params) { @@ -121,8 +112,8 @@ impl Ord for FnMetadata { } } -impl From<&crate::module::FuncInfo> for FnMetadata { - fn from(info: &crate::module::FuncInfo) -> Self { +impl<'a> From<&'a crate::module::FuncInfo> for FnMetadata<'a> { + fn from(info: &'a crate::module::FuncInfo) -> Self { let base_hash = calc_fn_hash(&info.name, info.params); let (typ, full_hash) = if info.func.is_script() { (FnType::Script, base_hash) @@ -142,24 +133,22 @@ impl From<&crate::module::FuncInfo> for FnMetadata { typ, num_params: info.params, params: info - .param_names + .param_names_and_types .iter() - .take(info.params) .map(|s| { let mut seg = s.splitn(2, ':'); - let name = seg - .next() - .map(|s| s.trim().into()) - .unwrap_or_else(|| "_".into()); - let typ = seg.next().map(|s| s.trim().into()); + let name = match seg.next().map(&str::trim).unwrap_or("_") { + "_" => None, + s => Some(s), + }; + let typ = seg.next().map(&str::trim); FnParam { name, typ } }) .collect(), - return_type: info - .param_names - .last() - .map(|s| s.as_str().into()) - .or_else(|| Some("()".into())), + return_type_name: match info.return_type_name.as_str() { + "" | "()" => None, + ty => Some(ty), + }, signature: info.gen_signature(), doc_comments: if info.func.is_script() { #[cfg(feature = "no_function")] @@ -171,7 +160,7 @@ impl From<&crate::module::FuncInfo> for FnMetadata { .expect("script-defined function") .comments .as_ref() - .map_or_else(|| Vec::new(), |v| v.to_vec()) + .map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect()) } else { Vec::new() }, @@ -181,14 +170,14 @@ impl From<&crate::module::FuncInfo> for FnMetadata { #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] -struct ModuleMetadata { +struct ModuleMetadata<'a> { #[serde(skip_serializing_if = "BTreeMap::is_empty")] - pub modules: BTreeMap, + pub modules: BTreeMap<&'a str, Self>, #[serde(skip_serializing_if = "Vec::is_empty")] - pub functions: Vec, + pub functions: Vec>, } -impl ModuleMetadata { +impl ModuleMetadata<'_> { #[inline(always)] pub fn new() -> Self { Self { @@ -198,15 +187,15 @@ impl ModuleMetadata { } } -impl From<&crate::Module> for ModuleMetadata { - fn from(module: &crate::Module) -> Self { +impl<'a> From<&'a crate::Module> for ModuleMetadata<'a> { + fn from(module: &'a crate::Module) -> Self { let mut functions: Vec<_> = module.iter_fn().map(|f| f.into()).collect(); functions.sort(); Self { modules: module .iter_sub_modules() - .map(|(name, m)| (name.to_string(), m.as_ref().into())) + .map(|(name, m)| (name, m.as_ref().into())) .collect(), functions, } @@ -232,20 +221,14 @@ impl Engine { let _ast = ast; let mut global = ModuleMetadata::new(); - if include_global { - self.global_modules - .iter() - .take(self.global_modules.len() - 1) - .flat_map(|m| m.iter_fn()) - .for_each(|f| global.functions.push(f.into())); - } - self.global_sub_modules.iter().for_each(|(name, m)| { - global.modules.insert(name.to_string(), m.as_ref().into()); + global.modules.insert(name, m.as_ref().into()); }); - self.global_namespace() - .iter_fn() + self.global_modules + .iter() + .take(if include_global { usize::MAX } else { 1 }) + .flat_map(|m| m.iter_fn()) .for_each(|f| global.functions.push(f.into())); #[cfg(not(feature = "no_function"))] From b85a9b3c1cefe88dad3a808ef0f7b43f97aa1f2c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 21 Dec 2021 16:14:07 +0800 Subject: [PATCH 32/97] Extract doc-comment on plugin functions. --- CHANGELOG.md | 1 + codegen/Cargo.toml | 4 +- codegen/src/attrs.rs | 48 +++++++++- codegen/src/function.rs | 14 +++ codegen/src/module.rs | 16 ++-- codegen/src/rhai_module.rs | 24 +++-- codegen/src/test/module.rs | 90 +++++++++++++++++++ codegen/ui_tests/rhai_fn_duplicate_attr.rs | 18 ++++ .../ui_tests/rhai_fn_duplicate_attr.stderr | 17 ++++ src/module/mod.rs | 53 ++++++++++- src/serde/metadata.rs | 11 ++- 11 files changed, 275 insertions(+), 21 deletions(-) create mode 100644 codegen/ui_tests/rhai_fn_duplicate_attr.rs create mode 100644 codegen/ui_tests/rhai_fn_duplicate_attr.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 46cf10f8..f5d9175d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Enhancements * Added `NativeCallContext::call_fn` to easily call a function. * A new syntax is introduced for `def_package!` that will replace the old syntax in future versions. +* Doc-comments on plugin module functions are extracted into the functions' metadata. Deprecated API's ---------------- diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 472c5ca1..8c471813 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai_codegen" -version = "1.2.0" +version = "1.3.0" edition = "2018" authors = ["jhwgh1968", "Stephen Chung"] description = "Procedural macros support package for Rhai, a scripting language and engine for Rust" @@ -16,7 +16,7 @@ default = [] metadata = [] [dev-dependencies] -rhai = { path = "..", version = "1.1" } +rhai = { path = "..", version = "1.4" } trybuild = "1" [dependencies] diff --git a/codegen/src/attrs.rs b/codegen/src/attrs.rs index 977e73de..ede7c88b 100644 --- a/codegen/src/attrs.rs +++ b/codegen/src/attrs.rs @@ -117,18 +117,60 @@ pub fn inner_item_attributes( attrs: &mut Vec, attr_name: &str, ) -> syn::Result { - // Find the #[rhai_fn] attribute which will turn be read for the function parameters. - if let Some(rhai_fn_idx) = attrs + // Find the #[rhai_fn] attribute which will turn be read for function parameters. + if let Some(index) = attrs .iter() .position(|a| a.path.get_ident().map(|i| *i == attr_name).unwrap_or(false)) { - let rhai_fn_attr = attrs.remove(rhai_fn_idx); + let rhai_fn_attr = attrs.remove(index); + + // Cannot have more than one #[rhai_fn] + if let Some(duplicate) = attrs + .iter() + .find(|a| a.path.get_ident().map(|i| *i == attr_name).unwrap_or(false)) + { + return Err(syn::Error::new( + duplicate.span(), + format!("duplicated attribute '{}'", attr_name), + )); + } + rhai_fn_attr.parse_args_with(T::parse_stream) } else { Ok(T::no_attrs()) } } +#[cfg(feature = "metadata")] +pub fn doc_attribute(attrs: &mut Vec) -> syn::Result { + // Find the #[doc] attribute which will turn be read for function documentation. + let mut comments = String::new(); + + while let Some(index) = attrs + .iter() + .position(|attr| attr.path.get_ident().map(|i| *i == "doc").unwrap_or(false)) + { + let attr = attrs.remove(index); + let meta = attr.parse_meta()?; + + match meta { + syn::Meta::NameValue(syn::MetaNameValue { + lit: syn::Lit::Str(s), + .. + }) => { + if !comments.is_empty() { + comments += "\n"; + } + + comments += &s.value(); + } + _ => continue, + } + } + + Ok(comments) +} + pub fn collect_cfg_attr(attrs: &[syn::Attribute]) -> Vec { attrs .iter() diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 5e70c8c8..53d1f95d 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -282,6 +282,8 @@ pub struct ExportedFn { mut_receiver: bool, params: ExportedFnParams, cfg_attrs: Vec, + #[cfg(feature = "metadata")] + comment: String, } impl Parse for ExportedFn { @@ -404,6 +406,8 @@ impl Parse for ExportedFn { mut_receiver, params: Default::default(), cfg_attrs, + #[cfg(feature = "metadata")] + comment: Default::default(), }) } } @@ -503,6 +507,16 @@ impl ExportedFn { } } + #[cfg(feature = "metadata")] + pub fn comment(&self) -> &str { + &self.comment + } + + #[cfg(feature = "metadata")] + pub fn set_comment(&mut self, comment: String) { + self.comment = comment + } + pub fn set_cfg_attrs(&mut self, cfg_attrs: Vec) { self.cfg_attrs = cfg_attrs } diff --git a/codegen/src/module.rs b/codegen/src/module.rs index 75099218..38b2509f 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -117,18 +117,22 @@ impl Parse for Module { syn::Item::Fn(f) => Some(f), _ => None, }) - .try_fold(Vec::new(), |mut vec, item_fn| { + .try_fold(Vec::new(), |mut vec, item_fn| -> syn::Result<_> { let params = crate::attrs::inner_item_attributes(&mut item_fn.attrs, "rhai_fn")?; - syn::parse2::(item_fn.to_token_stream()) - .and_then(|mut f| { + let f = + syn::parse2(item_fn.to_token_stream()).and_then(|mut f: ExportedFn| { f.set_params(params)?; f.set_cfg_attrs(crate::attrs::collect_cfg_attr(&item_fn.attrs)); + + #[cfg(feature = "metadata")] + f.set_comment(crate::attrs::doc_attribute(&mut item_fn.attrs)?); Ok(f) - }) - .map(|f| vec.push(f)) - .map(|_| vec) + })?; + + vec.push(f); + Ok(vec) })?; // Gather and parse constants definitions. for item in content.iter() { diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index a16eab56..bf12102c 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -166,20 +166,30 @@ pub fn generate_body( ); #[cfg(feature = "metadata")] - let param_names = quote! { - Some(#fn_token_name::PARAM_NAMES) - }; + let (param_names, comment) = ( + quote! { Some(#fn_token_name::PARAM_NAMES) }, + function.comment(), + ); #[cfg(not(feature = "metadata"))] - let param_names = quote! { None }; + let (param_names, comment) = (quote! { None }, ""); - set_fn_statements.push( + set_fn_statements.push(if comment.is_empty() { syn::parse2::(quote! { #(#cfg_attrs)* m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, #param_names, &[#(#fn_input_types),*], #fn_token_name().into()); }) - .unwrap(), - ); + .unwrap() + } else { + let comment_literal = syn::LitStr::new(comment, Span::call_site()); + + syn::parse2::(quote! { + #(#cfg_attrs)* + m.set_fn_with_comment(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, + #param_names, &[#(#fn_input_types),*], #comment_literal, #fn_token_name().into()); + }) + .unwrap() + }); } gen_fn_tokens.push(quote! { diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 3ada9401..597dbb46 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -37,6 +37,36 @@ mod module_tests { ); } + #[test] + fn one_factory_fn_with_comment_module() { + let input_tokens: TokenStream = quote! { + pub mod one_fn { + /// This is a doc-comment. + /// Another line. + /** block doc-comment */ + // Regular comment + /// Final line. + /** doc-comment + in multiple lines + */ + pub fn get_mystic_number() -> INT { + 42 + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert!(item_mod.consts().is_empty()); + assert_eq!(item_mod.fns().len(), 1); + assert_eq!(item_mod.fns()[0].name().to_string(), "get_mystic_number"); + assert_eq!(item_mod.fns()[0].comment(), " This is a doc-comment.\n Another line.\n block doc-comment \n Final line.\n doc-comment\n in multiple lines\n "); + assert_eq!(item_mod.fns()[0].arg_count(), 0); + assert_eq!( + item_mod.fns()[0].return_type().unwrap(), + &syn::parse2::(quote! { INT }).unwrap() + ); + } + #[test] fn one_single_arg_fn_module() { let input_tokens: TokenStream = quote! { @@ -323,6 +353,66 @@ mod generate_tests { assert_streams_eq(item_mod.generate(), expected_tokens); } + #[test] + fn one_factory_fn_with_comment_module() { + let input_tokens: TokenStream = quote! { + pub mod one_fn { + /// This is a doc-comment. + /// Another line. + /** block doc-comment */ + // Regular comment + /// Final line. + /** doc-comment + in multiple lines + */ + pub fn get_mystic_number() -> INT { + 42 + } + } + }; + + let expected_tokens = quote! { + pub mod one_fn { + pub fn get_mystic_number() -> INT { + 42 + } + #[allow(unused_imports)] + use super::*; + + pub fn rhai_module_generate() -> Module { + let mut m = Module::new(); + rhai_generate_into_module(&mut m, false); + m.build_index(); + m + } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_fn_with_comment("get_mystic_number", FnNamespace::Internal, FnAccess::Public, + Some(get_mystic_number_token::PARAM_NAMES), &[], " This is a doc-comment.\n Another line.\n block doc-comment \n Final line.\n doc-comment\n in multiple lines\n ", + get_mystic_number_token().into()); + if flatten {} else {} + } + #[allow(non_camel_case_types)] + pub struct get_mystic_number_token(); + impl get_mystic_number_token { + pub const PARAM_NAMES: &'static [&'static str] = &["INT"]; + #[inline(always)] pub fn param_types() -> [TypeId; 0usize] { [] } + } + impl PluginFunction for get_mystic_number_token { + #[inline(always)] + fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { + Ok(Dynamic::from(get_mystic_number())) + } + + #[inline(always)] fn is_method_call(&self) -> bool { false } + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + #[test] fn one_single_arg_global_fn_module() { let input_tokens: TokenStream = quote! { diff --git a/codegen/ui_tests/rhai_fn_duplicate_attr.rs b/codegen/ui_tests/rhai_fn_duplicate_attr.rs new file mode 100644 index 00000000..b775fe4d --- /dev/null +++ b/codegen/ui_tests/rhai_fn_duplicate_attr.rs @@ -0,0 +1,18 @@ +use rhai::plugin::*; + +#[export_module] +pub mod test_module { + #[rhai_fn(name = "test")] + #[rhai_fn(pure)] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_duplicate_attr.stderr b/codegen/ui_tests/rhai_fn_duplicate_attr.stderr new file mode 100644 index 00000000..983e5d1d --- /dev/null +++ b/codegen/ui_tests/rhai_fn_duplicate_attr.stderr @@ -0,0 +1,17 @@ +error: duplicated attribute 'rhai_fn' + --> ui_tests/rhai_fn_duplicate_attr.rs:6:5 + | +6 | #[rhai_fn(pure)] + | ^^^^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> ui_tests/rhai_fn_duplicate_attr.rs:13:8 + | +13 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` + +error[E0425]: cannot find value `n` in this scope + --> ui_tests/rhai_fn_duplicate_attr.rs:13:29 + | +13 | if test_module::test_fn(n) { + | ^ not found in this scope diff --git a/src/module/mod.rs b/src/module/mod.rs index 7a4a25ae..e29d22fe 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -10,7 +10,7 @@ use crate::tokenizer::Token; use crate::types::dynamic::Variant; use crate::{ calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult, - Identifier, ImmutableString, NativeCallContext, Shared, StaticVec, + Identifier, ImmutableString, NativeCallContext, Shared, SmartString, StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -53,6 +53,9 @@ pub struct FuncInfo { /// Return type name. #[cfg(feature = "metadata")] pub return_type_name: Identifier, + /// Comments. + #[cfg(feature = "metadata")] + pub comments: SmartString, } impl FuncInfo { @@ -488,6 +491,11 @@ impl Module { param_names_and_types, #[cfg(feature = "metadata")] return_type_name: self.identifiers.get("Dynamic"), + #[cfg(feature = "metadata")] + comments: fn_def + .comments + .as_ref() + .map_or(SmartString::new_const(), |v| v.join("\n").into()), func: Into::::into(fn_def).into(), } .into(), @@ -750,6 +758,8 @@ impl Module { param_names_and_types: param_names, #[cfg(feature = "metadata")] return_type_name, + #[cfg(feature = "metadata")] + comments: SmartString::new_const(), func: func.into(), } .into(), @@ -761,6 +771,47 @@ impl Module { hash_fn } + /// _(metadata)_ Set a Rust function into the [`Module`], returning a non-zero hash key. + /// Exported under the `metadata` feature only. + /// + /// If there is an existing Rust function of the same hash, it is replaced. + /// + /// # WARNING - Low Level API + /// + /// This function is very low level. + /// + /// ## Parameter Names and Types + /// + /// Each parameter name/type pair should be a single string of the format: `var_name: type`. + /// + /// ## Return Type + /// + /// The _last entry_ in the list should be the _return type_ of the function. + /// In other words, the number of entries should be one larger than the number of parameters. + #[cfg(feature = "metadata")] + #[inline] + pub fn set_fn_with_comment( + &mut self, + name: impl AsRef + Into, + namespace: FnNamespace, + access: FnAccess, + arg_names: Option<&[&str]>, + arg_types: &[TypeId], + comment: impl Into, + func: CallableFunction, + ) -> u64 { + let hash = self.set_fn(name, namespace, access, arg_names, arg_types, func); + + let comment = comment.into(); + + if !comment.is_empty() { + let f = self.functions.get_mut(&hash).expect("exists"); + f.comments = comment; + } + + hash + } + /// Set a Rust function taking a reference to the scripting [`Engine`][crate::Engine], /// the current set of functions, plus a list of mutable [`Dynamic`] references /// into the [`Module`], returning a non-zero hash key. diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index 2aa999b7..d214a701 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -60,7 +60,7 @@ impl PartialOrd for FnParam<'_> { Some(match self.name.partial_cmp(&other.name).expect("succeed") { Ordering::Less => Ordering::Less, Ordering::Greater => Ordering::Greater, - Ordering::Equal => self.typ.partial_cmp(other.typ).expect("succeed"), + Ordering::Equal => self.typ.partial_cmp(&other.typ).expect("succeed"), }) } } @@ -162,7 +162,14 @@ impl<'a> From<&'a crate::module::FuncInfo> for FnMetadata<'a> { .as_ref() .map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect()) } else { - Vec::new() + #[cfg(not(feature = "metadata"))] + { + Vec::new() + } + #[cfg(feature = "metadata")] + { + info.comments.split("\n").collect() + } }, } } From f9f36158781bf55175252b8d119f2264f358b2b6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 21 Dec 2021 16:36:53 +0800 Subject: [PATCH 33/97] Fix builds. --- src/module/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/module/mod.rs b/src/module/mod.rs index e29d22fe..c3dc4fc0 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -10,7 +10,7 @@ use crate::tokenizer::Token; use crate::types::dynamic::Variant; use crate::{ calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult, - Identifier, ImmutableString, NativeCallContext, Shared, SmartString, StaticVec, + Identifier, ImmutableString, NativeCallContext, Shared, StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -55,7 +55,7 @@ pub struct FuncInfo { pub return_type_name: Identifier, /// Comments. #[cfg(feature = "metadata")] - pub comments: SmartString, + pub comments: crate::SmartString, } impl FuncInfo { @@ -495,7 +495,7 @@ impl Module { comments: fn_def .comments .as_ref() - .map_or(SmartString::new_const(), |v| v.join("\n").into()), + .map_or(crate::SmartString::new_const(), |v| v.join("\n").into()), func: Into::::into(fn_def).into(), } .into(), @@ -759,7 +759,7 @@ impl Module { #[cfg(feature = "metadata")] return_type_name, #[cfg(feature = "metadata")] - comments: SmartString::new_const(), + comments: crate::SmartString::new_const(), func: func.into(), } .into(), @@ -797,7 +797,7 @@ impl Module { access: FnAccess, arg_names: Option<&[&str]>, arg_types: &[TypeId], - comment: impl Into, + comment: impl Into, func: CallableFunction, ) -> u64 { let hash = self.set_fn(name, namespace, access, arg_names, arg_types, func); From 7ff50451cc2df5f518e7322f4e173913cfab53c3 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 21 Dec 2021 22:16:03 +0800 Subject: [PATCH 34/97] Make comments multiline. --- codegen/src/attrs.rs | 22 ++++++---- codegen/src/function.rs | 12 +++--- codegen/src/module.rs | 2 +- codegen/src/rhai_module.rs | 18 ++++---- codegen/src/test/module.rs | 23 +++++++--- src/module/mod.rs | 88 ++++++++++++++++++++++++++++++-------- src/serde/metadata.rs | 4 +- 7 files changed, 120 insertions(+), 49 deletions(-) diff --git a/codegen/src/attrs.rs b/codegen/src/attrs.rs index ede7c88b..0997430b 100644 --- a/codegen/src/attrs.rs +++ b/codegen/src/attrs.rs @@ -142,27 +142,31 @@ pub fn inner_item_attributes( } #[cfg(feature = "metadata")] -pub fn doc_attribute(attrs: &mut Vec) -> syn::Result { +pub fn doc_attributes(attrs: &mut Vec) -> syn::Result> { // Find the #[doc] attribute which will turn be read for function documentation. - let mut comments = String::new(); + let mut comments = Vec::new(); while let Some(index) = attrs .iter() .position(|attr| attr.path.get_ident().map(|i| *i == "doc").unwrap_or(false)) { - let attr = attrs.remove(index); - let meta = attr.parse_meta()?; - - match meta { + match attrs.remove(index).parse_meta()? { syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { - if !comments.is_empty() { - comments += "\n"; + let mut line = s.value(); + + if line.contains('\n') { + // Must be a block comment `/** ... */` + line.insert_str(0, "/**"); + line.push_str("*/"); + } else { + // Single line - assume it is `///` + line.insert_str(0, "///"); } - comments += &s.value(); + comments.push(line); } _ => continue, } diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 53d1f95d..29e2a2cf 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -283,7 +283,7 @@ pub struct ExportedFn { params: ExportedFnParams, cfg_attrs: Vec, #[cfg(feature = "metadata")] - comment: String, + comments: Vec, } impl Parse for ExportedFn { @@ -407,7 +407,7 @@ impl Parse for ExportedFn { params: Default::default(), cfg_attrs, #[cfg(feature = "metadata")] - comment: Default::default(), + comments: Default::default(), }) } } @@ -508,13 +508,13 @@ impl ExportedFn { } #[cfg(feature = "metadata")] - pub fn comment(&self) -> &str { - &self.comment + pub fn comments(&self) -> &[String] { + &self.comments } #[cfg(feature = "metadata")] - pub fn set_comment(&mut self, comment: String) { - self.comment = comment + pub fn set_comments(&mut self, comments: Vec) { + self.comments = comments } pub fn set_cfg_attrs(&mut self, cfg_attrs: Vec) { diff --git a/codegen/src/module.rs b/codegen/src/module.rs index 38b2509f..14290c59 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -127,7 +127,7 @@ impl Parse for Module { f.set_cfg_attrs(crate::attrs::collect_cfg_attr(&item_fn.attrs)); #[cfg(feature = "metadata")] - f.set_comment(crate::attrs::doc_attribute(&mut item_fn.attrs)?); + f.set_comments(crate::attrs::doc_attributes(&mut item_fn.attrs)?); Ok(f) })?; diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index bf12102c..01bacf62 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -166,14 +166,18 @@ pub fn generate_body( ); #[cfg(feature = "metadata")] - let (param_names, comment) = ( + let (param_names, comments) = ( quote! { Some(#fn_token_name::PARAM_NAMES) }, - function.comment(), + function + .comments() + .iter() + .map(|s| syn::LitStr::new(s, Span::call_site())) + .collect::>(), ); #[cfg(not(feature = "metadata"))] - let (param_names, comment) = (quote! { None }, ""); + let (param_names, comments) = (quote! { None }, Vec::::new()); - set_fn_statements.push(if comment.is_empty() { + set_fn_statements.push(if comments.is_empty() { syn::parse2::(quote! { #(#cfg_attrs)* m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, @@ -181,12 +185,10 @@ pub fn generate_body( }) .unwrap() } else { - let comment_literal = syn::LitStr::new(comment, Span::call_site()); - syn::parse2::(quote! { #(#cfg_attrs)* - m.set_fn_with_comment(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, - #param_names, &[#(#fn_input_types),*], #comment_literal, #fn_token_name().into()); + m.set_fn_with_comments(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, + #param_names, &[#(#fn_input_types),*], &[#(#comments),*], #fn_token_name().into()); }) .unwrap() }); diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 597dbb46..4ba006f7 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -38,7 +38,7 @@ mod module_tests { } #[test] - fn one_factory_fn_with_comment_module() { + fn one_factory_fn_with_comments_module() { let input_tokens: TokenStream = quote! { pub mod one_fn { /// This is a doc-comment. @@ -59,7 +59,20 @@ mod module_tests { assert!(item_mod.consts().is_empty()); assert_eq!(item_mod.fns().len(), 1); assert_eq!(item_mod.fns()[0].name().to_string(), "get_mystic_number"); - assert_eq!(item_mod.fns()[0].comment(), " This is a doc-comment.\n Another line.\n block doc-comment \n Final line.\n doc-comment\n in multiple lines\n "); + assert_eq!( + item_mod.fns()[0] + .comments() + .iter() + .cloned() + .collect::>(), + vec![ + "/// This is a doc-comment.", + "/// Another line.", + "/// block doc-comment ", + "/// Final line.", + "/** doc-comment\n in multiple lines\n */" + ] + ); assert_eq!(item_mod.fns()[0].arg_count(), 0); assert_eq!( item_mod.fns()[0].return_type().unwrap(), @@ -354,7 +367,7 @@ mod generate_tests { } #[test] - fn one_factory_fn_with_comment_module() { + fn one_factory_fn_with_comments_module() { let input_tokens: TokenStream = quote! { pub mod one_fn { /// This is a doc-comment. @@ -387,8 +400,8 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn_with_comment("get_mystic_number", FnNamespace::Internal, FnAccess::Public, - Some(get_mystic_number_token::PARAM_NAMES), &[], " This is a doc-comment.\n Another line.\n block doc-comment \n Final line.\n doc-comment\n in multiple lines\n ", + m.set_fn_with_comments("get_mystic_number", FnNamespace::Internal, FnAccess::Public, + Some(get_mystic_number_token::PARAM_NAMES), &[], &["/// This is a doc-comment.","/// Another line.","/// block doc-comment ","/// Final line.","/** doc-comment\n in multiple lines\n */"], get_mystic_number_token().into()); if flatten {} else {} } diff --git a/src/module/mod.rs b/src/module/mod.rs index c3dc4fc0..1c252317 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -55,7 +55,7 @@ pub struct FuncInfo { pub return_type_name: Identifier, /// Comments. #[cfg(feature = "metadata")] - pub comments: crate::SmartString, + pub comments: Option]>>, } impl FuncInfo { @@ -492,10 +492,7 @@ impl Module { #[cfg(feature = "metadata")] return_type_name: self.identifiers.get("Dynamic"), #[cfg(feature = "metadata")] - comments: fn_def - .comments - .as_ref() - .map_or(crate::SmartString::new_const(), |v| v.join("\n").into()), + comments: None, func: Into::::into(fn_def).into(), } .into(), @@ -624,7 +621,7 @@ impl Module { self.functions.contains_key(&hash_fn) } - /// Update the metadata (parameter names/types and return type) of a registered function. + /// _(metadata)_ Update the metadata (parameter names/types and return type) of a registered function. /// Exported under the `metadata` feature only. /// /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. @@ -659,6 +656,47 @@ impl Module { self } + /// _(metadata)_ Update the metadata (parameter names/types, return type and doc-comments) of a + /// registered function. + /// Exported under the `metadata` feature only. + /// + /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. + /// + /// ## Parameter Names and Types + /// + /// Each parameter name/type pair should be a single string of the format: `var_name: type`. + /// + /// ## Return Type + /// + /// The _last entry_ in the list should be the _return type_ of the function. In other words, + /// the number of entries should be one larger than the number of parameters. + /// + /// ## Comments + /// + /// Block doc-comments should be kept in a single line. + /// + /// Line doc-comments should be kept in one string slice per line without the termination line-break. + /// + /// Leading white-spaces should be stripped, and each string slice always starts with the corresponding + /// doc-comment leader: `///` or `/**`. + #[cfg(feature = "metadata")] + #[inline] + pub fn update_fn_metadata_with_comments( + &mut self, + hash_fn: u64, + arg_names: &[impl AsRef], + comments: &[impl AsRef], + ) -> &mut Self { + self.update_fn_metadata(hash_fn, arg_names); + + if !comments.is_empty() { + let f = self.functions.get_mut(&hash_fn).expect("exists"); + f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect()); + } + + self + } + /// Update the namespace of a registered function. /// /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. @@ -759,7 +797,7 @@ impl Module { #[cfg(feature = "metadata")] return_type_name, #[cfg(feature = "metadata")] - comments: crate::SmartString::new_const(), + comments: None, func: func.into(), } .into(), @@ -788,25 +826,32 @@ impl Module { /// /// The _last entry_ in the list should be the _return type_ of the function. /// In other words, the number of entries should be one larger than the number of parameters. + /// + /// ## Comments + /// + /// Block doc-comments should be kept in a single line. + /// + /// Line doc-comments should be kept in one string slice per line without the termination line-break. + /// + /// Leading white-spaces should be stripped, and each string slice always starts with the corresponding + /// doc-comment leader: `///` or `/**`. #[cfg(feature = "metadata")] #[inline] - pub fn set_fn_with_comment( + pub fn set_fn_with_comments( &mut self, name: impl AsRef + Into, namespace: FnNamespace, access: FnAccess, arg_names: Option<&[&str]>, arg_types: &[TypeId], - comment: impl Into, + comments: &[impl AsRef], func: CallableFunction, ) -> u64 { let hash = self.set_fn(name, namespace, access, arg_names, arg_types, func); - let comment = comment.into(); - - if !comment.is_empty() { + if !comments.is_empty() { let f = self.functions.get_mut(&hash).expect("exists"); - f.comments = comment; + f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect()); } hash @@ -845,7 +890,7 @@ impl Module { /// /// # Function Metadata /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// No metadata for the function is registered. Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata. /// /// # Example /// @@ -953,7 +998,8 @@ impl Module { /// /// # Function Metadata /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// No metadata for the function is registered. + /// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata. /// /// # Example /// @@ -990,7 +1036,8 @@ impl Module { /// /// # Function Metadata /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// No metadata for the function is registered. + /// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata. /// /// # Example /// @@ -1036,7 +1083,8 @@ impl Module { /// /// # Function Metadata /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// No metadata for the function is registered. + /// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata. /// /// # Example /// @@ -1097,7 +1145,8 @@ impl Module { /// /// # Function Metadata /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// No metadata for the function is registered. + /// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata. /// /// # Example /// @@ -1158,7 +1207,8 @@ impl Module { /// /// # Function Metadata /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// No metadata for the function is registered. + /// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata. /// /// # Example /// diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index d214a701..f5c5a8dc 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -168,7 +168,9 @@ impl<'a> From<&'a crate::module::FuncInfo> for FnMetadata<'a> { } #[cfg(feature = "metadata")] { - info.comments.split("\n").collect() + info.comments + .as_ref() + .map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect()) } }, } From 1c50efbac82c5e159350de46291f8965d88b9410 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 22 Dec 2021 10:48:19 +0800 Subject: [PATCH 35/97] Unroll switch range case if range has only one number. --- src/ast/stmt.rs | 38 ++++++++++++++++++++++---------------- src/parser.rs | 30 +++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index c09c3ff5..1754ee56 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -144,10 +144,17 @@ impl fmt::Debug for StmtBlock { } } -impl From for Stmt { - #[inline(always)] - fn from(block: StmtBlock) -> Self { - Self::Block(block.0.into_boxed_slice(), block.1) +impl From for StmtBlock { + #[inline] + fn from(stmt: Stmt) -> Self { + match stmt { + Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos), + Stmt::Noop(pos) => Self(StaticVec::new_const(), pos), + _ => { + let pos = stmt.position(); + Self(vec![stmt].into(), pos) + } + } } } @@ -176,7 +183,13 @@ pub enum Stmt { Noop(Position), /// `if` expr `{` stmt `}` `else` `{` stmt `}` If(Expr, Box<(StmtBlock, StmtBlock)>, Position), - /// `switch` expr `if` condition `{` literal or _ `=>` stmt `,` ... `}` + /// `switch` expr `{` literal or range or _ `if` condition `=>` stmt `,` ... `}` + /// + /// ### Data Structure + /// + /// 0) Hash table for (condition, block) + /// 1) Default block + /// 2) List of ranges: (start, end, inclusive, condition, statement) Switch( Expr, Box<( @@ -262,17 +275,10 @@ impl Default for Stmt { } } -impl From for StmtBlock { - #[inline] - fn from(stmt: Stmt) -> Self { - match stmt { - Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos), - Stmt::Noop(pos) => Self(StaticVec::new_const(), pos), - _ => { - let pos = stmt.position(); - Self(vec![stmt].into(), pos) - } - } +impl From for Stmt { + #[inline(always)] + fn from(block: StmtBlock) -> Self { + Self::Block(block.0.into_boxed_slice(), block.1) } } diff --git a/src/parser.rs b/src/parser.rs index 2d0601ca..538e990e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -15,7 +15,7 @@ use crate::tokenizer::{ }; use crate::types::dynamic::AccessMode; use crate::{ - calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Engine, ExclusiveRange, + calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Dynamic, Engine, ExclusiveRange, Identifier, ImmutableString, InclusiveRange, LexError, ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST, INT, }; @@ -1095,7 +1095,29 @@ fn parse_switch( def_stmt = match (hash, range) { (None, Some(range)) => { - ranges.push((range.0, range.1, range.2, condition, stmt.into())); + let is_empty = if range.2 { + (range.0..=range.1).is_empty() + } else { + (range.0..range.1).is_empty() + }; + + if !is_empty { + match (range.1.checked_sub(range.0), range.2) { + // Unroll single range + (Some(1), false) | (Some(0), true) => { + let value = Dynamic::from_int(range.0); + let hasher = &mut get_hasher(); + value.hash(hasher); + let hash = hasher.finish(); + + if !table.contains_key(&hash) { + table.insert(hash, (condition.clone(), stmt.into()).into()); + } + } + // Other range + _ => ranges.push((range.0, range.1, range.2, condition, stmt.into())), + } + } None } (Some(hash), None) => { @@ -1103,9 +1125,7 @@ fn parse_switch( None } (None, None) => Some(stmt.into()), - (Some(_), Some(_)) => { - unreachable!("cannot have both a hash and a range in a `switch` statement") - } + _ => unreachable!("both hash and range in `switch` statement case"), }; match input.peek().expect(NEVER_ENDS) { From 422db4269e659b74f19171b193b0805e2a998c92 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 22 Dec 2021 12:41:55 +0800 Subject: [PATCH 36/97] Allow multiple packages in def_package. --- CHANGELOG.md | 2 +- src/packages/fn_basic.rs | 2 +- src/packages/mod.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5d9175d..2957d043 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,8 @@ New features Enhancements ------------ -* Added `NativeCallContext::call_fn` to easily call a function. * A new syntax is introduced for `def_package!` that will replace the old syntax in future versions. +* Added `NativeCallContext::call_fn` to easily call a function. * Doc-comments on plugin module functions are extracted into the functions' metadata. Deprecated API's diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index a727ede7..fa6f33f2 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -4,7 +4,7 @@ use crate::{def_package, FnPtr, ImmutableString, NativeCallContext}; use std::prelude::v1::*; def_package! { - /// Package of basic function poitner utilities. + /// Package of basic function pointer utilities. crate::BasicFnPackage => |lib| { lib.standard = true; diff --git a/src/packages/mod.rs b/src/packages/mod.rs index dffdb85b..d1cd4d02 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -70,7 +70,7 @@ pub trait Package { /// ``` #[macro_export] macro_rules! def_package { - ($(#[$outer:meta])* $root:ident :: $package:ident => | $lib:ident | $block:stmt) => { + ($($(#[$outer:meta])* $root:ident :: $package:ident => | $lib:ident | $block:block)+) => { $( $(#[$outer])* pub struct $package($root::Shared<$root::Module>); @@ -97,7 +97,7 @@ macro_rules! def_package { Self(module.into()) } } - }; + )* }; ($root:ident : $package:ident : $comment:expr , $lib:ident , $block:stmt) => { #[deprecated(since = "1.4.0", note = "this is an old syntax of `def_package!` and is deprecated; use the new syntax of `def_package!` instead")] #[doc=$comment] From 3751b6d018abf8d14bb7d0aefd54d6c1f004c6b9 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 22 Dec 2021 19:59:48 +0800 Subject: [PATCH 37/97] Build in array/blob concat functions. --- src/func/builtin.rs | 53 +++++++++++++++---------------------- src/packages/array_basic.rs | 29 +++++++++++--------- src/packages/blob_basic.rs | 18 ++++++++++--- src/packages/mod.rs | 4 +-- 4 files changed, 55 insertions(+), 49 deletions(-) diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 4ab2f938..be09add4 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -48,6 +48,8 @@ fn is_numeric(type_id: TypeId) -> bool { } /// Build in common binary operator implementations to avoid the cost of calling a registered function. +/// +/// The return function will be registered as a _method_, so the first parameter cannot be consumed. #[must_use] pub fn get_builtin_binary_op_fn( op: &str, @@ -291,28 +293,6 @@ pub fn get_builtin_binary_op_fn( } if type1 == type2 { return match op { - "+" => Some(|_, args| { - let blob1 = &*args[0].read_lock::().expect(BUILTIN); - let blob2 = &*args[1].read_lock::().expect(BUILTIN); - let result = if blob1.is_empty() { - if blob2.is_empty() { - Blob::new() - } else { - blob2.clone() - } - } else { - if blob2.is_empty() { - blob1.clone() - } else { - let mut blob = Blob::with_capacity(blob1.len() + blob2.len()); - blob.extend_from_slice(blob1); - blob.extend_from_slice(blob2); - blob - } - }; - - Ok(Dynamic::from_blob(result)) // do not use result.into() because it'll convert into an Array - }), "==" => Some(impl_op!(Blob == Blob)), "!=" => Some(impl_op!(Blob != Blob)), _ => None, @@ -533,6 +513,8 @@ pub fn get_builtin_binary_op_fn( } /// Build in common operator assignment implementations to avoid the cost of calling a registered function. +/// +/// The return function is registered as a _method_, so the first parameter cannot be consumed. #[must_use] pub fn get_builtin_op_assignment_fn( op: &str, @@ -770,22 +752,31 @@ pub fn get_builtin_op_assignment_fn( }; } + #[cfg(not(feature = "no_index"))] + if type1 == TypeId::of::() { + use crate::packages::array_basic::array_functions::*; + use crate::Array; + + return match op { + "+=" => Some(|_, args| { + let array2 = std::mem::take(args[1]).cast::(); + let array1 = &mut *args[0].write_lock::().expect(BUILTIN); + Ok(append(array1, array2).into()) + }), + _ => None, + }; + } + #[cfg(not(feature = "no_index"))] if type1 == TypeId::of::() { + use crate::packages::blob_basic::blob_functions::*; use crate::Blob; return match op { "+=" => Some(|_, args| { let blob2 = std::mem::take(args[1]).cast::(); - let mut blob1 = args[0].write_lock::().expect(BUILTIN); - if !blob2.is_empty() { - if blob1.is_empty() { - *blob1 = blob2; - } else { - blob1.extend_from_slice(&blob2); - } - } - Ok(Dynamic::UNIT) + let blob1 = &mut *args[0].write_lock::().expect(BUILTIN); + Ok(append(blob1, blob2).into()) }), _ => None, }; diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 8b9476e8..10c5d7f9 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -24,7 +24,7 @@ def_package! { } #[export_module] -mod array_functions { +pub mod array_functions { #[rhai_fn(name = "len", get = "len", pure)] pub fn len(array: &mut Array) -> INT { array.len() as INT @@ -33,26 +33,29 @@ mod array_functions { pub fn push(array: &mut Array, item: Dynamic) { array.push(item); } - #[rhai_fn(name = "append", name = "+=")] - pub fn append(array: &mut Array, y: Array) { - if !y.is_empty() { - if array.is_empty() { - *array = y; + #[rhai_fn(name = "append")] + pub fn append(array1: &mut Array, array2: Array) { + if !array2.is_empty() { + if array1.is_empty() { + *array1 = array2; } else { - array.extend(y); + array1.extend(array2); } } } #[rhai_fn(name = "+")] - pub fn concat(mut array: Array, y: Array) -> Array { - if !y.is_empty() { - if array.is_empty() { - array = y; + pub fn concat(array1: Array, array2: Array) -> Array { + if !array2.is_empty() { + if array1.is_empty() { + array2 } else { - array.extend(y); + let mut array = array1; + array.extend(array2); + array } + } else { + array1 } - array } pub fn insert(array: &mut Array, position: INT, item: Dynamic) { if array.is_empty() { diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index bc4e06cd..048b1226 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -26,7 +26,7 @@ def_package! { } #[export_module] -mod blob_functions { +pub mod blob_functions { pub const fn blob() -> Blob { Blob::new() } @@ -64,12 +64,10 @@ mod blob_functions { pub fn len(blob: &mut Blob) -> INT { blob.len() as INT } - #[rhai_fn(name = "push")] pub fn push(blob: &mut Blob, item: INT) { let item = (item & 0x000000ff) as u8; blob.push(item); } - #[rhai_fn(name = "append")] pub fn append(blob: &mut Blob, y: Blob) { if !y.is_empty() { if blob.is_empty() { @@ -79,6 +77,20 @@ mod blob_functions { } } } + #[rhai_fn(name = "+")] + pub fn concat(blob1: Blob, blob2: Blob) -> Blob { + if !blob2.is_empty() { + if blob1.is_empty() { + blob2 + } else { + let mut blob = blob1; + blob.extend(blob2); + blob + } + } else { + blob1 + } + } pub fn insert(blob: &mut Blob, position: INT, item: INT) { let item = (item & 0x000000ff) as u8; diff --git a/src/packages/mod.rs b/src/packages/mod.rs index d1cd4d02..7f73e774 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -3,8 +3,8 @@ use crate::{Module, Shared}; pub(crate) mod arithmetic; -mod array_basic; -mod blob_basic; +pub(crate) mod array_basic; +pub(crate) mod blob_basic; mod fn_basic; mod iter_basic; mod lang_core; From 5c31ec7f782e41360fa13ade87b73c4fd0a959fb Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 22 Dec 2021 22:22:20 +0800 Subject: [PATCH 38/97] Fix array bug. --- src/func/builtin.rs | 45 ++++++++++++++++++++++++------------- src/packages/array_basic.rs | 2 -- src/parser.rs | 6 ++--- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/func/builtin.rs b/src/func/builtin.rs index be09add4..2da123d4 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -656,7 +656,35 @@ pub fn get_builtin_op_assignment_fn( _ => None, }; } - // Blob op= int + + // array op= any + #[cfg(not(feature = "no_index"))] + if type1 == TypeId::of::() { + use crate::packages::array_basic::array_functions::*; + use crate::Array; + + if type2 == TypeId::of::() { + return match op { + "+=" => Some(|_, args| { + let array2 = std::mem::take(args[1]).cast::(); + let array1 = &mut *args[0].write_lock::().expect(BUILTIN); + Ok(append(array1, array2).into()) + }), + _ => None, + }; + } else { + return match op { + "+=" => Some(|_, args| { + let x = std::mem::take(args[1]); + let array = &mut *args[0].write_lock::().expect(BUILTIN); + Ok(push(array, x).into()) + }), + _ => None, + }; + } + } + + // blob op= int #[cfg(not(feature = "no_index"))] if types_pair == (TypeId::of::(), TypeId::of::()) { use crate::Blob; @@ -752,21 +780,6 @@ pub fn get_builtin_op_assignment_fn( }; } - #[cfg(not(feature = "no_index"))] - if type1 == TypeId::of::() { - use crate::packages::array_basic::array_functions::*; - use crate::Array; - - return match op { - "+=" => Some(|_, args| { - let array2 = std::mem::take(args[1]).cast::(); - let array1 = &mut *args[0].write_lock::().expect(BUILTIN); - Ok(append(array1, array2).into()) - }), - _ => None, - }; - } - #[cfg(not(feature = "no_index"))] if type1 == TypeId::of::() { use crate::packages::blob_basic::blob_functions::*; diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 10c5d7f9..b26a4ace 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -29,11 +29,9 @@ pub mod array_functions { pub fn len(array: &mut Array) -> INT { array.len() as INT } - #[rhai_fn(name = "push", name = "+=")] pub fn push(array: &mut Array, item: Dynamic) { array.push(item); } - #[rhai_fn(name = "append")] pub fn append(array1: &mut Array, array2: Array) { if !array2.is_empty() { if array1.is_empty() { diff --git a/src/parser.rs b/src/parser.rs index 538e990e..e2dd2162 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1110,9 +1110,9 @@ fn parse_switch( value.hash(hasher); let hash = hasher.finish(); - if !table.contains_key(&hash) { - table.insert(hash, (condition.clone(), stmt.into()).into()); - } + table + .entry(hash) + .or_insert_with(|| (condition.clone(), stmt.into()).into()); } // Other range _ => ranges.push((range.0, range.1, range.2, condition, stmt.into())), From 43363e06607b6674eec93767c65cb77b51a8cbe9 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 24 Dec 2021 14:59:14 +0800 Subject: [PATCH 39/97] Reserve is keyword. --- CHANGELOG.md | 9 +++++++-- src/tokenizer.rs | 15 ++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2957d043..7c0d1ee4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ Version 1.4.0 This version adds support for integer _ranges_ via the `..` and `..=` operators. Many standard API's are extended with range parameters where appropriate. +Script-breaking changes +----------------------- + +* `is` is (pun intended) now a reserved keyword to prepare for possible future type checking expressions (e.g. `x is "string"`). + New features ------------ @@ -100,8 +105,8 @@ Bug fixes Version 1.2.0 ============= -Bug fixes with breaking script changes -------------------------------------- +Bug fixes (potentially script-breaking) +-------------------------------------- * As originally intended, function calls with a bang (`!`) now operates directly on the caller's scope, allowing variables inside the scope to be mutated. * As originally intended, `Engine::XXX_with_scope` API's now properly propagate constants within the provided scope also to _functions_ in the script. diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 66fedac4..2a6d301f 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -786,11 +786,16 @@ impl Token { #[cfg(feature = "no_module")] "import" | "export" | "as" => Reserved(syntax.into()), - "===" | "!==" | "->" | "<-" | ":=" | "~" | "::<" | "(*" | "*)" | "#" | "#!" - | "public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var" - | "static" | "shared" | "with" | "goto" | "exit" | "match" | "case" | "default" - | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync" | "async" | "await" - | "yield" => Reserved(syntax.into()), + // List of reserved operators + "===" | "!==" | "->" | "<-" | ":=" | "~" | "::<" | "(*" | "*)" | "#" | "#!" => { + Reserved(syntax.into()) + } + + // List of reserved keywords + "public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var" + | "static" | "shared" | "with" | "is" | "goto" | "exit" | "match" | "case" + | "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync" + | "async" | "await" | "yield" => Reserved(syntax.into()), KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => { From 01c35808cb46d2c11b4611220c85ca2cfd8ec8d2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 25 Dec 2021 23:49:14 +0800 Subject: [PATCH 40/97] Use type alias --- src/api/call_fn.rs | 5 +- src/api/compile.rs | 30 +++--- src/api/deprecated.rs | 24 ++--- src/api/eval.rs | 19 ++-- src/api/events.rs | 4 +- src/api/files.rs | 19 ++-- src/api/register.rs | 20 ++-- src/api/run.rs | 18 +--- src/ast/expr.rs | 12 +-- src/engine.rs | 38 ++++---- src/func/builtin.rs | 17 +--- src/func/call.rs | 25 +++-- src/func/func.rs | 15 ++- src/func/native.rs | 16 ++-- src/func/plugin.rs | 2 +- src/func/register.rs | 18 ++-- src/func/script.rs | 6 +- src/lib.rs | 18 ++-- src/module/mod.rs | 56 ++++++----- src/module/resolvers/collection.rs | 4 +- src/module/resolvers/dummy.rs | 4 +- src/module/resolvers/file.rs | 9 +- src/module/resolvers/mod.rs | 6 +- src/module/resolvers/stat.rs | 5 +- src/packages/arithmetic.rs | 44 +++++---- src/packages/array_basic.rs | 98 ++++++++----------- src/packages/blob_basic.rs | 11 +-- src/packages/iter_basic.rs | 10 +- src/packages/lang_core.rs | 4 +- src/packages/logic.rs | 26 ++--- src/packages/map_basic.rs | 14 +-- src/packages/math_basic.rs | 40 ++++---- src/packages/string_more.rs | 6 +- src/packages/time_basic.rs | 45 +++------ src/parser.rs | 146 ++++++++++++++--------------- src/serde/de.rs | 140 +++++++++++---------------- src/serde/metadata.rs | 16 +--- src/serde/ser.rs | 134 ++++++++++++-------------- src/serde/str.rs | 74 +++++++-------- src/types/error.rs | 8 +- src/types/fn_ptr.rs | 28 +++--- src/types/parse_error.rs | 6 +- 42 files changed, 538 insertions(+), 702 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 6ac100b0..a78943ed 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -4,7 +4,8 @@ use crate::engine::{EvalState, Imports}; use crate::types::dynamic::Variant; use crate::{ - Dynamic, Engine, EvalAltResult, FuncArgs, Position, RhaiResult, Scope, StaticVec, AST, + Dynamic, Engine, EvalAltResult, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, + AST, }; use std::any::type_name; #[cfg(feature = "no_std")] @@ -60,7 +61,7 @@ impl Engine { ast: &AST, name: impl AsRef, args: impl FuncArgs, - ) -> Result> { + ) -> RhaiResultOf { let mut arg_values = StaticVec::new_const(); args.parse(&mut arg_values); diff --git a/src/api/compile.rs b/src/api/compile.rs index 9ca748cd..172e5db8 100644 --- a/src/api/compile.rs +++ b/src/api/compile.rs @@ -1,7 +1,7 @@ //! Module that defines the public compilation API of [`Engine`]. -use crate::parser::ParseState; -use crate::{Engine, ParseError, Scope, AST}; +use crate::parser::{ParseResult, ParseState}; +use crate::{Engine, RhaiResultOf, Scope, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -26,7 +26,7 @@ impl Engine { /// # } /// ``` #[inline(always)] - pub fn compile(&self, script: impl AsRef) -> Result { + pub fn compile(&self, script: impl AsRef) -> ParseResult { self.compile_with_scope(&Scope::new(), script) } /// Compile a string into an [`AST`] using own scope, which can be used later for evaluation. @@ -67,11 +67,7 @@ impl Engine { /// # } /// ``` #[inline(always)] - pub fn compile_with_scope( - &self, - scope: &Scope, - script: impl AsRef, - ) -> Result { + pub fn compile_with_scope(&self, scope: &Scope, script: impl AsRef) -> ParseResult { self.compile_scripts_with_scope(scope, &[script]) } /// Compile a string into an [`AST`] using own scope, which can be used later for evaluation, @@ -88,7 +84,7 @@ impl Engine { &self, scope: &Scope, script: impl AsRef, - ) -> Result> { + ) -> RhaiResultOf { use crate::{ ast::{ASTNode, Expr, Stmt}, func::native::shared_take_or_clone, @@ -201,7 +197,7 @@ impl Engine { &self, scope: &Scope, scripts: &[impl AsRef], - ) -> Result { + ) -> ParseResult { self.compile_with_scope_and_optimization_level( scope, scripts, @@ -222,7 +218,7 @@ impl Engine { scope: &Scope, scripts: &[impl AsRef], #[cfg(not(feature = "no_optimize"))] optimization_level: crate::OptimizationLevel, - ) -> Result { + ) -> ParseResult { let (stream, tokenizer_control) = self.lex_raw(scripts, self.token_mapper.as_ref().map(Box::as_ref)); let mut state = ParseState::new(self, tokenizer_control); @@ -255,7 +251,7 @@ impl Engine { /// # } /// ``` #[inline(always)] - pub fn compile_expression(&self, script: impl AsRef) -> Result { + pub fn compile_expression(&self, script: impl AsRef) -> ParseResult { self.compile_expression_with_scope(&Scope::new(), script) } /// Compile a string containing an expression into an [`AST`] using own scope, @@ -295,7 +291,7 @@ impl Engine { &self, scope: &Scope, script: impl AsRef, - ) -> Result { + ) -> ParseResult { let scripts = [script]; let (stream, tokenizer_control) = self.lex_raw(&scripts, self.token_mapper.as_ref().map(Box::as_ref)); @@ -355,18 +351,14 @@ impl Engine { /// ``` #[cfg(not(feature = "no_object"))] #[inline(always)] - pub fn parse_json( - &self, - json: impl AsRef, - has_null: bool, - ) -> Result> { + pub fn parse_json(&self, json: impl AsRef, has_null: bool) -> RhaiResultOf { use crate::tokenizer::Token; fn parse_json_inner( engine: &Engine, json: &str, has_null: bool, - ) -> Result> { + ) -> RhaiResultOf { let mut scope = Scope::new(); let json_text = json.trim_start(); let scripts = if json_text.starts_with(Token::MapStart.literal_syntax()) { diff --git a/src/api/deprecated.rs b/src/api/deprecated.rs index 9dbc3b5f..6ef2573b 100644 --- a/src/api/deprecated.rs +++ b/src/api/deprecated.rs @@ -2,7 +2,7 @@ use crate::{ Dynamic, Engine, EvalAltResult, Expression, FnPtr, ImmutableString, NativeCallContext, - RhaiResult, Scope, AST, + RhaiResult, RhaiResultOf, Scope, AST, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -22,7 +22,7 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[inline(always)] - pub fn consume_file(&self, path: std::path::PathBuf) -> Result<(), Box> { + pub fn consume_file(&self, path: std::path::PathBuf) -> RhaiResultOf<()> { self.run_file(path) } @@ -44,7 +44,7 @@ impl Engine { &self, scope: &mut Scope, path: std::path::PathBuf, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { self.run_file_with_scope(scope, path) } @@ -58,7 +58,7 @@ impl Engine { /// This method will be removed in the next major version. #[deprecated(since = "1.1.0", note = "use `run` instead")] #[inline(always)] - pub fn consume(&self, script: &str) -> Result<(), Box> { + pub fn consume(&self, script: &str) -> RhaiResultOf<()> { self.run(script) } @@ -72,11 +72,7 @@ impl Engine { /// This method will be removed in the next major version. #[deprecated(since = "1.1.0", note = "use `run_with_scope` instead")] #[inline(always)] - pub fn consume_with_scope( - &self, - scope: &mut Scope, - script: &str, - ) -> Result<(), Box> { + pub fn consume_with_scope(&self, scope: &mut Scope, script: &str) -> RhaiResultOf<()> { self.run_with_scope(scope, script) } @@ -90,7 +86,7 @@ impl Engine { /// This method will be removed in the next major version. #[deprecated(since = "1.1.0", note = "use `run_ast` instead")] #[inline(always)] - pub fn consume_ast(&self, ast: &AST) -> Result<(), Box> { + pub fn consume_ast(&self, ast: &AST) -> RhaiResultOf<()> { self.run_ast(ast) } @@ -104,11 +100,7 @@ impl Engine { /// This method will be removed in the next major version. #[deprecated(since = "1.1.0", note = "use `run_ast_with_scope` instead")] #[inline(always)] - pub fn consume_ast_with_scope( - &self, - scope: &mut Scope, - ast: &AST, - ) -> Result<(), Box> { + pub fn consume_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> { self.run_ast_with_scope(scope, ast) } /// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments @@ -258,7 +250,7 @@ impl NativeCallContext<'_> { #[allow(useless_deprecated)] #[deprecated(since = "1.2.0", note = "explicitly wrap `EvalAltResult` in `Err`")] -impl From for Result> { +impl From for RhaiResultOf { #[inline(always)] fn from(err: EvalAltResult) -> Self { Err(err.into()) diff --git a/src/api/eval.rs b/src/api/eval.rs index 6356c1e8..674e4da9 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -3,7 +3,9 @@ use crate::engine::{EvalState, Imports}; use crate::parser::ParseState; use crate::types::dynamic::Variant; -use crate::{Dynamic, Engine, EvalAltResult, Module, Position, RhaiResult, Scope, AST}; +use crate::{ + Dynamic, Engine, EvalAltResult, Module, Position, RhaiResult, RhaiResultOf, Scope, AST, +}; use std::any::type_name; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -24,7 +26,7 @@ impl Engine { /// # } /// ``` #[inline(always)] - pub fn eval(&self, script: &str) -> Result> { + pub fn eval(&self, script: &str) -> RhaiResultOf { self.eval_with_scope(&mut Scope::new(), script) } /// Evaluate a string with own scope. @@ -60,7 +62,7 @@ impl Engine { &self, scope: &mut Scope, script: &str, - ) -> Result> { + ) -> RhaiResultOf { let ast = self.compile_with_scope_and_optimization_level( scope, &[script], @@ -84,10 +86,7 @@ impl Engine { /// # } /// ``` #[inline(always)] - pub fn eval_expression( - &self, - script: &str, - ) -> Result> { + pub fn eval_expression(&self, script: &str) -> RhaiResultOf { self.eval_expression_with_scope(&mut Scope::new(), script) } /// Evaluate a string containing an expression with own scope. @@ -113,7 +112,7 @@ impl Engine { &self, scope: &mut Scope, script: &str, - ) -> Result> { + ) -> RhaiResultOf { let scripts = [script]; let (stream, tokenizer_control) = self.lex_raw(&scripts, self.token_mapper.as_ref().map(Box::as_ref)); @@ -149,7 +148,7 @@ impl Engine { /// # } /// ``` #[inline(always)] - pub fn eval_ast(&self, ast: &AST) -> Result> { + pub fn eval_ast(&self, ast: &AST) -> RhaiResultOf { self.eval_ast_with_scope(&mut Scope::new(), ast) } /// Evaluate an [`AST`] with own scope. @@ -186,7 +185,7 @@ impl Engine { &self, scope: &mut Scope, ast: &AST, - ) -> Result> { + ) -> RhaiResultOf { let mods = &mut Imports::new(); let result = self.eval_ast_with_scope_raw(scope, mods, ast, 0)?; diff --git a/src/api/events.rs b/src/api/events.rs index 9f981836..e43d4e4a 100644 --- a/src/api/events.rs +++ b/src/api/events.rs @@ -2,7 +2,7 @@ use crate::engine::EvalContext; use crate::func::SendSync; -use crate::{Dynamic, Engine, EvalAltResult, Position}; +use crate::{Dynamic, Engine, Position, RhaiResultOf}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -58,7 +58,7 @@ impl Engine { #[inline(always)] pub fn on_var( &mut self, - callback: impl Fn(&str, usize, &EvalContext) -> Result, Box> + callback: impl Fn(&str, usize, &EvalContext) -> RhaiResultOf> + SendSync + 'static, ) -> &mut Self { diff --git a/src/api/files.rs b/src/api/files.rs index 785bffbc..1adea263 100644 --- a/src/api/files.rs +++ b/src/api/files.rs @@ -3,13 +3,13 @@ #![cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] use crate::types::dynamic::Variant; -use crate::{Engine, EvalAltResult, Scope, AST}; +use crate::{Engine, EvalAltResult, RhaiResultOf, Scope, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; impl Engine { /// Read the contents of a file into a string. - fn read_file(path: std::path::PathBuf) -> Result> { + fn read_file(path: std::path::PathBuf) -> RhaiResultOf { use std::io::Read; let mut f = std::fs::File::open(path.clone()).map_err(|err| { @@ -62,7 +62,7 @@ impl Engine { /// # } /// ``` #[inline(always)] - pub fn compile_file(&self, path: std::path::PathBuf) -> Result> { + pub fn compile_file(&self, path: std::path::PathBuf) -> RhaiResultOf { self.compile_file_with_scope(&Scope::new(), path) } /// Compile a script file into an [`AST`] using own scope, which can be used later for evaluation. @@ -103,7 +103,7 @@ impl Engine { &self, scope: &Scope, path: std::path::PathBuf, - ) -> Result> { + ) -> RhaiResultOf { Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?)) } /// Evaluate a script file. @@ -124,10 +124,7 @@ impl Engine { /// # } /// ``` #[inline] - pub fn eval_file( - &self, - path: std::path::PathBuf, - ) -> Result> { + pub fn eval_file(&self, path: std::path::PathBuf) -> RhaiResultOf { Self::read_file(path).and_then(|contents| self.eval::(&contents)) } /// Evaluate a script file with own scope. @@ -162,7 +159,7 @@ impl Engine { &self, scope: &mut Scope, path: std::path::PathBuf, - ) -> Result> { + ) -> RhaiResultOf { Self::read_file(path).and_then(|contents| self.eval_with_scope(scope, &contents)) } /// Evaluate a file, returning any error (if any). @@ -171,7 +168,7 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[inline] - pub fn run_file(&self, path: std::path::PathBuf) -> Result<(), Box> { + pub fn run_file(&self, path: std::path::PathBuf) -> RhaiResultOf<()> { Self::read_file(path).and_then(|contents| self.run(&contents)) } /// Evaluate a file with own scope, returning any error (if any). @@ -190,7 +187,7 @@ impl Engine { &self, scope: &mut Scope, path: std::path::PathBuf, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { Self::read_file(path).and_then(|contents| self.run_with_scope(scope, &contents)) } } diff --git a/src/api/register.rs b/src/api/register.rs index 32c929d7..66a85220 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -3,7 +3,7 @@ use crate::func::{FnCallArgs, RegisterNativeFunction, SendSync}; use crate::types::dynamic::Variant; use crate::{ - Engine, EvalAltResult, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, Shared, + Engine, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, RhaiResultOf, Shared, SmartString, }; use std::any::{type_name, TypeId}; @@ -113,7 +113,7 @@ impl Engine { pub fn register_result_fn(&mut self, name: N, func: F) -> &mut Self where N: AsRef + Into, - F: RegisterNativeFunction>>, + F: RegisterNativeFunction>, { let param_types = F::param_types(); @@ -166,9 +166,7 @@ impl Engine { &mut self, name: N, arg_types: &[TypeId], - func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result> - + SendSync - + 'static, + func: impl Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResultOf + SendSync + 'static, ) -> &mut Self where N: AsRef + Into, @@ -383,7 +381,7 @@ impl Engine { pub fn register_get_result( &mut self, name: impl AsRef, - get_fn: impl Fn(&mut T) -> Result> + SendSync + 'static, + get_fn: impl Fn(&mut T) -> RhaiResultOf + SendSync + 'static, ) -> &mut Self { self.register_result_fn(&crate::engine::make_getter(name), get_fn) } @@ -449,7 +447,7 @@ impl Engine { /// /// impl TestStruct { /// fn new() -> Self { Self { field: 1 } } - /// fn set_field(&mut self, new_val: i64) -> Result<(), Box> { + /// fn set_field(&mut self, new_val: i64) -> Result<(), Box> { /// self.field = new_val; /// Ok(()) /// } @@ -478,7 +476,7 @@ impl Engine { pub fn register_set_result( &mut self, name: impl AsRef, - set_fn: impl Fn(&mut T, V) -> Result<(), Box> + SendSync + 'static, + set_fn: impl Fn(&mut T, V) -> RhaiResultOf<()> + SendSync + 'static, ) -> &mut Self { self.register_result_fn(&crate::engine::make_setter(name), set_fn) } @@ -657,7 +655,7 @@ impl Engine { V: Variant + Clone, >( &mut self, - get_fn: impl Fn(&mut T, X) -> Result> + SendSync + 'static, + get_fn: impl Fn(&mut T, X) -> RhaiResultOf + SendSync + 'static, ) -> &mut Self { #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { @@ -772,7 +770,7 @@ impl Engine { /// /// impl TestStruct { /// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } - /// fn set_field(&mut self, index: i64, value: i64) -> Result<(), Box> { + /// fn set_field(&mut self, index: i64, value: i64) -> Result<(), Box> { /// self.fields[index as usize] = value; /// Ok(()) /// } @@ -806,7 +804,7 @@ impl Engine { V: Variant + Clone, >( &mut self, - set_fn: impl Fn(&mut T, X, V) -> Result<(), Box> + SendSync + 'static, + set_fn: impl Fn(&mut T, X, V) -> RhaiResultOf<()> + SendSync + 'static, ) -> &mut Self { #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { diff --git a/src/api/run.rs b/src/api/run.rs index 84e2222d..08805846 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -2,14 +2,14 @@ use crate::engine::{EvalState, Imports}; use crate::parser::ParseState; -use crate::{Engine, EvalAltResult, Module, Scope, AST}; +use crate::{Engine, Module, RhaiResultOf, Scope, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; impl Engine { /// Evaluate a script, returning any error (if any). #[inline(always)] - pub fn run(&self, script: &str) -> Result<(), Box> { + pub fn run(&self, script: &str) -> RhaiResultOf<()> { self.run_with_scope(&mut Scope::new(), script) } /// Evaluate a script with own scope, returning any error (if any). @@ -20,11 +20,7 @@ impl Engine { /// the scope are propagated throughout the script _including_ functions. This allows functions /// to be optimized based on dynamic global constants. #[inline] - pub fn run_with_scope( - &self, - scope: &mut Scope, - script: &str, - ) -> Result<(), Box> { + pub fn run_with_scope(&self, scope: &mut Scope, script: &str) -> RhaiResultOf<()> { let scripts = [script]; let (stream, tokenizer_control) = self.lex_raw(&scripts, self.token_mapper.as_ref().map(Box::as_ref)); @@ -42,16 +38,12 @@ impl Engine { } /// Evaluate an [`AST`], returning any error (if any). #[inline(always)] - pub fn run_ast(&self, ast: &AST) -> Result<(), Box> { + pub fn run_ast(&self, ast: &AST) -> RhaiResultOf<()> { self.run_ast_with_scope(&mut Scope::new(), ast) } /// Evaluate an [`AST`] with own scope, returning any error (if any). #[inline] - pub fn run_ast_with_scope( - &self, - scope: &mut Scope, - ast: &AST, - ) -> Result<(), Box> { + pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> { let mods = &mut Imports::new(); let mut state = EvalState::new(); if ast.source_raw().is_some() { diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 66aba2f7..e50e5086 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -3,7 +3,7 @@ use super::{ASTNode, Ident, Stmt, StmtBlock}; use crate::engine::{OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE}; use crate::func::hashing::ALT_ZERO_HASH; -use crate::module::NamespaceRef; +use crate::module::Namespace; use crate::tokenizer::Token; use crate::types::dynamic::Union; use crate::{Dynamic, Identifier, ImmutableString, Position, StaticVec, INT}; @@ -62,7 +62,7 @@ impl CustomExpr { /// /// Two separate hashes are pre-calculated because of the following patterns: /// -/// ```ignore +/// ```js /// func(a, b, c); // Native: func(a, b, c) - 3 parameters /// // Script: func(a, b, c) - 3 parameters /// @@ -158,7 +158,7 @@ impl FnCallHashes { #[derive(Debug, Clone, Default, Hash)] pub struct FnCallExpr { /// Namespace of the function, if any. - pub namespace: Option, + pub namespace: Option, /// Function name. pub name: Identifier, /// Pre-calculated hashes. @@ -357,11 +357,7 @@ pub enum Expr { Variable( Option, Position, - Box<( - Option, - Option<(NamespaceRef, u64)>, - Identifier, - )>, + Box<(Option, Option<(Namespace, u64)>, Identifier)>, ), /// Property access - ((getter, hash), (setter, hash), prop) Property( diff --git a/src/engine.rs b/src/engine.rs index e88e76e3..aa25bc20 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -7,14 +7,14 @@ use crate::func::{ native::{OnDebugCallback, OnParseTokenCallback, OnPrintCallback, OnVarCallback}, CallableFunction, IteratorFn, }; -use crate::module::NamespaceRef; +use crate::module::Namespace; use crate::packages::{Package, StandardPackage}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::tokenizer::Token; use crate::types::dynamic::{map_std_type_name, AccessMode, Union, Variant}; use crate::{ calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, Identifier, ImmutableString, - Module, Position, RhaiResult, Scope, Shared, StaticVec, INT, + Module, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, INT, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -587,7 +587,7 @@ impl<'a> Target<'a> { /// Propagate a changed value back to the original source. /// This has no effect except for string indexing. #[inline] - pub fn propagate_changed_value(&mut self) -> Result<(), Box> { + pub fn propagate_changed_value(&mut self) -> RhaiResultOf<()> { match self { Self::RefMut(_) | Self::TempValue(_) => (), #[cfg(not(feature = "no_closure"))] @@ -1136,7 +1136,7 @@ impl Engine { &self, mods: &Imports, state: &mut EvalState, - namespace: &NamespaceRef, + namespace: &Namespace, ) -> Option> { let root = &namespace[0].name; @@ -1167,7 +1167,7 @@ impl Engine { lib: &[&Module], this_ptr: &'s mut Option<&mut Dynamic>, expr: &Expr, - ) -> Result<(Target<'s>, Position), Box> { + ) -> RhaiResultOf<(Target<'s>, Position)> { match expr { Expr::Variable(Some(_), _, _) => { self.search_scope_only(scope, mods, state, lib, this_ptr, expr) @@ -1258,7 +1258,7 @@ impl Engine { lib: &[&Module], this_ptr: &'s mut Option<&mut Dynamic>, expr: &Expr, - ) -> Result<(Target<'s>, Position), Box> { + ) -> RhaiResultOf<(Target<'s>, Position)> { // Make sure that the pointer indirection is taken only when absolutely necessary. let (index, var_pos) = match expr { @@ -1334,7 +1334,7 @@ impl Engine { chain_type: ChainType, level: usize, new_val: Option<((Dynamic, Position), (Option, Position))>, - ) -> Result<(Dynamic, bool), Box> { + ) -> RhaiResultOf<(Dynamic, bool)> { let is_ref_mut = target.is_ref(); let _terminate_chaining = terminate_chaining; @@ -1854,7 +1854,7 @@ impl Engine { idx_values: &mut StaticVec, size: usize, level: usize, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut mods.num_operations, expr.position())?; @@ -1869,7 +1869,7 @@ impl Engine { let (values, pos) = args.iter().try_fold( (StaticVec::with_capacity(args.len()), Position::NONE), - |(mut values, mut pos), expr| -> Result<_, Box> { + |(mut values, mut pos), expr| -> RhaiResultOf<_> { let (value, arg_pos) = self.get_arg_value( scope, mods, state, lib, this_ptr, level, expr, constants, )?; @@ -1916,7 +1916,7 @@ impl Engine { args.iter() .try_fold( (StaticVec::with_capacity(args.len()), Position::NONE), - |(mut values, mut pos), expr| -> Result<_, Box> { + |(mut values, mut pos), expr| -> RhaiResultOf<_> { let (value, arg_pos) = self.get_arg_value( scope, mods, state, lib, this_ptr, level, expr, constants, )?; @@ -1984,7 +1984,7 @@ impl Engine { add_if_not_found: bool, use_indexers: bool, level: usize, - ) -> Result, Box> { + ) -> RhaiResultOf> { #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut mods.num_operations, Position::NONE)?; @@ -2339,7 +2339,7 @@ impl Engine { .iter() .try_fold( crate::Array::with_capacity(x.len()), - |mut arr, item| -> Result<_, Box> { + |mut arr, item| -> RhaiResultOf<_> { arr.push( self.eval_expr(scope, mods, state, lib, this_ptr, item, level)? .flatten(), @@ -2355,7 +2355,7 @@ impl Engine { .iter() .try_fold( x.1.clone(), - |mut map, (Ident { name: key, .. }, expr)| -> Result<_, Box> { + |mut map, (Ident { name: key, .. }, expr)| -> RhaiResultOf<_> { let value_ref = map.get_mut(key.as_str()).expect("contains all keys"); *value_ref = self .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? @@ -2539,7 +2539,7 @@ impl Engine { target: &mut Target, root: (&str, Position), new_val: Dynamic, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { if target.is_read_only() { // Assignment to constant variable return Err( @@ -3243,7 +3243,7 @@ impl Engine { index, if rename.is_empty() { name } else { rename }.clone(), ); - Ok(()) as Result<_, Box> + Ok(()) as RhaiResultOf<_> } else { Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), *pos).into()) } @@ -3305,12 +3305,12 @@ impl Engine { #[cfg(feature = "unchecked")] #[inline(always)] - fn check_data_size(&self, _value: &Dynamic) -> Result<(), Box> { + fn check_data_size(&self, _value: &Dynamic) -> RhaiResultOf<()> { Ok(()) } #[cfg(not(feature = "unchecked"))] - fn check_data_size(&self, value: &Dynamic) -> Result<(), Box> { + fn check_data_size(&self, value: &Dynamic) -> RhaiResultOf<()> { // Recursively calculate the size of a value (especially `Array` and `Map`) fn calc_size(value: &Dynamic) -> (usize, usize, usize) { match value.0 { @@ -3424,7 +3424,7 @@ impl Engine { &self, num_operations: &mut u64, pos: Position, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { *num_operations += 1; // Guard against too many operations @@ -3459,7 +3459,7 @@ impl Engine { /// Make a `Box<`[`EvalAltResult`][EvalAltResult::ErrorMismatchDataType]`>`. #[inline] #[must_use] - pub(crate) fn make_type_mismatch_err(&self, typ: &str, pos: Position) -> Box { + pub(crate) fn make_type_mismatch_err(&self, typ: &str, pos: Position) -> RhaiError { EvalAltResult::ErrorMismatchDataType( self.map_type_name(type_name::()).into(), typ.into(), diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 2da123d4..3375f243 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -1,10 +1,9 @@ //! Built-in implementations for common operators. use super::call::FnCallArgs; +use super::native::FnBuiltin; use crate::engine::OP_CONTAINS; -use crate::{ - Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult, INT, -}; +use crate::{Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, INT}; use std::any::TypeId; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -51,11 +50,7 @@ fn is_numeric(type_id: TypeId) -> bool { /// /// The return function will be registered as a _method_, so the first parameter cannot be consumed. #[must_use] -pub fn get_builtin_binary_op_fn( - op: &str, - x: &Dynamic, - y: &Dynamic, -) -> Option RhaiResult> { +pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option { let type1 = x.type_id(); let type2 = y.type_id(); @@ -516,11 +511,7 @@ pub fn get_builtin_binary_op_fn( /// /// The return function is registered as a _method_, so the first parameter cannot be consumed. #[must_use] -pub fn get_builtin_op_assignment_fn( - op: &str, - x: &Dynamic, - y: &Dynamic, -) -> Option RhaiResult> { +pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option { let type1 = x.type_id(); let type2 = y.type_id(); diff --git a/src/func/call.rs b/src/func/call.rs index 7e6b1c38..958a6ec1 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -4,17 +4,16 @@ use super::callable_function::CallableFunction; use super::native::FnAny; use super::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; use crate::api::default_limits::MAX_DYNAMIC_PARAMETERS; -use crate::ast::FnCallHashes; +use crate::ast::{Expr, FnCallHashes, Stmt}; use crate::engine::{ EvalState, FnResolutionCacheEntry, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; -use crate::module::NamespaceRef; +use crate::module::Namespace; use crate::tokenizer::Token; use crate::{ - ast::{Expr, Stmt}, calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, EvalAltResult, FnPtr, - Identifier, ImmutableString, Module, Position, RhaiResult, Scope, StaticVec, + Identifier, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -108,7 +107,7 @@ pub fn ensure_no_data_race( fn_name: impl AsRef, args: &FnCallArgs, is_method_call: bool, -) -> Result<(), Box> { +) -> RhaiResultOf<()> { if let Some((n, _)) = args .iter() .enumerate() @@ -131,7 +130,7 @@ impl Engine { #[must_use] fn gen_call_signature( &self, - namespace: Option<&NamespaceRef>, + namespace: Option<&Namespace>, fn_name: impl AsRef, args: &[&mut Dynamic], ) -> String { @@ -318,7 +317,7 @@ impl Engine { is_ref_mut: bool, is_op_assign: bool, pos: Position, - ) -> Result<(Dynamic, bool), Box> { + ) -> RhaiResultOf<(Dynamic, bool)> { #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut mods.num_operations, pos)?; @@ -499,8 +498,8 @@ impl Engine { pos: Position, scope: Option<&mut Scope>, level: usize, - ) -> Result<(Dynamic, bool), Box> { - fn no_method_err(name: &str, pos: Position) -> Result<(Dynamic, bool), Box> { + ) -> RhaiResultOf<(Dynamic, bool)> { + fn no_method_err(name: &str, pos: Position) -> RhaiResultOf<(Dynamic, bool)> { let msg = format!("'{0}' should not be called this way. Try {0}(...);", name); Err(EvalAltResult::ErrorRuntime(msg.into(), pos).into()) } @@ -733,7 +732,7 @@ impl Engine { (call_args, call_arg_pos): &mut (StaticVec, Position), pos: Position, level: usize, - ) -> Result<(Dynamic, bool), Box> { + ) -> RhaiResultOf<(Dynamic, bool)> { let fn_name = fn_name.as_ref(); let is_ref_mut = target.is_ref(); @@ -893,7 +892,7 @@ impl Engine { level: usize, arg_expr: &Expr, constants: &[Dynamic], - ) -> Result<(Dynamic, Position), Box> { + ) -> RhaiResultOf<(Dynamic, Position)> { match arg_expr { Expr::Stack(slot, pos) => Ok((constants[*slot].clone(), *pos)), ref arg => self @@ -992,7 +991,7 @@ impl Engine { // Append the new curried arguments to the existing list. let fn_curry = a_expr.iter().skip(1).try_fold( fn_curry, - |mut curried, expr| -> Result<_, Box> { + |mut curried, expr| -> RhaiResultOf<_> { let (value, _) = self.get_arg_value( scope, mods, state, lib, this_ptr, level, expr, constants, )?; @@ -1181,7 +1180,7 @@ impl Engine { state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, - namespace: &NamespaceRef, + namespace: &Namespace, fn_name: impl AsRef, args_expr: &[Expr], constants: &[Dynamic], diff --git a/src/func/func.rs b/src/func/func.rs index f442b6fb..65636fbd 100644 --- a/src/func/func.rs +++ b/src/func/func.rs @@ -3,8 +3,9 @@ #![cfg(not(feature = "no_function"))] #![allow(non_snake_case)] +use crate::parser::ParseResult; use crate::types::dynamic::Variant; -use crate::{Engine, EvalAltResult, ParseError, Scope, SmartString, AST}; +use crate::{Engine, RhaiResultOf, Scope, SmartString, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -77,11 +78,7 @@ pub trait Func { /// # Ok(()) /// # } /// ``` - fn create_from_script( - self, - script: &str, - entry_point: &str, - ) -> Result; + fn create_from_script(self, script: &str, entry_point: &str) -> ParseResult; } macro_rules! def_anonymous_fn { @@ -92,9 +89,9 @@ macro_rules! def_anonymous_fn { impl<$($par: Variant + Clone,)* RET: Variant + Clone> Func<($($par,)*), RET> for Engine { #[cfg(feature = "sync")] - type Output = Box Result> + Send + Sync>; + type Output = Box RhaiResultOf + Send + Sync>; #[cfg(not(feature = "sync"))] - type Output = Box Result>>; + type Output = Box RhaiResultOf>; #[inline] fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output { @@ -103,7 +100,7 @@ macro_rules! def_anonymous_fn { } #[inline] - fn create_from_script(self, script: &str, entry_point: &str) -> Result { + fn create_from_script(self, script: &str, entry_point: &str) -> ParseResult { let ast = self.compile(script)?; Ok(Func::<($($par,)*), RET>::create_from_ast(self, ast, entry_point)) } diff --git a/src/func/native.rs b/src/func/native.rs index 56a8edfc..bbf989bb 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -8,7 +8,7 @@ use crate::tokenizer::{Token, TokenizeState}; use crate::types::dynamic::Variant; use crate::{ calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, FuncArgs, Module, Position, - RhaiResult, StaticVec, + RhaiResult, RhaiResultOf, StaticVec, }; use std::any::type_name; #[cfg(feature = "no_std")] @@ -233,7 +233,7 @@ impl<'a> NativeCallContext<'a> { &self, fn_name: impl AsRef, args: impl FuncArgs, - ) -> Result> { + ) -> RhaiResultOf { let mut arg_values = StaticVec::new_const(); args.parse(&mut arg_values); @@ -277,7 +277,7 @@ impl<'a> NativeCallContext<'a> { is_ref_mut: bool, is_method_call: bool, args: &mut [&mut Dynamic], - ) -> Result> { + ) -> RhaiResult { let fn_name = fn_name.as_ref(); let hash = if is_method_call { @@ -363,6 +363,9 @@ pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult; #[cfg(feature = "sync")] pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + Sync; +/// A trail object for built-in functions. +pub type FnBuiltin = fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult; + /// A standard function that gets an iterator from a type. pub type IteratorFn = fn(Dynamic) -> Box>; @@ -405,12 +408,9 @@ pub type OnParseTokenCallback = /// A standard callback function for variable access. #[cfg(not(feature = "sync"))] pub type OnVarCallback = - Box Result, Box> + 'static>; + Box RhaiResultOf> + 'static>; /// A standard callback function for variable access. #[cfg(feature = "sync")] pub type OnVarCallback = Box< - dyn Fn(&str, usize, &EvalContext) -> Result, Box> - + Send - + Sync - + 'static, + dyn Fn(&str, usize, &EvalContext) -> RhaiResultOf> + Send + Sync + 'static, >; diff --git a/src/func/plugin.rs b/src/func/plugin.rs index 3faf408b..dc20e6f3 100644 --- a/src/func/plugin.rs +++ b/src/func/plugin.rs @@ -10,7 +10,7 @@ pub use crate::{ use std::prelude::v1::*; pub use std::{any::TypeId, mem}; -pub type RhaiResult = Result>; +pub type RhaiResult = crate::RhaiResult; #[cfg(not(features = "no_module"))] pub use rhai_codegen::*; diff --git a/src/func/register.rs b/src/func/register.rs index 7230ab02..2a8a212c 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -8,7 +8,7 @@ use super::native::{FnAny, SendSync}; use crate::r#unsafe::unsafe_try_cast; use crate::tokenizer::Position; use crate::types::dynamic::{DynamicWriteLock, Variant}; -use crate::{Dynamic, EvalAltResult, NativeCallContext}; +use crate::{Dynamic, EvalAltResult, NativeCallContext, RhaiResultOf}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{any::TypeId, mem}; @@ -169,14 +169,14 @@ macro_rules! def_register { } impl< - FN: Fn($($param),*) -> Result> + SendSync + 'static, + FN: Fn($($param),*) -> RhaiResultOf + SendSync + 'static, $($par: Variant + Clone,)* RET: Variant + Clone - > RegisterNativeFunction<($($mark,)*), Result>> for FN { + > RegisterNativeFunction<($($mark,)*), RhaiResultOf> for FN { #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$par>()),*].into_boxed_slice() } - #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::>>() } - #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::>>() } + #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::>() } + #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::>() } #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) { @@ -194,14 +194,14 @@ macro_rules! def_register { } impl< - FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> Result> + SendSync + 'static, + FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> RhaiResultOf + SendSync + 'static, $($par: Variant + Clone,)* RET: Variant + Clone - > RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), Result>> for FN { + > RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), RhaiResultOf> for FN { #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$par>()),*].into_boxed_slice() } - #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::>>() } - #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::>>() } + #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::>() } + #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::>() } #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) { diff --git a/src/func/script.rs b/src/func/script.rs index 1bb56e3f..56326e64 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -5,7 +5,9 @@ use super::call::FnCallArgs; use crate::ast::ScriptFnDef; use crate::engine::{EvalState, Imports}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; -use crate::{Dynamic, Engine, EvalAltResult, Module, Position, RhaiResult, Scope, StaticVec}; +use crate::{ + Dynamic, Engine, EvalAltResult, Module, Position, RhaiError, RhaiResult, Scope, StaticVec, +}; use std::mem; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -39,7 +41,7 @@ impl Engine { name: String, fn_def: &ScriptFnDef, mods: &Imports, - err: Box, + err: RhaiError, pos: Position, ) -> RhaiResult { Err(EvalAltResult::ErrorInFunctionCall( diff --git a/src/lib.rs b/src/lib.rs index 04232dec..ee86a923 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,11 +42,10 @@ //! //! # #[cfg(not(feature = "no_std"))] //! # #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] -//! assert_eq!( -//! // Evaluate the script, expects a 'bool' return -//! engine.eval_file::("my_script.rhai".into())?, -//! true -//! ); +//! // Evaluate the script, expects a 'bool' return +//! let result = engine.eval_file::("my_script.rhai".into())?, +//! +//! assert_eq!(result, true); //! //! Ok(()) //! } @@ -83,7 +82,12 @@ mod tokenizer; mod types; mod r#unsafe; -type RhaiResult = Result>; +/// General evaluation error for Rhai scripts. +type RhaiError = Box; +/// Generic [`Result`] type for Rhai functions. +type RhaiResultOf = Result; +/// General [`Result`] type for Rhai functions returning [`Dynamic`] values. +type RhaiResult = RhaiResultOf; /// The system integer type. It is defined as [`i64`]. /// @@ -234,7 +238,7 @@ pub use ast::FloatWrapper; pub use engine::{EvalState, FnResolutionCache, FnResolutionCacheEntry, Imports}; #[cfg(feature = "internals")] -pub use module::NamespaceRef; +pub use module::Namespace; /// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a /// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored. diff --git a/src/module/mod.rs b/src/module/mod.rs index 1c252317..e7e9b745 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -9,8 +9,8 @@ use crate::parser::IdentifierBuilder; use crate::tokenizer::Token; use crate::types::dynamic::Variant; use crate::{ - calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult, - Identifier, ImmutableString, NativeCallContext, Shared, StaticVec, + calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, Identifier, + ImmutableString, NativeCallContext, RhaiResultOf, Shared, StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -456,9 +456,9 @@ impl Module { /// Name and Position in [`EvalAltResult`] are [`None`] and [`NONE`][Position::NONE] and must be set afterwards. #[cfg(not(feature = "no_module"))] #[inline] - pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box> { + pub(crate) fn get_qualified_var(&self, hash_var: u64) -> RhaiResultOf<&Dynamic> { self.all_variables.get(&hash_var).ok_or_else(|| { - EvalAltResult::ErrorVariableNotFound(String::new(), crate::Position::NONE).into() + crate::EvalAltResult::ErrorVariableNotFound(String::new(), crate::Position::NONE).into() }) } @@ -918,7 +918,7 @@ impl Module { /// *x *= 2; // the first argument can be mutated /// } /// - /// Ok(orig) // return Result> + /// Ok(orig) // return RhaiResult /// }); /// /// assert!(module.contains_fn(hash)); @@ -935,9 +935,7 @@ impl Module { where N: AsRef + Into, T: Variant + Clone, - F: Fn(NativeCallContext, &mut FnCallArgs) -> Result> - + SendSync - + 'static, + F: Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResultOf + SendSync + 'static, { let f = move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from); @@ -979,7 +977,7 @@ impl Module { where N: AsRef + Into, T: Variant + Clone, - F: RegisterNativeFunction>>, + F: RegisterNativeFunction>, { self.set_fn( name, @@ -1015,8 +1013,8 @@ impl Module { where A: Variant + Clone, T: Variant + Clone, - F: RegisterNativeFunction>>, - F: Fn(&mut A) -> Result> + SendSync + 'static, + F: RegisterNativeFunction>, + F: Fn(&mut A) -> RhaiResultOf + SendSync + 'static, { self.set_fn( &crate::engine::make_getter(name), @@ -1057,8 +1055,8 @@ impl Module { where A: Variant + Clone, B: Variant + Clone, - F: RegisterNativeFunction>>, - F: Fn(&mut A, B) -> Result<(), Box> + SendSync + 'static, + F: RegisterNativeFunction>, + F: Fn(&mut A, B) -> RhaiResultOf<()> + SendSync + 'static, { self.set_fn( &crate::engine::make_setter(name), @@ -1104,8 +1102,8 @@ impl Module { A: Variant + Clone, B: Variant + Clone, T: Variant + Clone, - F: RegisterNativeFunction>>, - F: Fn(&mut A, B) -> Result> + SendSync + 'static, + F: RegisterNativeFunction>, + F: Fn(&mut A, B) -> RhaiResultOf + SendSync + 'static, { #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { @@ -1166,8 +1164,8 @@ impl Module { A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, - F: RegisterNativeFunction>>, - F: Fn(&mut A, B, C) -> Result<(), Box> + SendSync + 'static, + F: RegisterNativeFunction>, + F: Fn(&mut A, B, C) -> RhaiResultOf<()> + SendSync + 'static, { #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { @@ -1231,8 +1229,8 @@ impl Module { #[inline(always)] pub fn set_indexer_get_set_fn( &mut self, - get_fn: impl Fn(&mut A, B) -> Result> + SendSync + 'static, - set_fn: impl Fn(&mut A, B, T) -> Result<(), Box> + SendSync + 'static, + get_fn: impl Fn(&mut A, B) -> RhaiResultOf + SendSync + 'static, + set_fn: impl Fn(&mut A, B, T) -> RhaiResultOf<()> + SendSync + 'static, ) -> (u64, u64) where A: Variant + Clone, @@ -1549,7 +1547,7 @@ impl Module { scope: crate::Scope, ast: &crate::AST, engine: &crate::Engine, - ) -> Result> { + ) -> RhaiResultOf { let mut scope = scope; let mut mods = crate::engine::Imports::new(); let orig_mods_len = mods.len(); @@ -1800,12 +1798,12 @@ impl Module { /// A [`StaticVec`] is used because most namespace-qualified access contains only one level, /// and it is wasteful to always allocate a [`Vec`] with one element. #[derive(Clone, Eq, PartialEq, Default, Hash)] -pub struct NamespaceRef { +pub struct Namespace { index: Option, path: StaticVec, } -impl fmt::Debug for NamespaceRef { +impl fmt::Debug for Namespace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(index) = self.index { write!(f, "{} -> ", index)?; @@ -1822,7 +1820,7 @@ impl fmt::Debug for NamespaceRef { } } -impl fmt::Display for NamespaceRef { +impl fmt::Display for Namespace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str( &self @@ -1835,7 +1833,7 @@ impl fmt::Display for NamespaceRef { } } -impl Deref for NamespaceRef { +impl Deref for Namespace { type Target = StaticVec; #[inline(always)] @@ -1844,14 +1842,14 @@ impl Deref for NamespaceRef { } } -impl DerefMut for NamespaceRef { +impl DerefMut for Namespace { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.path } } -impl From> for NamespaceRef { +impl From> for Namespace { #[inline(always)] fn from(mut path: Vec) -> Self { path.shrink_to_fit(); @@ -1862,7 +1860,7 @@ impl From> for NamespaceRef { } } -impl From> for NamespaceRef { +impl From> for Namespace { #[inline(always)] fn from(mut path: StaticVec) -> Self { path.shrink_to_fit(); @@ -1870,8 +1868,8 @@ impl From> for NamespaceRef { } } -impl NamespaceRef { - /// Create a new [`NamespaceRef`]. +impl Namespace { + /// Create a new [`Namespace`]. #[inline(always)] #[must_use] pub const fn new() -> Self { diff --git a/src/module/resolvers/collection.rs b/src/module/resolvers/collection.rs index a3581a99..14b4da57 100644 --- a/src/module/resolvers/collection.rs +++ b/src/module/resolvers/collection.rs @@ -1,4 +1,4 @@ -use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared}; +use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, RhaiResultOf, Shared}; use std::ops::AddAssign; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -123,7 +123,7 @@ impl ModuleResolver for ModuleResolversCollection { source_path: Option<&str>, path: &str, pos: Position, - ) -> Result, Box> { + ) -> RhaiResultOf> { for resolver in self.0.iter() { match resolver.resolve(engine, source_path, path, pos) { Ok(module) => return Ok(module), diff --git a/src/module/resolvers/dummy.rs b/src/module/resolvers/dummy.rs index 904f48ce..c183e1a6 100644 --- a/src/module/resolvers/dummy.rs +++ b/src/module/resolvers/dummy.rs @@ -1,4 +1,4 @@ -use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared}; +use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, RhaiResultOf, Shared}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -44,7 +44,7 @@ impl ModuleResolver for DummyModuleResolver { _: Option<&str>, path: &str, pos: Position, - ) -> Result, Box> { + ) -> RhaiResultOf> { Err(EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()) } } diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index d9d567e7..27269157 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -2,7 +2,10 @@ #![cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] use crate::func::native::shared_write_lock; -use crate::{Engine, EvalAltResult, Identifier, Module, ModuleResolver, Position, Scope, Shared}; +use crate::{ + Engine, EvalAltResult, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Scope, + Shared, +}; use std::{ collections::BTreeMap, @@ -259,7 +262,7 @@ impl ModuleResolver for FileModuleResolver { source_path: Option<&str>, path: &str, pos: Position, - ) -> Result, Box> { + ) -> RhaiResultOf> { // Load relative paths from source if there is no base path specified let source_path = source_path.and_then(|p| Path::new(p).parent().map(|p| p.to_string_lossy())); @@ -315,7 +318,7 @@ impl ModuleResolver for FileModuleResolver { source_path: Option<&str>, path: &str, pos: Position, - ) -> Option>> { + ) -> Option> { // Construct the script file path let file_path = self.get_file_path(path, source_path); diff --git a/src/module/resolvers/mod.rs b/src/module/resolvers/mod.rs index 47911c34..33f3d6e8 100644 --- a/src/module/resolvers/mod.rs +++ b/src/module/resolvers/mod.rs @@ -1,5 +1,5 @@ use crate::func::native::SendSync; -use crate::{Engine, EvalAltResult, Module, Position, Shared, AST}; +use crate::{Engine, Module, Position, RhaiResultOf, Shared, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -24,7 +24,7 @@ pub trait ModuleResolver: SendSync { source_path: Option<&str>, path: &str, pos: Position, - ) -> Result, Box>; + ) -> RhaiResultOf>; /// Resolve an `AST` based on a path string. /// @@ -43,7 +43,7 @@ pub trait ModuleResolver: SendSync { source_path: Option<&str>, path: &str, pos: Position, - ) -> Option>> { + ) -> Option> { None } } diff --git a/src/module/resolvers/stat.rs b/src/module/resolvers/stat.rs index 90b33ed7..278a7c00 100644 --- a/src/module/resolvers/stat.rs +++ b/src/module/resolvers/stat.rs @@ -1,5 +1,6 @@ use crate::{ - Engine, EvalAltResult, Identifier, Module, ModuleResolver, Position, Shared, SmartString, + Engine, EvalAltResult, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Shared, + SmartString, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -131,7 +132,7 @@ impl ModuleResolver for StaticModuleResolver { _: Option<&str>, path: &str, pos: Position, - ) -> Result, Box> { + ) -> RhaiResultOf> { self.0 .get(path) .cloned() diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index 452d0c22..58338391 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use crate::plugin::*; -use crate::{def_package, EvalAltResult, Position, INT}; +use crate::{def_package, EvalAltResult, Position, RhaiError, RhaiResultOf, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -10,7 +10,7 @@ use std::prelude::v1::*; use num_traits::Float; #[inline(never)] -pub fn make_err(msg: impl Into) -> Box { +pub fn make_err(msg: impl Into) -> RhaiError { EvalAltResult::ErrorArithmetic(msg.into(), Position::NONE).into() } @@ -22,7 +22,7 @@ macro_rules! gen_arithmetic_functions { #[export_module] pub mod functions { #[rhai_fn(name = "+", return_raw)] - pub fn add(x: $arg_type, y: $arg_type) -> Result<$arg_type, Box> { + pub fn add(x: $arg_type, y: $arg_type) -> RhaiResultOf<$arg_type> { if cfg!(not(feature = "unchecked")) { x.checked_add(y).ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y))) } else { @@ -30,7 +30,7 @@ macro_rules! gen_arithmetic_functions { } } #[rhai_fn(name = "-", return_raw)] - pub fn subtract(x: $arg_type, y: $arg_type) -> Result<$arg_type, Box> { + pub fn subtract(x: $arg_type, y: $arg_type) -> RhaiResultOf<$arg_type> { if cfg!(not(feature = "unchecked")) { x.checked_sub(y).ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y))) } else { @@ -38,7 +38,7 @@ macro_rules! gen_arithmetic_functions { } } #[rhai_fn(name = "*", return_raw)] - pub fn multiply(x: $arg_type, y: $arg_type) -> Result<$arg_type, Box> { + pub fn multiply(x: $arg_type, y: $arg_type) -> RhaiResultOf<$arg_type> { if cfg!(not(feature = "unchecked")) { x.checked_mul(y).ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y))) } else { @@ -46,7 +46,7 @@ macro_rules! gen_arithmetic_functions { } } #[rhai_fn(name = "/", return_raw)] - pub fn divide(x: $arg_type, y: $arg_type) -> Result<$arg_type, Box> { + pub fn divide(x: $arg_type, y: $arg_type) -> RhaiResultOf<$arg_type> { if cfg!(not(feature = "unchecked")) { // Detect division by zero if y == 0 { @@ -59,7 +59,7 @@ macro_rules! gen_arithmetic_functions { } } #[rhai_fn(name = "%", return_raw)] - pub fn modulo(x: $arg_type, y: $arg_type) -> Result<$arg_type, Box> { + pub fn modulo(x: $arg_type, y: $arg_type) -> RhaiResultOf<$arg_type> { if cfg!(not(feature = "unchecked")) { x.checked_rem(y).ok_or_else(|| make_err(format!("Modulo division by zero or overflow: {} % {}", x, y))) } else { @@ -67,7 +67,7 @@ macro_rules! gen_arithmetic_functions { } } #[rhai_fn(name = "**", return_raw)] - pub fn power(x: $arg_type, y: INT) -> Result<$arg_type, Box> { + pub fn power(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> { if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { Err(make_err(format!("Integer raised to too large an index: {} ~ {}", x, y))) @@ -82,7 +82,7 @@ macro_rules! gen_arithmetic_functions { } #[rhai_fn(name = "<<", return_raw)] - pub fn shift_left(x: $arg_type, y: INT) -> Result<$arg_type, Box> { + pub fn shift_left(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> { if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { Err(make_err(format!("Left-shift by too many bits: {} << {}", x, y))) @@ -96,7 +96,7 @@ macro_rules! gen_arithmetic_functions { } } #[rhai_fn(name = ">>", return_raw)] - pub fn shift_right(x: $arg_type, y: INT) -> Result<$arg_type, Box> { + pub fn shift_right(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> { if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { Err(make_err(format!("Right-shift by too many bits: {} >> {}", x, y))) @@ -146,7 +146,7 @@ macro_rules! gen_signed_functions { #[export_module] pub mod functions { #[rhai_fn(name = "-", return_raw)] - pub fn neg(x: $arg_type) -> Result<$arg_type, Box> { + pub fn neg(x: $arg_type) -> RhaiResultOf<$arg_type> { if cfg!(not(feature = "unchecked")) { x.checked_neg().ok_or_else(|| make_err(format!("Negation overflow: -{}", x))) } else { @@ -158,7 +158,7 @@ macro_rules! gen_signed_functions { x } #[rhai_fn(return_raw)] - pub fn abs(x: $arg_type) -> Result<$arg_type, Box> { + pub fn abs(x: $arg_type) -> RhaiResultOf<$arg_type> { if cfg!(not(feature = "unchecked")) { x.checked_abs().ok_or_else(|| make_err(format!("Negation overflow: -{}", x))) } else { @@ -254,8 +254,6 @@ gen_signed_functions!(signed_num_128 => i128); #[cfg(not(feature = "no_float"))] #[export_module] mod f32_functions { - use crate::EvalAltResult; - #[cfg(not(feature = "f32_float"))] pub mod basic_arithmetic { #[rhai_fn(name = "+")] @@ -337,7 +335,7 @@ mod f32_functions { x.abs() } #[rhai_fn(return_raw)] - pub fn sign(x: f32) -> Result> { + pub fn sign(x: f32) -> RhaiResultOf { match x.signum() { _ if x == 0.0 => Ok(0), x if x.is_nan() => Err(make_err("Sign of NaN is undefined")), @@ -349,7 +347,7 @@ mod f32_functions { x == 0.0 } #[rhai_fn(name = "**", return_raw)] - pub fn pow_f_i(x: f32, y: INT) -> Result> { + pub fn pow_f_i(x: f32, y: INT) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) { Err(make_err(format!( "Number raised to too large an index: {} ~ {}", @@ -445,7 +443,7 @@ mod f64_functions { x.abs() } #[rhai_fn(return_raw)] - pub fn sign(x: f64) -> Result> { + pub fn sign(x: f64) -> RhaiResultOf { match x.signum() { _ if x == 0.0 => Ok(0), x if x.is_nan() => Err(make_err("Sign of NaN is undefined")), @@ -465,7 +463,7 @@ pub mod decimal_functions { use rust_decimal::{prelude::Zero, Decimal, MathematicalOps}; #[rhai_fn(skip, return_raw)] - pub fn add(x: Decimal, y: Decimal) -> Result> { + pub fn add(x: Decimal, y: Decimal) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { x.checked_add(y) .ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y))) @@ -474,7 +472,7 @@ pub mod decimal_functions { } } #[rhai_fn(skip, return_raw)] - pub fn subtract(x: Decimal, y: Decimal) -> Result> { + pub fn subtract(x: Decimal, y: Decimal) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { x.checked_sub(y) .ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y))) @@ -483,7 +481,7 @@ pub mod decimal_functions { } } #[rhai_fn(skip, return_raw)] - pub fn multiply(x: Decimal, y: Decimal) -> Result> { + pub fn multiply(x: Decimal, y: Decimal) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { x.checked_mul(y) .ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y))) @@ -492,7 +490,7 @@ pub mod decimal_functions { } } #[rhai_fn(skip, return_raw)] - pub fn divide(x: Decimal, y: Decimal) -> Result> { + pub fn divide(x: Decimal, y: Decimal) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { // Detect division by zero if y == Decimal::zero() { @@ -506,7 +504,7 @@ pub mod decimal_functions { } } #[rhai_fn(skip, return_raw)] - pub fn modulo(x: Decimal, y: Decimal) -> Result> { + pub fn modulo(x: Decimal, y: Decimal) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { x.checked_rem(y).ok_or_else(|| { make_err(format!( @@ -519,7 +517,7 @@ pub mod decimal_functions { } } #[rhai_fn(skip, return_raw)] - pub fn power(x: Decimal, y: Decimal) -> Result> { + pub fn power(x: Decimal, y: Decimal) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { x.checked_powd(y) .ok_or_else(|| make_err(format!("Exponential overflow: {} + {}", x, y))) diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index b26a4ace..3e2903a1 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -5,7 +5,7 @@ use crate::engine::OP_EQUALS; use crate::plugin::*; use crate::{ def_package, Array, Dynamic, EvalAltResult, ExclusiveRange, FnPtr, InclusiveRange, - NativeCallContext, Position, INT, + NativeCallContext, Position, RhaiResultOf, INT, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -80,7 +80,7 @@ pub mod array_functions { array: &mut Array, len: INT, item: Dynamic, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { if len <= 0 { return Ok(()); } @@ -263,11 +263,7 @@ pub mod array_functions { } } #[rhai_fn(return_raw, pure)] - pub fn map( - ctx: NativeCallContext, - array: &mut Array, - mapper: FnPtr, - ) -> Result> { + pub fn map(ctx: NativeCallContext, array: &mut Array, mapper: FnPtr) -> RhaiResultOf { if array.is_empty() { return Ok(array.clone()); } @@ -304,7 +300,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, mapper: &str, - ) -> Result> { + ) -> RhaiResultOf { map(ctx, array, FnPtr::new(mapper)?) } @@ -313,7 +309,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, filter: FnPtr, - ) -> Result> { + ) -> RhaiResultOf { if array.is_empty() { return Ok(array.clone()); } @@ -353,7 +349,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, filter_func: &str, - ) -> Result> { + ) -> RhaiResultOf { filter(ctx, array, FnPtr::new(filter_func)?) } #[rhai_fn(return_raw, pure)] @@ -361,7 +357,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, value: Dynamic, - ) -> Result> { + ) -> RhaiResultOf { if array.is_empty() { return Ok(false); } @@ -396,7 +392,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, value: Dynamic, - ) -> Result> { + ) -> RhaiResultOf { if array.is_empty() { Ok(-1) } else { @@ -409,7 +405,7 @@ pub mod array_functions { array: &mut Array, value: Dynamic, start: INT, - ) -> Result> { + ) -> RhaiResultOf { if array.is_empty() { return Ok(-1); } @@ -455,7 +451,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, filter: &str, - ) -> Result> { + ) -> RhaiResultOf { index_of_filter(ctx, array, FnPtr::new(filter)?) } #[rhai_fn(name = "index_of", return_raw, pure)] @@ -463,7 +459,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, filter: FnPtr, - ) -> Result> { + ) -> RhaiResultOf { if array.is_empty() { Ok(-1) } else { @@ -476,7 +472,7 @@ pub mod array_functions { array: &mut Array, filter: FnPtr, start: INT, - ) -> Result> { + ) -> RhaiResultOf { if array.is_empty() { return Ok(-1); } @@ -526,15 +522,11 @@ pub mod array_functions { array: &mut Array, filter: &str, start: INT, - ) -> Result> { + ) -> RhaiResultOf { index_of_filter_starting_from(ctx, array, FnPtr::new(filter)?, start) } #[rhai_fn(return_raw, pure)] - pub fn some( - ctx: NativeCallContext, - array: &mut Array, - filter: FnPtr, - ) -> Result> { + pub fn some(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf { if array.is_empty() { return Ok(false); } @@ -572,15 +564,11 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, filter: &str, - ) -> Result> { + ) -> RhaiResultOf { some(ctx, array, FnPtr::new(filter)?) } #[rhai_fn(return_raw, pure)] - pub fn all( - ctx: NativeCallContext, - array: &mut Array, - filter: FnPtr, - ) -> Result> { + pub fn all(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf { if array.is_empty() { return Ok(true); } @@ -618,11 +606,11 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, filter: &str, - ) -> Result> { + ) -> RhaiResultOf { all(ctx, array, FnPtr::new(filter)?) } #[rhai_fn(return_raw)] - pub fn dedup(ctx: NativeCallContext, array: &mut Array) -> Result<(), Box> { + pub fn dedup(ctx: NativeCallContext, array: &mut Array) -> RhaiResultOf<()> { dedup_with_fn_name(ctx, array, OP_EQUALS) } #[rhai_fn(name = "dedup", return_raw)] @@ -630,7 +618,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, comparer: FnPtr, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { if array.is_empty() { return Ok(()); } @@ -650,15 +638,11 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, comparer: &str, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { dedup_by_comparer(ctx, array, FnPtr::new(comparer)?) } #[rhai_fn(return_raw, pure)] - pub fn reduce( - ctx: NativeCallContext, - array: &mut Array, - reducer: FnPtr, - ) -> Result> { + pub fn reduce(ctx: NativeCallContext, array: &mut Array, reducer: FnPtr) -> RhaiResult { reduce_with_initial(ctx, array, reducer, Dynamic::UNIT) } #[rhai_fn(name = "reduce", return_raw, pure)] @@ -666,7 +650,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, reducer: &str, - ) -> Result> { + ) -> RhaiResult { reduce(ctx, array, FnPtr::new(reducer)?) } #[rhai_fn(name = "reduce", return_raw, pure)] @@ -675,7 +659,7 @@ pub mod array_functions { array: &mut Array, reducer: FnPtr, initial: Dynamic, - ) -> Result> { + ) -> RhaiResult { if array.is_empty() { return Ok(initial); } @@ -713,15 +697,11 @@ pub mod array_functions { array: &mut Array, reducer: &str, initial: Dynamic, - ) -> Result> { + ) -> RhaiResult { reduce_with_initial(ctx, array, FnPtr::new(reducer)?, initial) } #[rhai_fn(return_raw, pure)] - pub fn reduce_rev( - ctx: NativeCallContext, - array: &mut Array, - reducer: FnPtr, - ) -> Result> { + pub fn reduce_rev(ctx: NativeCallContext, array: &mut Array, reducer: FnPtr) -> RhaiResult { reduce_rev_with_initial(ctx, array, reducer, Dynamic::UNIT) } #[rhai_fn(name = "reduce_rev", return_raw, pure)] @@ -729,7 +709,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, reducer: &str, - ) -> Result> { + ) -> RhaiResult { reduce_rev(ctx, array, FnPtr::new(reducer)?) } #[rhai_fn(name = "reduce_rev", return_raw, pure)] @@ -738,7 +718,7 @@ pub mod array_functions { array: &mut Array, reducer: FnPtr, initial: Dynamic, - ) -> Result> { + ) -> RhaiResult { if array.is_empty() { return Ok(initial); } @@ -777,7 +757,7 @@ pub mod array_functions { array: &mut Array, reducer: &str, initial: Dynamic, - ) -> Result> { + ) -> RhaiResult { reduce_rev_with_initial(ctx, array, FnPtr::new(reducer)?, initial) } #[rhai_fn(name = "sort", return_raw)] @@ -785,15 +765,11 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, comparer: &str, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { sort(ctx, array, FnPtr::new(comparer)?) } #[rhai_fn(return_raw)] - pub fn sort( - ctx: NativeCallContext, - array: &mut Array, - comparer: FnPtr, - ) -> Result<(), Box> { + pub fn sort(ctx: NativeCallContext, array: &mut Array, comparer: FnPtr) -> RhaiResultOf<()> { if array.len() <= 1 { return Ok(()); } @@ -815,7 +791,7 @@ pub mod array_functions { Ok(()) } #[rhai_fn(name = "sort", return_raw)] - pub fn sort_with_builtin(array: &mut Array) -> Result<(), Box> { + pub fn sort_with_builtin(array: &mut Array) -> RhaiResultOf<()> { if array.len() <= 1 { return Ok(()); } @@ -891,7 +867,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, filter: FnPtr, - ) -> Result> { + ) -> RhaiResultOf { if array.is_empty() { return Ok(Array::new()); } @@ -938,7 +914,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, filter: &str, - ) -> Result> { + ) -> RhaiResultOf { drain(ctx, array, FnPtr::new(filter)?) } #[rhai_fn(name = "drain")] @@ -985,7 +961,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, filter: FnPtr, - ) -> Result> { + ) -> RhaiResultOf { if array.is_empty() { return Ok(Array::new()); } @@ -1032,7 +1008,7 @@ pub mod array_functions { ctx: NativeCallContext, array: &mut Array, filter: &str, - ) -> Result> { + ) -> RhaiResultOf { retain(ctx, array, FnPtr::new(filter)?) } #[rhai_fn(name = "retain")] @@ -1082,7 +1058,7 @@ pub mod array_functions { ctx: NativeCallContext, array1: &mut Array, array2: Array, - ) -> Result> { + ) -> RhaiResultOf { if array1.len() != array2.len() { return Ok(false); } @@ -1122,7 +1098,7 @@ pub mod array_functions { ctx: NativeCallContext, array1: &mut Array, array2: Array, - ) -> Result> { + ) -> RhaiResultOf { equals(ctx, array1, array2).map(|r| !r) } } diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index 048b1226..342a97fc 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -4,7 +4,7 @@ use crate::plugin::*; use crate::{ def_package, Blob, Dynamic, EvalAltResult, ExclusiveRange, InclusiveRange, NativeCallContext, - Position, INT, + Position, RhaiResultOf, INT, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -31,10 +31,7 @@ pub mod blob_functions { Blob::new() } #[rhai_fn(name = "blob", return_raw)] - pub fn blob_with_capacity( - ctx: NativeCallContext, - len: INT, - ) -> Result> { + pub fn blob_with_capacity(ctx: NativeCallContext, len: INT) -> RhaiResultOf { blob_with_capacity_and_value(ctx, len, 0) } #[rhai_fn(name = "blob", return_raw)] @@ -42,7 +39,7 @@ pub mod blob_functions { ctx: NativeCallContext, len: INT, value: INT, - ) -> Result> { + ) -> RhaiResultOf { let len = if len < 0 { 0 } else { len as usize }; let _ctx = ctx; @@ -118,7 +115,7 @@ pub mod blob_functions { blob: &mut Blob, len: INT, item: INT, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { if len <= 0 { return Ok(()); } diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index bdbcfaf9..d6be661f 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -1,6 +1,6 @@ use crate::plugin::*; use crate::types::dynamic::Variant; -use crate::{def_package, EvalAltResult, ExclusiveRange, InclusiveRange, INT}; +use crate::{def_package, EvalAltResult, ExclusiveRange, InclusiveRange, RhaiResultOf, INT}; use std::iter::{ExactSizeIterator, FusedIterator}; use std::ops::{Range, RangeInclusive}; #[cfg(feature = "no_std")] @@ -22,7 +22,7 @@ impl StepRange where T: Variant + Copy + PartialOrd + Add + Sub, { - pub fn new(from: T, to: T, step: T) -> Result> { + pub fn new(from: T, to: T, step: T) -> RhaiResultOf { #[cfg(not(feature = "unchecked"))] if let Some(r) = from.checked_add(&step) { if r == from { @@ -117,7 +117,7 @@ struct BitRange(INT, INT, usize); const BITS: usize = std::mem::size_of::() * 8; impl BitRange { - pub fn new(value: INT, from: INT, len: INT) -> Result> { + pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf { let from = if from >= 0 { let offset = from as usize; @@ -334,7 +334,7 @@ def_package! { struct StepFloatRange(FLOAT, FLOAT, FLOAT); impl StepFloatRange { - pub fn new(from: FLOAT, to: FLOAT, step: FLOAT) -> Result> { + pub fn new(from: FLOAT, to: FLOAT, step: FLOAT) -> RhaiResultOf { #[cfg(not(feature = "unchecked"))] if step == 0.0 { return Err(EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), @@ -396,7 +396,7 @@ def_package! { struct StepDecimalRange(Decimal, Decimal, Decimal); impl StepDecimalRange { - pub fn new(from: Decimal, to: Decimal, step: Decimal) -> Result> { + pub fn new(from: Decimal, to: Decimal, step: Decimal) -> RhaiResultOf { #[cfg(not(feature = "unchecked"))] if step.is_zero() { return Err(EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index 75ac1d8e..ff9c7d87 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -1,7 +1,7 @@ use crate::def_package; use crate::plugin::*; use crate::types::dynamic::Tag; -use crate::{Dynamic, EvalAltResult, INT}; +use crate::{Dynamic, EvalAltResult, RhaiResultOf, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -21,7 +21,7 @@ mod core_functions { value.tag() as INT } #[rhai_fn(name = "set_tag", set = "tag", return_raw)] - pub fn set_tag(value: &mut Dynamic, tag: INT) -> Result<(), Box> { + pub fn set_tag(value: &mut Dynamic, tag: INT) -> RhaiResultOf<()> { if tag < Tag::MIN as INT { Err(EvalAltResult::ErrorArithmetic( format!( diff --git a/src/packages/logic.rs b/src/packages/logic.rs index a800425c..2aad954e 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use crate::plugin::*; -use crate::{def_package, EvalAltResult, ExclusiveRange, InclusiveRange, INT}; +use crate::{def_package, EvalAltResult, ExclusiveRange, InclusiveRange, RhaiResultOf, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -202,7 +202,7 @@ mod bit_field_functions { const BITS: usize = std::mem::size_of::() * 8; #[rhai_fn(return_raw)] - pub fn get_bit(value: INT, index: INT) -> Result> { + pub fn get_bit(value: INT, index: INT) -> RhaiResultOf { if index >= 0 { let offset = index as usize; @@ -225,7 +225,7 @@ mod bit_field_functions { } } #[rhai_fn(return_raw)] - pub fn set_bit(value: &mut INT, index: INT, new_value: bool) -> Result<(), Box> { + pub fn set_bit(value: &mut INT, index: INT, new_value: bool) -> RhaiResultOf<()> { if index >= 0 { let offset = index as usize; @@ -260,22 +260,19 @@ mod bit_field_functions { } } #[rhai_fn(name = "get_bits", return_raw)] - pub fn get_bits_range(value: INT, range: ExclusiveRange) -> Result> { + pub fn get_bits_range(value: INT, range: ExclusiveRange) -> RhaiResultOf { let from = INT::max(range.start, 0); let to = INT::max(range.end, from); get_bits(value, from, to - from) } #[rhai_fn(name = "get_bits", return_raw)] - pub fn get_bits_range_inclusive( - value: INT, - range: InclusiveRange, - ) -> Result> { + pub fn get_bits_range_inclusive(value: INT, range: InclusiveRange) -> RhaiResultOf { let from = INT::max(*range.start(), 0); let to = INT::max(*range.end(), from - 1); get_bits(value, from, to - from + 1) } #[rhai_fn(return_raw)] - pub fn get_bits(value: INT, index: INT, bits: INT) -> Result> { + pub fn get_bits(value: INT, index: INT, bits: INT) -> RhaiResultOf { if bits < 1 { return Ok(0); } @@ -321,7 +318,7 @@ mod bit_field_functions { value: &mut INT, range: ExclusiveRange, new_value: INT, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { let from = INT::max(range.start, 0); let to = INT::max(range.end, from); set_bits(value, from, to - from, new_value) @@ -331,18 +328,13 @@ mod bit_field_functions { value: &mut INT, range: InclusiveRange, new_value: INT, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { let from = INT::max(*range.start(), 0); let to = INT::max(*range.end(), from - 1); set_bits(value, from, to - from + 1, new_value) } #[rhai_fn(return_raw)] - pub fn set_bits( - value: &mut INT, - index: INT, - bits: INT, - new_value: INT, - ) -> Result<(), Box> { + pub fn set_bits(value: &mut INT, index: INT, bits: INT, new_value: INT) -> RhaiResultOf<()> { if bits < 1 { return Ok(()); } diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 7f6c8e2f..a1f2fa5d 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -2,7 +2,7 @@ use crate::engine::OP_EQUALS; use crate::plugin::*; -use crate::{def_package, Dynamic, ImmutableString, Map, INT}; +use crate::{def_package, Dynamic, ImmutableString, Map, RhaiResultOf, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -66,11 +66,7 @@ mod map_functions { } } #[rhai_fn(name = "==", return_raw, pure)] - pub fn equals( - ctx: NativeCallContext, - map1: &mut Map, - map2: Map, - ) -> Result> { + pub fn equals(ctx: NativeCallContext, map1: &mut Map, map2: Map) -> RhaiResultOf { if map1.len() != map2.len() { return Ok(false); } @@ -96,11 +92,7 @@ mod map_functions { Ok(true) } #[rhai_fn(name = "!=", return_raw, pure)] - pub fn not_equals( - ctx: NativeCallContext, - map1: &mut Map, - map2: Map, - ) -> Result> { + pub fn not_equals(ctx: NativeCallContext, map1: &mut Map, map2: Map) -> RhaiResultOf { equals(ctx, map1, map2).map(|r| !r) } diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index c596cc62..5f81f19d 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use crate::plugin::*; -use crate::{def_package, Position, INT, INT_BASE}; +use crate::{def_package, Position, RhaiResultOf, INT, INT_BASE}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -114,7 +114,7 @@ def_package! { #[export_module] mod int_functions { #[rhai_fn(name = "parse_int", return_raw)] - pub fn parse_int_radix(string: &str, radix: INT) -> Result> { + pub fn parse_int_radix(string: &str, radix: INT) -> RhaiResultOf { if !(2..=36).contains(&radix) { return Err(EvalAltResult::ErrorArithmetic( format!("Invalid radix: '{}'", radix), @@ -134,7 +134,7 @@ mod int_functions { }) } #[rhai_fn(name = "parse_int", return_raw)] - pub fn parse_int(string: &str) -> Result> { + pub fn parse_int(string: &str) -> RhaiResultOf { parse_int_radix(string, 10) } } @@ -263,7 +263,7 @@ mod float_functions { x.is_infinite() } #[rhai_fn(name = "to_int", return_raw)] - pub fn f32_to_int(x: f32) -> Result> { + pub fn f32_to_int(x: f32) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) { Err(EvalAltResult::ErrorArithmetic( format!("Integer overflow: to_int({})", x), @@ -275,7 +275,7 @@ mod float_functions { } } #[rhai_fn(name = "to_int", return_raw)] - pub fn f64_to_int(x: f64) -> Result> { + pub fn f64_to_int(x: f64) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) { Err(EvalAltResult::ErrorArithmetic( format!("Integer overflow: to_int({})", x), @@ -287,7 +287,7 @@ mod float_functions { } } #[rhai_fn(return_raw)] - pub fn parse_float(string: &str) -> Result> { + pub fn parse_float(string: &str) -> RhaiResultOf { string.trim().parse::().map_err(|err| { EvalAltResult::ErrorArithmetic( format!("Error parsing floating-point number '{}': {}", string, err), @@ -325,7 +325,7 @@ mod decimal_functions { } #[cfg(feature = "no_float")] #[rhai_fn(return_raw)] - pub fn parse_float(s: &str) -> Result> { + pub fn parse_float(s: &str) -> RhaiResultOf { parse_decimal(s) } @@ -339,12 +339,12 @@ mod decimal_functions { x.tan() } #[rhai_fn(return_raw)] - pub fn sqrt(x: Decimal) -> Result> { + pub fn sqrt(x: Decimal) -> RhaiResultOf { x.sqrt() .ok_or_else(|| make_err(format!("Error taking the square root of {}", x,))) } #[rhai_fn(return_raw)] - pub fn exp(x: Decimal) -> Result> { + pub fn exp(x: Decimal) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { x.checked_exp() .ok_or_else(|| make_err(format!("Exponential overflow: e ** {}", x,))) @@ -353,7 +353,7 @@ mod decimal_functions { } } #[rhai_fn(return_raw)] - pub fn ln(x: Decimal) -> Result> { + pub fn ln(x: Decimal) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { x.checked_ln() .ok_or_else(|| make_err(format!("Error taking the natural log of {}", x))) @@ -362,7 +362,7 @@ mod decimal_functions { } } #[rhai_fn(name = "log", return_raw)] - pub fn log10(x: Decimal) -> Result> { + pub fn log10(x: Decimal) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { x.checked_log10() .ok_or_else(|| make_err(format!("Error taking the log of {}", x))) @@ -383,7 +383,7 @@ mod decimal_functions { x.round() } #[rhai_fn(name = "round", return_raw)] - pub fn round_dp(x: Decimal, dp: INT) -> Result> { + pub fn round_dp(x: Decimal, dp: INT) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { if dp < 0 { return Err(make_err(format!( @@ -399,7 +399,7 @@ mod decimal_functions { Ok(x.round_dp(dp as u32)) } #[rhai_fn(return_raw)] - pub fn round_up(x: Decimal, dp: INT) -> Result> { + pub fn round_up(x: Decimal, dp: INT) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { if dp < 0 { return Err(make_err(format!( @@ -415,7 +415,7 @@ mod decimal_functions { Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::AwayFromZero)) } #[rhai_fn(return_raw)] - pub fn round_down(x: Decimal, dp: INT) -> Result> { + pub fn round_down(x: Decimal, dp: INT) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { if dp < 0 { return Err(make_err(format!( @@ -431,7 +431,7 @@ mod decimal_functions { Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::ToZero)) } #[rhai_fn(return_raw)] - pub fn round_half_up(x: Decimal, dp: INT) -> Result> { + pub fn round_half_up(x: Decimal, dp: INT) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { if dp < 0 { return Err(make_err(format!( @@ -447,7 +447,7 @@ mod decimal_functions { Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointAwayFromZero)) } #[rhai_fn(return_raw)] - pub fn round_half_down(x: Decimal, dp: INT) -> Result> { + pub fn round_half_down(x: Decimal, dp: INT) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) { if dp < 0 { return Err(make_err(format!( @@ -471,7 +471,7 @@ mod decimal_functions { x.fract() } #[rhai_fn(return_raw)] - pub fn parse_decimal(string: &str) -> Result> { + pub fn parse_decimal(string: &str) -> RhaiResultOf { Decimal::from_str(string) .or_else(|_| Decimal::from_scientific(string)) .map_err(|err| { @@ -485,7 +485,7 @@ mod decimal_functions { #[cfg(not(feature = "no_float"))] #[rhai_fn(name = "to_decimal", return_raw)] - pub fn f32_to_decimal(x: f32) -> Result> { + pub fn f32_to_decimal(x: f32) -> RhaiResultOf { Decimal::try_from(x).map_err(|_| { EvalAltResult::ErrorArithmetic( format!("Cannot convert to Decimal: to_decimal({})", x), @@ -496,7 +496,7 @@ mod decimal_functions { } #[cfg(not(feature = "no_float"))] #[rhai_fn(name = "to_decimal", return_raw)] - pub fn f64_to_decimal(x: f64) -> Result> { + pub fn f64_to_decimal(x: f64) -> RhaiResultOf { Decimal::try_from(x).map_err(|_| { EvalAltResult::ErrorArithmetic( format!("Cannot convert to Decimal: to_decimal({})", x), @@ -507,7 +507,7 @@ mod decimal_functions { } #[cfg(not(feature = "no_float"))] #[rhai_fn(return_raw)] - pub fn to_float(x: Decimal) -> Result> { + pub fn to_float(x: Decimal) -> RhaiResultOf { FLOAT::try_from(x).map_err(|_| { EvalAltResult::ErrorArithmetic( format!("Cannot convert to floating-point: to_float({})", x), diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index c29b4af0..ec1fc793 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use crate::plugin::*; -use crate::{def_package, Dynamic, ExclusiveRange, InclusiveRange, StaticVec, INT}; +use crate::{def_package, Dynamic, ExclusiveRange, InclusiveRange, RhaiResultOf, StaticVec, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{any::TypeId, mem}; @@ -500,7 +500,7 @@ mod string_functions { string: &mut ImmutableString, len: INT, character: char, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { if len <= 0 { return Ok(()); } @@ -544,7 +544,7 @@ mod string_functions { string: &mut ImmutableString, len: INT, padding: &str, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { if len <= 0 { return Ok(()); } diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index bf6a0d50..f4a6e7df 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -2,7 +2,7 @@ use super::{arithmetic::make_err as make_arithmetic_err, math_basic::MAX_INT}; use crate::plugin::*; -use crate::{def_package, Dynamic, EvalAltResult, INT}; +use crate::{def_package, Dynamic, EvalAltResult, RhaiResult, RhaiResultOf, INT}; #[cfg(not(feature = "no_float"))] use crate::FLOAT; @@ -30,7 +30,7 @@ mod time_functions { } #[rhai_fn(name = "elapsed", get = "elapsed", return_raw)] - pub fn elapsed(timestamp: Instant) -> Result> { + pub fn elapsed(timestamp: Instant) -> RhaiResult { #[cfg(not(feature = "no_float"))] if timestamp > Instant::now() { Err(make_arithmetic_err("Time-stamp is later than now")) @@ -56,10 +56,7 @@ mod time_functions { } #[rhai_fn(return_raw, name = "-")] - pub fn time_diff( - timestamp1: Instant, - timestamp2: Instant, - ) -> Result> { + pub fn time_diff(timestamp1: Instant, timestamp2: Instant) -> RhaiResult { #[cfg(not(feature = "no_float"))] return Ok(if timestamp2 > timestamp1 { -(timestamp2 - timestamp1).as_secs_f64() as FLOAT @@ -96,7 +93,7 @@ mod time_functions { #[cfg(not(feature = "no_float"))] pub mod float_functions { - fn add_impl(timestamp: Instant, seconds: FLOAT) -> Result> { + fn add_impl(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf { if seconds < 0.0 { subtract_impl(timestamp, -seconds) } else if cfg!(not(feature = "unchecked")) { @@ -119,10 +116,7 @@ mod time_functions { Ok(timestamp + Duration::from_millis((seconds * 1000.0) as u64)) } } - fn subtract_impl( - timestamp: Instant, - seconds: FLOAT, - ) -> Result> { + fn subtract_impl(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf { if seconds < 0.0 { add_impl(timestamp, -seconds) } else if cfg!(not(feature = "unchecked")) { @@ -147,32 +141,26 @@ mod time_functions { } #[rhai_fn(return_raw, name = "+")] - pub fn add(timestamp: Instant, seconds: FLOAT) -> Result> { + pub fn add(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf { add_impl(timestamp, seconds) } #[rhai_fn(return_raw, name = "+=")] - pub fn add_assign( - timestamp: &mut Instant, - seconds: FLOAT, - ) -> Result<(), Box> { + pub fn add_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> { *timestamp = add_impl(*timestamp, seconds)?; Ok(()) } #[rhai_fn(return_raw, name = "-")] - pub fn subtract(timestamp: Instant, seconds: FLOAT) -> Result> { + pub fn subtract(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf { subtract_impl(timestamp, seconds) } #[rhai_fn(return_raw, name = "-=")] - pub fn subtract_assign( - timestamp: &mut Instant, - seconds: FLOAT, - ) -> Result<(), Box> { + pub fn subtract_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> { *timestamp = subtract_impl(*timestamp, seconds)?; Ok(()) } } - fn add_impl(timestamp: Instant, seconds: INT) -> Result> { + fn add_impl(timestamp: Instant, seconds: INT) -> RhaiResultOf { if seconds < 0 { subtract_impl(timestamp, -seconds) } else if cfg!(not(feature = "unchecked")) { @@ -188,7 +176,7 @@ mod time_functions { Ok(timestamp + Duration::from_secs(seconds as u64)) } } - fn subtract_impl(timestamp: Instant, seconds: INT) -> Result> { + fn subtract_impl(timestamp: Instant, seconds: INT) -> RhaiResultOf { if seconds < 0 { add_impl(timestamp, -seconds) } else if cfg!(not(feature = "unchecked")) { @@ -206,23 +194,20 @@ mod time_functions { } #[rhai_fn(return_raw, name = "+")] - pub fn add(timestamp: Instant, seconds: INT) -> Result> { + pub fn add(timestamp: Instant, seconds: INT) -> RhaiResultOf { add_impl(timestamp, seconds) } #[rhai_fn(return_raw, name = "+=")] - pub fn add_assign(timestamp: &mut Instant, seconds: INT) -> Result<(), Box> { + pub fn add_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> { *timestamp = add_impl(*timestamp, seconds)?; Ok(()) } #[rhai_fn(return_raw, name = "-")] - pub fn subtract(timestamp: Instant, seconds: INT) -> Result> { + pub fn subtract(timestamp: Instant, seconds: INT) -> RhaiResultOf { subtract_impl(timestamp, seconds) } #[rhai_fn(return_raw, name = "-=")] - pub fn subtract_assign( - timestamp: &mut Instant, - seconds: INT, - ) -> Result<(), Box> { + pub fn subtract_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> { *timestamp = subtract_impl(*timestamp, seconds)?; Ok(()) } diff --git a/src/parser.rs b/src/parser.rs index e2dd2162..6190de72 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -8,7 +8,7 @@ use crate::ast::{ use crate::custom_syntax::{markers::*, CustomSyntax}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; use crate::func::hashing::get_hasher; -use crate::module::NamespaceRef; +use crate::module::Namespace; use crate::tokenizer::{ is_keyword_function, is_valid_function_name, is_valid_identifier, Token, TokenStream, TokenizerControl, @@ -28,9 +28,11 @@ use std::{ ops::AddAssign, }; +pub type ParseResult = Result; + type PERR = ParseErrorType; -type FunctionsLib = BTreeMap>; +type FnLib = BTreeMap>; /// Invalid variable name that acts as a search barrier in a [`Scope`]. const SCOPE_SEARCH_BARRIER_MARKER: &str = "$BARRIER$"; @@ -258,10 +260,7 @@ impl ParseSettings { /// Make sure that the current level of expression nesting is within the maximum limit. #[cfg(not(feature = "unchecked"))] #[inline] - pub fn ensure_level_within_max_limit( - &self, - limit: Option, - ) -> Result<(), ParseError> { + pub fn ensure_level_within_max_limit(&self, limit: Option) -> ParseResult<()> { if let Some(limit) = limit { if self.level > limit.get() { return Err(PERR::ExprTooDeep.into_err(self.pos)); @@ -296,7 +295,7 @@ impl Expr { } } /// Raise an error if the expression can never yield a boolean value. - fn ensure_bool_expr(self) -> Result { + fn ensure_bool_expr(self) -> ParseResult { let type_name = match self { Expr::Unit(_) => "()", Expr::DynamicConstant(ref v, _) if !v.is::() => v.type_name(), @@ -317,7 +316,7 @@ impl Expr { ) } /// Raise an error if the expression can never yield an iterable value. - fn ensure_iterable(self) -> Result { + fn ensure_iterable(self) -> ParseResult { let type_name = match self { Expr::Unit(_) => "()", Expr::BoolConstant(_, _) => "a boolean", @@ -340,10 +339,7 @@ impl Expr { /// Make sure that the next expression is not a statement expression (i.e. wrapped in `{}`). #[inline] -fn ensure_not_statement_expr( - input: &mut TokenStream, - type_name: impl ToString, -) -> Result<(), ParseError> { +fn ensure_not_statement_expr(input: &mut TokenStream, type_name: impl ToString) -> ParseResult<()> { match input.peek().expect(NEVER_ENDS) { (Token::LeftBrace, pos) => Err(PERR::ExprExpected(type_name.to_string()).into_err(*pos)), _ => Ok(()), @@ -352,7 +348,7 @@ fn ensure_not_statement_expr( /// Make sure that the next expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`). #[inline] -fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> { +fn ensure_not_assignment(input: &mut TokenStream) -> ParseResult<()> { match input.peek().expect(NEVER_ENDS) { (Token::Equals, pos) => Err(LexError::ImproperSymbol( "=".to_string(), @@ -395,7 +391,7 @@ fn match_token(input: &mut TokenStream, token: Token) -> (bool, Position) { } /// Parse a variable name. -fn parse_var_name(input: &mut TokenStream) -> Result<(Box, Position), ParseError> { +fn parse_var_name(input: &mut TokenStream) -> ParseResult<(Box, Position)> { match input.next().expect(NEVER_ENDS) { // Variable name (Token::Identifier(s), pos) => Ok((s, pos)), @@ -411,7 +407,7 @@ fn parse_var_name(input: &mut TokenStream) -> Result<(Box, Position), Parse } /// Parse a symbol. -fn parse_symbol(input: &mut TokenStream) -> Result<(Box, Position), ParseError> { +fn parse_symbol(input: &mut TokenStream) -> ParseResult<(Box, Position)> { match input.next().expect(NEVER_ENDS) { // Symbol (token, pos) if token.is_standard_symbol() => Ok((token.literal_syntax().into(), pos)), @@ -428,9 +424,9 @@ fn parse_symbol(input: &mut TokenStream) -> Result<(Box, Position), ParseEr fn parse_paren_expr( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -462,12 +458,12 @@ fn parse_paren_expr( fn parse_fn_call( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, id: Identifier, capture_parent_scope: bool, - namespace: Option, + namespace: Option, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -625,10 +621,10 @@ fn parse_fn_call( fn parse_index_chain( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, lhs: Expr, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -789,9 +785,9 @@ fn parse_index_chain( fn parse_array_literal( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -862,9 +858,9 @@ fn parse_array_literal( fn parse_map_literal( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -979,9 +975,9 @@ fn parse_map_literal( fn parse_switch( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -1164,9 +1160,9 @@ fn parse_switch( fn parse_primary( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -1258,7 +1254,7 @@ fn parse_primary( #[cfg(not(feature = "no_closure"))] new_state.external_vars.iter().try_for_each( - |(captured_var, &pos)| -> Result<_, ParseError> { + |(captured_var, &pos)| -> ParseResult<_> { let index = state.access_var(captured_var, pos); if !settings.is_closure && settings.strict_var && index.is_none() { @@ -1456,10 +1452,10 @@ fn parse_primary( fn parse_postfix( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, mut lhs: Expr, settings: ParseSettings, -) -> Result { +) -> ParseResult { let mut settings = settings; // Tail processing all possible postfix operators @@ -1522,7 +1518,7 @@ fn parse_postfix( if let Some((ref mut namespace, _)) = namespace { namespace.push(var_name_def); } else { - let mut ns = NamespaceRef::new(); + let mut ns = Namespace::new(); ns.push(var_name_def); namespace = Some((ns, 42)); } @@ -1607,9 +1603,9 @@ fn parse_postfix( fn parse_unary( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -1712,7 +1708,7 @@ fn make_assignment_stmt( lhs: Expr, rhs: Expr, op_pos: Position, -) -> Result { +) -> ParseResult { #[must_use] fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Option { match expr { @@ -1808,10 +1804,10 @@ fn make_assignment_stmt( fn parse_op_assignment_stmt( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, lhs: Expr, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -1842,7 +1838,7 @@ fn make_dot_expr( terminate_chaining: bool, rhs: Expr, op_pos: Position, -) -> Result { +) -> ParseResult { match (lhs, rhs) { // lhs[idx_expr].rhs (Expr::Index(mut x, term, pos), rhs) => { @@ -1956,11 +1952,11 @@ fn make_dot_expr( fn parse_binary_op( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, parent_precedence: Option, lhs: Expr, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -2119,12 +2115,12 @@ fn parse_binary_op( fn parse_custom_syntax( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, key: impl Into, syntax: &CustomSyntax, pos: Position, -) -> Result { +) -> ParseResult { let mut settings = settings; let mut inputs = StaticVec::::new(); let mut segments = StaticVec::new_const(); @@ -2294,9 +2290,9 @@ fn parse_custom_syntax( fn parse_expr( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -2313,9 +2309,9 @@ fn parse_expr( fn parse_if( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -2353,9 +2349,9 @@ fn parse_if( fn parse_while_loop( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -2384,9 +2380,9 @@ fn parse_while_loop( fn parse_do( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -2427,9 +2423,9 @@ fn parse_do( fn parse_for( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -2518,11 +2514,11 @@ fn parse_for( fn parse_let( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, var_type: AccessMode, is_export: bool, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -2573,9 +2569,9 @@ fn parse_let( fn parse_import( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -2608,9 +2604,9 @@ fn parse_import( fn parse_export( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -2681,9 +2677,9 @@ fn parse_export( fn parse_block( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -2783,9 +2779,9 @@ fn parse_block( fn parse_expr_stmt( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -2801,9 +2797,9 @@ fn parse_expr_stmt( fn parse_stmt( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { use AccessMode::{ReadOnly, ReadWrite}; let mut settings = settings; @@ -3015,9 +3011,9 @@ fn parse_stmt( fn parse_try_catch( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -3077,13 +3073,13 @@ fn parse_try_catch( fn parse_fn( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, access: crate::FnAccess, settings: ParseSettings, #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] comments: Vec>, -) -> Result { +) -> ParseResult { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -3222,9 +3218,9 @@ fn make_curry_from_externals( fn parse_anon_fn( input: &mut TokenStream, state: &mut ParseState, - lib: &mut FunctionsLib, + lib: &mut FnLib, settings: ParseSettings, -) -> Result<(Expr, ScriptFnDef), ParseError> { +) -> ParseResult<(Expr, ScriptFnDef)> { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -3332,7 +3328,7 @@ impl Engine { state: &mut ParseState, scope: &Scope, #[cfg(not(feature = "no_optimize"))] optimization_level: crate::OptimizationLevel, - ) -> Result { + ) -> ParseResult { let _scope = scope; let mut functions = BTreeMap::new(); @@ -3392,7 +3388,7 @@ impl Engine { &self, input: &mut TokenStream, state: &mut ParseState, - ) -> Result<(StaticVec, StaticVec>), ParseError> { + ) -> ParseResult<(StaticVec, StaticVec>)> { let mut statements = StaticVec::new_const(); let mut functions = BTreeMap::new(); @@ -3462,7 +3458,7 @@ impl Engine { state: &mut ParseState, scope: &Scope, #[cfg(not(feature = "no_optimize"))] optimization_level: crate::OptimizationLevel, - ) -> Result { + ) -> ParseResult { let _scope = scope; let (statements, _lib) = self.parse_global_level(input, state)?; diff --git a/src/serde/de.rs b/src/serde/de.rs index bd055ce0..04ac5681 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -1,8 +1,8 @@ //! Implement deserialization support of [`Dynamic`][crate::Dynamic] for [`serde`]. use crate::types::dynamic::Union; -use crate::{Dynamic, EvalAltResult, ImmutableString, LexError, Position}; -use serde::de::{DeserializeSeed, Error, IntoDeserializer, Visitor}; +use crate::{Dynamic, EvalAltResult, ImmutableString, LexError, Position, RhaiError, RhaiResultOf}; +use serde::de::{Error, IntoDeserializer, Visitor}; use serde::{Deserialize, Deserializer}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -26,11 +26,11 @@ impl<'de> DynamicDeserializer<'de> { Self { value } } /// Shortcut for a type conversion error. - fn type_error(&self) -> Result> { + fn type_error(&self) -> RhaiResultOf { self.type_error_str(type_name::()) } /// Shortcut for a type conversion error. - fn type_error_str(&self, error: &str) -> Result> { + fn type_error_str(&self, error: &str) -> RhaiResultOf { Err(EvalAltResult::ErrorMismatchOutputType( error.into(), self.value.type_name().into(), @@ -42,7 +42,7 @@ impl<'de> DynamicDeserializer<'de> { &mut self, v: crate::INT, visitor: V, - ) -> Result> { + ) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] return visitor.visit_i64(v); #[cfg(feature = "only_i32")] @@ -101,13 +101,11 @@ impl<'de> DynamicDeserializer<'de> { /// # Ok(()) /// # } /// ``` -pub fn from_dynamic<'de, T: Deserialize<'de>>( - value: &'de Dynamic, -) -> Result> { +pub fn from_dynamic<'de, T: Deserialize<'de>>(value: &'de Dynamic) -> RhaiResultOf { T::deserialize(&mut DynamicDeserializer::from_dynamic(value)) } -impl Error for Box { +impl Error for RhaiError { fn custom(err: T) -> Self { LexError::ImproperSymbol(String::new(), err.to_string()) .into_err(Position::NONE) @@ -116,9 +114,9 @@ impl Error for Box { } impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { - type Error = Box; + type Error = RhaiError; - fn deserialize_any>(self, visitor: V) -> Result> { + fn deserialize_any>(self, visitor: V) -> RhaiResultOf { match self.value.0 { Union::Unit(_, _, _) => self.deserialize_unit(visitor), Union::Bool(_, _, _) => self.deserialize_bool(visitor), @@ -172,11 +170,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_bool>(self, visitor: V) -> Result> { + fn deserialize_bool>(self, visitor: V) -> RhaiResultOf { visitor.visit_bool(self.value.as_bool().or_else(|_| self.type_error())?) } - fn deserialize_i8>(self, visitor: V) -> Result> { + fn deserialize_i8>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.value.as_int() { self.deserialize_int(v, visitor) } else { @@ -186,7 +184,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_i16>(self, visitor: V) -> Result> { + fn deserialize_i16>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.value.as_int() { self.deserialize_int(v, visitor) } else { @@ -196,7 +194,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_i32>(self, visitor: V) -> Result> { + fn deserialize_i32>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.value.as_int() { self.deserialize_int(v, visitor) } else if cfg!(feature = "only_i32") { @@ -208,7 +206,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_i64>(self, visitor: V) -> Result> { + fn deserialize_i64>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.value.as_int() { self.deserialize_int(v, visitor) } else if cfg!(not(feature = "only_i32")) { @@ -220,7 +218,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_i128>(self, visitor: V) -> Result> { + fn deserialize_i128>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.value.as_int() { self.deserialize_int(v, visitor) } else if cfg!(not(feature = "only_i32")) { @@ -232,7 +230,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_u8>(self, visitor: V) -> Result> { + fn deserialize_u8>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.value.as_int() { self.deserialize_int(v, visitor) } else { @@ -242,7 +240,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_u16>(self, visitor: V) -> Result> { + fn deserialize_u16>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.value.as_int() { self.deserialize_int(v, visitor) } else { @@ -252,7 +250,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_u32>(self, visitor: V) -> Result> { + fn deserialize_u32>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.value.as_int() { self.deserialize_int(v, visitor) } else { @@ -262,7 +260,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_u64>(self, visitor: V) -> Result> { + fn deserialize_u64>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.value.as_int() { self.deserialize_int(v, visitor) } else { @@ -272,7 +270,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_u128>(self, visitor: V) -> Result> { + fn deserialize_u128>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.value.as_int() { self.deserialize_int(v, visitor) } else { @@ -282,7 +280,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_f32>(self, _visitor: V) -> Result> { + fn deserialize_f32>(self, _visitor: V) -> RhaiResultOf { #[cfg(not(feature = "no_float"))] return self .value @@ -306,7 +304,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { return self.type_error_str("f32"); } - fn deserialize_f64>(self, _visitor: V) -> Result> { + fn deserialize_f64>(self, _visitor: V) -> RhaiResultOf { #[cfg(not(feature = "no_float"))] return self .value @@ -330,30 +328,24 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { return self.type_error_str("f64"); } - fn deserialize_char>(self, visitor: V) -> Result> { + fn deserialize_char>(self, visitor: V) -> RhaiResultOf { self.value .downcast_ref::() .map_or_else(|| self.type_error(), |&x| visitor.visit_char(x)) } - fn deserialize_str>(self, visitor: V) -> Result> { + fn deserialize_str>(self, visitor: V) -> RhaiResultOf { self.value.downcast_ref::().map_or_else( || self.type_error(), |x| visitor.visit_borrowed_str(x.as_str()), ) } - fn deserialize_string>( - self, - visitor: V, - ) -> Result> { + fn deserialize_string>(self, visitor: V) -> RhaiResultOf { self.deserialize_str(visitor) } - fn deserialize_bytes>( - self, - _visitor: V, - ) -> Result> { + fn deserialize_bytes>(self, _visitor: V) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] return self .value @@ -364,17 +356,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { return self.type_error(); } - fn deserialize_byte_buf>( - self, - visitor: V, - ) -> Result> { + fn deserialize_byte_buf>(self, visitor: V) -> RhaiResultOf { self.deserialize_bytes(visitor) } - fn deserialize_option>( - self, - visitor: V, - ) -> Result> { + fn deserialize_option>(self, visitor: V) -> RhaiResultOf { if self.value.is::<()>() { visitor.visit_none() } else { @@ -382,7 +368,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_unit>(self, visitor: V) -> Result> { + fn deserialize_unit>(self, visitor: V) -> RhaiResultOf { self.value .downcast_ref::<()>() .map_or_else(|| self.type_error(), |_| visitor.visit_unit()) @@ -392,7 +378,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self, _name: &'static str, visitor: V, - ) -> Result> { + ) -> RhaiResultOf { self.deserialize_unit(visitor) } @@ -400,11 +386,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self, _name: &'static str, visitor: V, - ) -> Result> { + ) -> RhaiResultOf { visitor.visit_newtype_struct(self) } - fn deserialize_seq>(self, _visitor: V) -> Result> { + fn deserialize_seq>(self, _visitor: V) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] return self.value.downcast_ref::().map_or_else( || self.type_error(), @@ -415,11 +401,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { return self.type_error(); } - fn deserialize_tuple>( - self, - _len: usize, - visitor: V, - ) -> Result> { + fn deserialize_tuple>(self, _len: usize, visitor: V) -> RhaiResultOf { self.deserialize_seq(visitor) } @@ -428,11 +410,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { _name: &'static str, _len: usize, visitor: V, - ) -> Result> { + ) -> RhaiResultOf { self.deserialize_seq(visitor) } - fn deserialize_map>(self, _visitor: V) -> Result> { + fn deserialize_map>(self, _visitor: V) -> RhaiResultOf { #[cfg(not(feature = "no_object"))] return self.value.downcast_ref::().map_or_else( || self.type_error(), @@ -453,7 +435,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { _name: &'static str, _fields: &'static [&'static str], visitor: V, - ) -> Result> { + ) -> RhaiResultOf { self.deserialize_map(visitor) } @@ -462,7 +444,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { _name: &'static str, _variants: &'static [&'static str], visitor: V, - ) -> Result> { + ) -> RhaiResultOf { if let Some(s) = self.value.read_lock::() { visitor.visit_enum(s.as_str().into_deserializer()) } else { @@ -487,17 +469,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } - fn deserialize_identifier>( - self, - visitor: V, - ) -> Result> { + fn deserialize_identifier>(self, visitor: V) -> RhaiResultOf { self.deserialize_str(visitor) } - fn deserialize_ignored_any>( - self, - visitor: V, - ) -> Result> { + fn deserialize_ignored_any>(self, visitor: V) -> RhaiResultOf { self.deserialize_any(visitor) } } @@ -521,12 +497,12 @@ impl<'a, ITER: Iterator> IterateDynamicArray<'a, ITER> { impl<'a: 'de, 'de, ITER: Iterator> serde::de::SeqAccess<'de> for IterateDynamicArray<'a, ITER> { - type Error = Box; + type Error = RhaiError; - fn next_element_seed>( + fn next_element_seed>( &mut self, seed: T, - ) -> Result, Box> { + ) -> RhaiResultOf> { // Deserialize each item coming out of the iterator. match self.iter.next() { None => Ok(None), @@ -568,12 +544,12 @@ where KEYS: Iterator, VALUES: Iterator, { - type Error = Box; + type Error = RhaiError; - fn next_key_seed>( + fn next_key_seed>( &mut self, seed: K, - ) -> Result, Box> { + ) -> RhaiResultOf> { // Deserialize each `Identifier` key coming out of the keys iterator. match self.keys.next() { None => Ok(None), @@ -583,10 +559,10 @@ where } } - fn next_value_seed>( + fn next_value_seed>( &mut self, seed: V, - ) -> Result> { + ) -> RhaiResultOf { // Deserialize each value item coming out of the iterator. seed.deserialize(&mut DynamicDeserializer::from_dynamic( self.values.next().expect("exists"), @@ -602,13 +578,13 @@ struct EnumDeserializer<'t, 'de: 't> { #[cfg(not(feature = "no_object"))] impl<'t, 'de> serde::de::EnumAccess<'de> for EnumDeserializer<'t, 'de> { - type Error = Box; + type Error = RhaiError; type Variant = Self; - fn variant_seed>( + fn variant_seed>( self, seed: V, - ) -> Result<(V::Value, Self::Variant), Self::Error> { + ) -> RhaiResultOf<(V::Value, Self::Variant)> { seed.deserialize(self.tag.into_deserializer()) .map(|v| (v, self)) } @@ -616,24 +592,20 @@ impl<'t, 'de> serde::de::EnumAccess<'de> for EnumDeserializer<'t, 'de> { #[cfg(not(feature = "no_object"))] impl<'t, 'de> serde::de::VariantAccess<'de> for EnumDeserializer<'t, 'de> { - type Error = Box; + type Error = RhaiError; - fn unit_variant(mut self) -> Result<(), Self::Error> { + fn unit_variant(mut self) -> RhaiResultOf<()> { Deserialize::deserialize(&mut self.content) } - fn newtype_variant_seed>( + fn newtype_variant_seed>( mut self, seed: T, - ) -> Result { + ) -> RhaiResultOf { seed.deserialize(&mut self.content) } - fn tuple_variant>( - mut self, - len: usize, - visitor: V, - ) -> Result { + fn tuple_variant>(mut self, len: usize, visitor: V) -> RhaiResultOf { self.content.deserialize_tuple(len, visitor) } @@ -641,7 +613,7 @@ impl<'t, 'de> serde::de::VariantAccess<'de> for EnumDeserializer<'t, 'de> { mut self, fields: &'static [&'static str], visitor: V, - ) -> Result { + ) -> RhaiResultOf { self.content.deserialize_struct("", fields, visitor) } } diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index f5c5a8dc..ce5f3f3d 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -1,3 +1,5 @@ +//! Serialization of functions metadata. + #![cfg(feature = "metadata")] use crate::module::calc_native_fn_hash; @@ -162,16 +164,9 @@ impl<'a> From<&'a crate::module::FuncInfo> for FnMetadata<'a> { .as_ref() .map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect()) } else { - #[cfg(not(feature = "metadata"))] - { - Vec::new() - } - #[cfg(feature = "metadata")] - { - info.comments - .as_ref() - .map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect()) - } + info.comments + .as_ref() + .map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect()) }, } } @@ -211,7 +206,6 @@ impl<'a> From<&'a crate::Module> for ModuleMetadata<'a> { } } -#[cfg(feature = "metadata")] impl Engine { /// _(metadata)_ Generate a list of all functions (including those defined in an /// [`AST`][crate::AST]) in JSON format. diff --git a/src/serde/ser.rs b/src/serde/ser.rs index b415d8fc..ede8db11 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -1,6 +1,6 @@ //! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`]. -use crate::{Dynamic, EvalAltResult, Position, RhaiResult}; +use crate::{Dynamic, EvalAltResult, Position, RhaiError, RhaiResult, RhaiResultOf}; use serde::ser::{ Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct, }; @@ -81,7 +81,7 @@ pub fn to_dynamic(value: T) -> RhaiResult { value.serialize(&mut s) } -impl Error for Box { +impl Error for RhaiError { fn custom(err: T) -> Self { EvalAltResult::ErrorRuntime(err.to_string().into(), Position::NONE).into() } @@ -89,47 +89,47 @@ impl Error for Box { impl Serializer for &mut DynamicSerializer { type Ok = Dynamic; - type Error = Box; + type Error = RhaiError; type SerializeSeq = DynamicSerializer; type SerializeTuple = DynamicSerializer; type SerializeTupleStruct = DynamicSerializer; #[cfg(not(any(feature = "no_object", feature = "no_index")))] type SerializeTupleVariant = TupleVariantSerializer; #[cfg(any(feature = "no_object", feature = "no_index"))] - type SerializeTupleVariant = serde::ser::Impossible>; + type SerializeTupleVariant = serde::ser::Impossible; type SerializeMap = DynamicSerializer; type SerializeStruct = DynamicSerializer; #[cfg(not(feature = "no_object"))] type SerializeStructVariant = StructVariantSerializer; #[cfg(feature = "no_object")] - type SerializeStructVariant = serde::ser::Impossible>; + type SerializeStructVariant = serde::ser::Impossible; - fn serialize_bool(self, v: bool) -> Result> { + fn serialize_bool(self, v: bool) -> RhaiResultOf { Ok(v.into()) } - fn serialize_i8(self, v: i8) -> Result> { + fn serialize_i8(self, v: i8) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] return self.serialize_i64(i64::from(v)); #[cfg(feature = "only_i32")] return self.serialize_i32(i32::from(v)); } - fn serialize_i16(self, v: i16) -> Result> { + fn serialize_i16(self, v: i16) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] return self.serialize_i64(i64::from(v)); #[cfg(feature = "only_i32")] return self.serialize_i32(i32::from(v)); } - fn serialize_i32(self, v: i32) -> Result> { + fn serialize_i32(self, v: i32) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] return self.serialize_i64(i64::from(v)); #[cfg(feature = "only_i32")] return Ok(v.into()); } - fn serialize_i64(self, v: i64) -> Result> { + fn serialize_i64(self, v: i64) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] { Ok(v.into()) @@ -142,7 +142,7 @@ impl Serializer for &mut DynamicSerializer { } } - fn serialize_i128(self, v: i128) -> Result> { + fn serialize_i128(self, v: i128) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] if v > i64::MAX as i128 { Ok(Dynamic::from(v)) @@ -157,21 +157,21 @@ impl Serializer for &mut DynamicSerializer { } } - fn serialize_u8(self, v: u8) -> Result> { + fn serialize_u8(self, v: u8) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] return self.serialize_i64(i64::from(v)); #[cfg(feature = "only_i32")] return self.serialize_i32(i32::from(v)); } - fn serialize_u16(self, v: u16) -> Result> { + fn serialize_u16(self, v: u16) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] return self.serialize_i64(i64::from(v)); #[cfg(feature = "only_i32")] return self.serialize_i32(i32::from(v)); } - fn serialize_u32(self, v: u32) -> Result> { + fn serialize_u32(self, v: u32) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] { self.serialize_i64(i64::from(v)) @@ -184,7 +184,7 @@ impl Serializer for &mut DynamicSerializer { } } - fn serialize_u64(self, v: u64) -> Result> { + fn serialize_u64(self, v: u64) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] if v > i64::MAX as u64 { Ok(Dynamic::from(v)) @@ -199,7 +199,7 @@ impl Serializer for &mut DynamicSerializer { } } - fn serialize_u128(self, v: u128) -> Result> { + fn serialize_u128(self, v: u128) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] if v > i64::MAX as u128 { Ok(Dynamic::from(v)) @@ -214,7 +214,7 @@ impl Serializer for &mut DynamicSerializer { } } - fn serialize_f32(self, v: f32) -> Result> { + fn serialize_f32(self, v: f32) -> RhaiResultOf { #[cfg(any(not(feature = "no_float"), not(feature = "decimal")))] return Ok(Dynamic::from(v)); @@ -230,7 +230,7 @@ impl Serializer for &mut DynamicSerializer { } } - fn serialize_f64(self, v: f64) -> Result> { + fn serialize_f64(self, v: f64) -> RhaiResultOf { #[cfg(any(not(feature = "no_float"), not(feature = "decimal")))] return Ok(Dynamic::from(v)); @@ -246,15 +246,15 @@ impl Serializer for &mut DynamicSerializer { } } - fn serialize_char(self, v: char) -> Result> { + fn serialize_char(self, v: char) -> RhaiResultOf { Ok(v.into()) } - fn serialize_str(self, v: &str) -> Result> { + fn serialize_str(self, v: &str) -> RhaiResultOf { Ok(v.into()) } - fn serialize_bytes(self, _v: &[u8]) -> Result> { + fn serialize_bytes(self, _v: &[u8]) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] return Ok(Dynamic::from_blob(_v.to_vec())); @@ -267,22 +267,19 @@ impl Serializer for &mut DynamicSerializer { .into()); } - fn serialize_none(self) -> Result> { + fn serialize_none(self) -> RhaiResultOf { Ok(Dynamic::UNIT) } - fn serialize_some( - self, - value: &T, - ) -> Result> { + fn serialize_some(self, value: &T) -> RhaiResultOf { value.serialize(&mut *self) } - fn serialize_unit(self) -> Result> { + fn serialize_unit(self) -> RhaiResultOf { Ok(Dynamic::UNIT) } - fn serialize_unit_struct(self, _name: &'static str) -> Result> { + fn serialize_unit_struct(self, _name: &'static str) -> RhaiResultOf { self.serialize_unit() } @@ -291,7 +288,7 @@ impl Serializer for &mut DynamicSerializer { _name: &'static str, _variant_index: u32, variant: &'static str, - ) -> Result> { + ) -> RhaiResultOf { self.serialize_str(variant) } @@ -299,7 +296,7 @@ impl Serializer for &mut DynamicSerializer { self, _name: &'static str, value: &T, - ) -> Result> { + ) -> RhaiResultOf { value.serialize(&mut *self) } @@ -309,7 +306,7 @@ impl Serializer for &mut DynamicSerializer { _variant_index: u32, _variant: &'static str, _value: &T, - ) -> Result> { + ) -> RhaiResultOf { #[cfg(not(feature = "no_object"))] { let content = to_dynamic(_value)?; @@ -324,7 +321,7 @@ impl Serializer for &mut DynamicSerializer { .into()); } - fn serialize_seq(self, _len: Option) -> Result> { + fn serialize_seq(self, _len: Option) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] return Ok(DynamicSerializer::new(crate::Array::new().into())); #[cfg(feature = "no_index")] @@ -336,7 +333,7 @@ impl Serializer for &mut DynamicSerializer { .into()); } - fn serialize_tuple(self, len: usize) -> Result> { + fn serialize_tuple(self, len: usize) -> RhaiResultOf { self.serialize_seq(Some(len)) } @@ -344,7 +341,7 @@ impl Serializer for &mut DynamicSerializer { self, _name: &'static str, len: usize, - ) -> Result> { + ) -> RhaiResultOf { self.serialize_seq(Some(len)) } @@ -354,7 +351,7 @@ impl Serializer for &mut DynamicSerializer { _variant_index: u32, _variant: &'static str, _len: usize, - ) -> Result> { + ) -> RhaiResultOf { #[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_index"))] return Ok(TupleVariantSerializer { @@ -370,7 +367,7 @@ impl Serializer for &mut DynamicSerializer { .into()); } - fn serialize_map(self, _len: Option) -> Result> { + fn serialize_map(self, _len: Option) -> RhaiResultOf { #[cfg(not(feature = "no_object"))] return Ok(DynamicSerializer::new(crate::Map::new().into())); #[cfg(feature = "no_object")] @@ -386,7 +383,7 @@ impl Serializer for &mut DynamicSerializer { self, _name: &'static str, len: usize, - ) -> Result> { + ) -> RhaiResultOf { self.serialize_map(Some(len)) } @@ -396,7 +393,7 @@ impl Serializer for &mut DynamicSerializer { _variant_index: u32, _variant: &'static str, _len: usize, - ) -> Result> { + ) -> RhaiResultOf { #[cfg(not(feature = "no_object"))] return Ok(StructVariantSerializer { variant: _variant, @@ -414,12 +411,9 @@ impl Serializer for &mut DynamicSerializer { impl SerializeSeq for DynamicSerializer { type Ok = Dynamic; - type Error = Box; + type Error = RhaiError; - fn serialize_element( - &mut self, - _value: &T, - ) -> Result<(), Box> { + fn serialize_element(&mut self, _value: &T) -> RhaiResultOf<()> { #[cfg(not(feature = "no_index"))] { let _value = _value.serialize(&mut *self)?; @@ -437,7 +431,7 @@ impl SerializeSeq for DynamicSerializer { } // Close the sequence. - fn end(self) -> Result> { + fn end(self) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] return Ok(self._value); #[cfg(feature = "no_index")] @@ -452,12 +446,9 @@ impl SerializeSeq for DynamicSerializer { impl SerializeTuple for DynamicSerializer { type Ok = Dynamic; - type Error = Box; + type Error = RhaiError; - fn serialize_element( - &mut self, - _value: &T, - ) -> Result<(), Box> { + fn serialize_element(&mut self, _value: &T) -> RhaiResultOf<()> { #[cfg(not(feature = "no_index"))] { let _value = _value.serialize(&mut *self)?; @@ -474,7 +465,7 @@ impl SerializeTuple for DynamicSerializer { .into()); } - fn end(self) -> Result> { + fn end(self) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] return Ok(self._value); #[cfg(feature = "no_index")] @@ -489,12 +480,9 @@ impl SerializeTuple for DynamicSerializer { impl SerializeTupleStruct for DynamicSerializer { type Ok = Dynamic; - type Error = Box; + type Error = RhaiError; - fn serialize_field( - &mut self, - _value: &T, - ) -> Result<(), Box> { + fn serialize_field(&mut self, _value: &T) -> RhaiResultOf<()> { #[cfg(not(feature = "no_index"))] { let _value = _value.serialize(&mut *self)?; @@ -511,7 +499,7 @@ impl SerializeTupleStruct for DynamicSerializer { .into()); } - fn end(self) -> Result> { + fn end(self) -> RhaiResultOf { #[cfg(not(feature = "no_index"))] return Ok(self._value); #[cfg(feature = "no_index")] @@ -526,9 +514,9 @@ impl SerializeTupleStruct for DynamicSerializer { impl SerializeMap for DynamicSerializer { type Ok = Dynamic; - type Error = Box; + type Error = RhaiError; - fn serialize_key(&mut self, _key: &T) -> Result<(), Box> { + fn serialize_key(&mut self, _key: &T) -> RhaiResultOf<()> { #[cfg(not(feature = "no_object"))] { self._key = _key.serialize(&mut *self)?; @@ -543,10 +531,7 @@ impl SerializeMap for DynamicSerializer { .into()); } - fn serialize_value( - &mut self, - _value: &T, - ) -> Result<(), Box> { + fn serialize_value(&mut self, _value: &T) -> RhaiResultOf<()> { #[cfg(not(feature = "no_object"))] { let key = std::mem::take(&mut self._key) @@ -576,7 +561,7 @@ impl SerializeMap for DynamicSerializer { &mut self, _key: &K, _value: &T, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { #[cfg(not(feature = "no_object"))] { let _key: Dynamic = _key.serialize(&mut *self)?; @@ -597,7 +582,7 @@ impl SerializeMap for DynamicSerializer { .into()); } - fn end(self) -> Result> { + fn end(self) -> RhaiResultOf { #[cfg(not(feature = "no_object"))] return Ok(self._value); #[cfg(feature = "no_object")] @@ -612,13 +597,13 @@ impl SerializeMap for DynamicSerializer { impl SerializeStruct for DynamicSerializer { type Ok = Dynamic; - type Error = Box; + type Error = RhaiError; fn serialize_field( &mut self, _key: &'static str, _value: &T, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { #[cfg(not(feature = "no_object"))] { let _value = _value.serialize(&mut *self)?; @@ -635,7 +620,7 @@ impl SerializeStruct for DynamicSerializer { .into()); } - fn end(self) -> Result> { + fn end(self) -> RhaiResultOf { #[cfg(not(feature = "no_object"))] return Ok(self._value); #[cfg(feature = "no_object")] @@ -657,18 +642,15 @@ struct TupleVariantSerializer { #[cfg(not(any(feature = "no_object", feature = "no_index")))] impl serde::ser::SerializeTupleVariant for TupleVariantSerializer { type Ok = Dynamic; - type Error = Box; + type Error = RhaiError; - fn serialize_field( - &mut self, - value: &T, - ) -> Result<(), Box> { + fn serialize_field(&mut self, value: &T) -> RhaiResultOf<()> { let value = to_dynamic(value)?; self.array.push(value); Ok(()) } - fn end(self) -> Result> { + fn end(self) -> RhaiResultOf { make_variant(self.variant, self.array.into()) } } @@ -682,19 +664,19 @@ struct StructVariantSerializer { #[cfg(not(feature = "no_object"))] impl serde::ser::SerializeStructVariant for StructVariantSerializer { type Ok = Dynamic; - type Error = Box; + type Error = RhaiError; fn serialize_field( &mut self, key: &'static str, value: &T, - ) -> Result<(), Box> { + ) -> RhaiResultOf<()> { let value = to_dynamic(value)?; self.map.insert(key.into(), value); Ok(()) } - fn end(self) -> Result> { + fn end(self) -> RhaiResultOf { make_variant(self.variant, self.map.into()) } } diff --git a/src/serde/str.rs b/src/serde/str.rs index 1ad17537..95e26dcc 100644 --- a/src/serde/str.rs +++ b/src/serde/str.rs @@ -1,6 +1,6 @@ //! Implement deserialization support of [`ImmutableString`][crate::ImmutableString] for [`serde`]. -use crate::{EvalAltResult, Position}; +use crate::{EvalAltResult, Position, RhaiError, RhaiResultOf}; use serde::de::{Deserializer, Visitor}; use std::any::type_name; #[cfg(feature = "no_std")] @@ -18,7 +18,7 @@ impl<'a> StringSliceDeserializer<'a> { Self { value } } /// Shortcut for a type conversion error. - fn type_error(&self) -> Result> { + fn type_error(&self) -> RhaiResultOf { Err(EvalAltResult::ErrorMismatchOutputType( type_name::().into(), "string".into(), @@ -29,91 +29,84 @@ impl<'a> StringSliceDeserializer<'a> { } impl<'de> Deserializer<'de> for &mut StringSliceDeserializer<'de> { - type Error = Box; + type Error = RhaiError; - fn deserialize_any>(self, v: V) -> Result> { + fn deserialize_any>(self, v: V) -> RhaiResultOf { self.deserialize_str(v) } - fn deserialize_bool>(self, _: V) -> Result> { + fn deserialize_bool>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_i8>(self, _: V) -> Result> { + fn deserialize_i8>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_i16>(self, _: V) -> Result> { + fn deserialize_i16>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_i32>(self, _: V) -> Result> { + fn deserialize_i32>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_i64>(self, _: V) -> Result> { + fn deserialize_i64>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_u8>(self, _: V) -> Result> { + fn deserialize_u8>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_u16>(self, _: V) -> Result> { + fn deserialize_u16>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_u32>(self, _: V) -> Result> { + fn deserialize_u32>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_u64>(self, _: V) -> Result> { + fn deserialize_u64>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_f32>(self, _: V) -> Result> { + fn deserialize_f32>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_f64>(self, _: V) -> Result> { + fn deserialize_f64>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_char>(self, _: V) -> Result> { + fn deserialize_char>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_str>(self, v: V) -> Result> { + fn deserialize_str>(self, v: V) -> RhaiResultOf { // Only allow deserialization into a string. v.visit_borrowed_str(self.value) } - fn deserialize_string>( - self, - visitor: V, - ) -> Result> { + fn deserialize_string>(self, visitor: V) -> RhaiResultOf { self.deserialize_str(visitor) } - fn deserialize_bytes>(self, _: V) -> Result> { + fn deserialize_bytes>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_byte_buf>(self, _: V) -> Result> { + fn deserialize_byte_buf>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_option>(self, _: V) -> Result> { + fn deserialize_option>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_unit>(self, _: V) -> Result> { + fn deserialize_unit>(self, _: V) -> RhaiResultOf { self.type_error() } fn deserialize_unit_struct>( self, _name: &'static str, v: V, - ) -> Result> { + ) -> RhaiResultOf { self.deserialize_unit(v) } fn deserialize_newtype_struct>( self, _name: &'static str, v: V, - ) -> Result> { + ) -> RhaiResultOf { v.visit_newtype_struct(self) } - fn deserialize_seq>(self, _: V) -> Result> { + fn deserialize_seq>(self, _: V) -> RhaiResultOf { self.type_error() } - fn deserialize_tuple>( - self, - _len: usize, - v: V, - ) -> Result> { + fn deserialize_tuple>(self, _len: usize, v: V) -> RhaiResultOf { self.deserialize_seq(v) } fn deserialize_tuple_struct>( @@ -121,10 +114,10 @@ impl<'de> Deserializer<'de> for &mut StringSliceDeserializer<'de> { _name: &'static str, _len: usize, v: V, - ) -> Result> { + ) -> RhaiResultOf { self.deserialize_seq(v) } - fn deserialize_map>(self, _: V) -> Result> { + fn deserialize_map>(self, _: V) -> RhaiResultOf { self.type_error() } fn deserialize_struct>( @@ -132,7 +125,7 @@ impl<'de> Deserializer<'de> for &mut StringSliceDeserializer<'de> { _name: &'static str, _fields: &'static [&'static str], v: V, - ) -> Result> { + ) -> RhaiResultOf { self.deserialize_map(v) } fn deserialize_enum>( @@ -140,16 +133,13 @@ impl<'de> Deserializer<'de> for &mut StringSliceDeserializer<'de> { _name: &'static str, _variants: &'static [&'static str], _: V, - ) -> Result> { + ) -> RhaiResultOf { self.type_error() } - fn deserialize_identifier>(self, v: V) -> Result> { + fn deserialize_identifier>(self, v: V) -> RhaiResultOf { self.deserialize_str(v) } - fn deserialize_ignored_any>( - self, - v: V, - ) -> Result> { + fn deserialize_ignored_any>(self, v: V) -> RhaiResultOf { self.deserialize_any(v) } } diff --git a/src/types/error.rs b/src/types/error.rs index f38a971c..da636525 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -1,6 +1,6 @@ //! Module containing error definitions for the evaluation process. -use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT}; +use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT, RhaiError}; #[cfg(feature = "no_std")] use core_error::Error; #[cfg(not(feature = "no_std"))] @@ -36,12 +36,12 @@ pub enum EvalAltResult { ErrorFunctionNotFound(String, Position), /// An error has occurred inside a called function. /// Wrapped values are the function name, function source, and the interior error. - ErrorInFunctionCall(String, String, Box, Position), + ErrorInFunctionCall(String, String, RhaiError, Position), /// Usage of an unknown [module][crate::Module]. Wrapped value is the [module][crate::Module] name. ErrorModuleNotFound(String, Position), /// An error has occurred while loading a [module][crate::Module]. /// Wrapped value are the [module][crate::Module] name and the interior error. - ErrorInModule(String, Box, Position), + ErrorInModule(String, RhaiError, Position), /// Access to `this` that is not bound. ErrorUnboundThis(Position), /// Data is not of the required type. @@ -222,7 +222,7 @@ impl> From for EvalAltResult { } } -impl> From for Box { +impl> From for RhaiError { #[inline(never)] fn from(err: T) -> Self { EvalAltResult::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE).into() diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 7475b7ef..205c6631 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -4,7 +4,7 @@ use crate::tokenizer::is_valid_identifier; use crate::types::dynamic::Variant; use crate::{ Dynamic, Engine, EvalAltResult, FuncArgs, Identifier, Module, NativeCallContext, Position, - RhaiResult, StaticVec, AST, + RhaiError, RhaiResult, RhaiResultOf, StaticVec, AST, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -32,7 +32,7 @@ impl fmt::Debug for FnPtr { impl FnPtr { /// Create a new function pointer. #[inline(always)] - pub fn new(name: impl Into) -> Result> { + pub fn new(name: impl Into) -> RhaiResultOf { name.into().try_into() } /// Create a new function pointer without checking its parameters. @@ -135,7 +135,7 @@ impl FnPtr { engine: &Engine, ast: &AST, args: impl FuncArgs, - ) -> Result> { + ) -> RhaiResultOf { let _ast = ast; let mut arg_values = crate::StaticVec::new_const(); args.parse(&mut arg_values); @@ -176,7 +176,7 @@ impl FnPtr { &self, context: &NativeCallContext, args: impl FuncArgs, - ) -> Result> { + ) -> RhaiResultOf { let mut arg_values = crate::StaticVec::new_const(); args.parse(&mut arg_values); @@ -247,10 +247,10 @@ impl fmt::Display for FnPtr { } impl TryFrom for FnPtr { - type Error = Box; + type Error = RhaiError; #[inline] - fn try_from(value: Identifier) -> Result { + fn try_from(value: Identifier) -> RhaiResultOf { if is_valid_identifier(value.chars()) { Ok(Self(value, StaticVec::new_const())) } else { @@ -260,40 +260,40 @@ impl TryFrom for FnPtr { } impl TryFrom for FnPtr { - type Error = Box; + type Error = RhaiError; #[inline(always)] - fn try_from(value: crate::ImmutableString) -> Result { + fn try_from(value: crate::ImmutableString) -> RhaiResultOf { let s: Identifier = value.into(); Self::try_from(s) } } impl TryFrom for FnPtr { - type Error = Box; + type Error = RhaiError; #[inline(always)] - fn try_from(value: String) -> Result { + fn try_from(value: String) -> RhaiResultOf { let s: Identifier = value.into(); Self::try_from(s) } } impl TryFrom> for FnPtr { - type Error = Box; + type Error = RhaiError; #[inline(always)] - fn try_from(value: Box) -> Result { + fn try_from(value: Box) -> RhaiResultOf { let s: Identifier = value.into(); Self::try_from(s) } } impl TryFrom<&str> for FnPtr { - type Error = Box; + type Error = RhaiError; #[inline(always)] - fn try_from(value: &str) -> Result { + fn try_from(value: &str) -> RhaiResultOf { let s: Identifier = value.into(); Self::try_from(s) } diff --git a/src/types/parse_error.rs b/src/types/parse_error.rs index b1c839f9..3c0870c9 100644 --- a/src/types/parse_error.rs +++ b/src/types/parse_error.rs @@ -1,7 +1,7 @@ //! Module containing error definitions for the parsing process. use crate::tokenizer::is_valid_identifier; -use crate::{EvalAltResult, Position}; +use crate::{EvalAltResult, Position, RhaiError}; #[cfg(feature = "no_std")] use core_error::Error; #[cfg(not(feature = "no_std"))] @@ -310,7 +310,7 @@ impl fmt::Display for ParseError { } } -impl From for Box { +impl From for RhaiError { #[inline(always)] fn from(err: ParseErrorType) -> Self { Box::new(err.into()) @@ -324,7 +324,7 @@ impl From for EvalAltResult { } } -impl From for Box { +impl From for RhaiError { #[inline(always)] fn from(err: ParseError) -> Self { Box::new(err.into()) From a07faf7dd9051e861e35f64da4fc07e94a045c43 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 26 Dec 2021 12:16:48 +0800 Subject: [PATCH 41/97] Fix doc test. --- src/custom_syntax.rs | 12 ++++++------ src/lib.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/custom_syntax.rs b/src/custom_syntax.rs index f718b0e3..95ec521a 100644 --- a/src/custom_syntax.rs +++ b/src/custom_syntax.rs @@ -3,12 +3,12 @@ use crate::ast::Expr; use crate::engine::EvalContext; use crate::func::native::SendSync; +use crate::parser::ParseResult; use crate::r#unsafe::unsafe_try_cast; use crate::tokenizer::{is_valid_identifier, Token}; use crate::types::dynamic::Variant; use crate::{ - Engine, Identifier, ImmutableString, LexError, ParseError, Position, RhaiResult, Shared, - StaticVec, INT, + Engine, Identifier, ImmutableString, LexError, Position, RhaiResult, Shared, StaticVec, INT, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -47,11 +47,11 @@ pub type FnCustomSyntaxEval = dyn Fn(&mut EvalContext, &[Expression]) -> RhaiRes /// A general expression parsing trait object. #[cfg(not(feature = "sync"))] pub type FnCustomSyntaxParse = - dyn Fn(&[ImmutableString], &str) -> Result, ParseError>; + dyn Fn(&[ImmutableString], &str) -> ParseResult>; /// A general expression parsing trait object. #[cfg(feature = "sync")] pub type FnCustomSyntaxParse = - dyn Fn(&[ImmutableString], &str) -> Result, ParseError> + Send + Sync; + dyn Fn(&[ImmutableString], &str) -> ParseResult> + Send + Sync; /// An expression sub-tree in an [`AST`][crate::AST]. #[derive(Debug, Clone)] @@ -216,7 +216,7 @@ impl Engine { symbols: &[S], scope_may_be_changed: bool, func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static, - ) -> Result<&mut Self, ParseError> { + ) -> ParseResult<&mut Self> { use markers::*; let mut segments = StaticVec::::new(); @@ -357,7 +357,7 @@ impl Engine { pub fn register_custom_syntax_raw( &mut self, key: impl Into, - parse: impl Fn(&[ImmutableString], &str) -> Result, ParseError> + parse: impl Fn(&[ImmutableString], &str) -> ParseResult> + SendSync + 'static, scope_may_be_changed: bool, diff --git a/src/lib.rs b/src/lib.rs index ee86a923..6bf9da8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,8 +42,8 @@ //! //! # #[cfg(not(feature = "no_std"))] //! # #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] -//! // Evaluate the script, expects a 'bool' return -//! let result = engine.eval_file::("my_script.rhai".into())?, +//! // Evaluate the script, expecting a 'bool' result +//! let result = engine.eval_file::("my_script.rhai".into())?; //! //! assert_eq!(result, true); //! From e7ca3f41dda6b3410b62f28ddbff540c4297ddeb Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 27 Dec 2021 11:43:11 +0800 Subject: [PATCH 42/97] Fix formatting. --- src/api/call_fn.rs | 7 ++++--- src/api/deprecated.rs | 8 +++++--- src/api/register.rs | 2 +- src/func/native.rs | 5 ++--- src/module/mod.rs | 2 +- src/packages/array_basic.rs | 24 ++++-------------------- src/packages/blob_basic.rs | 7 +------ src/types/error.rs | 2 +- src/types/fn_ptr.rs | 3 ++- 9 files changed, 21 insertions(+), 39 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index a78943ed..e9e13989 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -91,10 +91,11 @@ impl Engine { /// /// This function is very low level. /// - /// ## Arguments + /// # Arguments /// /// All the arguments are _consumed_, meaning that they're replaced by `()`. /// This is to avoid unnecessarily cloning the arguments. + /// /// Do not use the arguments after this call. If they are needed afterwards, /// clone them _before_ calling this function. /// @@ -130,10 +131,10 @@ impl Engine { /// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "bar", None, [])?; /// assert_eq!(result.cast::(), 21); /// - /// let mut value: Dynamic = 1_i64.into(); + /// let mut value = 1_i64.into(); /// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "action", Some(&mut value), [ 41_i64.into() ])?; /// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer - /// assert_eq!(value.as_int().expect("value should be INT"), 42); + /// assert_eq!(value.as_int().unwrap(), 42); /// /// engine.call_fn_raw(&mut scope, &ast, true, false, "decl", None, [ 42_i64.into() ])?; /// // ^^^^^ do not rewind scope diff --git a/src/api/deprecated.rs b/src/api/deprecated.rs index 6ef2573b..7320451b 100644 --- a/src/api/deprecated.rs +++ b/src/api/deprecated.rs @@ -120,10 +120,11 @@ impl Engine { /// /// This function is very low level. /// - /// ## Arguments + /// # Arguments /// /// All the arguments are _consumed_, meaning that they're replaced by `()`. /// This is to avoid unnecessarily cloning the arguments. + /// /// Do not use the arguments after this call. If they are needed afterwards, /// clone them _before_ calling this function. /// @@ -220,7 +221,7 @@ impl NativeCallContext<'_> { /// /// This function is very low level. /// - /// ## Arguments + /// # Arguments /// /// All arguments may be _consumed_, meaning that they may be replaced by `()`. This is to avoid /// unnecessarily cloning the arguments. @@ -276,10 +277,11 @@ impl FnPtr { /// /// This function is very low level. /// - /// ## Arguments + /// # Arguments /// /// All the arguments are _consumed_, meaning that they're replaced by `()`. /// This is to avoid unnecessarily cloning the arguments. + /// /// Do not use the arguments after this call. If they are needed afterwards, /// clone them _before_ calling this function. #[deprecated( diff --git a/src/api/register.rs b/src/api/register.rs index 66a85220..157f650e 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -150,7 +150,7 @@ impl Engine { /// This function is very low level. It takes a list of [`TypeId`][std::any::TypeId]'s /// indicating the actual types of the parameters. /// - /// ## Arguments + /// # Arguments /// /// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][crate::Dynamic]. /// The arguments are guaranteed to be of the correct types matching the [`TypeId`][std::any::TypeId]'s. diff --git a/src/func/native.rs b/src/func/native.rs index bbf989bb..5c9bf5e6 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -411,6 +411,5 @@ pub type OnVarCallback = Box RhaiResultOf> + 'static>; /// A standard callback function for variable access. #[cfg(feature = "sync")] -pub type OnVarCallback = Box< - dyn Fn(&str, usize, &EvalContext) -> RhaiResultOf> + Send + Sync + 'static, ->; +pub type OnVarCallback = + Box RhaiResultOf> + Send + Sync + 'static>; diff --git a/src/module/mod.rs b/src/module/mod.rs index e7e9b745..db55124d 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -871,7 +871,7 @@ impl Module { /// /// This function is very low level. /// - /// ## Arguments + /// # Arguments /// /// A list of [`TypeId`]'s is taken as the argument types. /// diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 3e2903a1..aa728088 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -305,11 +305,7 @@ pub mod array_functions { } #[rhai_fn(return_raw, pure)] - pub fn filter( - ctx: NativeCallContext, - array: &mut Array, - filter: FnPtr, - ) -> RhaiResultOf { + pub fn filter(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf { if array.is_empty() { return Ok(array.clone()); } @@ -863,11 +859,7 @@ pub mod array_functions { Ok(()) } #[rhai_fn(return_raw)] - pub fn drain( - ctx: NativeCallContext, - array: &mut Array, - filter: FnPtr, - ) -> RhaiResultOf { + pub fn drain(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf { if array.is_empty() { return Ok(Array::new()); } @@ -957,11 +949,7 @@ pub mod array_functions { array.drain(start..start + len).collect() } #[rhai_fn(return_raw)] - pub fn retain( - ctx: NativeCallContext, - array: &mut Array, - filter: FnPtr, - ) -> RhaiResultOf { + pub fn retain(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf { if array.is_empty() { return Ok(Array::new()); } @@ -1054,11 +1042,7 @@ pub mod array_functions { drained } #[rhai_fn(name = "==", return_raw, pure)] - pub fn equals( - ctx: NativeCallContext, - array1: &mut Array, - array2: Array, - ) -> RhaiResultOf { + pub fn equals(ctx: NativeCallContext, array1: &mut Array, array2: Array) -> RhaiResultOf { if array1.len() != array2.len() { return Ok(false); } diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index 342a97fc..1a03eae5 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -110,12 +110,7 @@ pub mod blob_functions { } } #[rhai_fn(return_raw)] - pub fn pad( - ctx: NativeCallContext, - blob: &mut Blob, - len: INT, - item: INT, - ) -> RhaiResultOf<()> { + pub fn pad(ctx: NativeCallContext, blob: &mut Blob, len: INT, item: INT) -> RhaiResultOf<()> { if len <= 0 { return Ok(()); } diff --git a/src/types/error.rs b/src/types/error.rs index da636525..90e9d834 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -1,6 +1,6 @@ //! Module containing error definitions for the evaluation process. -use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT, RhaiError}; +use crate::{Dynamic, ImmutableString, ParseErrorType, Position, RhaiError, INT}; #[cfg(feature = "no_std")] use core_error::Error; #[cfg(not(feature = "no_std"))] diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 205c6631..74576de1 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -204,10 +204,11 @@ impl FnPtr { /// /// This function is very low level. /// - /// ## Arguments + /// # Arguments /// /// All the arguments are _consumed_, meaning that they're replaced by `()`. /// This is to avoid unnecessarily cloning the arguments. + /// /// Do not use the arguments after this call. If they are needed afterwards, /// clone them _before_ calling this function. #[inline] From 05d4c81e7a88b7dbe4b4a6edd36569ac1e698c3b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 27 Dec 2021 12:27:31 +0800 Subject: [PATCH 43/97] Use type alias for error. --- src/api/call_fn.rs | 7 +- src/api/compile.rs | 2 +- src/api/eval.rs | 6 +- src/api/files.rs | 6 +- src/engine.rs | 218 ++++++++++++----------------- src/func/call.rs | 46 +++--- src/func/native.rs | 6 +- src/func/register.rs | 10 +- src/func/script.rs | 12 +- src/lib.rs | 6 +- src/module/mod.rs | 2 +- src/module/resolvers/collection.rs | 8 +- src/module/resolvers/dummy.rs | 4 +- src/module/resolvers/file.rs | 16 +-- src/module/resolvers/stat.rs | 5 +- src/packages/arithmetic.rs | 4 +- src/packages/array_basic.rs | 72 ++++------ src/packages/blob_basic.rs | 34 ++--- src/packages/iter_basic.rs | 29 ++-- src/packages/lang_core.rs | 6 +- src/packages/logic.rs | 26 ++-- src/packages/math_basic.rs | 30 ++-- src/packages/string_more.rs | 8 +- src/parser.rs | 24 ++-- src/serde/de.rs | 4 +- src/serde/ser.rs | 48 +++---- src/serde/str.rs | 10 +- src/types/fn_ptr.rs | 10 +- src/types/parse_error.rs | 12 +- 29 files changed, 293 insertions(+), 378 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index e9e13989..f7c3c4d0 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -4,8 +4,7 @@ use crate::engine::{EvalState, Imports}; use crate::types::dynamic::Variant; use crate::{ - Dynamic, Engine, EvalAltResult, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, - AST, + Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST, ERR, }; use std::any::type_name; #[cfg(feature = "no_std")] @@ -70,7 +69,7 @@ impl Engine { let typ = self.map_type_name(result.type_name()); result.try_cast().ok_or_else(|| { - EvalAltResult::ErrorMismatchOutputType( + ERR::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), Position::NONE, @@ -176,7 +175,7 @@ impl Engine { let fn_def = ast .shared_lib() .get_script_fn(name, args.len()) - .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?; + .ok_or_else(|| ERR::ErrorFunctionNotFound(name.into(), Position::NONE))?; // Check for data race. #[cfg(not(feature = "no_closure"))] diff --git a/src/api/compile.rs b/src/api/compile.rs index 172e5db8..882d047a 100644 --- a/src/api/compile.rs +++ b/src/api/compile.rs @@ -366,7 +366,7 @@ impl Engine { } else if json_text.starts_with(Token::LeftBrace.literal_syntax()) { ["#", json_text] } else { - return Err(crate::ParseErrorType::MissingToken( + return Err(crate::PERR::MissingToken( Token::LeftBrace.syntax().into(), "to start a JSON object hash".into(), ) diff --git a/src/api/eval.rs b/src/api/eval.rs index 674e4da9..66358191 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -3,9 +3,7 @@ use crate::engine::{EvalState, Imports}; use crate::parser::ParseState; use crate::types::dynamic::Variant; -use crate::{ - Dynamic, Engine, EvalAltResult, Module, Position, RhaiResult, RhaiResultOf, Scope, AST, -}; +use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR}; use std::any::type_name; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -193,7 +191,7 @@ impl Engine { let typ = self.map_type_name(result.type_name()); result.try_cast::().ok_or_else(|| { - EvalAltResult::ErrorMismatchOutputType( + ERR::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), Position::NONE, diff --git a/src/api/files.rs b/src/api/files.rs index 1adea263..004daf89 100644 --- a/src/api/files.rs +++ b/src/api/files.rs @@ -3,7 +3,7 @@ #![cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] use crate::types::dynamic::Variant; -use crate::{Engine, EvalAltResult, RhaiResultOf, Scope, AST}; +use crate::{Engine, RhaiResultOf, Scope, AST, ERR}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -13,7 +13,7 @@ impl Engine { use std::io::Read; let mut f = std::fs::File::open(path.clone()).map_err(|err| { - EvalAltResult::ErrorSystem( + ERR::ErrorSystem( format!("Cannot open script file '{}'", path.to_string_lossy()), err.into(), ) @@ -22,7 +22,7 @@ impl Engine { let mut contents = String::new(); f.read_to_string(&mut contents).map_err(|err| { - EvalAltResult::ErrorSystem( + ERR::ErrorSystem( format!("Cannot read script file '{}'", path.to_string_lossy()), err.into(), ) diff --git a/src/engine.rs b/src/engine.rs index aa25bc20..c26d54cc 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -13,8 +13,8 @@ use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::tokenizer::Token; use crate::types::dynamic::{map_std_type_name, AccessMode, Union, Variant}; use crate::{ - calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, Identifier, ImmutableString, - Module, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, INT, + calc_fn_params_hash, combine_hashes, Dynamic, Identifier, ImmutableString, Module, Position, + RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, ERR, INT, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -459,7 +459,7 @@ pub enum Target<'a> { Dynamic, ), ), - /// The target is a temporary `Dynamic` value (i.e. the mutation can cause no side effects). + /// The target is a temporary `Dynamic` value (i.e. its mutation can cause no side effects). TempValue(Dynamic), /// The target is a bit inside an [`INT`][crate::INT]. /// This is necessary because directly pointing to a bit inside an [`INT`][crate::INT] is impossible. @@ -557,7 +557,7 @@ impl<'a> Target<'a> { match self { Self::RefMut(r) => r.clone(), // Referenced value is cloned #[cfg(not(feature = "no_closure"))] - Self::LockGuard((_, orig)) => orig, // Original value is simply taken + Self::LockGuard((_, shared)) => shared, // Original shared value is simply taken Self::TempValue(v) => v, // Owned value is simply taken #[cfg(not(feature = "no_index"))] Self::Bit(_, value, _) => value, // Boolean is taken @@ -585,7 +585,7 @@ impl<'a> Target<'a> { self.take_or_clone().into() } /// Propagate a changed value back to the original source. - /// This has no effect except for string indexing. + /// This has no effect for direct references. #[inline] pub fn propagate_changed_value(&mut self) -> RhaiResultOf<()> { match self { @@ -596,7 +596,7 @@ impl<'a> Target<'a> { Self::Bit(value, new_val, index) => { // Replace the bit at the specified index position let new_bit = new_val.as_bool().map_err(|err| { - Box::new(EvalAltResult::ErrorMismatchDataType( + Box::new(ERR::ErrorMismatchDataType( "bool".to_string(), err.to_string(), Position::NONE, @@ -621,7 +621,7 @@ impl<'a> Target<'a> { // Replace the bit at the specified index position let new_value = new_val.as_int().map_err(|err| { - Box::new(EvalAltResult::ErrorMismatchDataType( + Box::new(ERR::ErrorMismatchDataType( "integer".to_string(), err.to_string(), Position::NONE, @@ -638,7 +638,7 @@ impl<'a> Target<'a> { Self::BlobByte(value, index, new_val) => { // Replace the byte at the specified index position let new_byte = new_val.as_int().map_err(|err| { - Box::new(EvalAltResult::ErrorMismatchDataType( + Box::new(ERR::ErrorMismatchDataType( "INT".to_string(), err.to_string(), Position::NONE, @@ -659,7 +659,7 @@ impl<'a> Target<'a> { Self::StringChar(s, index, new_val) => { // Replace the character at the specified index position let new_ch = new_val.as_char().map_err(|err| { - Box::new(EvalAltResult::ErrorMismatchDataType( + Box::new(ERR::ErrorMismatchDataType( "char".to_string(), err.to_string(), Position::NONE, @@ -1189,18 +1189,16 @@ impl Engine { Ok((target.into(), *_var_pos)) } Err(err) => Err(match *err { - EvalAltResult::ErrorVariableNotFound(_, _) => { - EvalAltResult::ErrorVariableNotFound( - format!( - "{}{}{}", - namespace, - Token::DoubleColon.literal_syntax(), - var_name - ), - namespace[0].pos, - ) - .into() - } + ERR::ErrorVariableNotFound(_, _) => ERR::ErrorVariableNotFound( + format!( + "{}{}{}", + namespace, + Token::DoubleColon.literal_syntax(), + var_name + ), + namespace[0].pos, + ) + .into(), _ => err.fill_position(*_var_pos), }), }; @@ -1220,7 +1218,7 @@ impl Engine { } } - return Err(EvalAltResult::ErrorVariableNotFound( + return Err(ERR::ErrorVariableNotFound( format!( "{}{}{}", namespace, @@ -1232,10 +1230,7 @@ impl Engine { .into()); } - Err( - EvalAltResult::ErrorModuleNotFound(namespace.to_string(), namespace[0].pos) - .into(), - ) + Err(ERR::ErrorModuleNotFound(namespace.to_string(), namespace[0].pos).into()) } #[cfg(feature = "no_module")] @@ -1267,7 +1262,7 @@ impl Engine { return if let Some(val) = this_ptr { Ok(((*val).into(), *pos)) } else { - Err(EvalAltResult::ErrorUnboundThis(*pos).into()) + Err(ERR::ErrorUnboundThis(*pos).into()) } } _ if state.always_search_scope => (0, expr.position()), @@ -1308,7 +1303,7 @@ impl Engine { let var_name = expr.get_variable_name(true).expect("`Variable`"); scope .get_index(var_name) - .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(var_name.to_string(), var_pos))? + .ok_or_else(|| ERR::ErrorVariableNotFound(var_name.to_string(), var_pos))? .0 }; @@ -1387,7 +1382,7 @@ impl Engine { root_pos, None, level, ) { // Just ignore if there is no index setter - if !matches!(*err, EvalAltResult::ErrorFunctionNotFound(_, _)) { + if !matches!(*err, ERR::ErrorFunctionNotFound(_, _)) { return Err(err); } } @@ -1416,7 +1411,7 @@ impl Engine { } // Can't index - try to call an index setter #[cfg(not(feature = "no_index"))] - Err(err) if matches!(*err, EvalAltResult::ErrorIndexingType(_, _)) => { + Err(err) if matches!(*err, ERR::ErrorIndexingType(_, _)) => { Some(new_val) } // Any other error @@ -1510,7 +1505,7 @@ impl Engine { ) .or_else(|err| match *err { // Try an indexer if property does not exist - EvalAltResult::ErrorDotExpr(_, _) => { + ERR::ErrorDotExpr(_, _) => { let prop = name.into(); self.get_indexed_mut( mods, state, lib, target, prop, *pos, false, true, @@ -1519,7 +1514,7 @@ impl Engine { .map(|v| (v.take_or_clone(), false)) .map_err( |idx_err| match *idx_err { - EvalAltResult::ErrorIndexingType(_, _) => err, + ERR::ErrorIndexingType(_, _) => err, _ => idx_err, }, ) @@ -1553,7 +1548,7 @@ impl Engine { ) .or_else(|err| match *err { // Try an indexer if property does not exist - EvalAltResult::ErrorDotExpr(_, _) => { + ERR::ErrorDotExpr(_, _) => { let args = &mut [target, &mut name.into(), &mut new_val]; let hash_set = crate::ast::FnCallHashes::from_native(mods.hash_idx_set()); @@ -1565,7 +1560,7 @@ impl Engine { ) .map_err( |idx_err| match *idx_err { - EvalAltResult::ErrorIndexingType(_, _) => err, + ERR::ErrorIndexingType(_, _) => err, _ => idx_err, }, ) @@ -1585,7 +1580,7 @@ impl Engine { .map_or_else( |err| match *err { // Try an indexer if property does not exist - EvalAltResult::ErrorDotExpr(_, _) => { + ERR::ErrorDotExpr(_, _) => { let prop = name.into(); self.get_indexed_mut( mods, state, lib, target, prop, *pos, false, true, level, @@ -1593,7 +1588,7 @@ impl Engine { .map(|v| (v.take_or_clone(), false)) .map_err(|idx_err| { match *idx_err { - EvalAltResult::ErrorIndexingType(_, _) => err, + ERR::ErrorIndexingType(_, _) => err, _ => idx_err, } }) @@ -1663,7 +1658,7 @@ impl Engine { ) .or_else(|err| match *err { // Try an indexer if property does not exist - EvalAltResult::ErrorDotExpr(_, _) => { + ERR::ErrorDotExpr(_, _) => { let prop = name.into(); self.get_indexed_mut( mods, state, lib, target, prop, *pos, false, true, @@ -1672,7 +1667,7 @@ impl Engine { .map(|v| (v.take_or_clone(), false)) .map_err( |idx_err| match *idx_err { - EvalAltResult::ErrorIndexingType(_, _) => err, + ERR::ErrorIndexingType(_, _) => err, _ => idx_err, }, ) @@ -1711,7 +1706,7 @@ impl Engine { .or_else( |err| match *err { // Try an indexer if property does not exist - EvalAltResult::ErrorDotExpr(_, _) => { + ERR::ErrorDotExpr(_, _) => { let args = &mut [target.as_mut(), &mut name.into(), val]; let hash_set = @@ -1723,7 +1718,7 @@ impl Engine { is_ref_mut, true, *pos, None, level, ) .or_else(|idx_err| match *idx_err { - EvalAltResult::ErrorIndexingType(_, _) => { + ERR::ErrorIndexingType(_, _) => { // If there is no setter, no need to feed it back because // the property is read-only Ok((Dynamic::UNIT, false)) @@ -1768,7 +1763,7 @@ impl Engine { } } // Syntax error - _ => Err(EvalAltResult::ErrorDotExpr("".into(), rhs.position()).into()), + _ => Err(ERR::ErrorDotExpr("".into(), rhs.position()).into()), } } } @@ -2007,11 +2002,10 @@ impl Engine { arr_len - index .checked_abs() - .ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos)) + .ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos)) .and_then(|n| { if n as usize > arr_len { - Err(EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos) - .into()) + Err(ERR::ErrorArrayBounds(arr_len, index, idx_pos).into()) } else { Ok(n as usize) } @@ -2029,7 +2023,7 @@ impl Engine { arr.get_mut(arr_idx) .map(Target::from) - .ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos).into()) + .ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos).into()) } #[cfg(not(feature = "no_index"))] @@ -2047,11 +2041,10 @@ impl Engine { arr_len - index .checked_abs() - .ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos)) + .ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos)) .and_then(|n| { if n as usize > arr_len { - Err(EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos) - .into()) + Err(ERR::ErrorArrayBounds(arr_len, index, idx_pos).into()) } else { Ok(n as usize) } @@ -2070,9 +2063,7 @@ impl Engine { let value = arr .get(arr_idx) .map(|&v| (v as INT).into()) - .ok_or_else(|| { - Box::new(EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos)) - })?; + .ok_or_else(|| Box::new(ERR::ErrorArrayBounds(arr_len, index, idx_pos)))?; Ok(Target::BlobByte(target, arr_idx, value)) } @@ -2110,9 +2101,9 @@ impl Engine { let end = range.end; if start < 0 || start as usize >= BITS { - return Err(EvalAltResult::ErrorBitFieldBounds(BITS, start, idx_pos).into()); + return Err(ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into()); } else if end < 0 || end as usize >= BITS { - return Err(EvalAltResult::ErrorBitFieldBounds(BITS, end, idx_pos).into()); + return Err(ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into()); } else if end <= start { (0, 0) } else if end as usize == BITS && start == 0 { @@ -2130,9 +2121,9 @@ impl Engine { let end = *range.end(); if start < 0 || start as usize >= BITS { - return Err(EvalAltResult::ErrorBitFieldBounds(BITS, start, idx_pos).into()); + return Err(ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into()); } else if end < 0 || end as usize >= BITS { - return Err(EvalAltResult::ErrorBitFieldBounds(BITS, end, idx_pos).into()); + return Err(ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into()); } else if end < start { (0, 0) } else if end as usize == BITS - 1 && start == 0 { @@ -2167,9 +2158,7 @@ impl Engine { let offset = index as usize; ( if offset >= BITS { - return Err( - EvalAltResult::ErrorBitFieldBounds(BITS, index, idx_pos).into() - ); + return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into()); } else { (*value & (1 << offset)) != 0 }, @@ -2180,16 +2169,14 @@ impl Engine { ( // Count from end if negative if offset > BITS { - return Err( - EvalAltResult::ErrorBitFieldBounds(BITS, index, idx_pos).into() - ); + return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into()); } else { (*value & (1 << (BITS - offset))) != 0 }, offset as u8, ) } else { - return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, idx_pos).into()); + return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into()); }; Ok(Target::Bit(target, bit_value.into(), offset)) @@ -2207,7 +2194,7 @@ impl Engine { ( s.chars().nth(offset).ok_or_else(|| { let chars_len = s.chars().count(); - EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos) + ERR::ErrorStringBounds(chars_len, index, idx_pos) })?, offset, ) @@ -2217,13 +2204,13 @@ impl Engine { // Count from end if negative s.chars().rev().nth(offset - 1).ok_or_else(|| { let chars_len = s.chars().count(); - EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos) + ERR::ErrorStringBounds(chars_len, index, idx_pos) })?, offset, ) } else { let chars_len = s.chars().count(); - return Err(EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos).into()); + return Err(ERR::ErrorStringBounds(chars_len, index, idx_pos).into()); }; Ok(Target::StringChar(target, offset, ch.into())) @@ -2240,7 +2227,7 @@ impl Engine { .map(|(v, _)| v.into()) } - _ => Err(EvalAltResult::ErrorIndexingType( + _ => Err(ERR::ErrorIndexingType( format!( "{} [{}]", self.map_type_name(target.type_name()), @@ -2277,7 +2264,7 @@ impl Engine { Expr::Variable(None, var_pos, x) if x.0.is_none() && x.2 == KEYWORD_THIS => this_ptr .as_deref() .cloned() - .ok_or_else(|| EvalAltResult::ErrorUnboundThis(*var_pos).into()), + .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()), Expr::Variable(_, _, _) => self .search_namespace(scope, mods, state, lib, this_ptr, expr) .map(|(val, _)| val.take_or_clone()), @@ -2542,9 +2529,7 @@ impl Engine { ) -> RhaiResultOf<()> { if target.is_read_only() { // Assignment to constant variable - return Err( - EvalAltResult::ErrorAssignmentToConstant(root.0.to_string(), root.1).into(), - ); + return Err(ERR::ErrorAssignmentToConstant(root.0.to_string(), root.1).into()); } let mut new_val = new_val; @@ -2575,7 +2560,7 @@ impl Engine { let args = &mut [lhs_ptr_inner, &mut new_val]; match self.call_native_fn(mods, state, lib, op, hash, args, true, true, op_pos) { - Err(err) if matches!(*err, EvalAltResult::ErrorFunctionNotFound(ref f, _) if f.starts_with(op)) => + Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, _) if f.starts_with(op)) => { // Expand to `var = var op rhs` let op = &op[..op.len() - 1]; // extract operator without = @@ -2638,11 +2623,7 @@ impl Engine { let var_name = lhs_expr.get_variable_name(false).expect("`Variable`"); if !lhs_ptr.is_ref() { - return Err(EvalAltResult::ErrorAssignmentToConstant( - var_name.to_string(), - pos, - ) - .into()); + return Err(ERR::ErrorAssignmentToConstant(var_name.to_string(), pos).into()); } #[cfg(not(feature = "unchecked"))] @@ -2829,8 +2810,8 @@ impl Engine { { Ok(_) => (), Err(err) => match *err { - EvalAltResult::LoopBreak(false, _) => (), - EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), + ERR::LoopBreak(false, _) => (), + ERR::LoopBreak(true, _) => return Ok(Dynamic::UNIT), _ => return Err(err), }, } @@ -2856,8 +2837,8 @@ impl Engine { { Ok(_) => (), Err(err) => match *err { - EvalAltResult::LoopBreak(false, _) => (), - EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), + ERR::LoopBreak(false, _) => (), + ERR::LoopBreak(true, _) => return Ok(Dynamic::UNIT), _ => return Err(err), }, } @@ -2874,8 +2855,8 @@ impl Engine { { Ok(_) => (), Err(err) => match *err { - EvalAltResult::LoopBreak(false, _) => continue, - EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), + ERR::LoopBreak(false, _) => continue, + ERR::LoopBreak(true, _) => return Ok(Dynamic::UNIT), _ => return Err(err), }, } @@ -2932,7 +2913,7 @@ impl Engine { if let Some(c) = counter_index { #[cfg(not(feature = "unchecked"))] if x > INT::MAX as usize { - return Err(EvalAltResult::ErrorArithmetic( + return Err(ERR::ErrorArithmetic( format!("for-loop counter overflow: {}", x), counter.as_ref().expect("`Some`").pos, ) @@ -2975,8 +2956,8 @@ impl Engine { match result { Ok(_) => (), Err(err) => match *err { - EvalAltResult::LoopBreak(false, _) => (), - EvalAltResult::LoopBreak(true, _) => break, + ERR::LoopBreak(false, _) => (), + ERR::LoopBreak(true, _) => break, _ => return Err(err), }, } @@ -2985,13 +2966,13 @@ impl Engine { scope.rewind(orig_scope_len); Ok(Dynamic::UNIT) } else { - Err(EvalAltResult::ErrorFor(expr.position()).into()) + Err(ERR::ErrorFor(expr.position()).into()) } } // Continue/Break statement Stmt::BreakLoop(options, pos) => { - Err(EvalAltResult::LoopBreak(options.contains(AST_OPTION_BREAK_OUT), *pos).into()) + Err(ERR::LoopBreak(options.contains(AST_OPTION_BREAK_OUT), *pos).into()) } // Namespace-qualified function call @@ -3044,7 +3025,7 @@ impl Engine { Err(err) if !err.is_catchable() => Err(err), Err(mut err) => { let err_value = match *err { - EvalAltResult::ErrorRuntime(ref x, _) => x.clone(), + ERR::ErrorRuntime(ref x, _) => x.clone(), #[cfg(feature = "no_object")] _ => { @@ -3096,7 +3077,7 @@ impl Engine { Ok(_) => Ok(Dynamic::UNIT), Err(result_err) => match *result_err { // Re-throw exception - EvalAltResult::ErrorRuntime(Dynamic(Union::Unit(_, _, _)), pos) => { + ERR::ErrorRuntime(Dynamic(Union::Unit(_, _, _)), pos) => { err.set_position(pos); Err(err) } @@ -3109,7 +3090,7 @@ impl Engine { // Throw value Stmt::Return(options, Some(expr), pos) if options.contains(AST_OPTION_BREAK_OUT) => { - Err(EvalAltResult::ErrorRuntime( + Err(ERR::ErrorRuntime( self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .flatten(), *pos, @@ -3119,11 +3100,11 @@ impl Engine { // Empty throw Stmt::Return(options, None, pos) if options.contains(AST_OPTION_BREAK_OUT) => { - Err(EvalAltResult::ErrorRuntime(Dynamic::UNIT, *pos).into()) + Err(ERR::ErrorRuntime(Dynamic::UNIT, *pos).into()) } // Return value - Stmt::Return(_, Some(expr), pos) => Err(EvalAltResult::Return( + Stmt::Return(_, Some(expr), pos) => Err(ERR::Return( self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .flatten(), *pos, @@ -3131,7 +3112,7 @@ impl Engine { .into()), // Empty return - Stmt::Return(_, None, pos) => Err(EvalAltResult::Return(Dynamic::UNIT, *pos).into()), + Stmt::Return(_, None, pos) => Err(ERR::Return(Dynamic::UNIT, *pos).into()), // Let/const statement Stmt::Var(expr, x, options, _) => { @@ -3178,7 +3159,7 @@ impl Engine { // Guard against too many modules #[cfg(not(feature = "unchecked"))] if mods.num_modules >= self.max_modules() { - return Err(EvalAltResult::ErrorTooManyModules(*_pos).into()); + return Err(ERR::ErrorTooManyModules(*_pos).into()); } if let Some(path) = self @@ -3194,11 +3175,7 @@ impl Engine { .embedded_module_resolver .as_ref() .and_then(|r| match r.resolve(self, source, &path, path_pos) { - Err(err) - if matches!(*err, EvalAltResult::ErrorModuleNotFound(_, _)) => - { - None - } + Err(err) if matches!(*err, ERR::ErrorModuleNotFound(_, _)) => None, result => Some(result), }) .or_else(|| { @@ -3207,10 +3184,7 @@ impl Engine { .map(|r| r.resolve(self, source, &path, path_pos)) }) .unwrap_or_else(|| { - Err( - EvalAltResult::ErrorModuleNotFound(path.to_string(), path_pos) - .into(), - ) + Err(ERR::ErrorModuleNotFound(path.to_string(), path_pos).into()) })?; if let Some(name) = export.as_ref().map(|x| x.name.clone()) { @@ -3245,7 +3219,7 @@ impl Engine { ); Ok(()) as RhaiResultOf<_> } else { - Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), *pos).into()) + Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into()) } }, )?; @@ -3380,11 +3354,9 @@ impl Engine { .max_string_size .map_or(usize::MAX, NonZeroUsize::get) { - return Err(EvalAltResult::ErrorDataTooLarge( - "Length of string".to_string(), - Position::NONE, - ) - .into()); + return Err( + ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(), + ); } #[cfg(not(feature = "no_index"))] @@ -3394,11 +3366,7 @@ impl Engine { .max_array_size .map_or(usize::MAX, NonZeroUsize::get) { - return Err(EvalAltResult::ErrorDataTooLarge( - "Size of array".to_string(), - Position::NONE, - ) - .into()); + return Err(ERR::ErrorDataTooLarge("Size of array".to_string(), Position::NONE).into()); } #[cfg(not(feature = "no_object"))] @@ -3408,11 +3376,9 @@ impl Engine { .max_map_size .map_or(usize::MAX, NonZeroUsize::get) { - return Err(EvalAltResult::ErrorDataTooLarge( - "Size of object map".to_string(), - Position::NONE, - ) - .into()); + return Err( + ERR::ErrorDataTooLarge("Size of object map".to_string(), Position::NONE).into(), + ); } Ok(()) @@ -3429,14 +3395,14 @@ impl Engine { // Guard against too many operations if self.max_operations() > 0 && *num_operations > self.max_operations() { - return Err(EvalAltResult::ErrorTooManyOperations(pos).into()); + return Err(ERR::ErrorTooManyOperations(pos).into()); } // Report progress - only in steps if let Some(ref progress) = self.progress { if let Some(token) = progress(*num_operations) { // Terminate script if progress returns a termination token - return Err(EvalAltResult::ErrorTerminated(token, pos).into()); + return Err(ERR::ErrorTerminated(token, pos).into()); } } @@ -3456,15 +3422,11 @@ impl Engine { .unwrap_or_else(|| map_std_type_name(name)) } - /// Make a `Box<`[`EvalAltResult`][EvalAltResult::ErrorMismatchDataType]`>`. + /// Make a `Box<`[`EvalAltResult`][ERR::ErrorMismatchDataType]`>`. #[inline] #[must_use] pub(crate) fn make_type_mismatch_err(&self, typ: &str, pos: Position) -> RhaiError { - EvalAltResult::ErrorMismatchDataType( - self.map_type_name(type_name::()).into(), - typ.into(), - pos, - ) - .into() + ERR::ErrorMismatchDataType(self.map_type_name(type_name::()).into(), typ.into(), pos) + .into() } } diff --git a/src/func/call.rs b/src/func/call.rs index 958a6ec1..a6c44e42 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -12,8 +12,8 @@ use crate::engine::{ use crate::module::Namespace; use crate::tokenizer::Token; use crate::{ - calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, EvalAltResult, FnPtr, - Identifier, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, + calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Identifier, + ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -114,7 +114,7 @@ pub fn ensure_no_data_race( .skip(if is_method_call { 1 } else { 0 }) .find(|(_, a)| a.is_locked()) { - return Err(EvalAltResult::ErrorDataRace( + return Err(ERR::ErrorDataRace( format!("argument #{} of function '{}'", n + 1, fn_name.as_ref()), Position::NONE, ) @@ -195,7 +195,7 @@ impl Engine { let max_bitmask = if !allow_dynamic { 0 } else { - 1usize << num_args.min(MAX_DYNAMIC_PARAMETERS) + 1usize << usize::min(num_args, MAX_DYNAMIC_PARAMETERS) }; let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic` @@ -369,7 +369,7 @@ impl Engine { KEYWORD_PRINT => { if let Some(ref print) = self.print { let text = result.into_immutable_string().map_err(|typ| { - EvalAltResult::ErrorMismatchOutputType( + ERR::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), pos, @@ -383,7 +383,7 @@ impl Engine { KEYWORD_DEBUG => { if let Some(ref debug) = self.debug { let text = result.into_immutable_string().map_err(|typ| { - EvalAltResult::ErrorMismatchOutputType( + ERR::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), pos, @@ -407,7 +407,7 @@ impl Engine { crate::engine::FN_IDX_GET => { assert!(args.len() == 2); - Err(EvalAltResult::ErrorIndexingType( + Err(ERR::ErrorIndexingType( format!( "{} [{}]", self.map_type_name(args[0].type_name()), @@ -423,7 +423,7 @@ impl Engine { crate::engine::FN_IDX_SET => { assert!(args.len() == 3); - Err(EvalAltResult::ErrorIndexingType( + Err(ERR::ErrorIndexingType( format!( "{} [{}] = {}", self.map_type_name(args[0].type_name()), @@ -440,7 +440,7 @@ impl Engine { _ if name.starts_with(crate::engine::FN_GET) => { assert!(args.len() == 1); - Err(EvalAltResult::ErrorDotExpr( + Err(ERR::ErrorDotExpr( format!( "Unknown property '{}' - a getter is not registered for type '{}'", &name[crate::engine::FN_GET.len()..], @@ -456,7 +456,7 @@ impl Engine { _ if name.starts_with(crate::engine::FN_SET) => { assert!(args.len() == 2); - Err(EvalAltResult::ErrorDotExpr( + Err(ERR::ErrorDotExpr( format!( "No writable property '{}' - a setter is not registered for type '{}' to handle '{}'", &name[crate::engine::FN_SET.len()..], @@ -469,11 +469,9 @@ impl Engine { } // Raise error - _ => Err(EvalAltResult::ErrorFunctionNotFound( - self.gen_call_signature(None, name, args), - pos, - ) - .into()), + _ => Err( + ERR::ErrorFunctionNotFound(self.gen_call_signature(None, name, args), pos).into(), + ), } } @@ -501,7 +499,7 @@ impl Engine { ) -> RhaiResultOf<(Dynamic, bool)> { fn no_method_err(name: &str, pos: Position) -> RhaiResultOf<(Dynamic, bool)> { let msg = format!("'{0}' should not be called this way. Try {0}(...);", name); - Err(EvalAltResult::ErrorRuntime(msg.into(), pos).into()) + Err(ERR::ErrorRuntime(msg.into(), pos).into()) } let fn_name = fn_name.as_ref(); @@ -669,8 +667,8 @@ impl Engine { scope, mods, state, lib, &mut None, statements, false, false, level, ) .or_else(|err| match *err { - EvalAltResult::Return(out, _) => Ok(out), - EvalAltResult::LoopBreak(_, _) => { + ERR::Return(out, _) => Ok(out), + ERR::LoopBreak(_, _) => { unreachable!("no outer loop scope to break out of") } _ => Err(err), @@ -707,7 +705,7 @@ impl Engine { // If new functions are defined within the eval string, it is an error #[cfg(not(feature = "no_function"))] if !ast.shared_lib().is_empty() { - return Err(crate::ParseErrorType::WrongFnDefinition.into()); + return Err(crate::PERR::WrongFnDefinition.into()); } let statements = ast.statements(); @@ -1071,7 +1069,7 @@ impl Engine { } return result.map_err(|err| { - EvalAltResult::ErrorInFunctionCall( + ERR::ErrorInFunctionCall( KEYWORD_EVAL.to_string(), mods.source .as_ref() @@ -1241,9 +1239,9 @@ impl Engine { } } - let module = self.search_imports(mods, state, namespace).ok_or_else(|| { - EvalAltResult::ErrorModuleNotFound(namespace.to_string(), namespace[0].pos) - })?; + let module = self + .search_imports(mods, state, namespace) + .ok_or_else(|| ERR::ErrorModuleNotFound(namespace.to_string(), namespace[0].pos))?; // First search in script-defined functions (can override built-in) let func = match module.get_qualified_fn(hash) { @@ -1310,7 +1308,7 @@ impl Engine { Some(f) => unreachable!("unknown function type: {:?}", f), - None => Err(EvalAltResult::ErrorFunctionNotFound( + None => Err(ERR::ErrorFunctionNotFound( self.gen_call_signature(Some(namespace), fn_name, &args), pos, ) diff --git a/src/func/native.rs b/src/func/native.rs index 5c9bf5e6..8251516f 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -7,8 +7,8 @@ use crate::plugin::PluginFunction; use crate::tokenizer::{Token, TokenizeState}; use crate::types::dynamic::Variant; use crate::{ - calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, FuncArgs, Module, Position, - RhaiResult, RhaiResultOf, StaticVec, + calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Module, Position, RhaiResult, + RhaiResultOf, StaticVec, ERR, }; use std::any::type_name; #[cfg(feature = "no_std")] @@ -244,7 +244,7 @@ impl<'a> NativeCallContext<'a> { let typ = self.engine().map_type_name(result.type_name()); result.try_cast().ok_or_else(|| { - EvalAltResult::ErrorMismatchOutputType( + ERR::ErrorMismatchOutputType( self.engine().map_type_name(type_name::()).into(), typ.into(), Position::NONE, diff --git a/src/func/register.rs b/src/func/register.rs index 2a8a212c..7a298f6f 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -8,7 +8,7 @@ use super::native::{FnAny, SendSync}; use crate::r#unsafe::unsafe_try_cast; use crate::tokenizer::Position; use crate::types::dynamic::{DynamicWriteLock, Variant}; -use crate::{Dynamic, EvalAltResult, NativeCallContext, RhaiResultOf}; +use crate::{Dynamic, ERR, NativeCallContext, RhaiResultOf}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{any::TypeId, mem}; @@ -124,7 +124,7 @@ macro_rules! def_register { #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) { - return Err(EvalAltResult::ErrorAssignmentToConstant(String::new(), Position::NONE).into()); + return Err(ERR::ErrorAssignmentToConstant(String::new(), Position::NONE).into()); } // The arguments are assumed to be of the correct number and types! @@ -152,7 +152,7 @@ macro_rules! def_register { #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) { - return Err(EvalAltResult::ErrorAssignmentToConstant(String::new(), Position::NONE).into()); + return Err(ERR::ErrorAssignmentToConstant(String::new(), Position::NONE).into()); } // The arguments are assumed to be of the correct number and types! @@ -180,7 +180,7 @@ macro_rules! def_register { #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) { - return Err(EvalAltResult::ErrorAssignmentToConstant(String::new(), Position::NONE).into()); + return Err(ERR::ErrorAssignmentToConstant(String::new(), Position::NONE).into()); } // The arguments are assumed to be of the correct number and types! @@ -205,7 +205,7 @@ macro_rules! def_register { #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) { - return Err(EvalAltResult::ErrorAssignmentToConstant(String::new(), Position::NONE).into()); + return Err(ERR::ErrorAssignmentToConstant(String::new(), Position::NONE).into()); } // The arguments are assumed to be of the correct number and types! diff --git a/src/func/script.rs b/src/func/script.rs index 56326e64..691e13d1 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -5,9 +5,7 @@ use super::call::FnCallArgs; use crate::ast::ScriptFnDef; use crate::engine::{EvalState, Imports}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; -use crate::{ - Dynamic, Engine, EvalAltResult, Module, Position, RhaiError, RhaiResult, Scope, StaticVec, -}; +use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, StaticVec, ERR}; use std::mem; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -44,7 +42,7 @@ impl Engine { err: RhaiError, pos: Position, ) -> RhaiResult { - Err(EvalAltResult::ErrorInFunctionCall( + Err(ERR::ErrorInFunctionCall( name, fn_def .lib @@ -70,7 +68,7 @@ impl Engine { // Check for stack overflow #[cfg(not(feature = "unchecked"))] if level > self.max_call_levels() { - return Err(EvalAltResult::ErrorStackOverflow(pos).into()); + return Err(ERR::ErrorStackOverflow(pos).into()); } let orig_scope_len = scope.len(); @@ -131,9 +129,9 @@ impl Engine { ) .or_else(|err| match *err { // Convert return statement to return value - EvalAltResult::Return(x, _) => Ok(x), + ERR::Return(x, _) => Ok(x), // Error in sub function call - EvalAltResult::ErrorInFunctionCall(name, src, err, _) => { + ERR::ErrorInFunctionCall(name, src, err, _) => { let fn_name = if src.is_empty() { format!("{} < {}", name, fn_def.name) } else { diff --git a/src/lib.rs b/src/lib.rs index 6bf9da8f..bea085e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,8 +82,12 @@ mod tokenizer; mod types; mod r#unsafe; +/// Error encountered when parsing a script. +type PERR = ParseErrorType; +/// Evaluation result. +type ERR = EvalAltResult; /// General evaluation error for Rhai scripts. -type RhaiError = Box; +type RhaiError = Box; /// Generic [`Result`] type for Rhai functions. type RhaiResultOf = Result; /// General [`Result`] type for Rhai functions returning [`Dynamic`] values. diff --git a/src/module/mod.rs b/src/module/mod.rs index db55124d..97481c74 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -458,7 +458,7 @@ impl Module { #[inline] pub(crate) fn get_qualified_var(&self, hash_var: u64) -> RhaiResultOf<&Dynamic> { self.all_variables.get(&hash_var).ok_or_else(|| { - crate::EvalAltResult::ErrorVariableNotFound(String::new(), crate::Position::NONE).into() + crate::ERR::ErrorVariableNotFound(String::new(), crate::Position::NONE).into() }) } diff --git a/src/module/resolvers/collection.rs b/src/module/resolvers/collection.rs index 14b4da57..444c0c72 100644 --- a/src/module/resolvers/collection.rs +++ b/src/module/resolvers/collection.rs @@ -1,4 +1,4 @@ -use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, RhaiResultOf, Shared}; +use crate::{Engine,ERR, Module, ModuleResolver, Position, RhaiResultOf, Shared}; use std::ops::AddAssign; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -128,14 +128,14 @@ impl ModuleResolver for ModuleResolversCollection { match resolver.resolve(engine, source_path, path, pos) { Ok(module) => return Ok(module), Err(err) => match *err { - EvalAltResult::ErrorModuleNotFound(_, _) => continue, - EvalAltResult::ErrorInModule(_, err, _) => return Err(err), + ERR::ErrorModuleNotFound(_, _) => continue, + ERR::ErrorInModule(_, err, _) => return Err(err), _ => panic!("ModuleResolver::resolve returns error that is not ErrorModuleNotFound or ErrorInModule"), }, } } - Err(EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()) + Err(ERR::ErrorModuleNotFound(path.into(), pos).into()) } } diff --git a/src/module/resolvers/dummy.rs b/src/module/resolvers/dummy.rs index c183e1a6..e67abd71 100644 --- a/src/module/resolvers/dummy.rs +++ b/src/module/resolvers/dummy.rs @@ -1,4 +1,4 @@ -use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, RhaiResultOf, Shared}; +use crate::{Engine,ERR, Module, ModuleResolver, Position, RhaiResultOf, Shared}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -45,6 +45,6 @@ impl ModuleResolver for DummyModuleResolver { path: &str, pos: Position, ) -> RhaiResultOf> { - Err(EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()) + Err(ERR::ErrorModuleNotFound(path.into(), pos).into()) } } diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index 27269157..b0e374fd 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -3,7 +3,7 @@ use crate::func::native::shared_write_lock; use crate::{ - Engine, EvalAltResult, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Scope, + Engine,ERR, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared, }; @@ -288,17 +288,17 @@ impl ModuleResolver for FileModuleResolver { let mut ast = engine .compile_file(file_path.clone()) .map_err(|err| match *err { - EvalAltResult::ErrorSystem(_, err) if err.is::() => { - Box::new(EvalAltResult::ErrorModuleNotFound(path.to_string(), pos)) + ERR::ErrorSystem(_, err) if err.is::() => { + Box::new(ERR::ErrorModuleNotFound(path.to_string(), pos)) } - _ => Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)), + _ => Box::new(ERR::ErrorInModule(path.to_string(), err, pos)), })?; ast.set_source(path); // Make a module from the AST let m: Shared = Module::eval_ast_as_new(scope, &ast, engine) - .map_err(|err| Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)))? + .map_err(|err| Box::new(ERR::ErrorInModule(path.to_string(), err, pos)))? .into(); // Put it into the cache @@ -331,10 +331,10 @@ impl ModuleResolver for FileModuleResolver { ast }) .map_err(|err| match *err { - EvalAltResult::ErrorSystem(_, err) if err.is::() => { - EvalAltResult::ErrorModuleNotFound(path.to_string(), pos).into() + ERR::ErrorSystem(_, err) if err.is::() => { + ERR::ErrorModuleNotFound(path.to_string(), pos).into() } - _ => EvalAltResult::ErrorInModule(path.to_string(), err, pos).into(), + _ => ERR::ErrorInModule(path.to_string(), err, pos).into(), }), ) } diff --git a/src/module/resolvers/stat.rs b/src/module/resolvers/stat.rs index 278a7c00..dc25c563 100644 --- a/src/module/resolvers/stat.rs +++ b/src/module/resolvers/stat.rs @@ -1,6 +1,5 @@ use crate::{ - Engine, EvalAltResult, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Shared, - SmartString, + Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Shared, SmartString, ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -136,7 +135,7 @@ impl ModuleResolver for StaticModuleResolver { self.0 .get(path) .cloned() - .ok_or_else(|| EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()) + .ok_or_else(|| ERR::ErrorModuleNotFound(path.into(), pos).into()) } } diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index 58338391..72f441f7 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use crate::plugin::*; -use crate::{def_package, EvalAltResult, Position, RhaiError, RhaiResultOf, INT}; +use crate::{def_package, Position, RhaiError, RhaiResultOf, ERR, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -11,7 +11,7 @@ use num_traits::Float; #[inline(never)] pub fn make_err(msg: impl Into) -> RhaiError { - EvalAltResult::ErrorArithmetic(msg.into(), Position::NONE).into() + ERR::ErrorArithmetic(msg.into(), Position::NONE).into() } macro_rules! gen_arithmetic_functions { diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index aa728088..7e61dce8 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -4,8 +4,8 @@ use crate::engine::OP_EQUALS; use crate::plugin::*; use crate::{ - def_package, Array, Dynamic, EvalAltResult, ExclusiveRange, FnPtr, InclusiveRange, - NativeCallContext, Position, RhaiResultOf, INT, + def_package, Array, Dynamic, ExclusiveRange, FnPtr, InclusiveRange, NativeCallContext, + Position, RhaiResultOf, ERR, INT, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -90,11 +90,7 @@ pub mod array_functions { // Check if array will be over max size limit #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_array_size() > 0 && (len as usize) > _ctx.engine().max_array_size() { - return Err(EvalAltResult::ErrorDataTooLarge( - "Size of array".to_string(), - Position::NONE, - ) - .into()); + return Err(ERR::ErrorDataTooLarge("Size of array".to_string(), Position::NONE).into()); } if len as usize > array.len() { @@ -174,7 +170,7 @@ pub mod array_functions { let arr_len = array.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| arr_len - usize::min(n as usize, arr_len)) } else if start as usize >= array.len() { array.extend(replace); return; @@ -213,7 +209,7 @@ pub mod array_functions { let arr_len = array.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| arr_len - usize::min(n as usize, arr_len)) } else if start as usize >= array.len() { return Array::new(); } else { @@ -275,7 +271,7 @@ pub mod array_functions { mapper .call_raw(&ctx, None, [item.clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(fn_sig, _) + ERR::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(mapper.fn_name()) => { mapper.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) @@ -283,7 +279,7 @@ pub mod array_functions { _ => Err(err), }) .map_err(|err| { - Box::new(EvalAltResult::ErrorInFunctionCall( + Box::new(ERR::ErrorInFunctionCall( "map".to_string(), ctx.source().unwrap_or("").to_string(), err, @@ -316,7 +312,7 @@ pub mod array_functions { if filter .call_raw(&ctx, None, [item.clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(fn_sig, _) + ERR::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) @@ -324,7 +320,7 @@ pub mod array_functions { _ => Err(err), }) .map_err(|err| { - Box::new(EvalAltResult::ErrorInFunctionCall( + Box::new(ERR::ErrorInFunctionCall( "filter".to_string(), ctx.source().unwrap_or("").to_string(), err, @@ -362,9 +358,7 @@ pub mod array_functions { if ctx .call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(ref fn_sig, _) - if fn_sig.starts_with(OP_EQUALS) => - { + ERR::ErrorFunctionNotFound(ref fn_sig, _) if fn_sig.starts_with(OP_EQUALS) => { if item.type_id() == value.type_id() { // No default when comparing same type Err(err) @@ -410,7 +404,7 @@ pub mod array_functions { let arr_len = array.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| arr_len - usize::min(n as usize, arr_len)) } else if start as usize >= array.len() { return Ok(-1); } else { @@ -421,9 +415,7 @@ pub mod array_functions { if ctx .call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(ref fn_sig, _) - if fn_sig.starts_with(OP_EQUALS) => - { + ERR::ErrorFunctionNotFound(ref fn_sig, _) if fn_sig.starts_with(OP_EQUALS) => { if item.type_id() == value.type_id() { // No default when comparing same type Err(err) @@ -477,7 +469,7 @@ pub mod array_functions { let arr_len = array.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| arr_len - usize::min(n as usize, arr_len)) } else if start as usize >= array.len() { return Ok(-1); } else { @@ -488,7 +480,7 @@ pub mod array_functions { if filter .call_raw(&ctx, None, [item.clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(fn_sig, _) + ERR::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) @@ -496,7 +488,7 @@ pub mod array_functions { _ => Err(err), }) .map_err(|err| { - Box::new(EvalAltResult::ErrorInFunctionCall( + Box::new(ERR::ErrorInFunctionCall( "index_of".to_string(), ctx.source().unwrap_or("").to_string(), err, @@ -531,7 +523,7 @@ pub mod array_functions { if filter .call_raw(&ctx, None, [item.clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(fn_sig, _) + ERR::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) @@ -539,7 +531,7 @@ pub mod array_functions { _ => Err(err), }) .map_err(|err| { - Box::new(EvalAltResult::ErrorInFunctionCall( + Box::new(ERR::ErrorInFunctionCall( "some".to_string(), ctx.source().unwrap_or("").to_string(), err, @@ -573,7 +565,7 @@ pub mod array_functions { if !filter .call_raw(&ctx, None, [item.clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(fn_sig, _) + ERR::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) @@ -581,7 +573,7 @@ pub mod array_functions { _ => Err(err), }) .map_err(|err| { - Box::new(EvalAltResult::ErrorInFunctionCall( + Box::new(ERR::ErrorInFunctionCall( "all".to_string(), ctx.source().unwrap_or("").to_string(), err, @@ -668,7 +660,7 @@ pub mod array_functions { result = reducer .call_raw(&ctx, None, [result.clone(), item.clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(fn_sig, _) + ERR::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(reducer.fn_name()) => { reducer.call_raw(&ctx, None, [result, item, (i as INT).into()]) @@ -676,7 +668,7 @@ pub mod array_functions { _ => Err(err), }) .map_err(|err| { - Box::new(EvalAltResult::ErrorInFunctionCall( + Box::new(ERR::ErrorInFunctionCall( "reduce".to_string(), ctx.source().unwrap_or("").to_string(), err, @@ -728,7 +720,7 @@ pub mod array_functions { result = reducer .call_raw(&ctx, None, [result.clone(), item.clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(fn_sig, _) + ERR::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(reducer.fn_name()) => { reducer.call_raw(&ctx, None, [result, item, ((len - 1 - i) as INT).into()]) @@ -736,7 +728,7 @@ pub mod array_functions { _ => Err(err), }) .map_err(|err| { - Box::new(EvalAltResult::ErrorInFunctionCall( + Box::new(ERR::ErrorInFunctionCall( "reduce_rev".to_string(), ctx.source().unwrap_or("").to_string(), err, @@ -795,7 +787,7 @@ pub mod array_functions { let type_id = array[0].type_id(); if array.iter().any(|a| a.type_id() != type_id) { - return Err(EvalAltResult::ErrorFunctionNotFound( + return Err(ERR::ErrorFunctionNotFound( "sort() cannot be called with elements of different types".into(), Position::NONE, ) @@ -873,7 +865,7 @@ pub mod array_functions { if filter .call_raw(&ctx, None, [array[x].clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(fn_sig, _) + ERR::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()]) @@ -881,7 +873,7 @@ pub mod array_functions { _ => Err(err), }) .map_err(|err| { - Box::new(EvalAltResult::ErrorInFunctionCall( + Box::new(ERR::ErrorInFunctionCall( "drain".to_string(), ctx.source().unwrap_or("").to_string(), err, @@ -931,7 +923,7 @@ pub mod array_functions { let arr_len = array.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| arr_len - usize::min(n as usize, arr_len)) } else if start as usize >= array.len() { return Array::new(); } else { @@ -963,7 +955,7 @@ pub mod array_functions { if !filter .call_raw(&ctx, None, [array[x].clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(fn_sig, _) + ERR::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()]) @@ -971,7 +963,7 @@ pub mod array_functions { _ => Err(err), }) .map_err(|err| { - Box::new(EvalAltResult::ErrorInFunctionCall( + Box::new(ERR::ErrorInFunctionCall( "retain".to_string(), ctx.source().unwrap_or("").to_string(), err, @@ -1021,7 +1013,7 @@ pub mod array_functions { let arr_len = array.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| arr_len - usize::min(n as usize, arr_len)) } else if start as usize >= array.len() { return mem::take(array); } else { @@ -1056,9 +1048,7 @@ pub mod array_functions { if !ctx .call_fn_raw(OP_EQUALS, true, false, &mut [a1, a2]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(ref fn_sig, _) - if fn_sig.starts_with(OP_EQUALS) => - { + ERR::ErrorFunctionNotFound(ref fn_sig, _) if fn_sig.starts_with(OP_EQUALS) => { if a1.type_id() == a2.type_id() { // No default when comparing same type Err(err) diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index 1a03eae5..b6c2d894 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -3,8 +3,8 @@ use crate::plugin::*; use crate::{ - def_package, Blob, Dynamic, EvalAltResult, ExclusiveRange, InclusiveRange, NativeCallContext, - Position, RhaiResultOf, INT, + def_package, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position, + RhaiResultOf, ERR, INT, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -46,11 +46,7 @@ pub mod blob_functions { // Check if blob will be over max size limit #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { - return Err(EvalAltResult::ErrorDataTooLarge( - "Size of BLOB".to_string(), - Position::NONE, - ) - .into()); + return Err(ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into()); } let mut blob = Blob::new(); @@ -121,11 +117,7 @@ pub mod blob_functions { // Check if blob will be over max size limit #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_array_size() > 0 && (len as usize) > _ctx.engine().max_array_size() { - return Err(EvalAltResult::ErrorDataTooLarge( - "Size of BLOB".to_string(), - Position::NONE, - ) - .into()); + return Err(ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into()); } if len as usize > blob.len() { @@ -205,7 +197,7 @@ pub mod blob_functions { let start = if start < 0 { start .checked_abs() - .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + .map_or(0, |n| blob_len - usize::min(n as usize, blob_len)) } else if start as usize >= blob_len { blob.extend(replace); return; @@ -244,7 +236,7 @@ pub mod blob_functions { let start = if start < 0 { start .checked_abs() - .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + .map_or(0, |n| blob_len - usize::min(n as usize, blob_len)) } else if start as usize >= blob_len { return Blob::new(); } else { @@ -308,7 +300,7 @@ pub mod blob_functions { let start = if start < 0 { start .checked_abs() - .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + .map_or(0, |n| blob_len - usize::min(n as usize, blob_len)) } else if start as usize >= blob_len { return Blob::new(); } else { @@ -344,7 +336,7 @@ pub mod blob_functions { let start = if start < 0 { start .checked_abs() - .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + .map_or(0, |n| blob_len - usize::min(n as usize, blob_len)) } else if start as usize >= blob_len { return mem::take(blob); } else { @@ -373,7 +365,7 @@ pub mod blob_functions { let start = if start < 0 { start .checked_abs() - .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + .map_or(0, |n| blob_len - usize::min(n as usize, blob_len)) } else if start as usize >= blob_len { return 0; } else { @@ -442,7 +434,7 @@ pub mod blob_functions { let start = if start < 0 { start .checked_abs() - .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + .map_or(0, |n| blob_len - usize::min(n as usize, blob_len)) } else if start as usize >= blob_len { return; } else { @@ -511,7 +503,7 @@ pub mod blob_functions { let start = if start < 0 { start .checked_abs() - .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + .map_or(0, |n| blob_len - usize::min(n as usize, blob_len)) } else if start as usize >= blob_len { return 0.0; } else { @@ -587,7 +579,7 @@ pub mod blob_functions { let start = if start < 0 { start .checked_abs() - .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + .map_or(0, |n| blob_len - usize::min(n as usize, blob_len)) } else if start as usize >= blob_len { return; } else { @@ -659,7 +651,7 @@ pub mod blob_functions { let start = if start < 0 { start .checked_abs() - .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + .map_or(0, |n| blob_len - usize::min(n as usize, blob_len)) } else if start as usize >= blob_len { return; } else { diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index d6be661f..ce1c3345 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -1,6 +1,6 @@ use crate::plugin::*; use crate::types::dynamic::Variant; -use crate::{def_package, EvalAltResult, ExclusiveRange, InclusiveRange, RhaiResultOf, INT}; +use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, ERR, INT}; use std::iter::{ExactSizeIterator, FusedIterator}; use std::ops::{Range, RangeInclusive}; #[cfg(feature = "no_std")] @@ -26,10 +26,10 @@ where #[cfg(not(feature = "unchecked"))] if let Some(r) = from.checked_add(&step) { if r == from { - return Err(EvalAltResult::ErrorInFunctionCall( + return Err(ERR::ErrorInFunctionCall( "range".to_string(), String::new(), - EvalAltResult::ErrorArithmetic( + ERR::ErrorArithmetic( "step value cannot be zero".to_string(), crate::Position::NONE, ) @@ -123,27 +123,18 @@ impl BitRange { #[cfg(not(feature = "unchecked"))] if offset >= BITS { - return Err( - EvalAltResult::ErrorBitFieldBounds(BITS, from, crate::Position::NONE).into(), - ); + return Err(ERR::ErrorBitFieldBounds(BITS, from, crate::Position::NONE).into()); } offset } else { #[cfg(not(feature = "unchecked"))] if let Some(abs_from) = from.checked_abs() { if (abs_from as usize) > BITS { - return Err(EvalAltResult::ErrorBitFieldBounds( - BITS, - from, - crate::Position::NONE, - ) - .into()); + return Err(ERR::ErrorBitFieldBounds(BITS, from, crate::Position::NONE).into()); } BITS - (abs_from as usize) } else { - return Err( - EvalAltResult::ErrorBitFieldBounds(BITS, from, crate::Position::NONE).into(), - ); + return Err(ERR::ErrorBitFieldBounds(BITS, from, crate::Position::NONE).into()); } #[cfg(feature = "unchecked")] @@ -337,8 +328,8 @@ def_package! { pub fn new(from: FLOAT, to: FLOAT, step: FLOAT) -> RhaiResultOf { #[cfg(not(feature = "unchecked"))] if step == 0.0 { - return Err(EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), - EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(), + return Err(ERR::ErrorInFunctionCall("range".to_string(), "".to_string(), + ERR::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(), crate::Position::NONE, ).into()); } @@ -399,8 +390,8 @@ def_package! { pub fn new(from: Decimal, to: Decimal, step: Decimal) -> RhaiResultOf { #[cfg(not(feature = "unchecked"))] if step.is_zero() { - return Err(EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), - EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(), + return Err(ERR::ErrorInFunctionCall("range".to_string(), "".to_string(), + ERR::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(), crate::Position::NONE, ).into()); } diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index ff9c7d87..400b1332 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -1,7 +1,7 @@ use crate::def_package; use crate::plugin::*; use crate::types::dynamic::Tag; -use crate::{Dynamic, EvalAltResult, RhaiResultOf, INT}; +use crate::{Dynamic, RhaiResultOf, ERR, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -23,7 +23,7 @@ mod core_functions { #[rhai_fn(name = "set_tag", set = "tag", return_raw)] pub fn set_tag(value: &mut Dynamic, tag: INT) -> RhaiResultOf<()> { if tag < Tag::MIN as INT { - Err(EvalAltResult::ErrorArithmetic( + Err(ERR::ErrorArithmetic( format!( "{} is too small to fit into a tag (must be between {} and {})", tag, @@ -34,7 +34,7 @@ mod core_functions { ) .into()) } else if tag > Tag::MAX as INT { - Err(EvalAltResult::ErrorArithmetic( + Err(ERR::ErrorArithmetic( format!( "{} is too large to fit into a tag (must be between {} and {})", tag, diff --git a/src/packages/logic.rs b/src/packages/logic.rs index 2aad954e..fb87a5f9 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use crate::plugin::*; -use crate::{def_package, EvalAltResult, ExclusiveRange, InclusiveRange, RhaiResultOf, INT}; +use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, ERR, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -207,7 +207,7 @@ mod bit_field_functions { let offset = index as usize; if offset >= BITS { - Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) + Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) } else { Ok((value & (1 << offset)) != 0) } @@ -216,12 +216,12 @@ mod bit_field_functions { // Count from end if negative if offset > BITS { - Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) + Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) } else { Ok((value & (1 << (BITS - offset))) != 0) } } else { - Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) + Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) } } #[rhai_fn(return_raw)] @@ -230,7 +230,7 @@ mod bit_field_functions { let offset = index as usize; if offset >= BITS { - Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) + Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) } else { let mask = 1 << offset; if new_value { @@ -245,7 +245,7 @@ mod bit_field_functions { // Count from end if negative if offset > BITS { - Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) + Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) } else { let mask = 1 << offset; if new_value { @@ -256,7 +256,7 @@ mod bit_field_functions { Ok(()) } } else { - Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) + Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) } } #[rhai_fn(name = "get_bits", return_raw)] @@ -281,7 +281,7 @@ mod bit_field_functions { let offset = index as usize; if offset >= BITS { - return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); + return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); } offset @@ -290,11 +290,11 @@ mod bit_field_functions { // Count from end if negative if offset > BITS { - return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); + return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); } BITS - offset } else { - return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); + return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); }; let bits = if offset + bits as usize > BITS { @@ -343,7 +343,7 @@ mod bit_field_functions { let offset = index as usize; if offset >= BITS { - return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); + return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); } offset @@ -352,11 +352,11 @@ mod bit_field_functions { // Count from end if negative if offset > BITS { - return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); + return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); } BITS - offset } else { - return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); + return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); }; let bits = if offset + bits as usize > BITS { diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 5f81f19d..6b9e3186 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use crate::plugin::*; -use crate::{def_package, Position, RhaiResultOf, INT, INT_BASE}; +use crate::{def_package, Position, RhaiResultOf, ERR, INT, INT_BASE}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -116,7 +116,7 @@ mod int_functions { #[rhai_fn(name = "parse_int", return_raw)] pub fn parse_int_radix(string: &str, radix: INT) -> RhaiResultOf { if !(2..=36).contains(&radix) { - return Err(EvalAltResult::ErrorArithmetic( + return Err(ERR::ErrorArithmetic( format!("Invalid radix: '{}'", radix), Position::NONE, ) @@ -126,7 +126,7 @@ mod int_functions { INT_BASE::from_str_radix(string.trim(), radix as u32) .map(|v| v as INT) .map_err(|err| { - EvalAltResult::ErrorArithmetic( + ERR::ErrorArithmetic( format!("Error parsing integer number '{}': {}", string, err), Position::NONE, ) @@ -265,11 +265,10 @@ mod float_functions { #[rhai_fn(name = "to_int", return_raw)] pub fn f32_to_int(x: f32) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) { - Err(EvalAltResult::ErrorArithmetic( - format!("Integer overflow: to_int({})", x), - Position::NONE, + Err( + ERR::ErrorArithmetic(format!("Integer overflow: to_int({})", x), Position::NONE) + .into(), ) - .into()) } else { Ok(x.trunc() as INT) } @@ -277,11 +276,10 @@ mod float_functions { #[rhai_fn(name = "to_int", return_raw)] pub fn f64_to_int(x: f64) -> RhaiResultOf { if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) { - Err(EvalAltResult::ErrorArithmetic( - format!("Integer overflow: to_int({})", x), - Position::NONE, + Err( + ERR::ErrorArithmetic(format!("Integer overflow: to_int({})", x), Position::NONE) + .into(), ) - .into()) } else { Ok(x.trunc() as INT) } @@ -289,7 +287,7 @@ mod float_functions { #[rhai_fn(return_raw)] pub fn parse_float(string: &str) -> RhaiResultOf { string.trim().parse::().map_err(|err| { - EvalAltResult::ErrorArithmetic( + ERR::ErrorArithmetic( format!("Error parsing floating-point number '{}': {}", string, err), Position::NONE, ) @@ -475,7 +473,7 @@ mod decimal_functions { Decimal::from_str(string) .or_else(|_| Decimal::from_scientific(string)) .map_err(|err| { - EvalAltResult::ErrorArithmetic( + ERR::ErrorArithmetic( format!("Error parsing decimal number '{}': {}", string, err), Position::NONE, ) @@ -487,7 +485,7 @@ mod decimal_functions { #[rhai_fn(name = "to_decimal", return_raw)] pub fn f32_to_decimal(x: f32) -> RhaiResultOf { Decimal::try_from(x).map_err(|_| { - EvalAltResult::ErrorArithmetic( + ERR::ErrorArithmetic( format!("Cannot convert to Decimal: to_decimal({})", x), Position::NONE, ) @@ -498,7 +496,7 @@ mod decimal_functions { #[rhai_fn(name = "to_decimal", return_raw)] pub fn f64_to_decimal(x: f64) -> RhaiResultOf { Decimal::try_from(x).map_err(|_| { - EvalAltResult::ErrorArithmetic( + ERR::ErrorArithmetic( format!("Cannot convert to Decimal: to_decimal({})", x), Position::NONE, ) @@ -509,7 +507,7 @@ mod decimal_functions { #[rhai_fn(return_raw)] pub fn to_float(x: Decimal) -> RhaiResultOf { FLOAT::try_from(x).map_err(|_| { - EvalAltResult::ErrorArithmetic( + ERR::ErrorArithmetic( format!("Cannot convert to floating-point: to_float({})", x), Position::NONE, ) diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index ec1fc793..dfaf047c 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -509,7 +509,7 @@ mod string_functions { // Check if string will be over max size limit #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() { - return Err(crate::EvalAltResult::ErrorDataTooLarge( + return Err(crate::ERR::ErrorDataTooLarge( "Length of string".to_string(), crate::Position::NONE, ) @@ -528,7 +528,7 @@ mod string_functions { #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size() { - return Err(crate::EvalAltResult::ErrorDataTooLarge( + return Err(crate::ERR::ErrorDataTooLarge( "Length of string".to_string(), crate::Position::NONE, ) @@ -553,7 +553,7 @@ mod string_functions { // Check if string will be over max size limit #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() { - return Err(crate::EvalAltResult::ErrorDataTooLarge( + return Err(crate::ERR::ErrorDataTooLarge( "Length of string".to_string(), crate::Position::NONE, ) @@ -579,7 +579,7 @@ mod string_functions { #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size() { - return Err(crate::EvalAltResult::ErrorDataTooLarge( + return Err(crate::ERR::ErrorDataTooLarge( "Length of string".to_string(), crate::Position::NONE, ) diff --git a/src/parser.rs b/src/parser.rs index 6190de72..edd1bca3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -16,8 +16,8 @@ use crate::tokenizer::{ use crate::types::dynamic::AccessMode; use crate::{ calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Dynamic, Engine, ExclusiveRange, - Identifier, ImmutableString, InclusiveRange, LexError, ParseError, ParseErrorType, Position, - Scope, Shared, StaticVec, AST, INT, + Identifier, ImmutableString, InclusiveRange, LexError, ParseError, Position, Scope, Shared, + StaticVec, AST, INT, PERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -30,8 +30,6 @@ use std::{ pub type ParseResult = Result; -type PERR = ParseErrorType; - type FnLib = BTreeMap>; /// Invalid variable name that acts as a search barrier in a [`Scope`]. @@ -498,7 +496,7 @@ fn parse_fn_call( let relax = false; if !relax && settings.strict_var && index.is_none() { - return Err(ParseErrorType::ModuleUndefined(modules[0].name.to_string()) + return Err(PERR::ModuleUndefined(modules[0].name.to_string()) .into_err(modules[0].pos)); } @@ -557,10 +555,8 @@ fn parse_fn_call( let relax = false; if !relax && settings.strict_var && index.is_none() { - return Err(ParseErrorType::ModuleUndefined( - modules[0].name.to_string(), - ) - .into_err(modules[0].pos)); + return Err(PERR::ModuleUndefined(modules[0].name.to_string()) + .into_err(modules[0].pos)); } modules.set_index(index); @@ -1259,8 +1255,7 @@ fn parse_primary( if !settings.is_closure && settings.strict_var && index.is_none() { // If the parent scope is not inside another capturing closure - Err(ParseErrorType::VariableUndefined(captured_var.to_string()) - .into_err(pos)) + Err(PERR::VariableUndefined(captured_var.to_string()).into_err(pos)) } else { Ok(()) } @@ -1384,9 +1379,7 @@ fn parse_primary( let index = state.access_var(&s, settings.pos); if settings.strict_var && index.is_none() { - return Err( - ParseErrorType::VariableUndefined(s.to_string()).into_err(settings.pos) - ); + return Err(PERR::VariableUndefined(s.to_string()).into_err(settings.pos)); } let short_index = index.and_then(|x| { @@ -1586,8 +1579,7 @@ fn parse_postfix( if !relax && settings.strict_var && index.is_none() { return Err( - ParseErrorType::ModuleUndefined(namespace[0].name.to_string()) - .into_err(namespace[0].pos), + PERR::ModuleUndefined(namespace[0].name.to_string()).into_err(namespace[0].pos) ); } diff --git a/src/serde/de.rs b/src/serde/de.rs index 04ac5681..0574f877 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -1,7 +1,7 @@ //! Implement deserialization support of [`Dynamic`][crate::Dynamic] for [`serde`]. use crate::types::dynamic::Union; -use crate::{Dynamic, EvalAltResult, ImmutableString, LexError, Position, RhaiError, RhaiResultOf}; +use crate::{Dynamic, ImmutableString, LexError, Position, RhaiError, RhaiResultOf, ERR}; use serde::de::{Error, IntoDeserializer, Visitor}; use serde::{Deserialize, Deserializer}; #[cfg(feature = "no_std")] @@ -31,7 +31,7 @@ impl<'de> DynamicDeserializer<'de> { } /// Shortcut for a type conversion error. fn type_error_str(&self, error: &str) -> RhaiResultOf { - Err(EvalAltResult::ErrorMismatchOutputType( + Err(ERR::ErrorMismatchOutputType( error.into(), self.value.type_name().into(), Position::NONE, diff --git a/src/serde/ser.rs b/src/serde/ser.rs index ede8db11..a9083243 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -1,6 +1,6 @@ //! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`]. -use crate::{Dynamic, EvalAltResult, Position, RhaiError, RhaiResult, RhaiResultOf}; +use crate::{Dynamic, Position, RhaiError, RhaiResult, RhaiResultOf, ERR}; use serde::ser::{ Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct, }; @@ -83,7 +83,7 @@ pub fn to_dynamic(value: T) -> RhaiResult { impl Error for RhaiError { fn custom(err: T) -> Self { - EvalAltResult::ErrorRuntime(err.to_string().into(), Position::NONE).into() + ERR::ErrorRuntime(err.to_string().into(), Position::NONE).into() } } @@ -259,7 +259,7 @@ impl Serializer for &mut DynamicSerializer { return Ok(Dynamic::from_blob(_v.to_vec())); #[cfg(feature = "no_index")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "BLOB's are not supported with 'no_index'".into(), Position::NONE, @@ -313,7 +313,7 @@ impl Serializer for &mut DynamicSerializer { make_variant(_variant, content) } #[cfg(feature = "no_object")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "object maps are not supported with 'no_object'".into(), Position::NONE, @@ -325,7 +325,7 @@ impl Serializer for &mut DynamicSerializer { #[cfg(not(feature = "no_index"))] return Ok(DynamicSerializer::new(crate::Array::new().into())); #[cfg(feature = "no_index")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "arrays are not supported with 'no_index'".into(), Position::NONE, @@ -359,7 +359,7 @@ impl Serializer for &mut DynamicSerializer { array: crate::Array::with_capacity(_len), }); #[cfg(any(feature = "no_object", feature = "no_index"))] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "tuples are not supported with 'no_index' or 'no_object'".into(), Position::NONE, @@ -371,7 +371,7 @@ impl Serializer for &mut DynamicSerializer { #[cfg(not(feature = "no_object"))] return Ok(DynamicSerializer::new(crate::Map::new().into())); #[cfg(feature = "no_object")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "object maps are not supported with 'no_object'".into(), Position::NONE, @@ -400,7 +400,7 @@ impl Serializer for &mut DynamicSerializer { map: crate::Map::new(), }); #[cfg(feature = "no_object")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "object maps are not supported with 'no_object'".into(), Position::NONE, @@ -422,7 +422,7 @@ impl SerializeSeq for DynamicSerializer { Ok(()) } #[cfg(feature = "no_index")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "arrays are not supported with 'no_index'".into(), Position::NONE, @@ -435,7 +435,7 @@ impl SerializeSeq for DynamicSerializer { #[cfg(not(feature = "no_index"))] return Ok(self._value); #[cfg(feature = "no_index")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "arrays are not supported with 'no_index'".into(), Position::NONE, @@ -457,7 +457,7 @@ impl SerializeTuple for DynamicSerializer { Ok(()) } #[cfg(feature = "no_index")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "tuples are not supported with 'no_index'".into(), Position::NONE, @@ -469,7 +469,7 @@ impl SerializeTuple for DynamicSerializer { #[cfg(not(feature = "no_index"))] return Ok(self._value); #[cfg(feature = "no_index")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "tuples are not supported with 'no_index'".into(), Position::NONE, @@ -491,7 +491,7 @@ impl SerializeTupleStruct for DynamicSerializer { Ok(()) } #[cfg(feature = "no_index")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "tuples are not supported with 'no_index'".into(), Position::NONE, @@ -503,7 +503,7 @@ impl SerializeTupleStruct for DynamicSerializer { #[cfg(not(feature = "no_index"))] return Ok(self._value); #[cfg(feature = "no_index")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "tuples are not supported with 'no_index'".into(), Position::NONE, @@ -523,7 +523,7 @@ impl SerializeMap for DynamicSerializer { Ok(()) } #[cfg(feature = "no_object")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "object maps are not supported with 'no_object'".into(), Position::NONE, @@ -537,11 +537,7 @@ impl SerializeMap for DynamicSerializer { let key = std::mem::take(&mut self._key) .into_immutable_string() .map_err(|typ| { - EvalAltResult::ErrorMismatchDataType( - "string".into(), - typ.into(), - Position::NONE, - ) + ERR::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE) })?; let _value = _value.serialize(&mut *self)?; let map = self._value.downcast_mut::().unwrap(); @@ -549,7 +545,7 @@ impl SerializeMap for DynamicSerializer { Ok(()) } #[cfg(feature = "no_object")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "object maps are not supported with 'no_object'".into(), Position::NONE, @@ -566,7 +562,7 @@ impl SerializeMap for DynamicSerializer { { let _key: Dynamic = _key.serialize(&mut *self)?; let _key = _key.into_immutable_string().map_err(|typ| { - EvalAltResult::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE) + ERR::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE) })?; let _value = _value.serialize(&mut *self)?; let map = self._value.downcast_mut::().unwrap(); @@ -574,7 +570,7 @@ impl SerializeMap for DynamicSerializer { Ok(()) } #[cfg(feature = "no_object")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "object maps are not supported with 'no_object'".into(), Position::NONE, @@ -586,7 +582,7 @@ impl SerializeMap for DynamicSerializer { #[cfg(not(feature = "no_object"))] return Ok(self._value); #[cfg(feature = "no_object")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "object maps are not supported with 'no_object'".into(), Position::NONE, @@ -612,7 +608,7 @@ impl SerializeStruct for DynamicSerializer { Ok(()) } #[cfg(feature = "no_object")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "object maps are not supported with 'no_object'".into(), Position::NONE, @@ -624,7 +620,7 @@ impl SerializeStruct for DynamicSerializer { #[cfg(not(feature = "no_object"))] return Ok(self._value); #[cfg(feature = "no_object")] - return Err(EvalAltResult::ErrorMismatchDataType( + return Err(ERR::ErrorMismatchDataType( "".into(), "object maps are not supported with 'no_object'".into(), Position::NONE, diff --git a/src/serde/str.rs b/src/serde/str.rs index 95e26dcc..fd19de8c 100644 --- a/src/serde/str.rs +++ b/src/serde/str.rs @@ -1,6 +1,6 @@ //! Implement deserialization support of [`ImmutableString`][crate::ImmutableString] for [`serde`]. -use crate::{EvalAltResult, Position, RhaiError, RhaiResultOf}; +use crate::{Position, RhaiError, RhaiResultOf, ERR}; use serde::de::{Deserializer, Visitor}; use std::any::type_name; #[cfg(feature = "no_std")] @@ -19,12 +19,10 @@ impl<'a> StringSliceDeserializer<'a> { } /// Shortcut for a type conversion error. fn type_error(&self) -> RhaiResultOf { - Err(EvalAltResult::ErrorMismatchOutputType( - type_name::().into(), - "string".into(), - Position::NONE, + Err( + ERR::ErrorMismatchOutputType(type_name::().into(), "string".into(), Position::NONE) + .into(), ) - .into()) } } diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 74576de1..9b707e39 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -3,8 +3,8 @@ use crate::tokenizer::is_valid_identifier; use crate::types::dynamic::Variant; use crate::{ - Dynamic, Engine, EvalAltResult, FuncArgs, Identifier, Module, NativeCallContext, Position, - RhaiError, RhaiResult, RhaiResultOf, StaticVec, AST, + Dynamic, Engine, FuncArgs, Identifier, Module, NativeCallContext, Position, RhaiError, + RhaiResult, RhaiResultOf, StaticVec, AST, ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -157,7 +157,7 @@ impl FnPtr { let typ = engine.map_type_name(result.type_name()); result.try_cast().ok_or_else(|| { - EvalAltResult::ErrorMismatchOutputType( + ERR::ErrorMismatchOutputType( engine.map_type_name(type_name::()).into(), typ.into(), Position::NONE, @@ -185,7 +185,7 @@ impl FnPtr { let typ = context.engine().map_type_name(result.type_name()); result.try_cast().ok_or_else(|| { - EvalAltResult::ErrorMismatchOutputType( + ERR::ErrorMismatchOutputType( context.engine().map_type_name(type_name::()).into(), typ.into(), Position::NONE, @@ -255,7 +255,7 @@ impl TryFrom for FnPtr { if is_valid_identifier(value.chars()) { Ok(Self(value, StaticVec::new_const())) } else { - Err(EvalAltResult::ErrorFunctionNotFound(value.to_string(), Position::NONE).into()) + Err(ERR::ErrorFunctionNotFound(value.to_string(), Position::NONE).into()) } } } diff --git a/src/types/parse_error.rs b/src/types/parse_error.rs index 3c0870c9..50ab93da 100644 --- a/src/types/parse_error.rs +++ b/src/types/parse_error.rs @@ -1,7 +1,7 @@ //! Module containing error definitions for the parsing process. use crate::tokenizer::is_valid_identifier; -use crate::{EvalAltResult, Position, RhaiError}; +use crate::{Position, RhaiError, ERR}; #[cfg(feature = "no_std")] use core_error::Error; #[cfg(not(feature = "no_std"))] @@ -66,7 +66,7 @@ impl LexError { } } -/// Type of error encountered when parsing a script. +/// Error encountered when parsing a script. /// /// Some errors never appear when certain features are turned on. /// They still exist so that the application can turn features on and off without going through @@ -317,10 +317,10 @@ impl From for RhaiError { } } -impl From for EvalAltResult { +impl From for ERR { #[inline(always)] fn from(err: ParseErrorType) -> Self { - EvalAltResult::ErrorParsing(err, Position::NONE) + ERR::ErrorParsing(err, Position::NONE) } } @@ -331,9 +331,9 @@ impl From for RhaiError { } } -impl From for EvalAltResult { +impl From for ERR { #[inline(always)] fn from(err: ParseError) -> Self { - EvalAltResult::ErrorParsing(*err.0, err.1) + ERR::ErrorParsing(*err.0, err.1) } } From dfb3378b28add4828582c9e53987c446c708c1db Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 27 Dec 2021 12:27:44 +0800 Subject: [PATCH 44/97] Expand function call arguments inline storage. --- src/engine.rs | 29 +++++++++++++---------------- src/func/call.rs | 28 ++++++++++++++-------------- src/lib.rs | 23 +++++++++++++++++++++++ 3 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index c26d54cc..39160104 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2,19 +2,16 @@ use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, Stmt, AST_OPTION_FLAGS::*}; use crate::custom_syntax::CustomSyntax; -use crate::func::{ - get_hasher, - native::{OnDebugCallback, OnParseTokenCallback, OnPrintCallback, OnVarCallback}, - CallableFunction, IteratorFn, -}; +use crate::func::native::{OnDebugCallback, OnParseTokenCallback, OnPrintCallback, OnVarCallback}; +use crate::func::{get_hasher, CallableFunction, IteratorFn}; use crate::module::Namespace; use crate::packages::{Package, StandardPackage}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::tokenizer::Token; use crate::types::dynamic::{map_std_type_name, AccessMode, Union, Variant}; use crate::{ - calc_fn_params_hash, combine_hashes, Dynamic, Identifier, ImmutableString, Module, Position, - RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, ERR, INT, + calc_fn_params_hash, combine_hashes, Dynamic, FnArgsVec, Identifier, ImmutableString, Module, + Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, ERR, INT, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -372,7 +369,7 @@ enum ChainArgument { /// Arguments to a dot method call. /// Wrapped values are the arguments plus the [position][Position] of the first argument. #[cfg(not(feature = "no_object"))] - MethodCallArgs(StaticVec, Position), + MethodCallArgs(FnArgsVec, Position), /// Index value and [position][Position]. #[cfg(not(feature = "no_index"))] IndexValue(Dynamic, Position), @@ -395,7 +392,7 @@ impl ChainArgument { #[inline(always)] #[cfg(not(feature = "no_object"))] #[must_use] - pub fn into_fn_call_args(self) -> Option<(StaticVec, Position)> { + pub fn into_fn_call_args(self) -> Option<(FnArgsVec, Position)> { match self { Self::MethodCallArgs(values, pos) => Some((values, pos)), _ => None, @@ -418,9 +415,9 @@ impl ChainArgument { } #[cfg(not(feature = "no_object"))] -impl From<(StaticVec, Position)> for ChainArgument { +impl From<(FnArgsVec, Position)> for ChainArgument { #[inline(always)] - fn from((values, pos): (StaticVec, Position)) -> Self { + fn from((values, pos): (FnArgsVec, Position)) -> Self { Self::MethodCallArgs(values, pos) } } @@ -1325,7 +1322,7 @@ impl Engine { root: (&str, Position), rhs: &Expr, terminate_chaining: bool, - idx_values: &mut StaticVec, + idx_values: &mut FnArgsVec, chain_type: ChainType, level: usize, new_val: Option<((Dynamic, Position), (Option, Position))>, @@ -1790,7 +1787,7 @@ impl Engine { _ => unreachable!("index or dot chain expected, but gets {:?}", expr), }; - let idx_values = &mut StaticVec::new_const(); + let idx_values = &mut FnArgsVec::new_const(); self.eval_dot_index_chain_arguments( scope, mods, state, lib, this_ptr, rhs, term, chain_type, idx_values, 0, level, @@ -1846,7 +1843,7 @@ impl Engine { expr: &Expr, terminate_chaining: bool, parent_chain_type: ChainType, - idx_values: &mut StaticVec, + idx_values: &mut FnArgsVec, size: usize, level: usize, ) -> RhaiResultOf<()> { @@ -1863,7 +1860,7 @@ impl Engine { } = x.as_ref(); let (values, pos) = args.iter().try_fold( - (StaticVec::with_capacity(args.len()), Position::NONE), + (FnArgsVec::with_capacity(args.len()), Position::NONE), |(mut values, mut pos), expr| -> RhaiResultOf<_> { let (value, arg_pos) = self.get_arg_value( scope, mods, state, lib, this_ptr, level, expr, constants, @@ -1910,7 +1907,7 @@ impl Engine { args.iter() .try_fold( - (StaticVec::with_capacity(args.len()), Position::NONE), + (FnArgsVec::with_capacity(args.len()), Position::NONE), |(mut values, mut pos), expr| -> RhaiResultOf<_> { let (value, arg_pos) = self.get_arg_value( scope, mods, state, lib, this_ptr, level, expr, constants, diff --git a/src/func/call.rs b/src/func/call.rs index a6c44e42..3090a35b 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -12,8 +12,8 @@ use crate::engine::{ use crate::module::Namespace; use crate::tokenizer::Token; use crate::{ - calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Identifier, - ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR, + calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnArgsVec, FnPtr, + Identifier, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -149,7 +149,7 @@ impl Engine { } else { self.map_type_name(a.type_name()) }) - .collect::>() + .collect::>() .join(", ") ) } @@ -727,7 +727,7 @@ impl Engine { fn_name: impl AsRef, mut hash: FnCallHashes, target: &mut crate::engine::Target, - (call_args, call_arg_pos): &mut (StaticVec, Position), + (call_args, call_arg_pos): &mut (FnArgsVec, Position), pos: Position, level: usize, ) -> RhaiResultOf<(Dynamic, bool)> { @@ -744,9 +744,9 @@ impl Engine { // Recalculate hashes let new_hash = calc_fn_hash(fn_name, args_len).into(); // Arguments are passed as-is, adding the curried arguments - let mut curry = StaticVec::with_capacity(fn_ptr.num_curried()); + let mut curry = FnArgsVec::with_capacity(fn_ptr.num_curried()); curry.extend(fn_ptr.curry().iter().cloned()); - let mut args = StaticVec::with_capacity(curry.len() + call_args.len()); + let mut args = FnArgsVec::with_capacity(curry.len() + call_args.len()); args.extend(curry.iter_mut()); args.extend(call_args.iter_mut()); @@ -782,9 +782,9 @@ impl Engine { calc_fn_hash(fn_name, args_len + 1), ); // Replace the first argument with the object pointer, adding the curried arguments - let mut curry = StaticVec::with_capacity(fn_ptr.num_curried()); + let mut curry = FnArgsVec::with_capacity(fn_ptr.num_curried()); curry.extend(fn_ptr.curry().iter().cloned()); - let mut args = StaticVec::with_capacity(curry.len() + call_args.len() + 1); + let mut args = FnArgsVec::with_capacity(curry.len() + call_args.len() + 1); args.push(target.as_mut()); args.extend(curry.iter_mut()); args.extend(call_args.iter_mut()); @@ -858,7 +858,7 @@ impl Engine { }; // Attached object pointer in front of the arguments - let mut args = StaticVec::with_capacity(call_args.len() + 1); + let mut args = FnArgsVec::with_capacity(call_args.len() + 1); args.push(target.as_mut()); args.extend(call_args.iter_mut()); @@ -918,7 +918,7 @@ impl Engine { let fn_name = fn_name.as_ref(); let mut a_expr = args_expr; let mut total_args = a_expr.len(); - let mut curry = StaticVec::new_const(); + let mut curry = FnArgsVec::new_const(); let mut name = fn_name; let mut hashes = hashes; let redirected; // Handle call() - Redirect function call @@ -1086,8 +1086,8 @@ impl Engine { } // Normal function call - except for Fn, curry, call and eval (handled above) - let mut arg_values = StaticVec::with_capacity(a_expr.len()); - let mut args = StaticVec::with_capacity(a_expr.len() + curry.len()); + let mut arg_values = FnArgsVec::with_capacity(a_expr.len()); + let mut args = FnArgsVec::with_capacity(a_expr.len() + curry.len()); let mut is_ref_mut = false; // Capture parent scope? @@ -1187,8 +1187,8 @@ impl Engine { level: usize, ) -> RhaiResult { let fn_name = fn_name.as_ref(); - let mut arg_values = StaticVec::with_capacity(args_expr.len()); - let mut args = StaticVec::with_capacity(args_expr.len()); + let mut arg_values = FnArgsVec::with_capacity(args_expr.len()); + let mut args = FnArgsVec::with_capacity(args_expr.len()); let mut first_arg_value = None; if args_expr.is_empty() { diff --git a/src/lib.rs b/src/lib.rs index bea085e1..d88dfc23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -313,6 +313,29 @@ type StaticVec = smallvec::SmallVec<[T; 3]>; #[cfg(feature = "internals")] pub type StaticVec = smallvec::SmallVec<[T; 3]>; +/// Inline arguments storage for function calls. +/// +/// # Notes +/// +/// Since most usage of this is during a function call to gather up arguments, this is mostly +/// allocated on the stack, so we can tolerate a larger number of values stored inline. +/// +/// Most functions have few parameters, but closures with a lot of captured variables can +/// potentially have many. Having a larger inline storage for arguments reduces allocations in +/// scripts with heavy closure usage. +/// +/// Under `no_closure`, this type aliases to [`StaticVec`][crate::StaticVec] instead. +#[cfg(not(feature = "no_closure"))] +type FnArgsVec = smallvec::SmallVec<[T; 8]>; + +/// Inline arguments storage for function calls. +/// +/// # Notes +/// +/// This type aliases to [`StaticVec`][crate::StaticVec] under `no_closure`. +#[cfg(feature = "no_closure")] +type FnArgsVec = crate::StaticVec; + pub(crate) type SmartString = smartstring::SmartString; // Compiler guards against mutually-exclusive feature flags From f443e4d9f6a21fc05691e5827091a8b047bc2883 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 27 Dec 2021 13:30:44 +0800 Subject: [PATCH 45/97] Fix formatting. --- src/func/register.rs | 2 +- src/module/resolvers/collection.rs | 2 +- src/module/resolvers/dummy.rs | 2 +- src/module/resolvers/file.rs | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/func/register.rs b/src/func/register.rs index 7a298f6f..d71ca50b 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -8,7 +8,7 @@ use super::native::{FnAny, SendSync}; use crate::r#unsafe::unsafe_try_cast; use crate::tokenizer::Position; use crate::types::dynamic::{DynamicWriteLock, Variant}; -use crate::{Dynamic, ERR, NativeCallContext, RhaiResultOf}; +use crate::{Dynamic, NativeCallContext, RhaiResultOf, ERR}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{any::TypeId, mem}; diff --git a/src/module/resolvers/collection.rs b/src/module/resolvers/collection.rs index 444c0c72..835a8863 100644 --- a/src/module/resolvers/collection.rs +++ b/src/module/resolvers/collection.rs @@ -1,4 +1,4 @@ -use crate::{Engine,ERR, Module, ModuleResolver, Position, RhaiResultOf, Shared}; +use crate::{Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, ERR}; use std::ops::AddAssign; #[cfg(feature = "no_std")] use std::prelude::v1::*; diff --git a/src/module/resolvers/dummy.rs b/src/module/resolvers/dummy.rs index e67abd71..f36148bc 100644 --- a/src/module/resolvers/dummy.rs +++ b/src/module/resolvers/dummy.rs @@ -1,4 +1,4 @@ -use crate::{Engine,ERR, Module, ModuleResolver, Position, RhaiResultOf, Shared}; +use crate::{Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, ERR}; #[cfg(feature = "no_std")] use std::prelude::v1::*; diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index b0e374fd..1dcedd8e 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -3,8 +3,7 @@ use crate::func::native::shared_write_lock; use crate::{ - Engine,ERR, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Scope, - Shared, + Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared, ERR, }; use std::{ From 757eacfdde099687b0bc19bc75421f824453127b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 27 Dec 2021 16:59:05 +0800 Subject: [PATCH 46/97] No need for types with Into::into. --- src/ast/expr.rs | 2 +- src/custom_syntax.rs | 4 ++-- src/func/builtin.rs | 4 ++-- src/func/call.rs | 2 +- src/packages/fn_basic.rs | 2 +- src/packages/map_basic.rs | 2 +- src/packages/string_more.rs | 36 +++++++++--------------------------- 7 files changed, 17 insertions(+), 35 deletions(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index e50e5086..3ec16433 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -286,7 +286,7 @@ impl FromStr for FloatWrapper { #[inline] fn from_str(s: &str) -> Result { - F::from_str(s).map(Into::::into) + F::from_str(s).map(Into::into) } } diff --git a/src/custom_syntax.rs b/src/custom_syntax.rs index 95ec521a..2cc109c6 100644 --- a/src/custom_syntax.rs +++ b/src/custom_syntax.rs @@ -211,9 +211,9 @@ impl Engine { /// Replacing one variable with another (i.e. adding a new variable and removing one variable at /// the same time so that the total _size_ of the [`Scope`][crate::Scope] is unchanged) also /// does NOT count, so `false` should be passed. - pub fn register_custom_syntax + Into>( + pub fn register_custom_syntax( &mut self, - symbols: &[S], + symbols: &[impl AsRef + Into], scope_may_be_changed: bool, func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static, ) -> ParseResult<&mut Self> { diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 3375f243..e1320e3d 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -89,7 +89,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option $func:ident ( $xx:ident, $yy:ident )) => { |_, args| { let x = args[0].$xx().expect(BUILTIN) as $base; let y = args[1].$yy().expect(BUILTIN) as $base; - $func(x, y).map(Into::::into) + $func(x, y).map(Into::into) } }; (from $base:ty => $xx:ident $op:tt $yy:ident) => { |_, args| { let x = <$base>::from(args[0].$xx().expect(BUILTIN)); @@ -104,7 +104,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option $func:ident ( $xx:ident, $yy:ident )) => { |_, args| { let x = <$base>::from(args[0].$xx().expect(BUILTIN)); let y = <$base>::from(args[1].$yy().expect(BUILTIN)); - $func(x, y).map(Into::::into) + $func(x, y).map(Into::into) } }; } diff --git a/src/func/call.rs b/src/func/call.rs index 3090a35b..90f27913 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -967,7 +967,7 @@ impl Engine { .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos)) .and_then(FnPtr::try_from) - .map(Into::::into) + .map(Into::into) .map_err(|err| err.fill_position(arg_pos)); } diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index fa6f33f2..6037761f 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -71,7 +71,7 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array { f.params .iter() .cloned() - .map(Into::::into) + .map(Into::into) .collect::() .into(), ); diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index a1f2fa5d..525ce5c0 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -102,7 +102,7 @@ mod map_functions { if map.is_empty() { Array::new() } else { - map.keys().cloned().map(Into::::into).collect() + map.keys().cloned().map(Into::into).collect() } } #[cfg(not(feature = "no_index"))] diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index dfaf047c..7ec7af76 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -599,7 +599,7 @@ mod string_functions { if string.is_empty() { Array::new() } else { - string.chars().map(Into::::into).collect() + string.chars().map(Into::into).collect() } } #[rhai_fn(name = "split")] @@ -624,57 +624,39 @@ mod string_functions { } } pub fn split(string: &str, delimiter: &str) -> Array { - string.split(delimiter).map(Into::::into).collect() + string.split(delimiter).map(Into::into).collect() } #[rhai_fn(name = "split")] pub fn splitn(string: &str, delimiter: &str, segments: INT) -> Array { let pieces: usize = if segments < 1 { 1 } else { segments as usize }; - string - .splitn(pieces, delimiter) - .map(Into::::into) - .collect() + string.splitn(pieces, delimiter).map(Into::into).collect() } #[rhai_fn(name = "split")] pub fn split_char(string: &str, delimiter: char) -> Array { - string.split(delimiter).map(Into::::into).collect() + string.split(delimiter).map(Into::into).collect() } #[rhai_fn(name = "split")] pub fn splitn_char(string: &str, delimiter: char, segments: INT) -> Array { let pieces: usize = if segments < 1 { 1 } else { segments as usize }; - string - .splitn(pieces, delimiter) - .map(Into::::into) - .collect() + string.splitn(pieces, delimiter).map(Into::into).collect() } #[rhai_fn(name = "split_rev")] pub fn rsplit(string: &str, delimiter: &str) -> Array { - string - .rsplit(delimiter) - .map(Into::::into) - .collect() + string.rsplit(delimiter).map(Into::into).collect() } #[rhai_fn(name = "split_rev")] pub fn rsplitn(string: &str, delimiter: &str, segments: INT) -> Array { let pieces: usize = if segments < 1 { 1 } else { segments as usize }; - string - .rsplitn(pieces, delimiter) - .map(Into::::into) - .collect() + string.rsplitn(pieces, delimiter).map(Into::into).collect() } #[rhai_fn(name = "split_rev")] pub fn rsplit_char(string: &str, delimiter: char) -> Array { - string - .rsplit(delimiter) - .map(Into::::into) - .collect() + string.rsplit(delimiter).map(Into::into).collect() } #[rhai_fn(name = "split_rev")] pub fn rsplitn_char(string: &str, delimiter: char, segments: INT) -> Array { let pieces: usize = if segments < 1 { 1 } else { segments as usize }; - string - .rsplitn(pieces, delimiter) - .map(Into::::into) - .collect() + string.rsplitn(pieces, delimiter).map(Into::into).collect() } } } From 7a15071e4e1dfc04f7a2e41c331a06cf92e5b712 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 27 Dec 2021 17:00:21 +0800 Subject: [PATCH 47/97] Reduce size of ChainArgument. --- src/engine.rs | 98 +++++++++++++++++++++++++++------------------------ src/lib.rs | 5 +-- 2 files changed, 52 insertions(+), 51 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 39160104..58dc68f2 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -368,8 +368,11 @@ enum ChainArgument { Property(Position), /// Arguments to a dot method call. /// Wrapped values are the arguments plus the [position][Position] of the first argument. + /// + /// Since many dotted function calls have no arguments (e.g. `string.len()`), it is better to + /// reduce the size of [`ChainArgument`] by using a boxed slice. #[cfg(not(feature = "no_object"))] - MethodCallArgs(FnArgsVec, Position), + MethodCallArgs(Option>, Position), /// Index value and [position][Position]. #[cfg(not(feature = "no_index"))] IndexValue(Dynamic, Position), @@ -389,13 +392,20 @@ impl ChainArgument { } } /// Return the list of method call arguments. + /// + /// # Panics + /// + /// Panics if the [`ChainArgument`] is not [`MethodCallArgs`][ChainArgument::MethodCallArgs]. #[inline(always)] #[cfg(not(feature = "no_object"))] #[must_use] - pub fn into_fn_call_args(self) -> Option<(FnArgsVec, Position)> { + pub fn into_fn_call_args(self) -> (FnArgsVec, Position) { match self { - Self::MethodCallArgs(values, pos) => Some((values, pos)), - _ => None, + Self::MethodCallArgs(None, pos) => (FnArgsVec::new_const(), pos), + Self::MethodCallArgs(Some(mut values), pos) => { + (values.iter_mut().map(std::mem::take).collect(), pos) + } + _ => unreachable!("`MethodCallArgs` expected"), } } /// Return the [position][Position]. @@ -412,20 +422,21 @@ impl ChainArgument { ChainArgument::IndexValue(_, pos) => *pos, } } -} - -#[cfg(not(feature = "no_object"))] -impl From<(FnArgsVec, Position)> for ChainArgument { + /// Create n [`MethodCallArgs`][ChainArgument::MethodCallArgs]. #[inline(always)] - fn from((values, pos): (FnArgsVec, Position)) -> Self { - Self::MethodCallArgs(values, pos) + #[cfg(not(feature = "no_object"))] + #[must_use] + pub fn from_fn_call_args(values: FnArgsVec, pos: Position) -> Self { + if values.is_empty() { + Self::MethodCallArgs(None, pos) + } else { + Self::MethodCallArgs(Some(values.into_vec().into()), pos) + } } -} - -#[cfg(not(feature = "no_index"))] -impl From<(Dynamic, Position)> for ChainArgument { + /// Create an [`IndexValue`][ChainArgument::IndexValue]. #[inline(always)] - fn from((value, pos): (Dynamic, Position)) -> Self { + #[must_use] + pub const fn from_index_value(value: Dynamic, pos: Position) -> Self { Self::IndexValue(value, pos) } } @@ -1322,7 +1333,7 @@ impl Engine { root: (&str, Position), rhs: &Expr, terminate_chaining: bool, - idx_values: &mut FnArgsVec, + idx_values: &mut StaticVec, chain_type: ChainType, level: usize, new_val: Option<((Dynamic, Position), (Option, Position))>, @@ -1445,9 +1456,7 @@ impl Engine { // xxx.fn_name(arg_expr_list) Expr::FnCall(x, pos) if !x.is_qualified() && new_val.is_none() => { let FnCallExpr { name, hashes, .. } = x.as_ref(); - let call_args = &mut idx_val - .into_fn_call_args() - .expect("`ChainType::Dot` with `Expr::FnCallExpr`"); + let call_args = &mut idx_val.into_fn_call_args(); self.make_method_call( mods, state, lib, name, *hashes, target, call_args, *pos, level, ) @@ -1611,9 +1620,7 @@ impl Engine { // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr Expr::FnCall(ref x, pos) if !x.is_qualified() => { let FnCallExpr { name, hashes, .. } = x.as_ref(); - let call_args = &mut idx_val.into_fn_call_args().expect( - "`chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`", - ); + let call_args = &mut idx_val.into_fn_call_args(); let (val, _) = self.make_method_call( mods, state, lib, name, *hashes, target, call_args, pos, level, )?; @@ -1736,9 +1743,7 @@ impl Engine { Expr::FnCall(ref f, pos) if !f.is_qualified() => { let FnCallExpr { name, hashes, .. } = f.as_ref(); let rhs_chain = match_chaining_type(rhs); - let args = &mut idx_val.into_fn_call_args().expect( - "`chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`", - ); + let args = &mut idx_val.into_fn_call_args(); let (mut val, _) = self.make_method_call( mods, state, lib, name, *hashes, target, args, pos, level, )?; @@ -1787,7 +1792,7 @@ impl Engine { _ => unreachable!("index or dot chain expected, but gets {:?}", expr), }; - let idx_values = &mut FnArgsVec::new_const(); + let idx_values = &mut StaticVec::new_const(); self.eval_dot_index_chain_arguments( scope, mods, state, lib, this_ptr, rhs, term, chain_type, idx_values, 0, level, @@ -1843,7 +1848,7 @@ impl Engine { expr: &Expr, terminate_chaining: bool, parent_chain_type: ChainType, - idx_values: &mut FnArgsVec, + idx_values: &mut StaticVec, size: usize, level: usize, ) -> RhaiResultOf<()> { @@ -1873,7 +1878,7 @@ impl Engine { }, )?; - idx_values.push((values, pos).into()); + idx_values.push(ChainArgument::from_fn_call_args(values, pos)); } #[cfg(not(feature = "no_object"))] Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => { @@ -1890,7 +1895,7 @@ impl Engine { let crate::ast::BinaryExpr { lhs, rhs, .. } = x.as_ref(); // Evaluate in left-to-right order - let lhs_val = match lhs { + let lhs_arg_val = match lhs { #[cfg(not(feature = "no_object"))] Expr::Property(x) if _parent_chain_type == ChainType::Dotting => { ChainArgument::Property((x.2).1) @@ -1905,21 +1910,20 @@ impl Engine { args, constants, .. } = x.as_ref(); - args.iter() - .try_fold( - (FnArgsVec::with_capacity(args.len()), Position::NONE), - |(mut values, mut pos), expr| -> RhaiResultOf<_> { - let (value, arg_pos) = self.get_arg_value( - scope, mods, state, lib, this_ptr, level, expr, constants, - )?; - if values.is_empty() { - pos = arg_pos - } - values.push(value.flatten()); - Ok((values, pos)) - }, - )? - .into() + let (values, pos) = args.iter().try_fold( + (FnArgsVec::with_capacity(args.len()), Position::NONE), + |(mut values, mut pos), expr| -> RhaiResultOf<_> { + let (value, arg_pos) = self.get_arg_value( + scope, mods, state, lib, this_ptr, level, expr, constants, + )?; + if values.is_empty() { + pos = arg_pos + } + values.push(value.flatten()); + Ok((values, pos)) + }, + )?; + ChainArgument::from_fn_call_args(values, pos) } #[cfg(not(feature = "no_object"))] Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => { @@ -1932,7 +1936,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] _ if _parent_chain_type == ChainType::Indexing => self .eval_expr(scope, mods, state, lib, this_ptr, lhs, level) - .map(|v| (v.flatten(), lhs.position()).into())?, + .map(|v| ChainArgument::from_index_value(v.flatten(), lhs.position()))?, expr => unreachable!("unknown chained expression: {:?}", expr), }; @@ -1944,7 +1948,7 @@ impl Engine { level, )?; - idx_values.push(lhs_val); + idx_values.push(lhs_arg_val); } #[cfg(not(feature = "no_object"))] @@ -1954,7 +1958,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] _ if _parent_chain_type == ChainType::Indexing => idx_values.push( self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) - .map(|v| (v.flatten(), expr.position()).into())?, + .map(|v| ChainArgument::from_index_value(v.flatten(), expr.position()))?, ), _ => unreachable!("unknown chained expression: {:?}", expr), } diff --git a/src/lib.rs b/src/lib.rs index d88dfc23..05651856 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -329,10 +329,7 @@ pub type StaticVec = smallvec::SmallVec<[T; 3]>; type FnArgsVec = smallvec::SmallVec<[T; 8]>; /// Inline arguments storage for function calls. -/// -/// # Notes -/// -/// This type aliases to [`StaticVec`][crate::StaticVec] under `no_closure`. +/// This type aliases to [`StaticVec`][crate::StaticVec]. #[cfg(feature = "no_closure")] type FnArgsVec = crate::StaticVec; From 4d226542faaa13c9daa54aad71fb2b05a83bf166 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 27 Dec 2021 21:56:50 +0800 Subject: [PATCH 48/97] Split out strings interner. --- src/api/register.rs | 108 ++++++++++++++++++++--------- src/engine.rs | 8 +-- src/lib.rs | 5 +- src/module/mod.rs | 30 ++++----- src/parser.rs | 153 +++++++++++++++++++----------------------- src/types/interner.rs | 71 ++++++++++++++++++++ src/types/mod.rs | 2 + 7 files changed, 241 insertions(+), 136 deletions(-) create mode 100644 src/types/interner.rs diff --git a/src/api/register.rs b/src/api/register.rs index 157f650e..22276593 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -193,8 +193,12 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { Self { field: 1 } } - /// fn update(&mut self, offset: i64) { self.field += offset; } + /// fn new() -> Self { + /// Self { field: 1 } + /// } + /// fn update(&mut self, offset: i64) { + /// self.field += offset; + /// } /// } /// /// # fn main() -> Result<(), Box> { @@ -233,7 +237,9 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { Self { field: 1 } } + /// fn new() -> Self { + /// Self { field: 1 } + /// } /// } /// /// # fn main() -> Result<(), Box> { @@ -308,9 +314,13 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { Self { field: 1 } } + /// fn new() -> Self { + /// Self { field: 1 } + /// } /// // Even a getter must start with `&mut self` and not `&self`. - /// fn get_field(&mut self) -> i64 { self.field } + /// fn get_field(&mut self) -> i64 { + /// self.field + /// } /// } /// /// # fn main() -> Result<(), Box> { @@ -336,7 +346,7 @@ impl Engine { name: impl AsRef, get_fn: impl Fn(&mut T) -> V + SendSync + 'static, ) -> &mut Self { - self.register_fn(&crate::engine::make_getter(name), get_fn) + self.register_fn(&crate::engine::make_getter(name.as_ref()), get_fn) } /// Register a getter function for a member of a registered type with the [`Engine`]. /// @@ -355,7 +365,9 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { Self { field: 1 } } + /// fn new() -> Self { + /// Self { field: 1 } + /// } /// // Even a getter must start with `&mut self` and not `&self`. /// fn get_field(&mut self) -> Result> { /// Ok(self.field) @@ -383,7 +395,7 @@ impl Engine { name: impl AsRef, get_fn: impl Fn(&mut T) -> RhaiResultOf + SendSync + 'static, ) -> &mut Self { - self.register_result_fn(&crate::engine::make_getter(name), get_fn) + self.register_result_fn(&crate::engine::make_getter(name.as_ref()), get_fn) } /// Register a setter function for a member of a registered type with the [`Engine`]. /// @@ -398,8 +410,12 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { Self { field: 1 } } - /// fn set_field(&mut self, new_val: i64) { self.field = new_val; } + /// fn new() -> Self { + /// Self { field: 1 } + /// } + /// fn set_field(&mut self, new_val: i64) { + /// self.field = new_val; + /// } /// } /// /// # fn main() -> Result<(), Box> { @@ -429,7 +445,7 @@ impl Engine { name: impl AsRef, set_fn: impl Fn(&mut T, V) + SendSync + 'static, ) -> &mut Self { - self.register_fn(&crate::engine::make_setter(name), set_fn) + self.register_fn(&crate::engine::make_setter(name.as_ref()), set_fn) } /// Register a setter function for a member of a registered type with the [`Engine`]. /// @@ -446,7 +462,9 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { Self { field: 1 } } + /// fn new() -> Self { + /// Self { field: 1 } + /// } /// fn set_field(&mut self, new_val: i64) -> Result<(), Box> { /// self.field = new_val; /// Ok(()) @@ -478,7 +496,7 @@ impl Engine { name: impl AsRef, set_fn: impl Fn(&mut T, V) -> RhaiResultOf<()> + SendSync + 'static, ) -> &mut Self { - self.register_result_fn(&crate::engine::make_setter(name), set_fn) + self.register_result_fn(&crate::engine::make_setter(name.as_ref()), set_fn) } /// Short-hand for registering both getter and setter functions /// of a registered type with the [`Engine`]. @@ -496,10 +514,16 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { Self { field: 1 } } + /// fn new() -> Self { + /// Self { field: 1 } + /// } /// // Even a getter must start with `&mut self` and not `&self`. - /// fn get_field(&mut self) -> i64 { self.field } - /// fn set_field(&mut self, new_val: i64) { self.field = new_val; } + /// fn get_field(&mut self) -> i64 { + /// self.field + /// } + /// fn set_field(&mut self, new_val: i64) { + /// self.field = new_val; + /// } /// } /// /// # fn main() -> Result<(), Box> { @@ -550,9 +574,13 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } + /// fn new() -> Self { + /// Self { fields: vec![1, 2, 3, 4, 5] } + /// } /// // Even a getter must start with `&mut self` and not `&self`. - /// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] } + /// fn get_field(&mut self, index: i64) -> i64 { + /// self.fields[index as usize] + /// } /// } /// /// # fn main() -> Result<(), Box> { @@ -623,7 +651,9 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } + /// fn new() -> Self { + /// Self { fields: vec![1, 2, 3, 4, 5] } + /// } /// // Even a getter must start with `&mut self` and not `&self`. /// fn get_field(&mut self, index: i64) -> Result> { /// Ok(self.fields[index as usize]) @@ -696,8 +726,12 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } - /// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; } + /// fn new() -> Self { + /// Self { fields: vec![1, 2, 3, 4, 5] } + /// } + /// fn set_field(&mut self, index: i64, value: i64) { + /// self.fields[index as usize] = value; + /// } /// } /// /// # fn main() -> Result<(), Box> { @@ -715,10 +749,10 @@ impl Engine { /// .register_indexer_set(TestStruct::set_field); /// /// # #[cfg(not(feature = "no_index"))] - /// assert_eq!( - /// engine.eval::("let a = new_ts(); a[2] = 42; a")?.fields[2], - /// 42 - /// ); + /// let result = engine.eval::("let a = new_ts(); a[2] = 42; a")?; + /// + /// # #[cfg(not(feature = "no_index"))] + /// assert_eq!(result.fields[2], 42); /// # Ok(()) /// # } /// ``` @@ -769,7 +803,9 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } + /// fn new() -> Self { + /// Self { fields: vec![1, 2, 3, 4, 5] } + /// } /// fn set_field(&mut self, index: i64, value: i64) -> Result<(), Box> { /// self.fields[index as usize] = value; /// Ok(()) @@ -789,10 +825,10 @@ impl Engine { /// .register_indexer_set_result(TestStruct::set_field); /// /// # #[cfg(not(feature = "no_index"))] - /// assert_eq!( - /// engine.eval::("let a = new_ts(); a[2] = 42; a")?.fields[2], - /// 42 - /// ); + /// let result = engine.eval::("let a = new_ts(); a[2] = 42; a")?; + /// + /// # #[cfg(not(feature = "no_index"))] + /// assert_eq!(result.fields[2], 42); /// # Ok(()) /// # } /// ``` @@ -845,10 +881,16 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } + /// fn new() -> Self { + /// Self { fields: vec![1, 2, 3, 4, 5] } + /// } /// // Even a getter must start with `&mut self` and not `&self`. - /// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] } - /// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; } + /// fn get_field(&mut self, index: i64) -> i64 { + /// self.fields[index as usize] + /// } + /// fn set_field(&mut self, index: i64, value: i64) { + /// self.fields[index as usize] = value; + /// } /// } /// /// # fn main() -> Result<(), Box> { diff --git a/src/engine.rs b/src/engine.rs index 58dc68f2..e4306199 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1013,16 +1013,16 @@ impl Default for Engine { #[cfg(not(feature = "no_object"))] #[inline] #[must_use] -pub fn make_getter(id: impl AsRef) -> String { - format!("{}{}", FN_GET, id.as_ref()) +pub fn make_getter(id: &str) -> String { + format!("{}{}", FN_GET, id) } /// Make setter function #[cfg(not(feature = "no_object"))] #[inline] #[must_use] -pub fn make_setter(id: impl AsRef) -> String { - format!("{}{}", FN_SET, id.as_ref()) +pub fn make_setter(id: &str) -> String { + format!("{}{}", FN_SET, id) } /// Is this function an anonymous function? diff --git a/src/lib.rs b/src/lib.rs index 05651856..67baf76d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -226,7 +226,10 @@ pub use tokenizer::{ }; #[cfg(feature = "internals")] -pub use parser::{IdentifierBuilder, ParseState}; +pub use types::StringsInterner; + +#[cfg(feature = "internals")] +pub use parser::ParseState; #[cfg(feature = "internals")] pub use ast::{ diff --git a/src/module/mod.rs b/src/module/mod.rs index 97481c74..98884c7c 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -5,9 +5,9 @@ use crate::func::{ shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, RegisterNativeFunction, SendSync, }; -use crate::parser::IdentifierBuilder; use crate::tokenizer::Token; use crate::types::dynamic::Variant; +use crate::types::StringsInterner; use crate::{ calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, Identifier, ImmutableString, NativeCallContext, RhaiResultOf, Shared, StaticVec, @@ -155,8 +155,8 @@ pub struct Module { indexed: bool, /// Does the [`Module`] contain indexed functions that have been exposed to the global namespace? contains_indexed_global_functions: bool, - /// Interned strings - identifiers: IdentifierBuilder, + /// Interned strings. + interner: StringsInterner, } impl Default for Module { @@ -253,7 +253,7 @@ impl Module { all_type_iterators: BTreeMap::new(), indexed: true, contains_indexed_global_functions: false, - identifiers: IdentifierBuilder::new(), + interner: StringsInterner::new(), } } @@ -477,7 +477,7 @@ impl Module { let param_names_and_types = fn_def .params .iter() - .map(|v| self.identifiers.get(v.as_str())) + .map(|v| self.interner.get("", v.as_str()).into()) .collect(); self.functions.insert( hash_script, @@ -490,7 +490,7 @@ impl Module { #[cfg(feature = "metadata")] param_names_and_types, #[cfg(feature = "metadata")] - return_type_name: self.identifiers.get("Dynamic"), + return_type_name: self.interner.get("", "Dynamic").into(), #[cfg(feature = "metadata")] comments: None, func: Into::::into(fn_def).into(), @@ -639,7 +639,7 @@ impl Module { pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[impl AsRef]) -> &mut Self { let mut param_names: StaticVec<_> = arg_names .iter() - .map(|name| self.identifiers.get(name.as_ref())) + .map(|name| self.interner.get("", name.as_ref()).into()) .collect(); if let Some(f) = self.functions.get_mut(&hash_fn) { @@ -771,7 +771,7 @@ impl Module { let mut names = _arg_names .iter() .flat_map(|&p| p.iter()) - .map(|&arg| self.identifiers.get(arg)) + .map(|&arg| self.interner.get("", arg).into()) .collect::>(); let return_type = if names.len() > arg_types.len() { names.pop().expect("exists") @@ -787,7 +787,7 @@ impl Module { self.functions.insert( hash_fn, FuncInfo { - name: self.identifiers.get(name), + name: self.interner.get("", name.as_ref()).into(), namespace, access, params: param_types.len(), @@ -1017,7 +1017,7 @@ impl Module { F: Fn(&mut A) -> RhaiResultOf + SendSync + 'static, { self.set_fn( - &crate::engine::make_getter(name), + &crate::engine::make_getter(name.as_ref()), FnNamespace::Global, FnAccess::Public, None, @@ -1059,7 +1059,7 @@ impl Module { F: Fn(&mut A, B) -> RhaiResultOf<()> + SendSync + 'static, { self.set_fn( - &crate::engine::make_setter(name), + &crate::engine::make_setter(name.as_ref()), FnNamespace::Global, FnAccess::Public, None, @@ -1285,7 +1285,7 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; - self.identifiers += other.identifiers; + self.interner += other.interner; self } @@ -1305,7 +1305,7 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; - self.identifiers += other.identifiers; + self.interner += other.interner; self } @@ -1334,7 +1334,7 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; - self.identifiers.merge(&other.identifiers); + self.interner.merge(&other.interner); self } @@ -1384,7 +1384,7 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; - self.identifiers.merge(&other.identifiers); + self.interner.merge(&other.interner); self } diff --git a/src/parser.rs b/src/parser.rs index edd1bca3..a9d1ab8d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -14,6 +14,7 @@ use crate::tokenizer::{ TokenizerControl, }; use crate::types::dynamic::AccessMode; +use crate::types::StringsInterner; use crate::{ calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Dynamic, Engine, ExclusiveRange, Identifier, ImmutableString, InclusiveRange, LexError, ParseError, Position, Scope, Shared, @@ -25,7 +26,6 @@ use std::{ collections::BTreeMap, hash::{Hash, Hasher}, num::{NonZeroU8, NonZeroUsize}, - ops::AddAssign, }; pub type ParseResult = Result; @@ -38,41 +38,6 @@ const SCOPE_SEARCH_BARRIER_MARKER: &str = "$BARRIER$"; /// The message: `TokenStream` never ends const NEVER_ENDS: &str = "`TokenStream` never ends"; -/// _(internals)_ A factory of identifiers from text strings. -/// Exported under the `internals` feature only. -/// -/// When [`SmartString`](https://crates.io/crates/smartstring) is used as [`Identifier`], -/// this just returns a copy because most identifiers in Rhai are short and ASCII-based. -/// -/// When [`ImmutableString`] is used as [`Identifier`], this type acts as an interner which keeps a -/// collection of strings and returns shared instances, only creating a new string when it is not -/// yet interned. -#[derive(Debug, Clone, Hash)] -pub struct IdentifierBuilder(); - -impl IdentifierBuilder { - /// Create a new [`IdentifierBuilder`]. - #[inline] - #[must_use] - pub const fn new() -> Self { - Self() - } - /// Get an identifier from a text string. - #[inline] - #[must_use] - pub fn get(&mut self, text: impl AsRef + Into) -> Identifier { - text.into() - } - /// Merge another [`IdentifierBuilder`] into this. - #[inline(always)] - pub fn merge(&mut self, _other: &Self) {} -} - -impl AddAssign for IdentifierBuilder { - #[inline(always)] - fn add_assign(&mut self, _rhs: Self) {} -} - /// _(internals)_ A type that encapsulates the current state of the parser. /// Exported under the `internals` feature only. #[derive(Debug)] @@ -82,7 +47,7 @@ pub struct ParseState<'e> { /// Input stream buffer containing the next character to read. pub tokenizer_control: TokenizerControl, /// Interned strings. - pub interned_strings: IdentifierBuilder, + pub interned_strings: StringsInterner, /// Encapsulates a local stack with variable names to simulate an actual runtime scope. pub stack: StaticVec<(Identifier, AccessMode)>, /// Size of the local variables stack upon entry of the current block scope. @@ -125,7 +90,7 @@ impl<'e> ParseState<'e> { external_vars: BTreeMap::new(), #[cfg(not(feature = "no_closure"))] allow_capture: true, - interned_strings: IdentifierBuilder::new(), + interned_strings: StringsInterner::new(), stack: StaticVec::new_const(), entry_stack_len: 0, #[cfg(not(feature = "no_module"))] @@ -204,11 +169,26 @@ impl<'e> ParseState<'e> { .and_then(|(i, _)| NonZeroUsize::new(i + 1)) } + /// Get an interned identifier, creating one if it is not yet interned. + #[inline(always)] + #[must_use] + pub fn get_identifier( + &mut self, + prefix: &'static str, + text: impl AsRef + Into + Into, + ) -> Identifier { + self.interned_strings.get(prefix, text).into() + } + /// Get an interned string, creating one if it is not yet interned. #[inline(always)] #[must_use] - pub fn get_identifier(&mut self, text: impl AsRef + Into) -> Identifier { - self.interned_strings.get(text) + pub fn get_interned_string( + &mut self, + prefix: &'static str, + text: impl AsRef + Into + Into, + ) -> ImmutableString { + self.interned_strings.get(prefix, text) } } @@ -278,15 +258,15 @@ impl Expr { match self { Self::Variable(_, pos, x) if x.1.is_none() => { let ident = x.2; - let getter = state.get_identifier(crate::engine::make_getter(&ident)); + let getter = state.get_identifier(crate::engine::FN_GET, ident.as_str()); let hash_get = calc_fn_hash(&getter, 1); - let setter = state.get_identifier(crate::engine::make_setter(&ident)); + let setter = state.get_identifier(crate::engine::FN_SET, ident.as_str()); let hash_set = calc_fn_hash(&setter, 2); Self::Property(Box::new(( (getter, hash_get), (setter, hash_set), - (state.get_identifier(ident).into(), pos), + (state.get_interned_string("", ident.as_str()), pos), ))) } _ => self, @@ -517,7 +497,7 @@ fn parse_fn_call( args.shrink_to_fit(); return Ok(FnCallExpr { - name: state.get_identifier(id), + name: state.get_identifier("", id), capture_parent_scope, namespace, hashes, @@ -576,7 +556,7 @@ fn parse_fn_call( args.shrink_to_fit(); return Ok(FnCallExpr { - name: state.get_identifier(id), + name: state.get_identifier("", id), capture_parent_scope, namespace, hashes, @@ -936,7 +916,7 @@ fn parse_map_literal( } let expr = parse_expr(input, state, lib, settings.level_up())?; - let name = state.get_identifier(name); + let name = state.get_identifier("", name); template.insert(name.clone(), crate::Dynamic::UNIT); map.push((Ident { name, pos }, expr)); @@ -1178,7 +1158,7 @@ fn parse_primary( Token::IntegerConstant(x) => Expr::IntegerConstant(x, settings.pos), Token::CharConstant(c) => Expr::CharConstant(c, settings.pos), Token::StringConstant(s) => { - Expr::StringConstant(state.get_identifier(s).into(), settings.pos) + Expr::StringConstant(state.get_identifier("", s).into(), settings.pos) } Token::True => Expr::BoolConstant(true, settings.pos), Token::False => Expr::BoolConstant(false, settings.pos), @@ -1357,7 +1337,7 @@ fn parse_primary( Expr::Variable( None, settings.pos, - (None, None, state.get_identifier(s)).into(), + (None, None, state.get_identifier("", s)).into(), ) } // Namespace qualification @@ -1371,7 +1351,7 @@ fn parse_primary( Expr::Variable( None, settings.pos, - (None, None, state.get_identifier(s)).into(), + (None, None, state.get_identifier("", s)).into(), ) } // Normal variable access @@ -1392,7 +1372,7 @@ fn parse_primary( Expr::Variable( short_index, settings.pos, - (index, None, state.get_identifier(s)).into(), + (index, None, state.get_identifier("", s)).into(), ) } } @@ -1410,14 +1390,14 @@ fn parse_primary( Token::LeftParen | Token::Bang if is_keyword_function(&s) => Expr::Variable( None, settings.pos, - (None, None, state.get_identifier(s)).into(), + (None, None, state.get_identifier("", s)).into(), ), // Access to `this` as a variable is OK within a function scope #[cfg(not(feature = "no_function"))] _ if &*s == KEYWORD_THIS && settings.is_function_scope => Expr::Variable( None, settings.pos, - (None, None, state.get_identifier(s)).into(), + (None, None, state.get_identifier("", s)).into(), ), // Cannot access to `this` as a variable not in a function scope _ if &*s == KEYWORD_THIS => { @@ -1519,7 +1499,7 @@ fn parse_postfix( Expr::Variable( None, pos2, - (None, namespace, state.get_identifier(id2)).into(), + (None, namespace, state.get_identifier("", id2)).into(), ) } // Indexing @@ -1636,7 +1616,7 @@ fn parse_unary( args.shrink_to_fit(); Ok(FnCallExpr { - name: state.get_identifier("-"), + name: state.get_identifier("", "-"), hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)), args, ..Default::default() @@ -1662,7 +1642,7 @@ fn parse_unary( args.shrink_to_fit(); Ok(FnCallExpr { - name: state.get_identifier("+"), + name: state.get_identifier("", "+"), hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)), args, ..Default::default() @@ -1679,7 +1659,7 @@ fn parse_unary( args.shrink_to_fit(); Ok(FnCallExpr { - name: state.get_identifier("!"), + name: state.get_identifier("", "!"), hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)), args, ..Default::default() @@ -2016,7 +1996,7 @@ fn parse_binary_op( let hash = calc_fn_hash(&op, 2); let op_base = FnCallExpr { - name: state.get_identifier(op.as_ref()), + name: state.get_identifier("", op.as_ref()), hashes: FnCallHashes::from_native(hash), ..Default::default() }; @@ -2071,7 +2051,7 @@ fn parse_binary_op( FnCallExpr { hashes: calc_fn_hash(OP_CONTAINS, 2).into(), args, - name: state.get_identifier(OP_CONTAINS), + name: state.get_identifier("", OP_CONTAINS), ..op_base } .into_fn_call_expr(pos) @@ -2122,7 +2102,7 @@ fn parse_custom_syntax( if syntax.scope_may_be_changed { // Add a barrier variable to the stack so earlier variables will not be matched. // Variable searches stop at the first barrier. - let marker = state.get_identifier(SCOPE_SEARCH_BARRIER_MARKER); + let marker = state.get_identifier("", SCOPE_SEARCH_BARRIER_MARKER); state.stack.push((marker, AccessMode::ReadWrite)); } @@ -2142,7 +2122,10 @@ fn parse_custom_syntax( if seg.starts_with(CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT) && seg.len() > CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT.len() => { - inputs.push(Expr::StringConstant(state.get_identifier(seg).into(), pos)); + inputs.push(Expr::StringConstant( + state.get_identifier("", seg).into(), + pos, + )); break; } Ok(Some(seg)) => seg, @@ -2153,28 +2136,28 @@ fn parse_custom_syntax( match required_token.as_str() { CUSTOM_SYNTAX_MARKER_IDENT => { let (name, pos) = parse_var_name(input)?; - let name = state.get_identifier(name); + let name = state.get_identifier("", name); segments.push(name.clone().into()); - tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_IDENT)); + tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_IDENT)); inputs.push(Expr::Variable(None, pos, (None, None, name).into())); } CUSTOM_SYNTAX_MARKER_SYMBOL => { let (symbol, pos) = parse_symbol(input)?; - let symbol: ImmutableString = state.get_identifier(symbol).into(); + let symbol: ImmutableString = state.get_identifier("", symbol).into(); segments.push(symbol.clone()); - tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_SYMBOL)); + tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_SYMBOL)); inputs.push(Expr::StringConstant(symbol, pos)); } CUSTOM_SYNTAX_MARKER_EXPR => { inputs.push(parse_expr(input, state, lib, settings)?); - let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_EXPR); + let keyword = state.get_identifier("", CUSTOM_SYNTAX_MARKER_EXPR); segments.push(keyword.clone().into()); tokens.push(keyword); } CUSTOM_SYNTAX_MARKER_BLOCK => match parse_block(input, state, lib, settings)? { block @ Stmt::Block(_, _) => { inputs.push(Expr::Stmt(Box::new(block.into()))); - let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_BLOCK); + let keyword = state.get_identifier("", CUSTOM_SYNTAX_MARKER_BLOCK); segments.push(keyword.clone().into()); tokens.push(keyword); } @@ -2183,8 +2166,8 @@ fn parse_custom_syntax( CUSTOM_SYNTAX_MARKER_BOOL => match input.next().expect(NEVER_ENDS) { (b @ Token::True, pos) | (b @ Token::False, pos) => { inputs.push(Expr::BoolConstant(b == Token::True, pos)); - segments.push(state.get_identifier(b.literal_syntax()).into()); - tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_BOOL)); + segments.push(state.get_identifier("", b.literal_syntax()).into()); + tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_BOOL)); } (_, pos) => { return Err( @@ -2197,7 +2180,7 @@ fn parse_custom_syntax( (Token::IntegerConstant(i), pos) => { inputs.push(Expr::IntegerConstant(i, pos)); segments.push(i.to_string().into()); - tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_INT)); + tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_INT)); } (_, pos) => { return Err( @@ -2213,6 +2196,7 @@ fn parse_custom_syntax( inputs.push(Expr::FloatConstant(f, pos)); segments.push(f.to_string().into()); tokens.push(state.get_identifier( + "", crate::custom_syntax::markers::CUSTOM_SYNTAX_MARKER_FLOAT, )); } @@ -2226,10 +2210,10 @@ fn parse_custom_syntax( } CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) { (Token::StringConstant(s), pos) => { - let s: ImmutableString = state.get_identifier(s).into(); + let s: ImmutableString = state.get_identifier("", s).into(); inputs.push(Expr::StringConstant(s.clone(), pos)); segments.push(s); - tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_STRING)); + tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_STRING)); } (_, pos) => { return Err(PERR::MissingSymbol("Expecting a string".to_string()).into_err(pos)) @@ -2477,13 +2461,13 @@ fn parse_for( let prev_stack_len = state.stack.len(); let counter_var = counter_name.map(|name| { - let name = state.get_identifier(name); + let name = state.get_identifier("", name); let pos = counter_pos.expect("`Some`"); state.stack.push((name.clone(), AccessMode::ReadWrite)); Ident { name, pos } }); - let loop_var = state.get_identifier(name); + let loop_var = state.get_identifier("", name); state.stack.push((loop_var.clone(), AccessMode::ReadWrite)); let loop_var = Ident { name: loop_var, @@ -2521,7 +2505,7 @@ fn parse_let( // let name ... let (name, pos) = parse_var_name(input)?; - let name = state.get_identifier(name); + let name = state.get_identifier("", name); let var_def = Ident { name: name.clone(), pos, @@ -2581,7 +2565,7 @@ fn parse_import( // import expr as name ... let (name, pos) = parse_var_name(input)?; - let name = state.get_identifier(name); + let name = state.get_identifier("", name); state.modules.push(name.clone()); Ok(Stmt::Import( @@ -2638,11 +2622,11 @@ fn parse_export( exports.push(( Ident { - name: state.get_identifier(id), + name: state.get_identifier("", id), pos: id_pos, }, Ident { - name: state.get_identifier(rename.as_ref().map_or("", |s| s.as_ref())), + name: state.get_identifier("", rename.as_ref().map_or("", |s| s.as_ref())), pos: rename_pos, }, )); @@ -3039,7 +3023,7 @@ fn parse_try_catch( .into_err(err_pos)); } - let name = state.get_identifier(name); + let name = state.get_identifier("", name); state.stack.push((name.clone(), AccessMode::ReadWrite)); Some(Ident { name, pos }) } else { @@ -3104,7 +3088,7 @@ fn parse_fn( PERR::FnDuplicatedParam(name.to_string(), s.to_string()).into_err(pos) ); } - let s = state.get_identifier(s); + let s = state.get_identifier("", s); state.stack.push((s.clone(), AccessMode::ReadWrite)); params.push((s, pos)) } @@ -3143,7 +3127,7 @@ fn parse_fn( params.shrink_to_fit(); Ok(ScriptFnDef { - name: state.get_identifier(name), + name: state.get_identifier("", name), access, params, body, @@ -3187,7 +3171,7 @@ fn make_curry_from_externals( ); let expr = FnCallExpr { - name: state.get_identifier(crate::engine::KEYWORD_FN_PTR_CURRY), + name: state.get_identifier("", crate::engine::KEYWORD_FN_PTR_CURRY), hashes: FnCallHashes::from_native(calc_fn_hash( crate::engine::KEYWORD_FN_PTR_CURRY, num_externals + 1, @@ -3229,7 +3213,7 @@ fn parse_anon_fn( PERR::FnDuplicatedParam("".to_string(), s.to_string()).into_err(pos) ); } - let s = state.get_identifier(s); + let s = state.get_identifier("", s); state.stack.push((s.clone(), AccessMode::ReadWrite)); params_list.push(s) } @@ -3287,7 +3271,10 @@ fn parse_anon_fn( body.hash(hasher); let hash = hasher.finish(); - let fn_name = state.get_identifier(&(format!("{}{:016x}", crate::engine::FN_ANONYMOUS, hash))); + let fn_name = state.get_identifier( + "", + &(format!("{}{:016x}", crate::engine::FN_ANONYMOUS, hash)), + ); // Define the function let script = ScriptFnDef { diff --git a/src/types/interner.rs b/src/types/interner.rs new file mode 100644 index 00000000..df3c4af7 --- /dev/null +++ b/src/types/interner.rs @@ -0,0 +1,71 @@ +use crate::engine::{make_getter, make_setter, FN_GET, FN_SET}; +use crate::{Identifier, ImmutableString}; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; +use std::{collections::BTreeMap, ops::AddAssign}; + +/// _(internals)_ A factory of identifiers from text strings. +/// Exported under the `internals` feature only. +/// +/// Since [`SmartString`](https://crates.io/crates/smartstring) is used as [`Identifier`], +/// this just returns a copy because most identifiers in Rhai are short and ASCII-based. +/// +/// Property getters and setters are interned separately. +#[derive(Debug, Clone, Default, Hash)] +pub struct StringsInterner { + /// Property getters. + getters: BTreeMap, + /// Property setters. + setters: BTreeMap, +} + +impl StringsInterner { + /// Create a new [`IdentifierBuilder`]. + #[inline] + #[must_use] + pub fn new() -> Self { + Self { + getters: BTreeMap::new(), + setters: BTreeMap::new(), + } + } + /// Get an identifier from a text string and prefix, adding it to the interner if necessary. + /// + /// # Panics + /// + /// Panics if the prefix is not recognized. + #[inline] + #[must_use] + pub fn get( + &mut self, + prefix: &'static str, + text: impl AsRef + Into + Into, + ) -> ImmutableString { + let (dict, mapper) = match prefix { + "" => return text.into(), + FN_GET => (&mut self.getters, make_getter as fn(&str) -> String), + FN_SET => (&mut self.setters, make_setter as fn(&str) -> String), + _ => unreachable!("unsupported prefix {}", prefix), + }; + + if dict.contains_key(text.as_ref()) { + self.getters.get(text.as_ref()).expect("exists").clone() + } else { + let value: ImmutableString = mapper(text.as_ref()).into(); + let text = text.into(); + dict.insert(text, value.clone()); + value + } + } + /// Merge another [`IdentifierBuilder`] into this. + #[inline(always)] + pub fn merge(&mut self, _other: &Self) {} +} + +impl AddAssign for StringsInterner { + #[inline(always)] + fn add_assign(&mut self, rhs: Self) { + self.getters.extend(rhs.getters.into_iter()); + self.setters.extend(rhs.setters.into_iter()); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index d20c2242..1012f8b1 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -4,6 +4,7 @@ pub mod dynamic; pub mod error; pub mod fn_ptr; pub mod immutable_string; +pub mod interner; pub mod parse_error; pub mod scope; @@ -11,5 +12,6 @@ pub use dynamic::Dynamic; pub use error::EvalAltResult; pub use fn_ptr::FnPtr; pub use immutable_string::ImmutableString; +pub use interner::StringsInterner; pub use parse_error::{LexError, ParseError, ParseErrorType}; pub use scope::Scope; From 9c7ced2b8082bc53b7ffc65d5cd7aa48ba8ce62d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 27 Dec 2021 22:02:34 +0800 Subject: [PATCH 49/97] Move custom syntax to api. --- src/{ => api}/custom_syntax.rs | 0 src/api/mod.rs | 2 ++ src/engine.rs | 2 +- src/lib.rs | 3 +-- src/parser.rs | 31 +++++++++++++------------------ 5 files changed, 17 insertions(+), 21 deletions(-) rename src/{ => api}/custom_syntax.rs (100%) diff --git a/src/custom_syntax.rs b/src/api/custom_syntax.rs similarity index 100% rename from src/custom_syntax.rs rename to src/api/custom_syntax.rs diff --git a/src/api/mod.rs b/src/api/mod.rs index 13c5488a..e42a489a 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -18,6 +18,8 @@ pub mod limits; pub mod events; +pub mod custom_syntax; + pub mod deprecated; use crate::engine::Precedence; diff --git a/src/engine.rs b/src/engine.rs index e4306199..121101fb 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,7 +1,7 @@ //! Main module defining the script evaluation [`Engine`]. +use crate::api::custom_syntax::CustomSyntax; use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, Stmt, AST_OPTION_FLAGS::*}; -use crate::custom_syntax::CustomSyntax; use crate::func::native::{OnDebugCallback, OnParseTokenCallback, OnPrintCallback, OnVarCallback}; use crate::func::{get_hasher, CallableFunction, IteratorFn}; use crate::module::Namespace; diff --git a/src/lib.rs b/src/lib.rs index 67baf76d..b74cb0d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,7 +70,6 @@ use std::prelude::v1::*; mod api; mod ast; -mod custom_syntax; mod engine; mod func; mod module; @@ -140,8 +139,8 @@ pub type FLOAT = f32; pub type ExclusiveRange = std::ops::Range; pub type InclusiveRange = std::ops::RangeInclusive; +pub use api::custom_syntax::Expression; pub use ast::{FnAccess, AST}; -pub use custom_syntax::Expression; pub use engine::{ Engine, EvalContext, OP_CONTAINS, OP_EQUALS, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE, }; diff --git a/src/parser.rs b/src/parser.rs index a9d1ab8d..a29c65d9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,11 +1,11 @@ //! Main module defining the lexer and parser. +use crate::api::custom_syntax::{markers::*, CustomSyntax}; use crate::api::options::LanguageOptions; use crate::ast::{ BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, OpAssignment, ScriptFnDef, Stmt, StmtBlock, AST_OPTION_FLAGS::*, }; -use crate::custom_syntax::{markers::*, CustomSyntax}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; use crate::func::hashing::get_hasher; use crate::module::Namespace; @@ -2190,24 +2190,19 @@ fn parse_custom_syntax( } }, #[cfg(not(feature = "no_float"))] - crate::custom_syntax::markers::CUSTOM_SYNTAX_MARKER_FLOAT => { - match input.next().expect(NEVER_ENDS) { - (Token::FloatConstant(f), pos) => { - inputs.push(Expr::FloatConstant(f, pos)); - segments.push(f.to_string().into()); - tokens.push(state.get_identifier( - "", - crate::custom_syntax::markers::CUSTOM_SYNTAX_MARKER_FLOAT, - )); - } - (_, pos) => { - return Err(PERR::MissingSymbol( - "Expecting a floating-point number".to_string(), - ) - .into_err(pos)) - } + CUSTOM_SYNTAX_MARKER_FLOAT => match input.next().expect(NEVER_ENDS) { + (Token::FloatConstant(f), pos) => { + inputs.push(Expr::FloatConstant(f, pos)); + segments.push(f.to_string().into()); + tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_FLOAT)); } - } + (_, pos) => { + return Err(PERR::MissingSymbol( + "Expecting a floating-point number".to_string(), + ) + .into_err(pos)) + } + }, CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) { (Token::StringConstant(s), pos) => { let s: ImmutableString = state.get_identifier("", s).into(); From e8b070cbf86aa54ec94c5a42e8b1af24ec489eb0 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 27 Dec 2021 22:28:11 +0800 Subject: [PATCH 50/97] Fix builds. --- src/api/compile.rs | 12 +++++--- src/engine.rs | 27 ++++++----------- src/optimizer.rs | 20 +++++++++++-- src/packages/blob_basic.rs | 10 +++++-- src/packages/iter_basic.rs | 28 +++++++++--------- src/parser.rs | 1 + src/types/interner.rs | 59 +++++++++++++++++++++++++------------- 7 files changed, 95 insertions(+), 62 deletions(-) diff --git a/src/api/compile.rs b/src/api/compile.rs index 882d047a..a23efc11 100644 --- a/src/api/compile.rs +++ b/src/api/compile.rs @@ -1,7 +1,7 @@ //! Module that defines the public compilation API of [`Engine`]. use crate::parser::{ParseResult, ParseState}; -use crate::{Engine, RhaiResultOf, Scope, AST}; +use crate::{Engine, Scope, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -84,7 +84,7 @@ impl Engine { &self, scope: &Scope, script: impl AsRef, - ) -> RhaiResultOf { + ) -> crate::RhaiResultOf { use crate::{ ast::{ASTNode, Expr, Stmt}, func::native::shared_take_or_clone, @@ -351,14 +351,18 @@ impl Engine { /// ``` #[cfg(not(feature = "no_object"))] #[inline(always)] - pub fn parse_json(&self, json: impl AsRef, has_null: bool) -> RhaiResultOf { + pub fn parse_json( + &self, + json: impl AsRef, + has_null: bool, + ) -> crate::RhaiResultOf { use crate::tokenizer::Token; fn parse_json_inner( engine: &Engine, json: &str, has_null: bool, - ) -> RhaiResultOf { + ) -> crate::RhaiResultOf { let mut scope = Scope::new(); let json_text = json.trim_start(); let scripts = if json_text.starts_with(Token::MapStart.literal_syntax()) { diff --git a/src/engine.rs b/src/engine.rs index 121101fb..eb0a1f8f 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -10,8 +10,8 @@ use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::tokenizer::Token; use crate::types::dynamic::{map_std_type_name, AccessMode, Union, Variant}; use crate::{ - calc_fn_params_hash, combine_hashes, Dynamic, FnArgsVec, Identifier, ImmutableString, Module, - Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, ERR, INT, + Dynamic, Identifier, ImmutableString, Module, Position, RhaiError, RhaiResult, RhaiResultOf, + Scope, Shared, StaticVec, ERR, INT, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -399,9 +399,9 @@ impl ChainArgument { #[inline(always)] #[cfg(not(feature = "no_object"))] #[must_use] - pub fn into_fn_call_args(self) -> (FnArgsVec, Position) { + pub fn into_fn_call_args(self) -> (crate::FnArgsVec, Position) { match self { - Self::MethodCallArgs(None, pos) => (FnArgsVec::new_const(), pos), + Self::MethodCallArgs(None, pos) => (crate::FnArgsVec::new_const(), pos), Self::MethodCallArgs(Some(mut values), pos) => { (values.iter_mut().map(std::mem::take).collect(), pos) } @@ -426,7 +426,7 @@ impl ChainArgument { #[inline(always)] #[cfg(not(feature = "no_object"))] #[must_use] - pub fn from_fn_call_args(values: FnArgsVec, pos: Position) -> Self { + pub fn from_fn_call_args(values: crate::FnArgsVec, pos: Position) -> Self { if values.is_empty() { Self::MethodCallArgs(None, pos) } else { @@ -435,6 +435,7 @@ impl ChainArgument { } /// Create an [`IndexValue`][ChainArgument::IndexValue]. #[inline(always)] + #[cfg(not(feature = "no_index"))] #[must_use] pub const fn from_index_value(value: Dynamic, pos: Position) -> Self { Self::IndexValue(value, pos) @@ -1865,7 +1866,7 @@ impl Engine { } = x.as_ref(); let (values, pos) = args.iter().try_fold( - (FnArgsVec::with_capacity(args.len()), Position::NONE), + (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), |(mut values, mut pos), expr| -> RhaiResultOf<_> { let (value, arg_pos) = self.get_arg_value( scope, mods, state, lib, this_ptr, level, expr, constants, @@ -1911,7 +1912,7 @@ impl Engine { } = x.as_ref(); let (values, pos) = args.iter().try_fold( - (FnArgsVec::with_capacity(args.len()), Position::NONE), + (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), |(mut values, mut pos), expr| -> RhaiResultOf<_> { let (value, arg_pos) = self.get_arg_value( scope, mods, state, lib, this_ptr, level, expr, constants, @@ -3246,18 +3247,6 @@ impl Engine { .map_err(|err| err.fill_position(stmt.position())) } - // Has a system function a Rust-native override? - pub(crate) fn has_native_fn_override(&self, hash_script: u64, arg_types: &[TypeId]) -> bool { - let hash_params = calc_fn_params_hash(arg_types.iter().cloned()); - let hash = combine_hashes(hash_script, hash_params); - - // First check the global namespace and packages, but skip modules that are standard because - // they should never conflict with system functions. - self.global_modules.iter().filter(|m| !m.standard).any(|m| m.contains_fn(hash)) - // Then check sub-modules - || self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash)) - } - /// Check a result to ensure that the data size is within allowable limit. fn check_return_value(&self, mut result: RhaiResult) -> RhaiResult { if let Ok(ref mut r) = result { diff --git a/src/optimizer.rs b/src/optimizer.rs index 2ad5bc47..6f58e2ae 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -9,10 +9,14 @@ use crate::func::builtin::get_builtin_binary_op_fn; use crate::func::hashing::get_hasher; use crate::tokenizer::Token; use crate::types::dynamic::AccessMode; -use crate::{calc_fn_hash, Dynamic, Engine, FnPtr, Position, Scope, StaticVec, AST, INT}; +use crate::{ + calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Position, Scope, + StaticVec, AST, INT, +}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ + any::TypeId, convert::TryFrom, hash::{Hash, Hasher}, mem, @@ -152,6 +156,18 @@ impl<'a> OptimizerState<'a> { } } +// Has a system function a Rust-native override? +fn has_native_fn_override(engine: &Engine, hash_script: u64, arg_types: &[TypeId]) -> bool { + let hash_params = calc_fn_params_hash(arg_types.iter().cloned()); + let hash = combine_hashes(hash_script, hash_params); + + // First check the global namespace and packages, but skip modules that are standard because + // they should never conflict with system functions. + engine.global_modules.iter().filter(|m| !m.standard).any(|m| m.contains_fn(hash)) + // Then check sub-modules + || engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash)) +} + /// Optimize a block of [statements][Stmt]. fn optimize_stmt_block( mut statements: StaticVec, @@ -970,7 +986,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { return; } // Overloaded operators can override built-in. - _ if x.args.len() == 2 && !state.engine.has_native_fn_override(x.hashes.native, arg_types.as_ref()) => { + _ if x.args.len() == 2 && !has_native_fn_override(state.engine, x.hashes.native, arg_types.as_ref()) => { if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1]) .and_then(|f| { #[cfg(not(feature = "no_function"))] diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index b6c2d894..ecd11b82 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -4,7 +4,7 @@ use crate::plugin::*; use crate::{ def_package, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position, - RhaiResultOf, ERR, INT, + RhaiResultOf, INT, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -46,7 +46,9 @@ pub mod blob_functions { // Check if blob will be over max size limit #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { - return Err(ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into()); + return Err( + crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(), + ); } let mut blob = Blob::new(); @@ -117,7 +119,9 @@ pub mod blob_functions { // Check if blob will be over max size limit #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_array_size() > 0 && (len as usize) > _ctx.engine().max_array_size() { - return Err(ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into()); + return Err( + crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(), + ); } if len as usize > blob.len() { diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index ce1c3345..cd5579ef 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -1,6 +1,6 @@ use crate::plugin::*; use crate::types::dynamic::Variant; -use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, ERR, INT}; +use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT}; use std::iter::{ExactSizeIterator, FusedIterator}; use std::ops::{Range, RangeInclusive}; #[cfg(feature = "no_std")] @@ -26,15 +26,15 @@ where #[cfg(not(feature = "unchecked"))] if let Some(r) = from.checked_add(&step) { if r == from { - return Err(ERR::ErrorInFunctionCall( + return Err(crate::ERR::ErrorInFunctionCall( "range".to_string(), String::new(), - ERR::ErrorArithmetic( + crate::ERR::ErrorArithmetic( "step value cannot be zero".to_string(), - crate::Position::NONE, + Position::NONE, ) .into(), - crate::Position::NONE, + Position::NONE, ) .into()); } @@ -123,18 +123,18 @@ impl BitRange { #[cfg(not(feature = "unchecked"))] if offset >= BITS { - return Err(ERR::ErrorBitFieldBounds(BITS, from, crate::Position::NONE).into()); + return Err(crate::ERR::ErrorBitFieldBounds(BITS, from, Position::NONE).into()); } offset } else { #[cfg(not(feature = "unchecked"))] if let Some(abs_from) = from.checked_abs() { if (abs_from as usize) > BITS { - return Err(ERR::ErrorBitFieldBounds(BITS, from, crate::Position::NONE).into()); + return Err(crate::ERR::ErrorBitFieldBounds(BITS, from, Position::NONE).into()); } BITS - (abs_from as usize) } else { - return Err(ERR::ErrorBitFieldBounds(BITS, from, crate::Position::NONE).into()); + return Err(crate::ERR::ErrorBitFieldBounds(BITS, from, Position::NONE).into()); } #[cfg(feature = "unchecked")] @@ -328,9 +328,9 @@ def_package! { pub fn new(from: FLOAT, to: FLOAT, step: FLOAT) -> RhaiResultOf { #[cfg(not(feature = "unchecked"))] if step == 0.0 { - return Err(ERR::ErrorInFunctionCall("range".to_string(), "".to_string(), - ERR::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(), - crate::Position::NONE, + return Err(crate::ERR::ErrorInFunctionCall("range".to_string(), "".to_string(), + crate::ERR::ErrorArithmetic("step value cannot be zero".to_string(), Position::NONE).into(), + Position::NONE, ).into()); } @@ -390,9 +390,9 @@ def_package! { pub fn new(from: Decimal, to: Decimal, step: Decimal) -> RhaiResultOf { #[cfg(not(feature = "unchecked"))] if step.is_zero() { - return Err(ERR::ErrorInFunctionCall("range".to_string(), "".to_string(), - ERR::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(), - crate::Position::NONE, + return Err(crate::ERR::ErrorInFunctionCall("range".to_string(), "".to_string(), + crate::ERR::ErrorArithmetic("step value cannot be zero".to_string(), Position::NONE).into(), + Position::NONE, ).into()); } diff --git a/src/parser.rs b/src/parser.rs index a29c65d9..e302a150 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -182,6 +182,7 @@ impl<'e> ParseState<'e> { /// Get an interned string, creating one if it is not yet interned. #[inline(always)] + #[allow(dead_code)] #[must_use] pub fn get_interned_string( &mut self, diff --git a/src/types/interner.rs b/src/types/interner.rs index df3c4af7..db9c2745 100644 --- a/src/types/interner.rs +++ b/src/types/interner.rs @@ -1,8 +1,9 @@ +#[cfg(not(feature = "no_object"))] use crate::engine::{make_getter, make_setter, FN_GET, FN_SET}; use crate::{Identifier, ImmutableString}; +use std::ops::AddAssign; #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{collections::BTreeMap, ops::AddAssign}; /// _(internals)_ A factory of identifiers from text strings. /// Exported under the `internals` feature only. @@ -14,9 +15,11 @@ use std::{collections::BTreeMap, ops::AddAssign}; #[derive(Debug, Clone, Default, Hash)] pub struct StringsInterner { /// Property getters. - getters: BTreeMap, + #[cfg(not(feature = "no_object"))] + getters: std::collections::BTreeMap, /// Property setters. - setters: BTreeMap, + #[cfg(not(feature = "no_object"))] + setters: std::collections::BTreeMap, } impl StringsInterner { @@ -25,8 +28,10 @@ impl StringsInterner { #[must_use] pub fn new() -> Self { Self { - getters: BTreeMap::new(), - setters: BTreeMap::new(), + #[cfg(not(feature = "no_object"))] + getters: std::collections::BTreeMap::new(), + #[cfg(not(feature = "no_object"))] + setters: std::collections::BTreeMap::new(), } } /// Get an identifier from a text string and prefix, adding it to the interner if necessary. @@ -41,20 +46,29 @@ impl StringsInterner { prefix: &'static str, text: impl AsRef + Into + Into, ) -> ImmutableString { - let (dict, mapper) = match prefix { - "" => return text.into(), - FN_GET => (&mut self.getters, make_getter as fn(&str) -> String), - FN_SET => (&mut self.setters, make_setter as fn(&str) -> String), - _ => unreachable!("unsupported prefix {}", prefix), - }; + #[cfg(not(feature = "no_object"))] + { + let (dict, mapper) = match prefix { + "" => return text.into(), + FN_GET => (&mut self.getters, make_getter as fn(&str) -> String), + FN_SET => (&mut self.setters, make_setter as fn(&str) -> String), + _ => unreachable!("unsupported prefix {}", prefix), + }; - if dict.contains_key(text.as_ref()) { - self.getters.get(text.as_ref()).expect("exists").clone() - } else { - let value: ImmutableString = mapper(text.as_ref()).into(); - let text = text.into(); - dict.insert(text, value.clone()); - value + if dict.contains_key(text.as_ref()) { + self.getters.get(text.as_ref()).expect("exists").clone() + } else { + let value: ImmutableString = mapper(text.as_ref()).into(); + let text = text.into(); + dict.insert(text, value.clone()); + value + } + } + + #[cfg(feature = "no_object")] + match prefix { + "" => return text.into(), + _ => unreachable!("unsupported prefix {}", prefix), } } /// Merge another [`IdentifierBuilder`] into this. @@ -65,7 +79,12 @@ impl StringsInterner { impl AddAssign for StringsInterner { #[inline(always)] fn add_assign(&mut self, rhs: Self) { - self.getters.extend(rhs.getters.into_iter()); - self.setters.extend(rhs.setters.into_iter()); + let _rhs = rhs; + + #[cfg(not(feature = "no_object"))] + { + self.getters.extend(_rhs.getters.into_iter()); + self.setters.extend(_rhs.setters.into_iter()); + } } } From a78488d9356788a00ca159d3ac53089e9c2b89e3 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 27 Dec 2021 23:03:30 +0800 Subject: [PATCH 51/97] Rename Imports to GlobalRuntimeState. --- src/api/call_fn.rs | 8 +- src/api/custom_syntax.rs | 2 +- src/api/eval.rs | 14 +- src/api/run.rs | 10 +- src/ast/script_fn.rs | 4 +- src/engine.rs | 412 +++++++++++++++++++-------------------- src/func/call.rs | 155 +++++++++------ src/func/native.rs | 33 ++-- src/func/script.rs | 32 +-- src/lib.rs | 2 +- src/module/mod.rs | 32 +-- src/optimizer.rs | 7 +- src/parser.rs | 4 +- 13 files changed, 373 insertions(+), 342 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index f7c3c4d0..28226949 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -1,7 +1,7 @@ //! Module that defines the `call_fn` API of [`Engine`]. #![cfg(not(feature = "no_function"))] -use crate::engine::{EvalState, Imports}; +use crate::engine::{EvalState, GlobalRuntimeState}; use crate::types::dynamic::Variant; use crate::{ Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST, ERR, @@ -154,13 +154,13 @@ impl Engine { arg_values: impl AsMut<[Dynamic]>, ) -> RhaiResult { let state = &mut EvalState::new(); - let mods = &mut Imports::new(); + let global = &mut GlobalRuntimeState::new(); let statements = ast.statements(); let orig_scope_len = scope.len(); if eval_ast && !statements.is_empty() { - self.eval_global_statements(scope, mods, state, statements, &[ast.as_ref()], 0)?; + self.eval_global_statements(scope, global, state, statements, &[ast.as_ref()], 0)?; if rewind_scope { scope.rewind(orig_scope_len); @@ -183,7 +183,7 @@ impl Engine { let result = self.call_script_fn( scope, - mods, + global, state, &[ast.as_ref()], &mut this_ptr, diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index 2cc109c6..5331eb6a 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -163,7 +163,7 @@ impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> { pub fn eval_expression_tree(&mut self, expr: &Expression) -> RhaiResult { self.engine.eval_expr( self.scope, - self.mods, + self.global, self.state, self.lib, self.this_ptr, diff --git a/src/api/eval.rs b/src/api/eval.rs index 66358191..51dadfec 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -1,6 +1,6 @@ //! Module that defines the public evaluation API of [`Engine`]. -use crate::engine::{EvalState, Imports}; +use crate::engine::{EvalState, GlobalRuntimeState}; use crate::parser::ParseState; use crate::types::dynamic::Variant; use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR}; @@ -184,9 +184,9 @@ impl Engine { scope: &mut Scope, ast: &AST, ) -> RhaiResultOf { - let mods = &mut Imports::new(); + let global = &mut GlobalRuntimeState::new(); - let result = self.eval_ast_with_scope_raw(scope, mods, ast, 0)?; + let result = self.eval_ast_with_scope_raw(scope, global, ast, 0)?; let typ = self.map_type_name(result.type_name()); @@ -204,17 +204,17 @@ impl Engine { pub(crate) fn eval_ast_with_scope_raw<'a>( &self, scope: &mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, ast: &'a AST, level: usize, ) -> RhaiResult { let mut state = EvalState::new(); if ast.source_raw().is_some() { - mods.source = ast.source_raw().cloned(); + global.source = ast.source_raw().cloned(); } #[cfg(not(feature = "no_module"))] { - mods.embedded_module_resolver = ast.resolver().cloned(); + global.embedded_module_resolver = ast.resolver().cloned(); } let statements = ast.statements(); @@ -232,6 +232,6 @@ impl Engine { } else { &lib }; - self.eval_global_statements(scope, mods, &mut state, statements, lib, level) + self.eval_global_statements(scope, global, &mut state, statements, lib, level) } } diff --git a/src/api/run.rs b/src/api/run.rs index 08805846..aa27683f 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -1,6 +1,6 @@ //! Module that defines the public evaluation API of [`Engine`]. -use crate::engine::{EvalState, Imports}; +use crate::engine::{EvalState, GlobalRuntimeState}; use crate::parser::ParseState; use crate::{Engine, Module, RhaiResultOf, Scope, AST}; #[cfg(feature = "no_std")] @@ -44,14 +44,14 @@ impl Engine { /// Evaluate an [`AST`] with own scope, returning any error (if any). #[inline] pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> { - let mods = &mut Imports::new(); + let global = &mut GlobalRuntimeState::new(); let mut state = EvalState::new(); if ast.source_raw().is_some() { - mods.source = ast.source_raw().cloned(); + global.source = ast.source_raw().cloned(); } #[cfg(not(feature = "no_module"))] { - mods.embedded_module_resolver = ast.resolver().cloned(); + global.embedded_module_resolver = ast.resolver().cloned(); } let statements = ast.statements(); @@ -65,7 +65,7 @@ impl Engine { } else { &lib }; - self.eval_global_statements(scope, mods, &mut state, statements, lib, 0)?; + self.eval_global_statements(scope, global, &mut state, statements, lib, 0)?; } Ok(()) } diff --git a/src/ast/script_fn.rs b/src/ast/script_fn.rs index c0bd55ca..a8dd6d39 100644 --- a/src/ast/script_fn.rs +++ b/src/ast/script_fn.rs @@ -15,11 +15,11 @@ pub struct ScriptFnDef { pub body: StmtBlock, /// Encapsulated running environment, if any. pub lib: Option>, - /// Encapsulated imported modules. + /// Encapsulated [`GlobalRuntimeState`][crate::GlobalRuntimeState]. /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] - pub mods: crate::engine::Imports, + pub global: crate::engine::GlobalRuntimeState, /// Function name. pub name: Identifier, /// Function access mode. diff --git a/src/engine.rs b/src/engine.rs index eb0a1f8f..41fd4242 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -28,7 +28,7 @@ use std::{ pub type Precedence = NonZeroU8; -/// _(internals)_ A stack of imported [modules][Module] plus mutable runtime global states. +/// _(internals)_ A stack of imported [modules][Module] plus mutable global runtime states. /// Exported under the `internals` feature only. // // # Implementation Notes @@ -37,7 +37,7 @@ pub type Precedence = NonZeroU8; // Most usage will be looking up a particular key from the list and then getting the module that // corresponds to that key. #[derive(Clone)] -pub struct Imports { +pub struct GlobalRuntimeState { /// Stack of module names. // // # Implementation Notes @@ -47,12 +47,14 @@ pub struct Imports { keys: StaticVec, /// Stack of imported modules. modules: StaticVec>, + /// Source of the current context. pub source: Option, /// Number of operations performed. pub num_operations: u64, /// Number of modules loaded. - pub num_modules: usize, + pub num_modules_loaded: usize, + /// Function call hashes to index getters and setters. /// /// Not available under `no_index` and `no_object`. @@ -63,23 +65,24 @@ pub struct Imports { /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] pub embedded_module_resolver: Option>, + /// Cache of globally-defined constants. /// /// Not available under `no_module` and `no_function`. #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] - global_constants: Option>>>, + constants: Option>>>, } -impl Default for Imports { +impl Default for GlobalRuntimeState { #[inline(always)] fn default() -> Self { Self::new() } } -impl Imports { - /// Create a new stack of imported [modules][Module]. +impl GlobalRuntimeState { + /// Create a new [`GlobalRuntimeState`]. #[inline(always)] #[must_use] pub const fn new() -> Self { @@ -88,45 +91,39 @@ impl Imports { modules: StaticVec::new_const(), source: None, num_operations: 0, - num_modules: 0, + num_modules_loaded: 0, #[cfg(not(feature = "no_module"))] embedded_module_resolver: None, #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn_hash_indexing: (0, 0), #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] - global_constants: None, + constants: None, } } - /// Get the length of this stack of imported [modules][Module]. + /// Get the length of the stack of globally-imported [modules][Module]. #[inline(always)] #[must_use] - pub fn len(&self) -> usize { + pub fn num_imported_modules(&self) -> usize { self.keys.len() } - /// Is this stack of imported [modules][Module] empty? + /// Get the globally-imported [module][Module] at a particular index. #[inline(always)] #[must_use] - pub fn is_empty(&self) -> bool { - self.keys.is_empty() - } - /// Get the imported [module][Module] at a particular index. - #[inline(always)] - #[must_use] - pub fn get(&self, index: usize) -> Option> { + pub fn get_shared_module(&self, index: usize) -> Option> { self.modules.get(index).cloned() } - /// Get the imported [module][Module] at a particular index. + /// Get a mutable reference to the globally-imported [module][Module] at a particular index. #[allow(dead_code)] #[inline(always)] #[must_use] - pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut Shared> { + pub(crate) fn get_shared_module_mut(&mut self, index: usize) -> Option<&mut Shared> { self.modules.get_mut(index) } - /// Get the index of an imported [module][Module] by name. + /// Get the index of a globally-imported [module][Module] by name. #[inline] #[must_use] - pub fn find(&self, name: impl AsRef) -> Option { + pub fn find_module(&self, name: impl AsRef) -> Option { let name = name.as_ref(); let len = self.keys.len(); @@ -140,46 +137,46 @@ impl Imports { } /// Push an imported [module][Module] onto the stack. #[inline(always)] - pub fn push(&mut self, name: impl Into, module: impl Into>) { + pub fn push_module(&mut self, name: impl Into, module: impl Into>) { self.keys.push(name.into()); self.modules.push(module.into()); } - /// Truncate the stack of imported [modules][Module] to a particular length. + /// Truncate the stack of globally-imported [modules][Module] to a particular length. #[inline(always)] - pub fn truncate(&mut self, size: usize) { + pub fn truncate_modules(&mut self, size: usize) { self.keys.truncate(size); self.modules.truncate(size); } - /// Get an iterator to this stack of imported [modules][Module] in reverse order. + /// Get an iterator to the stack of globally-imported [modules][Module] in reverse order. #[allow(dead_code)] #[inline] - pub fn iter(&self) -> impl Iterator { + pub fn iter_modules(&self) -> impl Iterator { self.keys .iter() .rev() .zip(self.modules.iter().rev()) .map(|(name, module)| (name.as_str(), module.as_ref())) } - /// Get an iterator to this stack of imported [modules][Module] in reverse order. + /// Get an iterator to the stack of globally-imported [modules][Module] in reverse order. #[allow(dead_code)] #[inline] - pub(crate) fn iter_raw(&self) -> impl Iterator)> { + pub(crate) fn iter_modules_raw(&self) -> impl Iterator)> { self.keys.iter().rev().zip(self.modules.iter().rev()) } - /// Get an iterator to this stack of imported [modules][Module] in forward order. + /// Get an iterator to the stack of globally-imported [modules][Module] in forward order. #[allow(dead_code)] #[inline] - pub(crate) fn scan_raw(&self) -> impl Iterator)> { + pub(crate) fn scan_modules_raw(&self) -> impl Iterator)> { self.keys.iter().zip(self.modules.iter()) } - /// Does the specified function hash key exist in this stack of imported [modules][Module]? + /// Does the specified function hash key exist in the stack of globally-imported [modules][Module]? #[allow(dead_code)] #[inline] #[must_use] pub fn contains_fn(&self, hash: u64) -> bool { self.modules.iter().any(|m| m.contains_qualified_fn(hash)) } - /// Get the specified function via its hash key from this stack of imported [modules][Module]. + /// Get the specified function via its hash key from the stack of globally-imported [modules][Module]. #[inline] #[must_use] pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> { @@ -188,15 +185,15 @@ impl Imports { .rev() .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) } - /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in this stack of - /// imported [modules][Module]? + /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of + /// globally-imported [modules][Module]? #[allow(dead_code)] #[inline] #[must_use] pub fn contains_iter(&self, id: TypeId) -> bool { self.modules.iter().any(|m| m.contains_qualified_iter(id)) } - /// Get the specified [`TypeId`][std::any::TypeId] iterator from this stack of imported + /// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported /// [modules][Module]. #[inline] #[must_use] @@ -210,10 +207,10 @@ impl Imports { #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] #[must_use] - pub(crate) fn global_constants_mut<'a>( + pub(crate) fn constants_mut<'a>( &'a mut self, ) -> Option> + 'a> { - if let Some(ref global_constants) = self.global_constants { + if let Some(ref global_constants) = self.constants { Some(crate::func::native::shared_write_lock(global_constants)) } else { None @@ -222,13 +219,13 @@ impl Imports { /// Set a constant into the cache of globally-defined constants. #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] - pub(crate) fn set_global_constant(&mut self, name: impl Into, value: Dynamic) { - if self.global_constants.is_none() { + pub(crate) fn set_constant(&mut self, name: impl Into, value: Dynamic) { + if self.constants.is_none() { let dict: crate::Locked<_> = BTreeMap::new().into(); - self.global_constants = Some(dict.into()); + self.constants = Some(dict.into()); } - crate::func::native::shared_write_lock(self.global_constants.as_mut().expect("`Some`")) + crate::func::native::shared_write_lock(self.constants.as_mut().expect("`Some`")) .insert(name.into(), value); } /// Get the pre-calculated index getter hash. @@ -259,7 +256,7 @@ impl Imports { } } -impl IntoIterator for Imports { +impl IntoIterator for GlobalRuntimeState { type Item = (Identifier, Shared); type IntoIter = Zip>, Rev; 3]>>>; @@ -273,7 +270,7 @@ impl IntoIterator for Imports { } } -impl, M: Into>> FromIterator<(K, M)> for Imports { +impl, M: Into>> FromIterator<(K, M)> for GlobalRuntimeState { fn from_iter>(iter: T) -> Self { let mut lib = Self::new(); lib.extend(iter); @@ -281,7 +278,7 @@ impl, M: Into>> FromIterator<(K, M)> for Impo } } -impl, M: Into>> Extend<(K, M)> for Imports { +impl, M: Into>> Extend<(K, M)> for GlobalRuntimeState { fn extend>(&mut self, iter: T) { iter.into_iter().for_each(|(k, m)| { self.keys.push(k.into()); @@ -290,17 +287,12 @@ impl, M: Into>> Extend<(K, M)> for Imports { } } -impl fmt::Debug for Imports { +impl fmt::Debug for GlobalRuntimeState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Imports")?; - - if self.is_empty() { - f.debug_map().finish() - } else { - f.debug_map() - .entries(self.keys.iter().zip(self.modules.iter())) - .finish() - } + f.debug_map() + .entries(self.keys.iter().zip(self.modules.iter())) + .finish() } } @@ -846,7 +838,7 @@ impl EvalState { pub struct EvalContext<'a, 'x, 'px, 'm, 's, 'b, 't, 'pt> { pub(crate) engine: &'a Engine, pub(crate) scope: &'x mut Scope<'px>, - pub(crate) mods: &'m mut Imports, + pub(crate) global: &'m mut GlobalRuntimeState, pub(crate) state: &'s mut EvalState, pub(crate) lib: &'b [&'b Module], pub(crate) this_ptr: &'t mut Option<&'pt mut Dynamic>, @@ -864,7 +856,7 @@ impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> { #[inline(always)] #[must_use] pub fn source(&self) -> Option<&str> { - self.mods.source.as_ref().map(|s| s.as_str()) + self.global.source.as_ref().map(|s| s.as_str()) } /// The current [`Scope`]. #[inline(always)] @@ -882,16 +874,16 @@ impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> { #[cfg(not(feature = "no_module"))] #[inline(always)] pub fn iter_imports(&self) -> impl Iterator { - self.mods.iter() + self.global.iter_modules() } - /// _(internals)_ The current set of modules imported via `import` statements. + /// _(internals)_ The current [`GlobalRuntimeState`]. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] - pub const fn imports(&self) -> &Imports { - self.mods + pub const fn global_runtime_state(&self) -> &GlobalRuntimeState { + self.global } /// Get an iterator over the namespaces containing definition of all script-defined functions. #[inline] @@ -1143,7 +1135,7 @@ impl Engine { #[must_use] pub(crate) fn search_imports( &self, - mods: &Imports, + global: &GlobalRuntimeState, state: &mut EvalState, namespace: &Namespace, ) -> Option> { @@ -1157,11 +1149,12 @@ impl Engine { }; if let Some(index) = index { - let offset = mods.len() - index.get(); - Some(mods.get(offset).expect("within range")) + let offset = global.num_imported_modules() - index.get(); + Some(global.get_shared_module(offset).expect("within range")) } else { - mods.find(root) - .map(|n| mods.get(n).expect("valid index")) + global + .find_module(root) + .map(|n| global.get_shared_module(n).expect("valid index")) .or_else(|| self.global_sub_modules.get(root).cloned()) } } @@ -1171,7 +1164,7 @@ impl Engine { pub(crate) fn search_namespace<'s>( &self, scope: &'s mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], this_ptr: &'s mut Option<&mut Dynamic>, @@ -1179,16 +1172,16 @@ impl Engine { ) -> RhaiResultOf<(Target<'s>, Position)> { match expr { Expr::Variable(Some(_), _, _) => { - self.search_scope_only(scope, mods, state, lib, this_ptr, expr) + self.search_scope_only(scope, global, state, lib, this_ptr, expr) } Expr::Variable(None, _var_pos, v) => match v.as_ref() { // Normal variable access - (_, None, _) => self.search_scope_only(scope, mods, state, lib, this_ptr, expr), + (_, None, _) => self.search_scope_only(scope, global, state, lib, this_ptr, expr), // Qualified variable access #[cfg(not(feature = "no_module"))] (_, Some((namespace, hash_var)), var_name) => { - if let Some(module) = self.search_imports(mods, state, namespace) { + if let Some(module) = self.search_imports(global, state, namespace) { // foo:bar::baz::VARIABLE return match module.get_qualified_var(*hash_var) { Ok(target) => { @@ -1216,7 +1209,7 @@ impl Engine { #[cfg(not(feature = "no_function"))] if namespace.len() == 1 && namespace[0].name == KEYWORD_GLOBAL { // global::VARIABLE - let global_constants = mods.global_constants_mut(); + let global_constants = global.constants_mut(); if let Some(mut guard) = global_constants { if let Some(value) = guard.get_mut(var_name) { @@ -1257,7 +1250,7 @@ impl Engine { pub(crate) fn search_scope_only<'s>( &self, scope: &'s mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], this_ptr: &'s mut Option<&mut Dynamic>, @@ -1285,7 +1278,7 @@ impl Engine { let context = EvalContext { engine: self, scope, - mods, + global, state, lib, this_ptr, @@ -1326,7 +1319,7 @@ impl Engine { #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn eval_dot_index_chain_helper( &self, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, @@ -1363,13 +1356,13 @@ impl Engine { let (try_setter, result) = { let mut obj = self.get_indexed_mut( - mods, state, lib, target, idx_val, idx_pos, false, true, level, + global, state, lib, target, idx_val, idx_pos, false, true, level, )?; let is_obj_temp_val = obj.is_temp_value(); let obj_ptr = &mut obj; match self.eval_dot_index_chain_helper( - mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, *term, + global, state, lib, this_ptr, obj_ptr, root, &x.rhs, *term, idx_values, rhs_chain, level, new_val, ) { Ok((result, true)) if is_obj_temp_val => { @@ -1383,11 +1376,11 @@ impl Engine { if let Some(mut new_val) = try_setter { // Try to call index setter if value is changed let hash_set = - crate::ast::FnCallHashes::from_native(mods.hash_idx_set()); + crate::ast::FnCallHashes::from_native(global.hash_idx_set()); let args = &mut [target, &mut idx_val_for_setter, &mut new_val]; if let Err(err) = self.exec_fn_call( - mods, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true, + global, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true, root_pos, None, level, ) { // Just ignore if there is no index setter @@ -1408,12 +1401,12 @@ impl Engine { let mut idx_val_for_setter = idx_val.clone(); let try_setter = match self.get_indexed_mut( - mods, state, lib, target, idx_val, pos, true, false, level, + global, state, lib, target, idx_val, pos, true, false, level, ) { // Indexed value is a reference - update directly Ok(ref mut obj_ptr) => { self.eval_op_assignment( - mods, state, lib, op_info, op_pos, obj_ptr, root, new_val, + global, state, lib, op_info, op_pos, obj_ptr, root, new_val, ) .map_err(|err| err.fill_position(new_pos))?; None @@ -1430,11 +1423,11 @@ impl Engine { if let Some(mut new_val) = try_setter { // Try to call index setter let hash_set = - crate::ast::FnCallHashes::from_native(mods.hash_idx_set()); + crate::ast::FnCallHashes::from_native(global.hash_idx_set()); let args = &mut [target, &mut idx_val_for_setter, &mut new_val]; self.exec_fn_call( - mods, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true, + global, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true, root_pos, None, level, )?; } @@ -1446,7 +1439,9 @@ impl Engine { } // xxx[rhs] _ => self - .get_indexed_mut(mods, state, lib, target, idx_val, pos, false, true, level) + .get_indexed_mut( + global, state, lib, target, idx_val, pos, false, true, level, + ) .map(|v| (v.take_or_clone(), false)), } } @@ -1459,7 +1454,7 @@ impl Engine { let FnCallExpr { name, hashes, .. } = x.as_ref(); let call_args = &mut idx_val.into_fn_call_args(); self.make_method_call( - mods, state, lib, name, *hashes, target, call_args, *pos, level, + global, state, lib, name, *hashes, target, call_args, *pos, level, ) } // xxx.fn_name(...) = ??? @@ -1477,10 +1472,10 @@ impl Engine { let index = name.into(); { let val_target = &mut self.get_indexed_mut( - mods, state, lib, target, index, *pos, true, false, level, + global, state, lib, target, index, *pos, true, false, level, )?; self.eval_op_assignment( - mods, state, lib, op_info, op_pos, val_target, root, new_val, + global, state, lib, op_info, op_pos, val_target, root, new_val, ) .map_err(|err| err.fill_position(new_pos))?; } @@ -1493,7 +1488,7 @@ impl Engine { let (name, pos) = &x.2; let index = name.into(); let val = self.get_indexed_mut( - mods, state, lib, target, index, *pos, false, false, level, + global, state, lib, target, index, *pos, false, false, level, )?; Ok((val.take_or_clone(), false)) } @@ -1507,7 +1502,7 @@ impl Engine { let args = &mut [target.as_mut()]; let (mut orig_val, _) = self .exec_fn_call( - mods, state, lib, getter, hash, args, is_ref_mut, true, *pos, + global, state, lib, getter, hash, args, is_ref_mut, true, *pos, None, level, ) .or_else(|err| match *err { @@ -1515,7 +1510,7 @@ impl Engine { ERR::ErrorDotExpr(_, _) => { let prop = name.into(); self.get_indexed_mut( - mods, state, lib, target, prop, *pos, false, true, + global, state, lib, target, prop, *pos, false, true, level, ) .map(|v| (v.take_or_clone(), false)) @@ -1530,7 +1525,7 @@ impl Engine { })?; self.eval_op_assignment( - mods, + global, state, lib, op_info, @@ -1550,7 +1545,7 @@ impl Engine { let hash = crate::ast::FnCallHashes::from_native(*hash_set); let args = &mut [target.as_mut(), &mut new_val]; self.exec_fn_call( - mods, state, lib, setter, hash, args, is_ref_mut, true, *pos, None, + global, state, lib, setter, hash, args, is_ref_mut, true, *pos, None, level, ) .or_else(|err| match *err { @@ -1558,12 +1553,12 @@ impl Engine { ERR::ErrorDotExpr(_, _) => { let args = &mut [target, &mut name.into(), &mut new_val]; let hash_set = - crate::ast::FnCallHashes::from_native(mods.hash_idx_set()); + crate::ast::FnCallHashes::from_native(global.hash_idx_set()); let pos = Position::NONE; self.exec_fn_call( - mods, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true, - pos, None, level, + global, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, + true, pos, None, level, ) .map_err( |idx_err| match *idx_err { @@ -1581,7 +1576,7 @@ impl Engine { let hash = crate::ast::FnCallHashes::from_native(*hash_get); let args = &mut [target.as_mut()]; self.exec_fn_call( - mods, state, lib, getter, hash, args, is_ref_mut, true, *pos, None, + global, state, lib, getter, hash, args, is_ref_mut, true, *pos, None, level, ) .map_or_else( @@ -1590,7 +1585,7 @@ impl Engine { ERR::ErrorDotExpr(_, _) => { let prop = name.into(); self.get_indexed_mut( - mods, state, lib, target, prop, *pos, false, true, level, + global, state, lib, target, prop, *pos, false, true, level, ) .map(|v| (v.take_or_clone(), false)) .map_err(|idx_err| { @@ -1615,7 +1610,7 @@ impl Engine { let (name, pos) = &p.2; let index = name.into(); self.get_indexed_mut( - mods, state, lib, target, index, *pos, false, true, level, + global, state, lib, target, index, *pos, false, true, level, )? } // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr @@ -1623,7 +1618,8 @@ impl Engine { let FnCallExpr { name, hashes, .. } = x.as_ref(); let call_args = &mut idx_val.into_fn_call_args(); let (val, _) = self.make_method_call( - mods, state, lib, name, *hashes, target, call_args, pos, level, + global, state, lib, name, *hashes, target, call_args, pos, + level, )?; val.into() } @@ -1637,7 +1633,7 @@ impl Engine { let rhs_chain = match_chaining_type(rhs); self.eval_dot_index_chain_helper( - mods, state, lib, this_ptr, val_target, root, &x.rhs, *term, + global, state, lib, this_ptr, val_target, root, &x.rhs, *term, idx_values, rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(*x_pos)) @@ -1658,16 +1654,16 @@ impl Engine { // Assume getters are always pure let (mut val, _) = self .exec_fn_call( - mods, state, lib, getter, hash_get, args, is_ref_mut, true, - *pos, None, level, + global, state, lib, getter, hash_get, args, is_ref_mut, + true, *pos, None, level, ) .or_else(|err| match *err { // Try an indexer if property does not exist ERR::ErrorDotExpr(_, _) => { let prop = name.into(); self.get_indexed_mut( - mods, state, lib, target, prop, *pos, false, true, - level, + global, state, lib, target, prop, *pos, false, + true, level, ) .map(|v| (v.take_or_clone(), false)) .map_err( @@ -1684,7 +1680,7 @@ impl Engine { let (result, may_be_changed) = self .eval_dot_index_chain_helper( - mods, + global, state, lib, this_ptr, @@ -1705,8 +1701,8 @@ impl Engine { let mut arg_values = [target.as_mut(), val]; let args = &mut arg_values; self.exec_fn_call( - mods, state, lib, setter, hash_set, args, is_ref_mut, true, - *pos, None, level, + global, state, lib, setter, hash_set, args, is_ref_mut, + true, *pos, None, level, ) .or_else( |err| match *err { @@ -1716,10 +1712,10 @@ impl Engine { &mut [target.as_mut(), &mut name.into(), val]; let hash_set = crate::ast::FnCallHashes::from_native( - mods.hash_idx_set(), + global.hash_idx_set(), ); self.exec_fn_call( - mods, state, lib, FN_IDX_SET, hash_set, args, + global, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true, *pos, None, level, ) .or_else(|idx_err| match *idx_err { @@ -1746,13 +1742,13 @@ impl Engine { let rhs_chain = match_chaining_type(rhs); let args = &mut idx_val.into_fn_call_args(); let (mut val, _) = self.make_method_call( - mods, state, lib, name, *hashes, target, args, pos, level, + global, state, lib, name, *hashes, target, args, pos, level, )?; let val = &mut val; let target = &mut val.into(); self.eval_dot_index_chain_helper( - mods, state, lib, this_ptr, target, root, &x.rhs, *term, + global, state, lib, this_ptr, target, root, &x.rhs, *term, idx_values, rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(pos)) @@ -1777,7 +1773,7 @@ impl Engine { fn eval_dot_index_chain( &self, scope: &mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, @@ -1796,24 +1792,24 @@ impl Engine { let idx_values = &mut StaticVec::new_const(); self.eval_dot_index_chain_arguments( - scope, mods, state, lib, this_ptr, rhs, term, chain_type, idx_values, 0, level, + scope, global, state, lib, this_ptr, rhs, term, chain_type, idx_values, 0, level, )?; match lhs { // id.??? or id[???] Expr::Variable(_, var_pos, x) => { #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, *var_pos)?; + self.inc_operations(&mut global.num_operations, *var_pos)?; let (mut target, _) = - self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?; + self.search_namespace(scope, global, state, lib, this_ptr, lhs)?; let obj_ptr = &mut target; let root = (x.2.as_str(), *var_pos); self.eval_dot_index_chain_helper( - mods, state, lib, &mut None, obj_ptr, root, rhs, term, idx_values, chain_type, - level, new_val, + global, state, lib, &mut None, obj_ptr, root, rhs, term, idx_values, + chain_type, level, new_val, ) .map(|(v, _)| v) .map_err(|err| err.fill_position(op_pos)) @@ -1822,11 +1818,11 @@ impl Engine { _ if new_val.is_some() => unreachable!("cannot assign to an expression"), // {expr}.??? or {expr}[???] expr => { - let value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; + let value = self.eval_expr(scope, global, state, lib, this_ptr, expr, level)?; let obj_ptr = &mut value.into(); let root = ("", expr.position()); self.eval_dot_index_chain_helper( - mods, state, lib, this_ptr, obj_ptr, root, rhs, term, idx_values, chain_type, + global, state, lib, this_ptr, obj_ptr, root, rhs, term, idx_values, chain_type, level, new_val, ) .map(|(v, _)| v) @@ -1842,7 +1838,7 @@ impl Engine { fn eval_dot_index_chain_arguments( &self, scope: &mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, @@ -1854,7 +1850,7 @@ impl Engine { level: usize, ) -> RhaiResultOf<()> { #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, expr.position())?; + self.inc_operations(&mut global.num_operations, expr.position())?; let _parent_chain_type = parent_chain_type; @@ -1869,7 +1865,7 @@ impl Engine { (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), |(mut values, mut pos), expr| -> RhaiResultOf<_> { let (value, arg_pos) = self.get_arg_value( - scope, mods, state, lib, this_ptr, level, expr, constants, + scope, global, state, lib, this_ptr, level, expr, constants, )?; if values.is_empty() { pos = arg_pos; @@ -1915,7 +1911,7 @@ impl Engine { (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), |(mut values, mut pos), expr| -> RhaiResultOf<_> { let (value, arg_pos) = self.get_arg_value( - scope, mods, state, lib, this_ptr, level, expr, constants, + scope, global, state, lib, this_ptr, level, expr, constants, )?; if values.is_empty() { pos = arg_pos @@ -1936,7 +1932,7 @@ impl Engine { } #[cfg(not(feature = "no_index"))] _ if _parent_chain_type == ChainType::Indexing => self - .eval_expr(scope, mods, state, lib, this_ptr, lhs, level) + .eval_expr(scope, global, state, lib, this_ptr, lhs, level) .map(|v| ChainArgument::from_index_value(v.flatten(), lhs.position()))?, expr => unreachable!("unknown chained expression: {:?}", expr), }; @@ -1945,7 +1941,7 @@ impl Engine { let chain_type = match_chaining_type(expr); self.eval_dot_index_chain_arguments( - scope, mods, state, lib, this_ptr, rhs, *term, chain_type, idx_values, size, + scope, global, state, lib, this_ptr, rhs, *term, chain_type, idx_values, size, level, )?; @@ -1958,7 +1954,7 @@ impl Engine { } #[cfg(not(feature = "no_index"))] _ if _parent_chain_type == ChainType::Indexing => idx_values.push( - self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) + self.eval_expr(scope, global, state, lib, this_ptr, expr, level) .map(|v| ChainArgument::from_index_value(v.flatten(), expr.position()))?, ), _ => unreachable!("unknown chained expression: {:?}", expr), @@ -1972,7 +1968,7 @@ impl Engine { #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn get_indexed_mut<'t>( &self, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], target: &'t mut Dynamic, @@ -1983,7 +1979,7 @@ impl Engine { level: usize, ) -> RhaiResultOf> { #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, Position::NONE)?; + self.inc_operations(&mut global.num_operations, Position::NONE)?; let mut idx = idx; let _add_if_not_found = add_if_not_found; @@ -2220,11 +2216,12 @@ impl Engine { _ if use_indexers => { let args = &mut [target, &mut idx]; - let hash_get = crate::ast::FnCallHashes::from_native(mods.hash_idx_get()); + let hash_get = crate::ast::FnCallHashes::from_native(global.hash_idx_get()); let idx_pos = Position::NONE; self.exec_fn_call( - mods, state, lib, FN_IDX_GET, hash_get, args, true, true, idx_pos, None, level, + global, state, lib, FN_IDX_GET, hash_get, args, true, true, idx_pos, None, + level, ) .map(|(v, _)| v.into()) } @@ -2245,7 +2242,7 @@ impl Engine { pub(crate) fn eval_expr( &self, scope: &mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, @@ -2253,7 +2250,7 @@ impl Engine { level: usize, ) -> RhaiResult { #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, expr.position())?; + self.inc_operations(&mut global.num_operations, expr.position())?; let result = match expr { Expr::DynamicConstant(x, _) => Ok(x.as_ref().clone()), @@ -2268,25 +2265,25 @@ impl Engine { .cloned() .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()), Expr::Variable(_, _, _) => self - .search_namespace(scope, mods, state, lib, this_ptr, expr) + .search_namespace(scope, global, state, lib, this_ptr, expr) .map(|(val, _)| val.take_or_clone()), // Statement block Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), Expr::Stmt(x) => { - self.eval_stmt_block(scope, mods, state, lib, this_ptr, x, true, true, level) + self.eval_stmt_block(scope, global, state, lib, this_ptr, x, true, true, level) } // lhs[idx_expr] #[cfg(not(feature = "no_index"))] Expr::Index(_, _, _) => { - self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None) + self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None) } // lhs.dot_rhs #[cfg(not(feature = "no_object"))] Expr::Dot(_, _, _) => { - self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None) + self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None) } // `... ${...} ...` @@ -2295,10 +2292,10 @@ impl Engine { let mut result: Dynamic = self.const_empty_string().into(); x.iter().try_for_each(|expr| { - let item = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; + let item = self.eval_expr(scope, global, state, lib, this_ptr, expr, level)?; self.eval_op_assignment( - mods, + global, state, lib, Some(OpAssignment::new(OP_CONCAT)), @@ -2330,7 +2327,7 @@ impl Engine { crate::Array::with_capacity(x.len()), |mut arr, item| -> RhaiResultOf<_> { arr.push( - self.eval_expr(scope, mods, state, lib, this_ptr, item, level)? + self.eval_expr(scope, global, state, lib, this_ptr, item, level)? .flatten(), ); Ok(arr) @@ -2347,7 +2344,7 @@ impl Engine { |mut map, (Ident { name: key, .. }, expr)| -> RhaiResultOf<_> { let value_ref = map.get_mut(key.as_str()).expect("contains all keys"); *value_ref = self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .eval_expr(scope, global, state, lib, this_ptr, expr, level)? .flatten(); Ok(map) }, @@ -2367,7 +2364,7 @@ impl Engine { let namespace = namespace.as_ref().expect("qualified function call"); let hash = hashes.native; self.make_qualified_function_call( - scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash, + scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, *pos, level, ) } @@ -2383,19 +2380,19 @@ impl Engine { .. } = x.as_ref(); self.make_function_call( - scope, mods, state, lib, this_ptr, name, args, constants, *hashes, *pos, + scope, global, state, lib, this_ptr, name, args, constants, *hashes, *pos, *capture, level, ) } Expr::And(x, _) => { Ok((self - .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? + .eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? && // Short-circuit using && self - .eval_expr(scope, mods, state, lib, this_ptr, &x.rhs, level)? + .eval_expr(scope, global, state, lib, this_ptr, &x.rhs, level)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) .into()) @@ -2403,12 +2400,12 @@ impl Engine { Expr::Or(x, _) => { Ok((self - .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? + .eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? || // Short-circuit using || self - .eval_expr(scope, mods, state, lib, this_ptr, &x.rhs, level)? + .eval_expr(scope, global, state, lib, this_ptr, &x.rhs, level)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) .into()) @@ -2424,7 +2421,7 @@ impl Engine { let mut context = EvalContext { engine: self, scope, - mods, + global, state, lib, this_ptr, @@ -2444,7 +2441,7 @@ impl Engine { pub(crate) fn eval_stmt_block( &self, scope: &mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, @@ -2459,7 +2456,7 @@ impl Engine { let orig_always_search_scope = state.always_search_scope; let orig_scope_len = scope.len(); - let orig_mods_len = mods.len(); + let orig_mods_len = global.num_imported_modules(); let orig_fn_resolution_caches_len = state.fn_resolution_caches_len(); if rewind_scope { @@ -2467,16 +2464,16 @@ impl Engine { } let result = statements.iter().try_fold(Dynamic::UNIT, |_, stmt| { - let _mods_len = mods.len(); + let _mods_len = global.num_imported_modules(); - let r = self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)?; + let r = self.eval_stmt(scope, global, state, lib, this_ptr, stmt, level)?; #[cfg(not(feature = "no_module"))] if matches!(stmt, Stmt::Import(_, _, _)) { // Get the extra modules - see if any functions are marked global. // Without global functions, the extra modules never affect function resolution. - if mods - .scan_raw() + if global + .scan_modules_raw() .skip(_mods_len) .any(|(_, m)| m.contains_indexed_global_functions()) { @@ -2506,7 +2503,7 @@ impl Engine { state.scope_level -= 1; } if restore_orig_state { - mods.truncate(orig_mods_len); + global.truncate_modules(orig_mods_len); // The impact of new local variables goes away at the end of a block // because any new variables introduced will go out of scope @@ -2520,7 +2517,7 @@ impl Engine { /// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and should be set afterwards. pub(crate) fn eval_op_assignment( &self, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], op_info: Option, @@ -2561,7 +2558,7 @@ impl Engine { let hash = hash_op_assign; let args = &mut [lhs_ptr_inner, &mut new_val]; - match self.call_native_fn(mods, state, lib, op, hash, args, true, true, op_pos) { + match self.call_native_fn(global, state, lib, op, hash, args, true, true, op_pos) { Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, _) if f.starts_with(op)) => { // Expand to `var = var op rhs` @@ -2569,7 +2566,7 @@ impl Engine { // Run function let (value, _) = self.call_native_fn( - mods, state, lib, op, hash_op, args, true, false, op_pos, + global, state, lib, op, hash_op, args, true, false, op_pos, )?; *args[0] = value.flatten(); @@ -2594,7 +2591,7 @@ impl Engine { pub(crate) fn eval_stmt( &self, scope: &mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, @@ -2602,7 +2599,7 @@ impl Engine { level: usize, ) -> RhaiResult { #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, stmt.position())?; + self.inc_operations(&mut global.num_operations, stmt.position())?; let result = match stmt { // No-op @@ -2610,17 +2607,17 @@ impl Engine { // Expression as statement Stmt::Expr(expr) => Ok(self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .eval_expr(scope, global, state, lib, this_ptr, expr, level)? .flatten()), // var op= rhs Stmt::Assignment(x, op_pos) if x.0.is_variable_access(false) => { let (lhs_expr, op_info, rhs_expr) = x.as_ref(); let rhs_val = self - .eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)? + .eval_expr(scope, global, state, lib, this_ptr, rhs_expr, level)? .flatten(); let (mut lhs_ptr, pos) = - self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?; + self.search_namespace(scope, global, state, lib, this_ptr, lhs_expr)?; let var_name = lhs_expr.get_variable_name(false).expect("`Variable`"); @@ -2629,10 +2626,10 @@ impl Engine { } #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, pos)?; + self.inc_operations(&mut global.num_operations, pos)?; self.eval_op_assignment( - mods, + global, state, lib, *op_info, @@ -2655,7 +2652,7 @@ impl Engine { Stmt::Assignment(x, op_pos) => { let (lhs_expr, op_info, rhs_expr) = x.as_ref(); let rhs_val = self - .eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)? + .eval_expr(scope, global, state, lib, this_ptr, rhs_expr, level)? .flatten(); let _new_val = Some(((rhs_val, rhs_expr.position()), (*op_info, *op_pos))); @@ -2669,7 +2666,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] Expr::Index(_, _, _) => { self.eval_dot_index_chain( - scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, + scope, global, state, lib, this_ptr, lhs_expr, level, _new_val, )?; Ok(Dynamic::UNIT) } @@ -2677,7 +2674,7 @@ impl Engine { #[cfg(not(feature = "no_object"))] Expr::Dot(_, _, _) => { self.eval_dot_index_chain( - scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, + scope, global, state, lib, this_ptr, lhs_expr, level, _new_val, )?; Ok(Dynamic::UNIT) } @@ -2688,20 +2685,20 @@ impl Engine { // Block scope Stmt::Block(statements, _) if statements.is_empty() => Ok(Dynamic::UNIT), Stmt::Block(statements, _) => self.eval_stmt_block( - scope, mods, state, lib, this_ptr, statements, true, true, level, + scope, global, state, lib, this_ptr, statements, true, true, level, ), // If statement Stmt::If(expr, x, _) => { let guard_val = self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .eval_expr(scope, global, state, lib, this_ptr, expr, level)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; if guard_val { if !x.0.is_empty() { self.eval_stmt_block( - scope, mods, state, lib, this_ptr, &x.0, true, true, level, + scope, global, state, lib, this_ptr, &x.0, true, true, level, ) } else { Ok(Dynamic::UNIT) @@ -2709,7 +2706,7 @@ impl Engine { } else { if !x.1.is_empty() { self.eval_stmt_block( - scope, mods, state, lib, this_ptr, &x.1, true, true, level, + scope, global, state, lib, this_ptr, &x.1, true, true, level, ) } else { Ok(Dynamic::UNIT) @@ -2721,7 +2718,8 @@ impl Engine { Stmt::Switch(match_expr, x, _) => { let (table, def_stmt, ranges) = x.as_ref(); - let value = self.eval_expr(scope, mods, state, lib, this_ptr, match_expr, level)?; + let value = + self.eval_expr(scope, global, state, lib, this_ptr, match_expr, level)?; let stmt_block = if value.is_hashable() { let hasher = &mut get_hasher(); @@ -2732,7 +2730,7 @@ impl Engine { if let Some(t) = table.get(&hash) { if let Some(ref c) = t.0 { if self - .eval_expr(scope, mods, state, lib, this_ptr, &c, level) + .eval_expr(scope, global, state, lib, this_ptr, &c, level) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::(typ, c.position()) @@ -2759,7 +2757,7 @@ impl Engine { { if let Some(c) = condition { if !self - .eval_expr(scope, mods, state, lib, this_ptr, &c, level) + .eval_expr(scope, global, state, lib, this_ptr, &c, level) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::(typ, c.position()) @@ -2787,7 +2785,7 @@ impl Engine { if let Some(statements) = stmt_block { if !statements.is_empty() { self.eval_stmt_block( - scope, mods, state, lib, this_ptr, statements, true, true, level, + scope, global, state, lib, this_ptr, statements, true, true, level, ) } else { Ok(Dynamic::UNIT) @@ -2796,7 +2794,7 @@ impl Engine { // Default match clause if !def_stmt.is_empty() { self.eval_stmt_block( - scope, mods, state, lib, this_ptr, def_stmt, true, true, level, + scope, global, state, lib, this_ptr, def_stmt, true, true, level, ) } else { Ok(Dynamic::UNIT) @@ -2807,9 +2805,9 @@ impl Engine { // Loop Stmt::While(Expr::Unit(_), body, _) => loop { if !body.is_empty() { - match self - .eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, true, level) - { + match self.eval_stmt_block( + scope, global, state, lib, this_ptr, body, true, true, level, + ) { Ok(_) => (), Err(err) => match *err { ERR::LoopBreak(false, _) => (), @@ -2819,14 +2817,14 @@ impl Engine { } } else { #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, body.position())?; + self.inc_operations(&mut global.num_operations, body.position())?; } }, // While loop Stmt::While(expr, body, _) => loop { let condition = self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .eval_expr(scope, global, state, lib, this_ptr, expr, level)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; @@ -2834,9 +2832,9 @@ impl Engine { return Ok(Dynamic::UNIT); } if !body.is_empty() { - match self - .eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, true, level) - { + match self.eval_stmt_block( + scope, global, state, lib, this_ptr, body, true, true, level, + ) { Ok(_) => (), Err(err) => match *err { ERR::LoopBreak(false, _) => (), @@ -2852,9 +2850,9 @@ impl Engine { let is_while = !options.contains(AST_OPTION_NEGATED); if !body.is_empty() { - match self - .eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, true, level) - { + match self.eval_stmt_block( + scope, global, state, lib, this_ptr, body, true, true, level, + ) { Ok(_) => (), Err(err) => match *err { ERR::LoopBreak(false, _) => continue, @@ -2865,7 +2863,7 @@ impl Engine { } let condition = self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .eval_expr(scope, global, state, lib, this_ptr, expr, level)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; @@ -2878,7 +2876,7 @@ impl Engine { Stmt::For(expr, x, _) => { let (Ident { name, .. }, counter, statements) = x.as_ref(); let iter_obj = self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .eval_expr(scope, global, state, lib, this_ptr, expr, level)? .flatten(); let iter_type = iter_obj.type_id(); @@ -2893,7 +2891,7 @@ impl Engine { .global_modules .iter() .find_map(|m| m.get_iter(iter_type)) - .or_else(|| mods.get_iter(iter_type)) + .or_else(|| global.get_iter(iter_type)) .or_else(|| { self.global_sub_modules .values() @@ -2945,14 +2943,14 @@ impl Engine { } #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, statements.position())?; + self.inc_operations(&mut global.num_operations, statements.position())?; if statements.is_empty() { continue; } let result = self.eval_stmt_block( - scope, mods, state, lib, this_ptr, statements, true, true, level, + scope, global, state, lib, this_ptr, statements, true, true, level, ); match result { @@ -2990,7 +2988,7 @@ impl Engine { let namespace = namespace.as_ref().expect("qualified function call"); let hash = hashes.native; self.make_qualified_function_call( - scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash, + scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, *pos, level, ) } @@ -3006,7 +3004,7 @@ impl Engine { .. } = x.as_ref(); self.make_function_call( - scope, mods, state, lib, this_ptr, name, args, constants, *hashes, *pos, + scope, global, state, lib, this_ptr, name, args, constants, *hashes, *pos, *capture, level, ) } @@ -3017,7 +3015,7 @@ impl Engine { let result = self .eval_stmt_block( - scope, mods, state, lib, this_ptr, try_stmt, true, true, level, + scope, global, state, lib, this_ptr, try_stmt, true, true, level, ) .map(|_| Dynamic::UNIT); @@ -3041,7 +3039,7 @@ impl Engine { err_map.insert("message".into(), err.to_string().into()); - if let Some(ref source) = mods.source { + if let Some(ref source) = global.source { err_map.insert("source".into(), source.as_str().into()); } @@ -3070,7 +3068,7 @@ impl Engine { }); let result = self.eval_stmt_block( - scope, mods, state, lib, this_ptr, catch_stmt, true, true, level, + scope, global, state, lib, this_ptr, catch_stmt, true, true, level, ); scope.rewind(orig_scope_len); @@ -3093,7 +3091,7 @@ impl Engine { // Throw value Stmt::Return(options, Some(expr), pos) if options.contains(AST_OPTION_BREAK_OUT) => { Err(ERR::ErrorRuntime( - self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + self.eval_expr(scope, global, state, lib, this_ptr, expr, level)? .flatten(), *pos, ) @@ -3107,7 +3105,7 @@ impl Engine { // Return value Stmt::Return(_, Some(expr), pos) => Err(ERR::Return( - self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + self.eval_expr(scope, global, state, lib, this_ptr, expr, level)? .flatten(), *pos, ) @@ -3127,14 +3125,14 @@ impl Engine { let export = options.contains(AST_OPTION_PUBLIC); let value = self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .eval_expr(scope, global, state, lib, this_ptr, expr, level)? .flatten(); let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() { #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_module"))] if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) { - mods.set_global_constant(name.clone(), value.clone()); + global.set_constant(name.clone(), value.clone()); } ( @@ -3160,20 +3158,20 @@ impl Engine { Stmt::Import(expr, export, _pos) => { // Guard against too many modules #[cfg(not(feature = "unchecked"))] - if mods.num_modules >= self.max_modules() { + if global.num_modules_loaded >= self.max_modules() { return Err(ERR::ErrorTooManyModules(*_pos).into()); } if let Some(path) = self - .eval_expr(scope, mods, state, lib, this_ptr, &expr, level)? + .eval_expr(scope, global, state, lib, this_ptr, &expr, level)? .try_cast::() { use crate::ModuleResolver; - let source = mods.source.as_ref().map(|s| s.as_str()); + let source = global.source.as_ref().map(|s| s.as_str()); let path_pos = expr.position(); - let module = mods + let module = global .embedded_module_resolver .as_ref() .and_then(|r| match r.resolve(self, source, &path, path_pos) { @@ -3194,13 +3192,13 @@ impl Engine { // Index the module (making a clone copy if necessary) if it is not indexed let mut module = crate::func::native::shared_take_or_clone(module); module.build_index(); - mods.push(name, module); + global.push_module(name, module); } else { - mods.push(name, module); + global.push_module(name, module); } } - mods.num_modules += 1; + global.num_modules_loaded += 1; Ok(Dynamic::UNIT) } else { diff --git a/src/func/call.rs b/src/func/call.rs index 90f27913..7b43937b 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -6,8 +6,9 @@ use super::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; use crate::api::default_limits::MAX_DYNAMIC_PARAMETERS; use crate::ast::{Expr, FnCallHashes, Stmt}; use crate::engine::{ - EvalState, FnResolutionCacheEntry, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, - KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, + EvalState, FnResolutionCacheEntry, GlobalRuntimeState, KEYWORD_DEBUG, KEYWORD_EVAL, + KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, + KEYWORD_TYPE_OF, }; use crate::module::Namespace; use crate::tokenizer::Token; @@ -165,7 +166,7 @@ impl Engine { #[must_use] fn resolve_fn<'s>( &self, - mods: &Imports, + global: &GlobalRuntimeState, state: &'s mut EvalState, lib: &[&Module], fn_name: impl AsRef, @@ -217,7 +218,8 @@ impl Engine { }) }) .or_else(|| { - mods.get_fn(hash) + global + .get_fn(hash) .map(|(func, source)| FnResolutionCacheEntry { func: func.clone(), source: source.cloned(), @@ -308,7 +310,7 @@ impl Engine { /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! pub(crate) fn call_native_fn( &self, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], name: impl AsRef, @@ -319,13 +321,22 @@ impl Engine { pos: Position, ) -> RhaiResultOf<(Dynamic, bool)> { #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, pos)?; + self.inc_operations(&mut global.num_operations, pos)?; let name = name.as_ref(); - let parent_source = mods.source.clone(); + let parent_source = global.source.clone(); // Check if function access already in the cache - let func = self.resolve_fn(mods, state, lib, name, hash, Some(args), true, is_op_assign); + let func = self.resolve_fn( + global, + state, + lib, + name, + hash, + Some(args), + true, + is_op_assign, + ); if let Some(FnResolutionCacheEntry { func, source }) = func { assert!(func.is_native()); @@ -347,7 +358,7 @@ impl Engine { .or_else(|| parent_source.as_ref()) .map(|s| s.as_str()); - let context = (self, name, source, &*mods, lib, pos).into(); + let context = (self, name, source, &*global, lib, pos).into(); let result = if func.is_plugin_fn() { func.get_plugin_fn() @@ -389,7 +400,7 @@ impl Engine { pos, ) })?; - let source = mods.source.as_ref().map(|s| s.as_str()); + let source = global.source.as_ref().map(|s| s.as_str()); (debug(&text, source, pos).into(), false) } else { (Dynamic::UNIT, false) @@ -485,7 +496,7 @@ impl Engine { /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! pub(crate) fn exec_fn_call( &self, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], fn_name: impl AsRef, @@ -535,7 +546,7 @@ impl Engine { false } else { let hash_script = calc_fn_hash(fn_name.as_str(), num_params as usize); - self.has_script_fn(Some(mods), state, lib, hash_script) + self.has_script_fn(Some(global), state, lib, hash_script) } .into(), false, @@ -562,7 +573,16 @@ impl Engine { // Script-defined function call? #[cfg(not(feature = "no_function"))] if let Some(FnResolutionCacheEntry { func, source }) = self - .resolve_fn(mods, state, lib, fn_name, hashes.script, None, false, false) + .resolve_fn( + global, + state, + lib, + fn_name, + hashes.script, + None, + false, + false, + ) .cloned() { // Script function call @@ -587,14 +607,14 @@ impl Engine { // Method call of script function - map first argument to `this` let (first_arg, rest_args) = args.split_first_mut().expect("not empty"); - let orig_source = mods.source.take(); - mods.source = source; + let orig_source = global.source.take(); + global.source = source; let level = _level + 1; let result = self.call_script_fn( scope, - mods, + global, state, lib, &mut Some(*first_arg), @@ -606,7 +626,7 @@ impl Engine { ); // Restore the original source - mods.source = orig_source; + global.source = orig_source; result? } else { @@ -621,17 +641,17 @@ impl Engine { .change_first_arg_to_copy(args); } - let orig_source = mods.source.take(); - mods.source = source; + let orig_source = global.source.take(); + global.source = source; let level = _level + 1; let result = self.call_script_fn( - scope, mods, state, lib, &mut None, func, args, pos, true, level, + scope, global, state, lib, &mut None, func, args, pos, true, level, ); // Restore the original source - mods.source = orig_source; + global.source = orig_source; // Restore the original reference if let Some(bk) = backup { @@ -647,7 +667,7 @@ impl Engine { // Native function call let hash = hashes.native; self.call_native_fn( - mods, state, lib, fn_name, hash, args, is_ref_mut, false, pos, + global, state, lib, fn_name, hash, args, is_ref_mut, false, pos, ) } @@ -657,14 +677,14 @@ impl Engine { pub(crate) fn eval_global_statements( &self, scope: &mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, statements: &[Stmt], lib: &[&Module], level: usize, ) -> RhaiResult { self.eval_stmt_block( - scope, mods, state, lib, &mut None, statements, false, false, level, + scope, global, state, lib, &mut None, statements, false, false, level, ) .or_else(|err| match *err { ERR::Return(out, _) => Ok(out), @@ -679,14 +699,14 @@ impl Engine { fn eval_script_expr_in_place( &self, scope: &mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, lib: &[&Module], script: impl AsRef, _pos: Position, level: usize, ) -> RhaiResult { #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, _pos)?; + self.inc_operations(&mut global.num_operations, _pos)?; let script = script.as_ref().trim(); if script.is_empty() { @@ -714,14 +734,14 @@ impl Engine { } // Evaluate the AST - self.eval_global_statements(scope, mods, &mut EvalState::new(), statements, lib, level) + self.eval_global_statements(scope, global, &mut EvalState::new(), statements, lib, level) } /// Call a dot method. #[cfg(not(feature = "no_object"))] pub(crate) fn make_method_call( &self, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], fn_name: impl AsRef, @@ -752,7 +772,8 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - mods, state, lib, fn_name, new_hash, &mut args, false, false, pos, None, level, + global, state, lib, fn_name, new_hash, &mut args, false, false, pos, None, + level, ) } KEYWORD_FN_PTR_CALL => { @@ -791,7 +812,7 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - mods, state, lib, fn_name, new_hash, &mut args, is_ref_mut, true, pos, None, + global, state, lib, fn_name, new_hash, &mut args, is_ref_mut, true, pos, None, level, ) } @@ -863,7 +884,8 @@ impl Engine { args.extend(call_args.iter_mut()); self.exec_fn_call( - mods, state, lib, fn_name, hash, &mut args, is_ref_mut, true, pos, None, level, + global, state, lib, fn_name, hash, &mut args, is_ref_mut, true, pos, None, + level, ) } }?; @@ -883,7 +905,7 @@ impl Engine { pub(crate) fn get_arg_value( &self, scope: &mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, @@ -894,7 +916,7 @@ impl Engine { match arg_expr { Expr::Stack(slot, pos) => Ok((constants[*slot].clone(), *pos)), ref arg => self - .eval_expr(scope, mods, state, lib, this_ptr, arg, level) + .eval_expr(scope, global, state, lib, this_ptr, arg, level) .map(|v| (v, arg.position())), } } @@ -903,7 +925,7 @@ impl Engine { pub(crate) fn make_function_call( &self, scope: &mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, @@ -927,7 +949,7 @@ impl Engine { // Handle call() KEYWORD_FN_PTR_CALL if total_args >= 1 => { let (arg, arg_pos) = self.get_arg_value( - scope, mods, state, lib, this_ptr, level, &a_expr[0], constants, + scope, global, state, lib, this_ptr, level, &a_expr[0], constants, )?; if !arg.is::() { @@ -959,7 +981,7 @@ impl Engine { // Handle Fn() KEYWORD_FN_PTR if total_args == 1 => { let (arg, arg_pos) = self.get_arg_value( - scope, mods, state, lib, this_ptr, level, &a_expr[0], constants, + scope, global, state, lib, this_ptr, level, &a_expr[0], constants, )?; // Fn - only in function call style @@ -974,7 +996,7 @@ impl Engine { // Handle curry() KEYWORD_FN_PTR_CURRY if total_args > 1 => { let (arg, arg_pos) = self.get_arg_value( - scope, mods, state, lib, this_ptr, level, &a_expr[0], constants, + scope, global, state, lib, this_ptr, level, &a_expr[0], constants, )?; if !arg.is::() { @@ -991,7 +1013,7 @@ impl Engine { fn_curry, |mut curried, expr| -> RhaiResultOf<_> { let (value, _) = self.get_arg_value( - scope, mods, state, lib, this_ptr, level, expr, constants, + scope, global, state, lib, this_ptr, level, expr, constants, )?; curried.push(value); Ok(curried) @@ -1005,7 +1027,7 @@ impl Engine { #[cfg(not(feature = "no_closure"))] crate::engine::KEYWORD_IS_SHARED if total_args == 1 => { let (arg, _) = self.get_arg_value( - scope, mods, state, lib, this_ptr, level, &a_expr[0], constants, + scope, global, state, lib, this_ptr, level, &a_expr[0], constants, )?; return Ok(arg.is_shared().into()); } @@ -1014,7 +1036,7 @@ impl Engine { #[cfg(not(feature = "no_function"))] crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => { let (arg, arg_pos) = self.get_arg_value( - scope, mods, state, lib, this_ptr, level, &a_expr[0], constants, + scope, global, state, lib, this_ptr, level, &a_expr[0], constants, )?; let fn_name = arg @@ -1022,7 +1044,7 @@ impl Engine { .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; let (arg, arg_pos) = self.get_arg_value( - scope, mods, state, lib, this_ptr, level, &a_expr[1], constants, + scope, global, state, lib, this_ptr, level, &a_expr[1], constants, )?; let num_params = arg @@ -1033,7 +1055,7 @@ impl Engine { false } else { let hash_script = calc_fn_hash(&fn_name, num_params as usize); - self.has_script_fn(Some(mods), state, lib, hash_script) + self.has_script_fn(Some(global), state, lib, hash_script) } .into()); } @@ -1041,7 +1063,7 @@ impl Engine { // Handle is_def_var() KEYWORD_IS_DEF_VAR if total_args == 1 => { let (arg, arg_pos) = self.get_arg_value( - scope, mods, state, lib, this_ptr, level, &a_expr[0], constants, + scope, global, state, lib, this_ptr, level, &a_expr[0], constants, )?; let var_name = arg .into_immutable_string() @@ -1054,13 +1076,13 @@ impl Engine { // eval - only in function call style let orig_scope_len = scope.len(); let (value, pos) = self.get_arg_value( - scope, mods, state, lib, this_ptr, level, &a_expr[0], constants, + scope, global, state, lib, this_ptr, level, &a_expr[0], constants, )?; let script = &value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; let result = - self.eval_script_expr_in_place(scope, mods, lib, script, pos, level + 1); + self.eval_script_expr_in_place(scope, global, lib, script, pos, level + 1); // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. @@ -1071,7 +1093,8 @@ impl Engine { return result.map_err(|err| { ERR::ErrorInFunctionCall( KEYWORD_EVAL.to_string(), - mods.source + global + .source .as_ref() .map(Identifier::to_string) .unwrap_or_default(), @@ -1096,7 +1119,7 @@ impl Engine { // variable access) to &mut because `scope` is needed. if capture_scope && !scope.is_empty() { a_expr.iter().try_for_each(|expr| { - self.get_arg_value(scope, mods, state, lib, this_ptr, level, expr, constants) + self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants) .map(|(value, _)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1107,7 +1130,8 @@ impl Engine { return self .exec_fn_call( - mods, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, scope, level, + global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, scope, + level, ) .map(|(v, _)| v); } @@ -1124,19 +1148,19 @@ impl Engine { let (first_expr, rest_expr) = a_expr.split_first().expect("not empty"); rest_expr.iter().try_for_each(|expr| { - self.get_arg_value(scope, mods, state, lib, this_ptr, level, expr, constants) + self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants) .map(|(value, _)| arg_values.push(value.flatten())) })?; let (mut target, _pos) = - self.search_namespace(scope, mods, state, lib, this_ptr, first_expr)?; + self.search_namespace(scope, global, state, lib, this_ptr, first_expr)?; if target.as_ref().is_read_only() { target = target.into_owned(); } #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, _pos)?; + self.inc_operations(&mut global.num_operations, _pos)?; #[cfg(not(feature = "no_closure"))] let target_is_shared = target.is_shared(); @@ -1156,7 +1180,7 @@ impl Engine { } else { // func(..., ...) a_expr.iter().try_for_each(|expr| { - self.get_arg_value(scope, mods, state, lib, this_ptr, level, expr, constants) + self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants) .map(|(value, _)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1165,7 +1189,7 @@ impl Engine { } self.exec_fn_call( - mods, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, None, level, + global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, None, level, ) .map(|(v, _)| v) } @@ -1174,7 +1198,7 @@ impl Engine { pub(crate) fn make_qualified_function_call( &self, scope: &mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, @@ -1202,16 +1226,16 @@ impl Engine { arg_values.push(Dynamic::UNIT); args_expr.iter().skip(1).try_for_each(|expr| { - self.get_arg_value(scope, mods, state, lib, this_ptr, level, expr, constants) + self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants) .map(|(value, _)| arg_values.push(value.flatten())) })?; // Get target reference to first argument let (target, _pos) = - self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?; + self.search_scope_only(scope, global, state, lib, this_ptr, &args_expr[0])?; #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, _pos)?; + self.inc_operations(&mut global.num_operations, _pos)?; #[cfg(not(feature = "no_closure"))] let target_is_shared = target.is_shared(); @@ -1232,7 +1256,7 @@ impl Engine { } else { // func(..., ...) or func(mod::x, ...) args_expr.iter().try_for_each(|expr| { - self.get_arg_value(scope, mods, state, lib, this_ptr, level, expr, constants) + self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants) .map(|(value, _)| arg_values.push(value.flatten())) })?; args.extend(arg_values.iter_mut()); @@ -1240,7 +1264,7 @@ impl Engine { } let module = self - .search_imports(mods, state, namespace) + .search_imports(global, state, namespace) .ok_or_else(|| ERR::ErrorModuleNotFound(namespace.to_string(), namespace[0].pos))?; // First search in script-defined functions (can override built-in) @@ -1248,7 +1272,7 @@ impl Engine { // Then search in Rust functions None => { #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, pos)?; + self.inc_operations(&mut global.num_operations, pos)?; let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id())); let hash_qualified_fn = combine_hashes(hash, hash_params); @@ -1277,22 +1301,23 @@ impl Engine { let new_scope = &mut Scope::new(); let mut source = module.id_raw().cloned(); - mem::swap(&mut mods.source, &mut source); + mem::swap(&mut global.source, &mut source); let level = level + 1; let result = self.call_script_fn( - new_scope, mods, state, lib, &mut None, fn_def, &mut args, pos, true, level, + new_scope, global, state, lib, &mut None, fn_def, &mut args, pos, true, + level, ); - mods.source = source; + global.source = source; result } } Some(f) if f.is_plugin_fn() => { - let context = (self, fn_name, module.id(), &*mods, lib, pos).into(); + let context = (self, fn_name, module.id(), &*global, lib, pos).into(); f.get_plugin_fn() .expect("plugin function") .clone() @@ -1302,7 +1327,7 @@ impl Engine { Some(f) if f.is_native() => { let func = f.get_native_fn().expect("native function"); - let context = (self, fn_name, module.id(), &*mods, lib, pos).into(); + let context = (self, fn_name, module.id(), &*global, lib, pos).into(); func(context, &mut args).map_err(|err| err.fill_position(pos)) } diff --git a/src/func/native.rs b/src/func/native.rs index 8251516f..da7e104d 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -2,7 +2,7 @@ use super::call::FnCallArgs; use crate::ast::FnCallHashes; -use crate::engine::{EvalState, Imports}; +use crate::engine::{EvalState, GlobalRuntimeState}; use crate::plugin::PluginFunction; use crate::tokenizer::{Token, TokenizeState}; use crate::types::dynamic::Variant; @@ -61,7 +61,7 @@ pub struct NativeCallContext<'a> { engine: &'a Engine, fn_name: &'a str, source: Option<&'a str>, - mods: Option<&'a Imports>, + global: Option<&'a GlobalRuntimeState>, lib: &'a [&'a Module], pos: Position, } @@ -71,7 +71,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef + 'a + ?Sized> &'a Engine, &'a S, Option<&'a S>, - &'a Imports, + &'a GlobalRuntimeState, &'a M, Position, )> for NativeCallContext<'a> @@ -82,7 +82,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef + 'a + ?Sized> &'a Engine, &'a S, Option<&'a S>, - &'a Imports, + &'a GlobalRuntimeState, &'a M, Position, ), @@ -91,7 +91,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef + 'a + ?Sized> engine: value.0, fn_name: value.1.as_ref(), source: value.2.map(|v| v.as_ref()), - mods: Some(value.3), + global: Some(value.3), lib: value.4.as_ref(), pos: value.5, } @@ -107,7 +107,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef + 'a + ?Sized> engine: value.0, fn_name: value.1.as_ref(), source: None, - mods: None, + global: None, lib: value.2.as_ref(), pos: Position::NONE, } @@ -132,7 +132,7 @@ impl<'a> NativeCallContext<'a> { engine, fn_name: fn_name.as_ref(), source: None, - mods: None, + global: None, lib, pos: Position::NONE, } @@ -149,7 +149,7 @@ impl<'a> NativeCallContext<'a> { engine: &'a Engine, fn_name: &'a (impl AsRef + 'a + ?Sized), source: Option<&'a (impl AsRef + 'a + ?Sized)>, - imports: &'a Imports, + global: &'a GlobalRuntimeState, lib: &'a [&Module], pos: Position, ) -> Self { @@ -157,7 +157,7 @@ impl<'a> NativeCallContext<'a> { engine, fn_name: fn_name.as_ref(), source: source.map(|v| v.as_ref()), - mods: Some(imports), + global: Some(global), lib, pos, } @@ -192,7 +192,7 @@ impl<'a> NativeCallContext<'a> { #[cfg(not(feature = "no_module"))] #[inline] pub fn iter_imports(&self) -> impl Iterator { - self.mods.iter().flat_map(|&m| m.iter()) + self.global.iter().flat_map(|&m| m.iter_modules()) } /// Get an iterator over the current set of modules imported via `import` statements. #[cfg(not(feature = "no_module"))] @@ -201,9 +201,9 @@ impl<'a> NativeCallContext<'a> { pub(crate) fn iter_imports_raw( &self, ) -> impl Iterator)> { - self.mods.iter().flat_map(|&m| m.iter_raw()) + self.global.iter().flat_map(|&m| m.iter_modules_raw()) } - /// _(internals)_ The current set of modules imported via `import` statements. + /// _(internals)_ The current [`GlobalRuntimeState`]. /// Exported under the `internals` feature only. /// /// Not available under `no_module`. @@ -211,8 +211,8 @@ impl<'a> NativeCallContext<'a> { #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] - pub const fn imports(&self) -> Option<&Imports> { - self.mods + pub const fn global_runtime_state(&self) -> Option<&GlobalRuntimeState> { + self.global } /// Get an iterator over the namespaces containing definitions of all script-defined functions. #[inline] @@ -292,7 +292,10 @@ impl<'a> NativeCallContext<'a> { self.engine() .exec_fn_call( - &mut self.mods.cloned().unwrap_or_else(|| Imports::new()), + &mut self + .global + .cloned() + .unwrap_or_else(|| GlobalRuntimeState::new()), &mut EvalState::new(), self.lib, fn_name, diff --git a/src/func/script.rs b/src/func/script.rs index 691e13d1..285e3045 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -3,7 +3,7 @@ use super::call::FnCallArgs; use crate::ast::ScriptFnDef; -use crate::engine::{EvalState, Imports}; +use crate::engine::{EvalState, GlobalRuntimeState}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, StaticVec, ERR}; use std::mem; @@ -24,7 +24,7 @@ impl Engine { pub(crate) fn call_script_fn( &self, scope: &mut Scope, - mods: &mut Imports, + global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, @@ -38,7 +38,7 @@ impl Engine { fn make_error( name: String, fn_def: &ScriptFnDef, - mods: &Imports, + global: &GlobalRuntimeState, err: RhaiError, pos: Position, ) -> RhaiResult { @@ -48,7 +48,7 @@ impl Engine { .lib .as_ref() .and_then(|m| m.id().map(|id| id.to_string())) - .or_else(|| mods.source.as_ref().map(|s| s.to_string())) + .or_else(|| global.source.as_ref().map(|s| s.to_string())) .unwrap_or_default(), err, pos, @@ -59,7 +59,7 @@ impl Engine { assert!(fn_def.params.len() == args.len()); #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut mods.num_operations, pos)?; + self.inc_operations(&mut global.num_operations, pos)?; if fn_def.body.is_empty() { return Ok(Dynamic::UNIT); @@ -72,7 +72,7 @@ impl Engine { } let orig_scope_len = scope.len(); - let orig_mods_len = mods.len(); + let orig_mods_len = global.num_imported_modules(); // Put arguments into scope as variables // Actually consume the arguments instead of cloning them @@ -106,11 +106,11 @@ impl Engine { }; #[cfg(not(feature = "no_module"))] - if !fn_def.mods.is_empty() { + if fn_def.global.num_imported_modules() > 0 { fn_def - .mods - .iter_raw() - .for_each(|(n, m)| mods.push(n.clone(), m.clone())); + .global + .iter_modules_raw() + .for_each(|(n, m)| global.push_module(n.clone(), m.clone())); } // Evaluate the function @@ -118,7 +118,7 @@ impl Engine { let result = self .eval_stmt_block( scope, - mods, + global, state, lib, this_ptr, @@ -138,7 +138,7 @@ impl Engine { format!("{} @ '{}' < {}", name, src, fn_def.name) }; - make_error(fn_name, fn_def, mods, err, pos) + make_error(fn_name, fn_def, global, err, pos) } // System errors are passed straight-through mut err if err.is_system_exception() => { @@ -146,7 +146,7 @@ impl Engine { Err(err.into()) } // Other errors are wrapped in `ErrorInFunctionCall` - _ => make_error(fn_def.name.to_string(), fn_def, mods, err, pos), + _ => make_error(fn_def.name.to_string(), fn_def, global, err, pos), }); // Remove all local variables @@ -157,7 +157,7 @@ impl Engine { scope.remove_range(orig_scope_len, args.len()) } - mods.truncate(orig_mods_len); + global.truncate_modules(orig_mods_len); state.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); result @@ -167,7 +167,7 @@ impl Engine { #[must_use] pub(crate) fn has_script_fn( &self, - mods: Option<&Imports>, + global: Option<&GlobalRuntimeState>, state: &mut EvalState, lib: &[&Module], hash_script: u64, @@ -183,7 +183,7 @@ impl Engine { // Then check the global namespace and packages || self.global_modules.iter().any(|m| m.contains_fn(hash_script)) // Then check imported modules - || mods.map_or(false, |m| m.contains_fn(hash_script)) + || global.map_or(false, |m| m.contains_fn(hash_script)) // Then check sub-modules || self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script)); diff --git a/src/lib.rs b/src/lib.rs index b74cb0d4..d9e61697 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,7 +241,7 @@ pub use ast::{ pub use ast::FloatWrapper; #[cfg(feature = "internals")] -pub use engine::{EvalState, FnResolutionCache, FnResolutionCacheEntry, Imports}; +pub use engine::{EvalState, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState}; #[cfg(feature = "internals")] pub use module::Namespace; diff --git a/src/module/mod.rs b/src/module/mod.rs index 98884c7c..c3b332ee 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1549,11 +1549,11 @@ impl Module { engine: &crate::Engine, ) -> RhaiResultOf { let mut scope = scope; - let mut mods = crate::engine::Imports::new(); - let orig_mods_len = mods.len(); + let mut global = crate::engine::GlobalRuntimeState::new(); + let orig_mods_len = global.num_imported_modules(); // Run the script - engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast, 0)?; + engine.eval_ast_with_scope_raw(&mut scope, &mut global, &ast, 0)?; // Create new module let mut module = @@ -1580,12 +1580,15 @@ impl Module { }); // Extra modules left in the scope become sub-modules - let mut func_mods = crate::engine::Imports::new(); + let mut func_global = crate::engine::GlobalRuntimeState::new(); - mods.into_iter().skip(orig_mods_len).for_each(|(alias, m)| { - func_mods.push(alias.clone(), m.clone()); - module.set_sub_module(alias, m); - }); + global + .into_iter() + .skip(orig_mods_len) + .for_each(|(alias, m)| { + func_global.push_module(alias.clone(), m.clone()); + module.set_sub_module(alias, m); + }); // Non-private functions defined become module functions #[cfg(not(feature = "no_function"))] @@ -1606,7 +1609,7 @@ impl Module { .as_ref() .clone(); func.lib = Some(ast.shared_lib().clone()); - func.mods = func_mods.clone(); + func.global = func_global.clone(); module.set_script_fn(func); }); } @@ -1790,13 +1793,14 @@ impl Module { } } -/// _(internals)_ A chain of [module][Module] names to namespace-qualify a variable or function call. -/// Exported under the `internals` feature only. +/// _(internals)_ A chain of [module][Module] names to namespace-qualify a variable or function +/// call. Exported under the `internals` feature only. /// -/// A [`u64`] offset to the current [`Scope`][crate::Scope] is cached for quick search purposes. +/// A [`u64`] offset to the current [stack of imported modules][crate::GlobalRuntimeState] is +/// cached for quick search purposes. /// -/// A [`StaticVec`] is used because most namespace-qualified access contains only one level, -/// and it is wasteful to always allocate a [`Vec`] with one element. +/// A [`StaticVec`] is used because the vast majority of namespace-qualified access contains only +/// one level, and it is wasteful to always allocate a [`Vec`] with one element. #[derive(Clone, Eq, PartialEq, Default, Hash)] pub struct Namespace { index: Option, diff --git a/src/optimizer.rs b/src/optimizer.rs index 6f58e2ae..77390f99 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -3,7 +3,8 @@ use crate::ast::{Expr, OpAssignment, Stmt, AST_OPTION_FLAGS::*}; use crate::engine::{ - EvalState, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF, + EvalState, GlobalRuntimeState, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, + KEYWORD_TYPE_OF, }; use crate::func::builtin::get_builtin_binary_op_fn; use crate::func::hashing::get_hasher; @@ -141,7 +142,7 @@ impl<'a> OptimizerState<'a> { self.engine .call_native_fn( - &mut Imports::new(), + &mut GlobalRuntimeState::new(), &mut EvalState::new(), lib, &fn_name, @@ -1154,7 +1155,7 @@ pub fn optimize_into_ast( params: fn_def.params.clone(), lib: None, #[cfg(not(feature = "no_module"))] - mods: Imports::new(), + global: GlobalRuntimeState::new(), #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] comments: None, diff --git a/src/parser.rs b/src/parser.rs index e302a150..5226cca2 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3129,7 +3129,7 @@ fn parse_fn( body, lib: None, #[cfg(not(feature = "no_module"))] - mods: crate::engine::Imports::new(), + global: crate::engine::GlobalRuntimeState::new(), #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] comments: if comments.is_empty() { @@ -3280,7 +3280,7 @@ fn parse_anon_fn( body: body.into(), lib: None, #[cfg(not(feature = "no_module"))] - mods: crate::engine::Imports::new(), + global: crate::engine::GlobalRuntimeState::new(), #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] comments: None, From 9deddc679c20a54c3058e1903f75a81f0fd3fe63 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 27 Dec 2021 23:15:25 +0800 Subject: [PATCH 52/97] Use Box. --- src/engine.rs | 538 +++++++++++++++++++++++------------------------ src/func/call.rs | 16 +- 2 files changed, 277 insertions(+), 277 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 41fd4242..75f44be0 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -28,274 +28,6 @@ use std::{ pub type Precedence = NonZeroU8; -/// _(internals)_ A stack of imported [modules][Module] plus mutable global runtime states. -/// Exported under the `internals` feature only. -// -// # Implementation Notes -// -// This implementation splits the module names from the shared modules to improve data locality. -// Most usage will be looking up a particular key from the list and then getting the module that -// corresponds to that key. -#[derive(Clone)] -pub struct GlobalRuntimeState { - /// Stack of module names. - // - // # Implementation Notes - // - // We cannot use Cow here because `eval` may load a [module][Module] and - // the module name will live beyond the AST of the eval script text. - keys: StaticVec, - /// Stack of imported modules. - modules: StaticVec>, - - /// Source of the current context. - pub source: Option, - /// Number of operations performed. - pub num_operations: u64, - /// Number of modules loaded. - pub num_modules_loaded: usize, - - /// Function call hashes to index getters and setters. - /// - /// Not available under `no_index` and `no_object`. - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - fn_hash_indexing: (u64, u64), - /// Embedded module resolver. - /// - /// Not available under `no_module`. - #[cfg(not(feature = "no_module"))] - pub embedded_module_resolver: Option>, - - /// Cache of globally-defined constants. - /// - /// Not available under `no_module` and `no_function`. - #[cfg(not(feature = "no_module"))] - #[cfg(not(feature = "no_function"))] - constants: Option>>>, -} - -impl Default for GlobalRuntimeState { - #[inline(always)] - fn default() -> Self { - Self::new() - } -} - -impl GlobalRuntimeState { - /// Create a new [`GlobalRuntimeState`]. - #[inline(always)] - #[must_use] - pub const fn new() -> Self { - Self { - keys: StaticVec::new_const(), - modules: StaticVec::new_const(), - source: None, - num_operations: 0, - num_modules_loaded: 0, - #[cfg(not(feature = "no_module"))] - embedded_module_resolver: None, - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - fn_hash_indexing: (0, 0), - #[cfg(not(feature = "no_module"))] - #[cfg(not(feature = "no_function"))] - constants: None, - } - } - /// Get the length of the stack of globally-imported [modules][Module]. - #[inline(always)] - #[must_use] - pub fn num_imported_modules(&self) -> usize { - self.keys.len() - } - /// Get the globally-imported [module][Module] at a particular index. - #[inline(always)] - #[must_use] - pub fn get_shared_module(&self, index: usize) -> Option> { - self.modules.get(index).cloned() - } - /// Get a mutable reference to the globally-imported [module][Module] at a particular index. - #[allow(dead_code)] - #[inline(always)] - #[must_use] - pub(crate) fn get_shared_module_mut(&mut self, index: usize) -> Option<&mut Shared> { - self.modules.get_mut(index) - } - /// Get the index of a globally-imported [module][Module] by name. - #[inline] - #[must_use] - pub fn find_module(&self, name: impl AsRef) -> Option { - let name = name.as_ref(); - let len = self.keys.len(); - - self.keys.iter().rev().enumerate().find_map(|(i, key)| { - if key == name { - Some(len - 1 - i) - } else { - None - } - }) - } - /// Push an imported [module][Module] onto the stack. - #[inline(always)] - pub fn push_module(&mut self, name: impl Into, module: impl Into>) { - self.keys.push(name.into()); - self.modules.push(module.into()); - } - /// Truncate the stack of globally-imported [modules][Module] to a particular length. - #[inline(always)] - pub fn truncate_modules(&mut self, size: usize) { - self.keys.truncate(size); - self.modules.truncate(size); - } - /// Get an iterator to the stack of globally-imported [modules][Module] in reverse order. - #[allow(dead_code)] - #[inline] - pub fn iter_modules(&self) -> impl Iterator { - self.keys - .iter() - .rev() - .zip(self.modules.iter().rev()) - .map(|(name, module)| (name.as_str(), module.as_ref())) - } - /// Get an iterator to the stack of globally-imported [modules][Module] in reverse order. - #[allow(dead_code)] - #[inline] - pub(crate) fn iter_modules_raw(&self) -> impl Iterator)> { - self.keys.iter().rev().zip(self.modules.iter().rev()) - } - /// Get an iterator to the stack of globally-imported [modules][Module] in forward order. - #[allow(dead_code)] - #[inline] - pub(crate) fn scan_modules_raw(&self) -> impl Iterator)> { - self.keys.iter().zip(self.modules.iter()) - } - /// Does the specified function hash key exist in the stack of globally-imported [modules][Module]? - #[allow(dead_code)] - #[inline] - #[must_use] - pub fn contains_fn(&self, hash: u64) -> bool { - self.modules.iter().any(|m| m.contains_qualified_fn(hash)) - } - /// Get the specified function via its hash key from the stack of globally-imported [modules][Module]. - #[inline] - #[must_use] - pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> { - self.modules - .iter() - .rev() - .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) - } - /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of - /// globally-imported [modules][Module]? - #[allow(dead_code)] - #[inline] - #[must_use] - pub fn contains_iter(&self, id: TypeId) -> bool { - self.modules.iter().any(|m| m.contains_qualified_iter(id)) - } - /// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported - /// [modules][Module]. - #[inline] - #[must_use] - pub fn get_iter(&self, id: TypeId) -> Option { - self.modules - .iter() - .rev() - .find_map(|m| m.get_qualified_iter(id)) - } - /// Get a mutable reference to the cache of globally-defined constants. - #[cfg(not(feature = "no_module"))] - #[cfg(not(feature = "no_function"))] - #[must_use] - pub(crate) fn constants_mut<'a>( - &'a mut self, - ) -> Option> + 'a> { - if let Some(ref global_constants) = self.constants { - Some(crate::func::native::shared_write_lock(global_constants)) - } else { - None - } - } - /// Set a constant into the cache of globally-defined constants. - #[cfg(not(feature = "no_module"))] - #[cfg(not(feature = "no_function"))] - pub(crate) fn set_constant(&mut self, name: impl Into, value: Dynamic) { - if self.constants.is_none() { - let dict: crate::Locked<_> = BTreeMap::new().into(); - self.constants = Some(dict.into()); - } - - crate::func::native::shared_write_lock(self.constants.as_mut().expect("`Some`")) - .insert(name.into(), value); - } - /// Get the pre-calculated index getter hash. - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - #[must_use] - pub(crate) fn hash_idx_get(&mut self) -> u64 { - if self.fn_hash_indexing != (0, 0) { - self.fn_hash_indexing.0 - } else { - let n1 = crate::calc_fn_hash(FN_IDX_GET, 2); - let n2 = crate::calc_fn_hash(FN_IDX_SET, 3); - self.fn_hash_indexing = (n1, n2); - n1 - } - } - /// Get the pre-calculated index setter hash. - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - #[must_use] - pub(crate) fn hash_idx_set(&mut self) -> u64 { - if self.fn_hash_indexing != (0, 0) { - self.fn_hash_indexing.1 - } else { - let n1 = crate::calc_fn_hash(FN_IDX_GET, 2); - let n2 = crate::calc_fn_hash(FN_IDX_SET, 3); - self.fn_hash_indexing = (n1, n2); - n2 - } - } -} - -impl IntoIterator for GlobalRuntimeState { - type Item = (Identifier, Shared); - type IntoIter = - Zip>, Rev; 3]>>>; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.keys - .into_iter() - .rev() - .zip(self.modules.into_iter().rev()) - } -} - -impl, M: Into>> FromIterator<(K, M)> for GlobalRuntimeState { - fn from_iter>(iter: T) -> Self { - let mut lib = Self::new(); - lib.extend(iter); - lib - } -} - -impl, M: Into>> Extend<(K, M)> for GlobalRuntimeState { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each(|(k, m)| { - self.keys.push(k.into()); - self.modules.push(m.into()); - }) - } -} - -impl fmt::Debug for GlobalRuntimeState { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Imports")?; - f.debug_map() - .entries(self.keys.iter().zip(self.modules.iter())) - .finish() - } -} - pub const KEYWORD_PRINT: &str = "print"; pub const KEYWORD_DEBUG: &str = "debug"; pub const KEYWORD_TYPE_OF: &str = "type_of"; @@ -757,6 +489,274 @@ impl> From for Target<'_> { } } +/// _(internals)_ A stack of imported [modules][Module] plus mutable global runtime states. +/// Exported under the `internals` feature only. +// +// # Implementation Notes +// +// This implementation splits the module names from the shared modules to improve data locality. +// Most usage will be looking up a particular key from the list and then getting the module that +// corresponds to that key. +#[derive(Clone)] +pub struct GlobalRuntimeState { + /// Stack of module names. + // + // # Implementation Notes + // + // We cannot use Cow here because `eval` may load a [module][Module] and + // the module name will live beyond the AST of the eval script text. + keys: StaticVec, + /// Stack of imported modules. + modules: StaticVec>, + + /// Source of the current context. + pub source: Option, + /// Number of operations performed. + pub num_operations: u64, + /// Number of modules loaded. + pub num_modules_loaded: usize, + + /// Function call hashes to index getters and setters. + /// + /// Not available under `no_index` and `no_object`. + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + fn_hash_indexing: (u64, u64), + /// Embedded module resolver. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] + pub embedded_module_resolver: Option>, + + /// Cache of globally-defined constants. + /// + /// Not available under `no_module` and `no_function`. + #[cfg(not(feature = "no_module"))] + #[cfg(not(feature = "no_function"))] + constants: Option>>>, +} + +impl Default for GlobalRuntimeState { + #[inline(always)] + fn default() -> Self { + Self::new() + } +} + +impl GlobalRuntimeState { + /// Create a new [`GlobalRuntimeState`]. + #[inline(always)] + #[must_use] + pub const fn new() -> Self { + Self { + keys: StaticVec::new_const(), + modules: StaticVec::new_const(), + source: None, + num_operations: 0, + num_modules_loaded: 0, + #[cfg(not(feature = "no_module"))] + embedded_module_resolver: None, + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + fn_hash_indexing: (0, 0), + #[cfg(not(feature = "no_module"))] + #[cfg(not(feature = "no_function"))] + constants: None, + } + } + /// Get the length of the stack of globally-imported [modules][Module]. + #[inline(always)] + #[must_use] + pub fn num_imported_modules(&self) -> usize { + self.keys.len() + } + /// Get the globally-imported [module][Module] at a particular index. + #[inline(always)] + #[must_use] + pub fn get_shared_module(&self, index: usize) -> Option> { + self.modules.get(index).cloned() + } + /// Get a mutable reference to the globally-imported [module][Module] at a particular index. + #[allow(dead_code)] + #[inline(always)] + #[must_use] + pub(crate) fn get_shared_module_mut(&mut self, index: usize) -> Option<&mut Shared> { + self.modules.get_mut(index) + } + /// Get the index of a globally-imported [module][Module] by name. + #[inline] + #[must_use] + pub fn find_module(&self, name: impl AsRef) -> Option { + let name = name.as_ref(); + let len = self.keys.len(); + + self.keys.iter().rev().enumerate().find_map(|(i, key)| { + if key == name { + Some(len - 1 - i) + } else { + None + } + }) + } + /// Push an imported [module][Module] onto the stack. + #[inline(always)] + pub fn push_module(&mut self, name: impl Into, module: impl Into>) { + self.keys.push(name.into()); + self.modules.push(module.into()); + } + /// Truncate the stack of globally-imported [modules][Module] to a particular length. + #[inline(always)] + pub fn truncate_modules(&mut self, size: usize) { + self.keys.truncate(size); + self.modules.truncate(size); + } + /// Get an iterator to the stack of globally-imported [modules][Module] in reverse order. + #[allow(dead_code)] + #[inline] + pub fn iter_modules(&self) -> impl Iterator { + self.keys + .iter() + .rev() + .zip(self.modules.iter().rev()) + .map(|(name, module)| (name.as_str(), module.as_ref())) + } + /// Get an iterator to the stack of globally-imported [modules][Module] in reverse order. + #[allow(dead_code)] + #[inline] + pub(crate) fn iter_modules_raw(&self) -> impl Iterator)> { + self.keys.iter().rev().zip(self.modules.iter().rev()) + } + /// Get an iterator to the stack of globally-imported [modules][Module] in forward order. + #[allow(dead_code)] + #[inline] + pub(crate) fn scan_modules_raw(&self) -> impl Iterator)> { + self.keys.iter().zip(self.modules.iter()) + } + /// Does the specified function hash key exist in the stack of globally-imported [modules][Module]? + #[allow(dead_code)] + #[inline] + #[must_use] + pub fn contains_fn(&self, hash: u64) -> bool { + self.modules.iter().any(|m| m.contains_qualified_fn(hash)) + } + /// Get the specified function via its hash key from the stack of globally-imported [modules][Module]. + #[inline] + #[must_use] + pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> { + self.modules + .iter() + .rev() + .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) + } + /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of + /// globally-imported [modules][Module]? + #[allow(dead_code)] + #[inline] + #[must_use] + pub fn contains_iter(&self, id: TypeId) -> bool { + self.modules.iter().any(|m| m.contains_qualified_iter(id)) + } + /// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported + /// [modules][Module]. + #[inline] + #[must_use] + pub fn get_iter(&self, id: TypeId) -> Option { + self.modules + .iter() + .rev() + .find_map(|m| m.get_qualified_iter(id)) + } + /// Get a mutable reference to the cache of globally-defined constants. + #[cfg(not(feature = "no_module"))] + #[cfg(not(feature = "no_function"))] + #[must_use] + pub(crate) fn constants_mut<'a>( + &'a mut self, + ) -> Option> + 'a> { + if let Some(ref global_constants) = self.constants { + Some(crate::func::native::shared_write_lock(global_constants)) + } else { + None + } + } + /// Set a constant into the cache of globally-defined constants. + #[cfg(not(feature = "no_module"))] + #[cfg(not(feature = "no_function"))] + pub(crate) fn set_constant(&mut self, name: impl Into, value: Dynamic) { + if self.constants.is_none() { + let dict: crate::Locked<_> = BTreeMap::new().into(); + self.constants = Some(dict.into()); + } + + crate::func::native::shared_write_lock(self.constants.as_mut().expect("`Some`")) + .insert(name.into(), value); + } + /// Get the pre-calculated index getter hash. + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + #[must_use] + pub(crate) fn hash_idx_get(&mut self) -> u64 { + if self.fn_hash_indexing != (0, 0) { + self.fn_hash_indexing.0 + } else { + let n1 = crate::calc_fn_hash(FN_IDX_GET, 2); + let n2 = crate::calc_fn_hash(FN_IDX_SET, 3); + self.fn_hash_indexing = (n1, n2); + n1 + } + } + /// Get the pre-calculated index setter hash. + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + #[must_use] + pub(crate) fn hash_idx_set(&mut self) -> u64 { + if self.fn_hash_indexing != (0, 0) { + self.fn_hash_indexing.1 + } else { + let n1 = crate::calc_fn_hash(FN_IDX_GET, 2); + let n2 = crate::calc_fn_hash(FN_IDX_SET, 3); + self.fn_hash_indexing = (n1, n2); + n2 + } + } +} + +impl IntoIterator for GlobalRuntimeState { + type Item = (Identifier, Shared); + type IntoIter = + Zip>, Rev; 3]>>>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.keys + .into_iter() + .rev() + .zip(self.modules.into_iter().rev()) + } +} + +impl, M: Into>> FromIterator<(K, M)> for GlobalRuntimeState { + fn from_iter>(iter: T) -> Self { + let mut lib = Self::new(); + lib.extend(iter); + lib + } +} + +impl, M: Into>> Extend<(K, M)> for GlobalRuntimeState { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each(|(k, m)| { + self.keys.push(k.into()); + self.modules.push(m.into()); + }) + } +} + +impl fmt::Debug for GlobalRuntimeState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Imports")?; + f.debug_map() + .entries(self.keys.iter().zip(self.modules.iter())) + .finish() + } +} + /// _(internals)_ An entry in a function resolution cache. /// Exported under the `internals` feature only. #[derive(Debug, Clone)] @@ -764,7 +764,7 @@ pub struct FnResolutionCacheEntry { /// Function. pub func: CallableFunction, /// Optional source. - pub source: Option, + pub source: Option>, } /// _(internals)_ A function resolution cache. diff --git a/src/func/call.rs b/src/func/call.rs index 7b43937b..24dabc9d 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -206,14 +206,14 @@ impl Engine { .find_map(|m| { m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry { func, - source: m.id_raw().cloned(), + source: m.id_raw().map(|s| s.to_string().into_boxed_str()), }) }) .or_else(|| { self.global_modules.iter().find_map(|m| { m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry { func, - source: m.id_raw().cloned(), + source: m.id_raw().map(|s| s.to_string().into_boxed_str()), }) }) }) @@ -222,7 +222,7 @@ impl Engine { .get_fn(hash) .map(|(func, source)| FnResolutionCacheEntry { func: func.clone(), - source: source.cloned(), + source: source.map(|s| s.to_string().into_boxed_str()), }) }) .or_else(|| { @@ -230,7 +230,7 @@ impl Engine { m.get_qualified_fn(hash).cloned().map(|func| { FnResolutionCacheEntry { func, - source: m.id_raw().cloned(), + source: m.id_raw().map(|s| s.to_string().into_boxed_str()), } }) }) @@ -355,8 +355,8 @@ impl Engine { // Run external function let source = source .as_ref() - .or_else(|| parent_source.as_ref()) - .map(|s| s.as_str()); + .map(|s| &**s) + .or_else(|| parent_source.as_ref().map(|s| s.as_str())); let context = (self, name, source, &*global, lib, pos).into(); @@ -608,7 +608,7 @@ impl Engine { let (first_arg, rest_args) = args.split_first_mut().expect("not empty"); let orig_source = global.source.take(); - global.source = source; + global.source = source.map(Into::into); let level = _level + 1; @@ -642,7 +642,7 @@ impl Engine { } let orig_source = global.source.take(); - global.source = source; + global.source = source.map(Into::into); let level = _level + 1; From 807240e2496764ab53d88effe942bae28da555ca Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 28 Dec 2021 11:42:52 +0800 Subject: [PATCH 53/97] Rename Imports to GlobalRuntimeState. --- src/ast/script_fn.rs | 4 +-- src/engine.rs | 41 +++++++++++++++++++-------- src/func/call.rs | 47 ++----------------------------- src/func/native.rs | 10 ++++--- src/func/script.rs | 66 +++++++++++++++++++++++++++++++++++++------- src/lib.rs | 4 ++- src/module/mod.rs | 18 ++++++------ src/optimizer.rs | 2 +- src/parser.rs | 4 +-- 9 files changed, 112 insertions(+), 84 deletions(-) diff --git a/src/ast/script_fn.rs b/src/ast/script_fn.rs index a8dd6d39..a9d924e7 100644 --- a/src/ast/script_fn.rs +++ b/src/ast/script_fn.rs @@ -15,11 +15,11 @@ pub struct ScriptFnDef { pub body: StmtBlock, /// Encapsulated running environment, if any. pub lib: Option>, - /// Encapsulated [`GlobalRuntimeState`][crate::GlobalRuntimeState]. + /// Encapsulated stack of imported modules, if any. /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] - pub global: crate::engine::GlobalRuntimeState, + pub global: Option)]>>, /// Function name. pub name: Identifier, /// Function access mode. diff --git a/src/engine.rs b/src/engine.rs index 75f44be0..9ae4d5bb 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -771,10 +771,10 @@ pub struct FnResolutionCacheEntry { /// Exported under the `internals` feature only. pub type FnResolutionCache = BTreeMap>>; -/// _(internals)_ A type that holds all the current states of the [`Engine`]. +/// _(internals)_ A type that holds all volatile evaluation states data. /// Exported under the `internals` feature only. -#[derive(Debug, Clone)] -pub struct EvalState { +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Hash)] +pub struct EvalStateData { /// Normally, access to variables are parsed with a relative offset into the [`Scope`] to avoid a lookup. /// In some situation, e.g. after running an `eval` statement, or after a custom syntax statement, /// subsequent offsets may become mis-aligned. @@ -783,6 +783,24 @@ pub struct EvalState { /// Level of the current scope. The global (root) level is zero, a new block (or function call) /// is one level higher, and so on. pub scope_level: usize, +} + +impl EvalStateData { + /// Create a new [`EvalStateData`]. + pub const fn new() -> Self { + Self { + always_search_scope: false, + scope_level: 0, + } + } +} + +/// _(internals)_ A type that holds all the current states of the [`Engine`]. +/// Exported under the `internals` feature only. +#[derive(Debug, Clone)] +pub struct EvalState { + /// Volatile states data. + pub data: EvalStateData, /// Stack of function resolution caches. fn_resolution_caches: StaticVec, } @@ -793,8 +811,7 @@ impl EvalState { #[must_use] pub const fn new() -> Self { Self { - always_search_scope: false, - scope_level: 0, + data: EvalStateData::new(), fn_resolution_caches: StaticVec::new_const(), } } @@ -802,7 +819,7 @@ impl EvalState { #[inline(always)] #[must_use] pub const fn is_global(&self) -> bool { - self.scope_level == 0 + self.data.scope_level == 0 } /// Get the number of function resolution cache(s) in the stack. #[inline(always)] @@ -1142,7 +1159,7 @@ impl Engine { let root = &namespace[0].name; // Qualified - check if the root module is directly indexed - let index = if state.always_search_scope { + let index = if state.data.always_search_scope { None } else { namespace.index() @@ -1267,7 +1284,7 @@ impl Engine { Err(ERR::ErrorUnboundThis(*pos).into()) } } - _ if state.always_search_scope => (0, expr.position()), + _ if state.data.always_search_scope => (0, expr.position()), Expr::Variable(Some(i), pos, _) => (i.get() as usize, *pos), Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos), _ => unreachable!("Expr::Variable expected, but gets {:?}", expr), @@ -2454,13 +2471,13 @@ impl Engine { return Ok(Dynamic::UNIT); } - let orig_always_search_scope = state.always_search_scope; + let orig_always_search_scope = state.data.always_search_scope; let orig_scope_len = scope.len(); let orig_mods_len = global.num_imported_modules(); let orig_fn_resolution_caches_len = state.fn_resolution_caches_len(); if rewind_scope { - state.scope_level += 1; + state.data.scope_level += 1; } let result = statements.iter().try_fold(Dynamic::UNIT, |_, stmt| { @@ -2500,14 +2517,14 @@ impl Engine { if rewind_scope { scope.rewind(orig_scope_len); - state.scope_level -= 1; + state.data.scope_level -= 1; } if restore_orig_state { global.truncate_modules(orig_mods_len); // The impact of new local variables goes away at the end of a block // because any new variables introduced will go out of scope - state.always_search_scope = orig_always_search_scope; + state.data.always_search_scope = orig_always_search_scope; } result diff --git a/src/func/call.rs b/src/func/call.rs index 24dabc9d..b6881dd9 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -695,48 +695,6 @@ impl Engine { }) } - /// Evaluate a text script in place - used primarily for 'eval'. - fn eval_script_expr_in_place( - &self, - scope: &mut Scope, - global: &mut GlobalRuntimeState, - lib: &[&Module], - script: impl AsRef, - _pos: Position, - level: usize, - ) -> RhaiResult { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, _pos)?; - - let script = script.as_ref().trim(); - if script.is_empty() { - return Ok(Dynamic::UNIT); - } - - // Compile the script text - // No optimizations because we only run it once - let ast = self.compile_with_scope_and_optimization_level( - &Scope::new(), - &[script], - #[cfg(not(feature = "no_optimize"))] - crate::OptimizationLevel::None, - )?; - - // If new functions are defined within the eval string, it is an error - #[cfg(not(feature = "no_function"))] - if !ast.shared_lib().is_empty() { - return Err(crate::PERR::WrongFnDefinition.into()); - } - - let statements = ast.statements(); - if statements.is_empty() { - return Ok(Dynamic::UNIT); - } - - // Evaluate the AST - self.eval_global_statements(scope, global, &mut EvalState::new(), statements, lib, level) - } - /// Call a dot method. #[cfg(not(feature = "no_object"))] pub(crate) fn make_method_call( @@ -1081,13 +1039,14 @@ impl Engine { let script = &value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; + let level = level + 1; let result = - self.eval_script_expr_in_place(scope, global, lib, script, pos, level + 1); + self.eval_script_expr_in_place(scope, global, state, lib, script, pos, level); // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. if scope.len() != orig_scope_len { - state.always_search_scope = true; + state.data.always_search_scope = true; } return result.map_err(|err| { diff --git a/src/func/native.rs b/src/func/native.rs index da7e104d..f3d45d1d 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -186,7 +186,8 @@ impl<'a> NativeCallContext<'a> { pub const fn source(&self) -> Option<&str> { self.source } - /// Get an iterator over the current set of modules imported via `import` statements. + /// Get an iterator over the current set of modules imported via `import` statements + /// in reverse order. /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] @@ -194,7 +195,7 @@ impl<'a> NativeCallContext<'a> { pub fn iter_imports(&self) -> impl Iterator { self.global.iter().flat_map(|&m| m.iter_modules()) } - /// Get an iterator over the current set of modules imported via `import` statements. + /// Get an iterator over the current set of modules imported via `import` statements in reverse order. #[cfg(not(feature = "no_module"))] #[allow(dead_code)] #[inline] @@ -214,10 +215,11 @@ impl<'a> NativeCallContext<'a> { pub const fn global_runtime_state(&self) -> Option<&GlobalRuntimeState> { self.global } - /// Get an iterator over the namespaces containing definitions of all script-defined functions. + /// Get an iterator over the namespaces containing definitions of all script-defined functions + /// in reverse order. #[inline] pub fn iter_namespaces(&self) -> impl Iterator { - self.lib.iter().cloned() + self.lib.iter().rev().cloned() } /// _(internals)_ The current set of namespaces containing definitions of all script-defined functions. /// Exported under the `internals` feature only. diff --git a/src/func/script.rs b/src/func/script.rs index 285e3045..02c08ad9 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -3,7 +3,7 @@ use super::call::FnCallArgs; use crate::ast::ScriptFnDef; -use crate::engine::{EvalState, GlobalRuntimeState}; +use crate::engine::{EvalState, EvalStateData, GlobalRuntimeState}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, StaticVec, ERR}; use std::mem; @@ -91,6 +91,8 @@ impl Engine { // Merge in encapsulated environment, if any let mut lib_merged = StaticVec::with_capacity(lib.len() + 1); let orig_fn_resolution_caches_len = state.fn_resolution_caches_len(); + let orig_states_data = state.data; + state.data = EvalStateData::new(); let lib = if let Some(ref fn_lib) = fn_def.lib { if fn_lib.is_empty() { @@ -106,15 +108,14 @@ impl Engine { }; #[cfg(not(feature = "no_module"))] - if fn_def.global.num_imported_modules() > 0 { - fn_def - .global - .iter_modules_raw() - .for_each(|(n, m)| global.push_module(n.clone(), m.clone())); + if let Some(ref modules) = fn_def.global { + modules + .iter() + .cloned() + .for_each(|(n, m)| global.push_module(n, m)); } // Evaluate the function - let body = &fn_def.body; let result = self .eval_stmt_block( scope, @@ -122,7 +123,7 @@ impl Engine { state, lib, this_ptr, - body, + &fn_def.body, true, rewind_scope, level, @@ -149,15 +150,17 @@ impl Engine { _ => make_error(fn_def.name.to_string(), fn_def, global, err, pos), }); - // Remove all local variables + // Remove all local variables and imported modules if rewind_scope { scope.rewind(orig_scope_len); } else if !args.is_empty() { // Remove arguments only, leaving new variables in the scope scope.remove_range(orig_scope_len, args.len()) } - global.truncate_modules(orig_mods_len); + + // Restore state + state.data = orig_states_data; state.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); result @@ -193,4 +196,47 @@ impl Engine { result } + + /// Evaluate a text script in place - used primarily for 'eval'. + pub(crate) fn eval_script_expr_in_place( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + state: &mut EvalState, + lib: &[&Module], + script: impl AsRef, + _pos: Position, + level: usize, + ) -> RhaiResult { + #[cfg(not(feature = "unchecked"))] + self.inc_operations(&mut global.num_operations, _pos)?; + + let script = script.as_ref().trim(); + if script.is_empty() { + return Ok(Dynamic::UNIT); + } + + // Compile the script text + // No optimizations because we only run it once + let ast = self.compile_with_scope_and_optimization_level( + &Scope::new(), + &[script], + #[cfg(not(feature = "no_optimize"))] + crate::OptimizationLevel::None, + )?; + + // If new functions are defined within the eval string, it is an error + #[cfg(not(feature = "no_function"))] + if !ast.shared_lib().is_empty() { + return Err(crate::PERR::WrongFnDefinition.into()); + } + + let statements = ast.statements(); + if statements.is_empty() { + return Ok(Dynamic::UNIT); + } + + // Evaluate the AST + self.eval_global_statements(scope, global, state, statements, lib, level) + } } diff --git a/src/lib.rs b/src/lib.rs index d9e61697..757c3135 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,7 +241,9 @@ pub use ast::{ pub use ast::FloatWrapper; #[cfg(feature = "internals")] -pub use engine::{EvalState, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState}; +pub use engine::{ + EvalState, EvalStateData, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState, +}; #[cfg(feature = "internals")] pub use module::Namespace; diff --git a/src/module/mod.rs b/src/module/mod.rs index c3b332ee..a182574e 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1580,15 +1580,17 @@ impl Module { }); // Extra modules left in the scope become sub-modules - let mut func_global = crate::engine::GlobalRuntimeState::new(); + let mut func_global = None; - global - .into_iter() - .skip(orig_mods_len) - .for_each(|(alias, m)| { - func_global.push_module(alias.clone(), m.clone()); - module.set_sub_module(alias, m); - }); + global.into_iter().skip(orig_mods_len).for_each(|kv| { + if func_global.is_none() { + func_global = Some(StaticVec::new()); + } + func_global.as_mut().expect("`Some`").push(kv.clone()); + module.set_sub_module(kv.0, kv.1); + }); + + let func_global = func_global.map(|v| v.into_boxed_slice()); // Non-private functions defined become module functions #[cfg(not(feature = "no_function"))] diff --git a/src/optimizer.rs b/src/optimizer.rs index 77390f99..aca3df1c 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1155,7 +1155,7 @@ pub fn optimize_into_ast( params: fn_def.params.clone(), lib: None, #[cfg(not(feature = "no_module"))] - global: GlobalRuntimeState::new(), + global: None, #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] comments: None, diff --git a/src/parser.rs b/src/parser.rs index 5226cca2..46d1a742 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3129,7 +3129,7 @@ fn parse_fn( body, lib: None, #[cfg(not(feature = "no_module"))] - global: crate::engine::GlobalRuntimeState::new(), + global: None, #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] comments: if comments.is_empty() { @@ -3280,7 +3280,7 @@ fn parse_anon_fn( body: body.into(), lib: None, #[cfg(not(feature = "no_module"))] - global: crate::engine::GlobalRuntimeState::new(), + global: None, #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] comments: None, From 280010c4272b8a998e8f4a2eae819b0be093d044 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 28 Dec 2021 12:00:01 +0800 Subject: [PATCH 54/97] Fix builds. --- src/func/call.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ src/func/script.rs | 43 ------------------------------------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/func/call.rs b/src/func/call.rs index b6881dd9..1dd171cd 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1299,4 +1299,47 @@ impl Engine { .into()), } } + + /// Evaluate a text script in place - used primarily for 'eval'. + pub(crate) fn eval_script_expr_in_place( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + state: &mut EvalState, + lib: &[&Module], + script: impl AsRef, + _pos: Position, + level: usize, + ) -> RhaiResult { + #[cfg(not(feature = "unchecked"))] + self.inc_operations(&mut global.num_operations, _pos)?; + + let script = script.as_ref().trim(); + if script.is_empty() { + return Ok(Dynamic::UNIT); + } + + // Compile the script text + // No optimizations because we only run it once + let ast = self.compile_with_scope_and_optimization_level( + &Scope::new(), + &[script], + #[cfg(not(feature = "no_optimize"))] + crate::OptimizationLevel::None, + )?; + + // If new functions are defined within the eval string, it is an error + #[cfg(not(feature = "no_function"))] + if !ast.shared_lib().is_empty() { + return Err(crate::PERR::WrongFnDefinition.into()); + } + + let statements = ast.statements(); + if statements.is_empty() { + return Ok(Dynamic::UNIT); + } + + // Evaluate the AST + self.eval_global_statements(scope, global, state, statements, lib, level) + } } diff --git a/src/func/script.rs b/src/func/script.rs index 02c08ad9..0ebb2e68 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -196,47 +196,4 @@ impl Engine { result } - - /// Evaluate a text script in place - used primarily for 'eval'. - pub(crate) fn eval_script_expr_in_place( - &self, - scope: &mut Scope, - global: &mut GlobalRuntimeState, - state: &mut EvalState, - lib: &[&Module], - script: impl AsRef, - _pos: Position, - level: usize, - ) -> RhaiResult { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, _pos)?; - - let script = script.as_ref().trim(); - if script.is_empty() { - return Ok(Dynamic::UNIT); - } - - // Compile the script text - // No optimizations because we only run it once - let ast = self.compile_with_scope_and_optimization_level( - &Scope::new(), - &[script], - #[cfg(not(feature = "no_optimize"))] - crate::OptimizationLevel::None, - )?; - - // If new functions are defined within the eval string, it is an error - #[cfg(not(feature = "no_function"))] - if !ast.shared_lib().is_empty() { - return Err(crate::PERR::WrongFnDefinition.into()); - } - - let statements = ast.statements(); - if statements.is_empty() { - return Ok(Dynamic::UNIT); - } - - // Evaluate the AST - self.eval_global_statements(scope, global, state, statements, lib, level) - } } From 5b667a69b7be357d3dced2dc42840fd0dd0225ef Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 28 Dec 2021 12:19:20 +0800 Subject: [PATCH 55/97] Reverse EvalStateData. --- src/engine.rs | 41 ++++++++++++----------------------------- src/func/call.rs | 2 +- src/func/script.rs | 5 +---- src/lib.rs | 4 +--- 4 files changed, 15 insertions(+), 37 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 9ae4d5bb..75f44be0 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -771,10 +771,10 @@ pub struct FnResolutionCacheEntry { /// Exported under the `internals` feature only. pub type FnResolutionCache = BTreeMap>>; -/// _(internals)_ A type that holds all volatile evaluation states data. +/// _(internals)_ A type that holds all the current states of the [`Engine`]. /// Exported under the `internals` feature only. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Hash)] -pub struct EvalStateData { +#[derive(Debug, Clone)] +pub struct EvalState { /// Normally, access to variables are parsed with a relative offset into the [`Scope`] to avoid a lookup. /// In some situation, e.g. after running an `eval` statement, or after a custom syntax statement, /// subsequent offsets may become mis-aligned. @@ -783,24 +783,6 @@ pub struct EvalStateData { /// Level of the current scope. The global (root) level is zero, a new block (or function call) /// is one level higher, and so on. pub scope_level: usize, -} - -impl EvalStateData { - /// Create a new [`EvalStateData`]. - pub const fn new() -> Self { - Self { - always_search_scope: false, - scope_level: 0, - } - } -} - -/// _(internals)_ A type that holds all the current states of the [`Engine`]. -/// Exported under the `internals` feature only. -#[derive(Debug, Clone)] -pub struct EvalState { - /// Volatile states data. - pub data: EvalStateData, /// Stack of function resolution caches. fn_resolution_caches: StaticVec, } @@ -811,7 +793,8 @@ impl EvalState { #[must_use] pub const fn new() -> Self { Self { - data: EvalStateData::new(), + always_search_scope: false, + scope_level: 0, fn_resolution_caches: StaticVec::new_const(), } } @@ -819,7 +802,7 @@ impl EvalState { #[inline(always)] #[must_use] pub const fn is_global(&self) -> bool { - self.data.scope_level == 0 + self.scope_level == 0 } /// Get the number of function resolution cache(s) in the stack. #[inline(always)] @@ -1159,7 +1142,7 @@ impl Engine { let root = &namespace[0].name; // Qualified - check if the root module is directly indexed - let index = if state.data.always_search_scope { + let index = if state.always_search_scope { None } else { namespace.index() @@ -1284,7 +1267,7 @@ impl Engine { Err(ERR::ErrorUnboundThis(*pos).into()) } } - _ if state.data.always_search_scope => (0, expr.position()), + _ if state.always_search_scope => (0, expr.position()), Expr::Variable(Some(i), pos, _) => (i.get() as usize, *pos), Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos), _ => unreachable!("Expr::Variable expected, but gets {:?}", expr), @@ -2471,13 +2454,13 @@ impl Engine { return Ok(Dynamic::UNIT); } - let orig_always_search_scope = state.data.always_search_scope; + let orig_always_search_scope = state.always_search_scope; let orig_scope_len = scope.len(); let orig_mods_len = global.num_imported_modules(); let orig_fn_resolution_caches_len = state.fn_resolution_caches_len(); if rewind_scope { - state.data.scope_level += 1; + state.scope_level += 1; } let result = statements.iter().try_fold(Dynamic::UNIT, |_, stmt| { @@ -2517,14 +2500,14 @@ impl Engine { if rewind_scope { scope.rewind(orig_scope_len); - state.data.scope_level -= 1; + state.scope_level -= 1; } if restore_orig_state { global.truncate_modules(orig_mods_len); // The impact of new local variables goes away at the end of a block // because any new variables introduced will go out of scope - state.data.always_search_scope = orig_always_search_scope; + state.always_search_scope = orig_always_search_scope; } result diff --git a/src/func/call.rs b/src/func/call.rs index 1dd171cd..efde2226 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1046,7 +1046,7 @@ impl Engine { // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. if scope.len() != orig_scope_len { - state.data.always_search_scope = true; + state.always_search_scope = true; } return result.map_err(|err| { diff --git a/src/func/script.rs b/src/func/script.rs index 0ebb2e68..8e1aea01 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -3,7 +3,7 @@ use super::call::FnCallArgs; use crate::ast::ScriptFnDef; -use crate::engine::{EvalState, EvalStateData, GlobalRuntimeState}; +use crate::engine::{EvalState, GlobalRuntimeState}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, StaticVec, ERR}; use std::mem; @@ -91,8 +91,6 @@ impl Engine { // Merge in encapsulated environment, if any let mut lib_merged = StaticVec::with_capacity(lib.len() + 1); let orig_fn_resolution_caches_len = state.fn_resolution_caches_len(); - let orig_states_data = state.data; - state.data = EvalStateData::new(); let lib = if let Some(ref fn_lib) = fn_def.lib { if fn_lib.is_empty() { @@ -160,7 +158,6 @@ impl Engine { global.truncate_modules(orig_mods_len); // Restore state - state.data = orig_states_data; state.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); result diff --git a/src/lib.rs b/src/lib.rs index 757c3135..d9e61697 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,9 +241,7 @@ pub use ast::{ pub use ast::FloatWrapper; #[cfg(feature = "internals")] -pub use engine::{ - EvalState, EvalStateData, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState, -}; +pub use engine::{EvalState, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState}; #[cfg(feature = "internals")] pub use module::Namespace; From cbaf095c7a4023bbf929cba08f7089aae5155c19 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 28 Dec 2021 17:50:49 +0800 Subject: [PATCH 56/97] Merge restore_state and rewind_scope. --- src/engine.rs | 53 +++++++++++++++++++--------------------------- src/func/call.rs | 2 +- src/func/script.rs | 1 - tests/eval.rs | 6 +++--- 4 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 75f44be0..4dc116ed 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2271,7 +2271,7 @@ impl Engine { // Statement block Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), Expr::Stmt(x) => { - self.eval_stmt_block(scope, global, state, lib, this_ptr, x, true, true, level) + self.eval_stmt_block(scope, global, state, lib, this_ptr, x, true, level) } // lhs[idx_expr] @@ -2447,7 +2447,6 @@ impl Engine { this_ptr: &mut Option<&mut Dynamic>, statements: &[Stmt], restore_orig_state: bool, - rewind_scope: bool, level: usize, ) -> RhaiResult { if statements.is_empty() { @@ -2459,7 +2458,7 @@ impl Engine { let orig_mods_len = global.num_imported_modules(); let orig_fn_resolution_caches_len = state.fn_resolution_caches_len(); - if rewind_scope { + if restore_orig_state { state.scope_level += 1; } @@ -2498,11 +2497,9 @@ impl Engine { // If imports list is modified, pop the functions lookup cache state.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); - if rewind_scope { + if restore_orig_state { scope.rewind(orig_scope_len); state.scope_level -= 1; - } - if restore_orig_state { global.truncate_modules(orig_mods_len); // The impact of new local variables goes away at the end of a block @@ -2684,9 +2681,9 @@ impl Engine { // Block scope Stmt::Block(statements, _) if statements.is_empty() => Ok(Dynamic::UNIT), - Stmt::Block(statements, _) => self.eval_stmt_block( - scope, global, state, lib, this_ptr, statements, true, true, level, - ), + Stmt::Block(statements, _) => { + self.eval_stmt_block(scope, global, state, lib, this_ptr, statements, true, level) + } // If statement Stmt::If(expr, x, _) => { @@ -2697,17 +2694,13 @@ impl Engine { if guard_val { if !x.0.is_empty() { - self.eval_stmt_block( - scope, global, state, lib, this_ptr, &x.0, true, true, level, - ) + self.eval_stmt_block(scope, global, state, lib, this_ptr, &x.0, true, level) } else { Ok(Dynamic::UNIT) } } else { if !x.1.is_empty() { - self.eval_stmt_block( - scope, global, state, lib, this_ptr, &x.1, true, true, level, - ) + self.eval_stmt_block(scope, global, state, lib, this_ptr, &x.1, true, level) } else { Ok(Dynamic::UNIT) } @@ -2785,7 +2778,7 @@ impl Engine { if let Some(statements) = stmt_block { if !statements.is_empty() { self.eval_stmt_block( - scope, global, state, lib, this_ptr, statements, true, true, level, + scope, global, state, lib, this_ptr, statements, true, level, ) } else { Ok(Dynamic::UNIT) @@ -2794,7 +2787,7 @@ impl Engine { // Default match clause if !def_stmt.is_empty() { self.eval_stmt_block( - scope, global, state, lib, this_ptr, def_stmt, true, true, level, + scope, global, state, lib, this_ptr, def_stmt, true, level, ) } else { Ok(Dynamic::UNIT) @@ -2805,9 +2798,9 @@ impl Engine { // Loop Stmt::While(Expr::Unit(_), body, _) => loop { if !body.is_empty() { - match self.eval_stmt_block( - scope, global, state, lib, this_ptr, body, true, true, level, - ) { + match self + .eval_stmt_block(scope, global, state, lib, this_ptr, body, true, level) + { Ok(_) => (), Err(err) => match *err { ERR::LoopBreak(false, _) => (), @@ -2832,9 +2825,9 @@ impl Engine { return Ok(Dynamic::UNIT); } if !body.is_empty() { - match self.eval_stmt_block( - scope, global, state, lib, this_ptr, body, true, true, level, - ) { + match self + .eval_stmt_block(scope, global, state, lib, this_ptr, body, true, level) + { Ok(_) => (), Err(err) => match *err { ERR::LoopBreak(false, _) => (), @@ -2850,9 +2843,9 @@ impl Engine { let is_while = !options.contains(AST_OPTION_NEGATED); if !body.is_empty() { - match self.eval_stmt_block( - scope, global, state, lib, this_ptr, body, true, true, level, - ) { + match self + .eval_stmt_block(scope, global, state, lib, this_ptr, body, true, level) + { Ok(_) => (), Err(err) => match *err { ERR::LoopBreak(false, _) => continue, @@ -2950,7 +2943,7 @@ impl Engine { } let result = self.eval_stmt_block( - scope, global, state, lib, this_ptr, statements, true, true, level, + scope, global, state, lib, this_ptr, statements, true, level, ); match result { @@ -3014,9 +3007,7 @@ impl Engine { let (try_stmt, err_var, catch_stmt) = x.as_ref(); let result = self - .eval_stmt_block( - scope, global, state, lib, this_ptr, try_stmt, true, true, level, - ) + .eval_stmt_block(scope, global, state, lib, this_ptr, try_stmt, true, level) .map(|_| Dynamic::UNIT); match result { @@ -3068,7 +3059,7 @@ impl Engine { }); let result = self.eval_stmt_block( - scope, global, state, lib, this_ptr, catch_stmt, true, true, level, + scope, global, state, lib, this_ptr, catch_stmt, true, level, ); scope.rewind(orig_scope_len); diff --git a/src/func/call.rs b/src/func/call.rs index efde2226..6a0444fd 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -684,7 +684,7 @@ impl Engine { level: usize, ) -> RhaiResult { self.eval_stmt_block( - scope, global, state, lib, &mut None, statements, false, false, level, + scope, global, state, lib, &mut None, statements, false, level, ) .or_else(|err| match *err { ERR::Return(out, _) => Ok(out), diff --git a/src/func/script.rs b/src/func/script.rs index 8e1aea01..774648d1 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -122,7 +122,6 @@ impl Engine { lib, this_ptr, &fn_def.body, - true, rewind_scope, level, ) diff --git a/tests/eval.rs b/tests/eval.rs index 18b4145d..494a36f4 100644 --- a/tests/eval.rs +++ b/tests/eval.rs @@ -18,9 +18,9 @@ fn test_eval_blocks() -> Result<(), Box> { r#" let x = 999; - eval("let x = x + 123"); + eval("let x = x - 1000"); - let y = if x > 0 { + let y = if x < 0 { eval("let x = 42"); x } else { @@ -30,7 +30,7 @@ fn test_eval_blocks() -> Result<(), Box> { x + y "# )?, - 1164 + 41 ); Ok(()) From 4bd482352e02526f690a9d6e6ba5e1f895c784de Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 28 Dec 2021 23:00:31 +0800 Subject: [PATCH 57/97] Fix eval call. --- src/func/call.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/func/call.rs b/src/func/call.rs index 6a0444fd..55e9e9e1 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1039,9 +1039,15 @@ impl Engine { let script = &value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; - let level = level + 1; - let result = - self.eval_script_expr_in_place(scope, global, state, lib, script, pos, level); + let result = self.eval_script_expr_in_place( + scope, + global, + state, + lib, + script, + pos, + level + 1, + ); // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. @@ -1339,7 +1345,19 @@ impl Engine { return Ok(Dynamic::UNIT); } + // Backup the original state + let orig_scope_level = state.scope_level; + + // Scope level is set to zero in order to retain all variables + state.scope_level = 0; + // Evaluate the AST - self.eval_global_statements(scope, global, state, statements, lib, level) + let result = self.eval_global_statements(scope, global, state, statements, lib, level); + + // Restore original state + state.scope_level = orig_scope_level; + + // Return result + result } } From 3488dbe74bc23d6e24a3d2191a221acb1399a7c9 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 29 Dec 2021 14:26:54 +0800 Subject: [PATCH 58/97] Fix merging strings interner. --- src/types/interner.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/types/interner.rs b/src/types/interner.rs index db9c2745..f3680a65 100644 --- a/src/types/interner.rs +++ b/src/types/interner.rs @@ -8,8 +8,9 @@ use std::prelude::v1::*; /// _(internals)_ A factory of identifiers from text strings. /// Exported under the `internals` feature only. /// -/// Since [`SmartString`](https://crates.io/crates/smartstring) is used as [`Identifier`], -/// this just returns a copy because most identifiers in Rhai are short and ASCII-based. +/// Normal identifiers are not interned since [`SmartString`][crate::SmartString] and thus copying +/// is relatively fast , this just returns a copy +/// because most identifiers in Rhai are short and ASCII-based. /// /// Property getters and setters are interned separately. #[derive(Debug, Clone, Default, Hash)] @@ -71,12 +72,9 @@ impl StringsInterner { _ => unreachable!("unsupported prefix {}", prefix), } } - /// Merge another [`IdentifierBuilder`] into this. - #[inline(always)] - pub fn merge(&mut self, _other: &Self) {} } -impl AddAssign for StringsInterner { +impl AddAssign for StringsInterner { #[inline(always)] fn add_assign(&mut self, rhs: Self) { let _rhs = rhs; @@ -88,3 +86,18 @@ impl AddAssign for StringsInterner { } } } + +impl AddAssign<&Self> for StringsInterner { + #[inline(always)] + fn add_assign(&mut self, rhs: &Self) { + let _rhs = rhs; + + #[cfg(not(feature = "no_object"))] + { + self.getters + .extend(_rhs.getters.iter().map(|(k, v)| (k.clone(), v.clone()))); + self.setters + .extend(_rhs.setters.iter().map(|(k, v)| (k.clone(), v.clone()))); + } + } +} From be4ae6e7636cf8ebd6ff7aa831ae528d15fc2f3c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 30 Dec 2021 08:57:23 +0800 Subject: [PATCH 59/97] Add rewind_scope to eval_stmt. --- src/engine.rs | 52 +++++++++++++++++++++++++----------------------- src/func/call.rs | 14 +------------ 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 4dc116ed..4a8424f4 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -501,35 +501,24 @@ impl> From for Target<'_> { pub struct GlobalRuntimeState { /// Stack of module names. // - // # Implementation Notes - // // We cannot use Cow here because `eval` may load a [module][Module] and // the module name will live beyond the AST of the eval script text. keys: StaticVec, - /// Stack of imported modules. + /// Stack of imported [modules][Module]. modules: StaticVec>, - /// Source of the current context. pub source: Option, /// Number of operations performed. pub num_operations: u64, /// Number of modules loaded. pub num_modules_loaded: usize, - /// Function call hashes to index getters and setters. - /// - /// Not available under `no_index` and `no_object`. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn_hash_indexing: (u64, u64), - /// Embedded module resolver. - /// - /// Not available under `no_module`. + /// Embedded [module][Module] resolver. #[cfg(not(feature = "no_module"))] pub embedded_module_resolver: Option>, - /// Cache of globally-defined constants. - /// - /// Not available under `no_module` and `no_function`. #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] constants: Option>>>, @@ -775,13 +764,18 @@ pub type FnResolutionCache = BTreeMap>>; /// Exported under the `internals` feature only. #[derive(Debug, Clone)] pub struct EvalState { + /// Force a [`Scope`] search by name. + /// /// Normally, access to variables are parsed with a relative offset into the [`Scope`] to avoid a lookup. + /// /// In some situation, e.g. after running an `eval` statement, or after a custom syntax statement, /// subsequent offsets may become mis-aligned. - /// When that happens, this flag is turned on to force a [`Scope`] search by name. + /// + /// When that happens, this flag is turned on. pub always_search_scope: bool, - /// Level of the current scope. The global (root) level is zero, a new block (or function call) - /// is one level higher, and so on. + /// Level of the current scope. + /// + /// The global (root) level is zero, a new block (or function call) is one level higher, and so on. pub scope_level: usize, /// Stack of function resolution caches. fn_resolution_caches: StaticVec, @@ -798,12 +792,6 @@ impl EvalState { fn_resolution_caches: StaticVec::new_const(), } } - /// Is the state currently at global (root) level? - #[inline(always)] - #[must_use] - pub const fn is_global(&self) -> bool { - self.scope_level == 0 - } /// Get the number of function resolution cache(s) in the stack. #[inline(always)] #[must_use] @@ -2465,7 +2453,16 @@ impl Engine { let result = statements.iter().try_fold(Dynamic::UNIT, |_, stmt| { let _mods_len = global.num_imported_modules(); - let r = self.eval_stmt(scope, global, state, lib, this_ptr, stmt, level)?; + let r = self.eval_stmt( + scope, + global, + state, + lib, + this_ptr, + stmt, + restore_orig_state, + level, + )?; #[cfg(not(feature = "no_module"))] if matches!(stmt, Stmt::Import(_, _, _)) { @@ -2593,6 +2590,7 @@ impl Engine { lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, stmt: &Stmt, + rewind_scope: bool, level: usize, ) -> RhaiResult { #[cfg(not(feature = "unchecked"))] @@ -3119,10 +3117,14 @@ impl Engine { .eval_expr(scope, global, state, lib, this_ptr, expr, level)? .flatten(); - let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() { + let (var_name, _alias): (Cow<'_, str>, _) = if !rewind_scope { #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_module"))] - if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) { + if state.scope_level == 0 + && entry_type == AccessMode::ReadOnly + && lib.iter().any(|&m| !m.is_empty()) + { + // Add a global constant if at top level and there are functions global.set_constant(name.clone(), value.clone()); } diff --git a/src/func/call.rs b/src/func/call.rs index 55e9e9e1..fd20e4ad 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1345,19 +1345,7 @@ impl Engine { return Ok(Dynamic::UNIT); } - // Backup the original state - let orig_scope_level = state.scope_level; - - // Scope level is set to zero in order to retain all variables - state.scope_level = 0; - // Evaluate the AST - let result = self.eval_global_statements(scope, global, state, statements, lib, level); - - // Restore original state - state.scope_level = orig_scope_level; - - // Return result - result + self.eval_global_statements(scope, global, state, statements, lib, level) } } From 1fd242ed2cf5ecd6acb85eac0d23dbd42f2ce141 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 30 Dec 2021 12:14:54 +0800 Subject: [PATCH 60/97] Flatten nested block scopes. --- src/ast/stmt.rs | 24 +++++++++++++++++++++++- src/optimizer.rs | 32 +++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 1754ee56..c90d405e 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -1,6 +1,7 @@ //! Module defining script statements. use super::{ASTNode, Expr, FnCallExpr, Ident, OptionFlags, AST_OPTION_FLAGS}; +use crate::engine::KEYWORD_EVAL; use crate::tokenizer::Token; use crate::{calc_fn_hash, Position, StaticVec, INT}; #[cfg(feature = "no_std")] @@ -466,12 +467,33 @@ impl Stmt { Self::Share(_) => false, } } + /// Does this statement's behavior depend on its containing block? + /// + /// A statement that depends on its containing block behaves differently when promoted + /// to an upper block. + /// + /// Currently only variable definitions (i.e. `let` and `const`), `import`/`export` statements, + /// and `eval` calls (which may in turn call define variables) fall under this category. + #[inline] + #[must_use] + pub fn is_block_dependent(&self) -> bool { + match self { + Self::Var(_, _, _, _) => true, + + Self::FnCall(x, _) if x.name == KEYWORD_EVAL => true, + + #[cfg(not(feature = "no_module"))] + Self::Import(_, _, _) | Self::Export(_, _) => true, + + _ => false, + } + } /// Is this statement _pure_ within the containing block? /// /// An internally pure statement only has side effects that disappear outside the block. /// /// Currently only variable definitions (i.e. `let` and `const`) and `import`/`export` - /// statements are internally pure. + /// statements are internally pure, other than pure expressions. #[inline] #[must_use] pub fn is_internally_pure(&self) -> bool { diff --git a/src/optimizer.rs b/src/optimizer.rs index aca3df1c..fb3c7cd4 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -189,6 +189,32 @@ fn optimize_stmt_block( Stmt::is_pure }; + // Flatten blocks + loop { + if let Some(n) = statements.iter().enumerate().find_map(|(i, s)| match s { + Stmt::Block(block, _) if !block.iter().any(Stmt::is_block_dependent) => Some(i), + _ => None, + }) { + let (first, second) = statements.split_at_mut(n); + let stmt = mem::take(&mut second[0]); + let mut stmts = match stmt { + Stmt::Block(block, _) => block, + _ => unreachable!("Stmt::Block expected but gets {:?}", stmt), + }; + statements = first + .iter_mut() + .map(mem::take) + .chain(stmts.iter_mut().map(mem::take)) + .chain(second.iter_mut().skip(1).map(mem::take)) + .collect(); + } else { + break; + } + + is_dirty = true; + } + + // Optimize loop { state.clear_dirty(); @@ -411,7 +437,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b x.2 = value; } } - _ => unreachable!(), + ref expr => unreachable!("Expr::FnCall expected but gets {:?}", expr), } } @@ -695,8 +721,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b state.set_dirty(); *stmt = Stmt::Noop(*pos); } - // Only one statement - promote - [s] => { + // Only one statement which is not block-dependent - promote + [s] if !s.is_block_dependent() => { state.set_dirty(); *stmt = mem::take(s); } From 64bf2eef5c26b7d48faa461cf61843538182ed3b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 30 Dec 2021 12:19:41 +0800 Subject: [PATCH 61/97] Better error messages. --- src/ast/expr.rs | 5 +++- src/ast/stmt.rs | 8 ++++- src/engine.rs | 14 ++++----- src/module/mod.rs | 4 +-- src/optimizer.rs | 2 +- src/packages/array_basic.rs | 2 +- src/parser.rs | 60 +++++++++++++++++++------------------ src/tokenizer.rs | 12 ++++---- src/types/dynamic.rs | 31 ++++++++++--------- 9 files changed, 74 insertions(+), 64 deletions(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 3ec16433..137d5214 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -469,7 +469,10 @@ impl fmt::Debug for Expr { Self::Dot(_, _, _) => "Dot", Self::And(_, _) => "And", Self::Or(_, _) => "Or", - _ => unreachable!(), + expr => unreachable!( + "Self::Dot or Self::And or Self::Or expected but gets {:?}", + expr + ), }; display_pos = *pos; diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index c90d405e..cda7bad5 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -480,7 +480,11 @@ impl Stmt { match self { Self::Var(_, _, _, _) => true, - Self::FnCall(x, _) if x.name == KEYWORD_EVAL => true, + Self::Expr(Expr::Stmt(s)) => s.iter().all(Stmt::is_block_dependent), + + Self::FnCall(x, _) | Self::Expr(Expr::FnCall(x, _)) => { + x.namespace.is_none() && x.name == KEYWORD_EVAL + } #[cfg(not(feature = "no_module"))] Self::Import(_, _, _) | Self::Export(_, _) => true, @@ -500,6 +504,8 @@ impl Stmt { match self { Self::Var(expr, _, _, _) => expr.is_pure(), + Self::Expr(Expr::Stmt(s)) => s.iter().all(Stmt::is_internally_pure), + #[cfg(not(feature = "no_module"))] Self::Import(expr, _, _) => expr.is_pure(), #[cfg(not(feature = "no_module"))] diff --git a/src/engine.rs b/src/engine.rs index 4a8424f4..fd4104f8 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -129,7 +129,7 @@ impl ChainArgument { Self::MethodCallArgs(Some(mut values), pos) => { (values.iter_mut().map(std::mem::take).collect(), pos) } - _ => unreachable!("`MethodCallArgs` expected"), + x => unreachable!("ChainArgument::MethodCallArgs expected but gets {:?}", x), } } /// Return the [position][Position]. @@ -174,7 +174,7 @@ fn match_chaining_type(expr: &Expr) -> ChainType { Expr::Index(_, _, _) => ChainType::Indexing, #[cfg(not(feature = "no_object"))] Expr::Dot(_, _, _) => ChainType::Dotting, - _ => unreachable!("`expr` should only be `Index` or `Dot`, but got {:?}", expr), + expr => unreachable!("Expr::Index or Expr::Dot expected but gets {:?}", expr), } } @@ -1226,7 +1226,7 @@ impl Engine { #[cfg(feature = "no_module")] (_, Some((_, _)), _) => unreachable!("qualified access under no_module"), }, - _ => unreachable!("Expr::Variable expected, but gets {:?}", expr), + _ => unreachable!("Expr::Variable expected but gets {:?}", expr), } } @@ -1258,7 +1258,7 @@ impl Engine { _ if state.always_search_scope => (0, expr.position()), Expr::Variable(Some(i), pos, _) => (i.get() as usize, *pos), Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos), - _ => unreachable!("Expr::Variable expected, but gets {:?}", expr), + _ => unreachable!("Expr::Variable expected but gets {:?}", expr), }; // Check the variable resolver, if any @@ -1774,7 +1774,7 @@ impl Engine { Expr::Index(x, term, pos) => (x.as_ref(), ChainType::Indexing, *term, *pos), #[cfg(not(feature = "no_object"))] Expr::Dot(x, term, pos) => (x.as_ref(), ChainType::Dotting, *term, *pos), - _ => unreachable!("index or dot chain expected, but gets {:?}", expr), + expr => unreachable!("Expr::Index or Expr::Dot expected but gets {:?}", expr), }; let idx_values = &mut StaticVec::new_const(); @@ -2123,7 +2123,7 @@ impl Engine { ) } } else { - unreachable!("`Range` or `RangeInclusive`"); + unreachable!("Range or RangeInclusive expected but gets {:?}", idx); }; let field_value = (*value & mask) >> shift; @@ -2655,7 +2655,7 @@ impl Engine { match lhs_expr { // name op= rhs (handled above) Expr::Variable(_, _, _) => { - unreachable!("Expr::Variable case should already been handled") + unreachable!("Expr::Variable case is already handled") } // idx_lhs[idx_expr] op= rhs #[cfg(not(feature = "no_index"))] diff --git a/src/module/mod.rs b/src/module/mod.rs index a182574e..0662e483 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1334,7 +1334,7 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; - self.interner.merge(&other.interner); + self.interner += &other.interner; self } @@ -1384,7 +1384,7 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; - self.interner.merge(&other.interner); + self.interner += &other.interner; self } diff --git a/src/optimizer.rs b/src/optimizer.rs index fb3c7cd4..64d3037d 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -199,7 +199,7 @@ fn optimize_stmt_block( let stmt = mem::take(&mut second[0]); let mut stmts = match stmt { Stmt::Block(block, _) => block, - _ => unreachable!("Stmt::Block expected but gets {:?}", stmt), + stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt), }; statements = first .iter_mut() diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 7e61dce8..6c07f94d 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -771,7 +771,7 @@ pub mod array_functions { v if v > 0 => Ordering::Greater, v if v < 0 => Ordering::Less, 0 => Ordering::Equal, - _ => unreachable!(), + _ => unreachable!("v is {}", v), }) .unwrap_or_else(|| x.type_id().cmp(&y.type_id())) }); diff --git a/src/parser.rs b/src/parser.rs index 46d1a742..627a750c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -349,7 +349,7 @@ fn eat_token(input: &mut TokenStream, expected_token: Token) -> Position { if t != expected_token { unreachable!( - "expecting {} (found {}) at {}", + "{} expected but gets {} at {}", expected_token.syntax(), t.syntax(), pos @@ -1098,7 +1098,7 @@ fn parse_switch( None } (None, None) => Some(stmt.into()), - _ => unreachable!("both hash and range in `switch` statement case"), + _ => unreachable!("both hash and range in switch statement case"), }; match input.peek().expect(NEVER_ENDS) { @@ -1163,7 +1163,7 @@ fn parse_primary( } Token::True => Expr::BoolConstant(true, settings.pos), Token::False => Expr::BoolConstant(false, settings.pos), - _ => unreachable!(), + token => unreachable!("token is {:?}", token), }, #[cfg(not(feature = "no_float"))] Token::FloatConstant(x) => { @@ -1182,7 +1182,7 @@ fn parse_primary( Token::LeftBrace if settings.allow_stmt_expr => { match parse_block(input, state, lib, settings.level_up())? { block @ Stmt::Block(_, _) => Expr::Stmt(Box::new(block.into())), - stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt), + stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt), } } // ( - grouped expression @@ -1253,16 +1253,17 @@ fn parse_primary( Token::InterpolatedString(_) => { let mut segments = StaticVec::::new(); - if let (Token::InterpolatedString(s), pos) = input.next().expect(NEVER_ENDS) { - segments.push(Expr::StringConstant(s.into(), pos)); - } else { - unreachable!(); + match input.next().expect(NEVER_ENDS) { + (Token::InterpolatedString(s), pos) => { + segments.push(Expr::StringConstant(s.into(), pos)); + } + token => unreachable!("Token::InterpolatedString expected but gets {:?}", token), } loop { let expr = match parse_block(input, state, lib, settings.level_up())? { block @ Stmt::Block(_, _) => Expr::Stmt(Box::new(block.into())), - stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt), + stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt), }; segments.push(expr); @@ -1288,7 +1289,7 @@ fn parse_primary( return Err(err.into_err(pos)) } (token, _) => unreachable!( - "expected a string within an interpolated string literal, but gets {:?}", + "string within an interpolated string literal expected but gets {:?}", token ), } @@ -1324,7 +1325,7 @@ fn parse_primary( Token::Identifier(_) => { let s = match input.next().expect(NEVER_ENDS) { (Token::Identifier(s), _) => s, - _ => unreachable!(), + token => unreachable!("Token::Identifier expected but gets {:?}", token), }; match input.peek().expect(NEVER_ENDS).0 { @@ -1383,7 +1384,7 @@ fn parse_primary( Token::Reserved(_) => { let s = match input.next().expect(NEVER_ENDS) { (Token::Reserved(s), _) => s, - _ => unreachable!(), + token => unreachable!("Token::Reserved expected but gets {:?}", token), }; match input.peek().expect(NEVER_ENDS).0 { @@ -1411,7 +1412,7 @@ fn parse_primary( Token::LexError(_) => match input.next().expect(NEVER_ENDS) { (Token::LexError(err), _) => return Err(err.into_err(settings.pos)), - _ => unreachable!(), + token => unreachable!("Token::LexError expected but gets {:?}", token), }, _ => { @@ -1694,12 +1695,12 @@ fn make_assignment_stmt( ref e => Some(e.position()), }, Expr::Index(x, term, _) | Expr::Dot(x, term, _) => match x.lhs { - Expr::Property(_) => unreachable!("unexpected `Expr::Property` in indexing"), + Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), _ if !term => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _, _))), _ => None, }, Expr::Property(_) if parent_is_dot => None, - Expr::Property(_) => unreachable!("unexpected `Expr::Property` in indexing"), + Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), e if parent_is_dot => Some(e.position()), _ => None, } @@ -1838,7 +1839,7 @@ fn make_dot_expr( let (x, term, pos, is_dot) = match rhs { Expr::Dot(x, term, pos) => (x, term, pos, true), Expr::Index(x, term, pos) => (x, term, pos, false), - _ => unreachable!(), + expr => unreachable!("Expr::Dot or Expr::Index expected but gets {:?}", expr), }; match x.lhs { @@ -1877,7 +1878,7 @@ fn make_dot_expr( }; Ok(Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)) } - _ => unreachable!("invalid dot expression: {:?}", x.lhs), + expr => unreachable!("invalid dot expression: {:?}", expr), } } // lhs.nnn::func(...) @@ -2162,7 +2163,7 @@ fn parse_custom_syntax( segments.push(keyword.clone().into()); tokens.push(keyword); } - stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt), + stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt), }, CUSTOM_SYNTAX_MARKER_BOOL => match input.next().expect(NEVER_ENDS) { (b @ Token::True, pos) | (b @ Token::False, pos) => { @@ -2338,7 +2339,7 @@ fn parse_while_loop( (expr, pos) } (Token::Loop, pos) => (Expr::Unit(Position::NONE), pos), - _ => unreachable!(), + token => unreachable!("Token::While or Token::Loop expected but gets {:?}", token), }; settings.pos = token_pos; settings.is_breakable = true; @@ -2789,7 +2790,7 @@ fn parse_stmt( } if !crate::tokenizer::is_doc_comment(comment) { - unreachable!("expecting doc-comment, but gets {:?}", comment); + unreachable!("doc-comment expected but gets {:?}", comment); } if !settings.is_global { @@ -2806,7 +2807,7 @@ fn parse_stmt( _ => return Err(PERR::WrongDocComment.into_err(comments_pos)), } } - _ => unreachable!(), + token => unreachable!("Token::Comment expected but gets {:?}", token), } } @@ -2933,14 +2934,15 @@ fn parse_stmt( let (return_type, token_pos) = input .next() .map(|(token, pos)| { - ( - match token { - Token::Return => AST_OPTION_NONE, - Token::Throw => AST_OPTION_BREAK_OUT, - _ => unreachable!(), - }, - pos, - ) + let flags = match token { + Token::Return => AST_OPTION_NONE, + Token::Throw => AST_OPTION_BREAK_OUT, + token => unreachable!( + "Token::Return or Token::Throw expected but gets {:?}", + token + ), + }; + (flags, pos) }) .expect(NEVER_ENDS); diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 2a6d301f..9387bbee 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -250,7 +250,7 @@ impl fmt::Display for Position { #[cfg(not(feature = "no_position"))] write!(f, "line {}, position {}", self.line, self.pos)?; #[cfg(feature = "no_position")] - unreachable!(); + unreachable!("no position"); } Ok(()) @@ -285,7 +285,7 @@ impl Add for Position { }, }; #[cfg(feature = "no_position")] - unreachable!(); + unreachable!("no position"); } } } @@ -1159,7 +1159,7 @@ pub fn parse_string_literal( 'x' => 2, 'u' => 4, 'U' => 8, - _ => unreachable!(), + c => unreachable!("x or u or U expected but gets '{}'", c), }; for _ in 0..len { @@ -1506,14 +1506,14 @@ fn get_next_token_inner( 'x' | 'X' => is_hex_digit, 'o' | 'O' => is_numeric_digit, 'b' | 'B' => is_numeric_digit, - _ => unreachable!(), + c => unreachable!("x/X or o/O or b/B expected but gets '{}'", c), }; radix_base = Some(match ch { 'x' | 'X' => 16, 'o' | 'O' => 8, 'b' | 'B' => 2, - _ => unreachable!(), + c => unreachable!("x/X or o/O or b/B expected but gets '{}'", c), }); } @@ -2238,7 +2238,7 @@ impl<'a> Iterator for TokenIterator<'a> { (Token::Custom(token.syntax().into()), pos) } else { // Active standard keyword - should never be a custom keyword! - unreachable!("`{:?}` is an active keyword", token) + unreachable!("{:?} is an active keyword", token) } } // Disabled symbol diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 09d24950..140903a8 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -9,6 +9,7 @@ use std::{ any::{type_name, Any, TypeId}, fmt, hash::{Hash, Hasher}, + mem, ops::{Deref, DerefMut}, str::FromStr, }; @@ -472,7 +473,7 @@ impl Hash for Dynamic { /// /// Panics if the [`Dynamic`] value contains an unrecognized trait object. fn hash(&self, state: &mut H) { - std::mem::discriminant(&self.0).hash(state); + mem::discriminant(&self.0).hash(state); match self.0 { Union::Unit(_, _, _) => ().hash(state), @@ -1614,21 +1615,19 @@ impl Dynamic { pub(crate) fn flatten_in_place(&mut self) -> &mut Self { match self.0 { #[cfg(not(feature = "no_closure"))] - Union::Shared(_, _, _) => match std::mem::take(self).0 { - Union::Shared(cell, _, _) => { - *self = crate::func::native::shared_try_take(cell).map_or_else( - #[cfg(not(feature = "sync"))] - |cell| cell.borrow().clone(), - #[cfg(feature = "sync")] - |cell| cell.read().unwrap().clone(), - #[cfg(not(feature = "sync"))] - |value| value.into_inner(), - #[cfg(feature = "sync")] - |value| value.into_inner().unwrap(), - ); - } - _ => unreachable!(), - }, + Union::Shared(ref mut cell, _, _) => { + let cell = mem::take(cell); + *self = crate::func::native::shared_try_take(cell).map_or_else( + #[cfg(not(feature = "sync"))] + |cell| cell.borrow().clone(), + #[cfg(feature = "sync")] + |cell| cell.read().unwrap().clone(), + #[cfg(not(feature = "sync"))] + |value| value.into_inner(), + #[cfg(feature = "sync")] + |value| value.into_inner().unwrap(), + ); + } _ => (), } self From 80ccd52319e1696d5c24747a035db21ea5da6852 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 30 Dec 2021 12:23:35 +0800 Subject: [PATCH 62/97] Add tests. --- tests/eval.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++ tests/functions.rs | 38 ++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/tests/eval.rs b/tests/eval.rs index 494a36f4..5187acf8 100644 --- a/tests/eval.rs +++ b/tests/eval.rs @@ -6,6 +6,20 @@ fn test_eval() -> Result<(), Box> { assert_eq!(engine.eval::(r#"eval("40 + 2")"#)?, 42); + assert_eq!( + engine.eval::( + r#" + let foo = 42; + + eval("let foo = 123"); + eval("let xyz = 10"); + + foo + xyz + "# + )?, + 133 + ); + Ok(()) } @@ -33,6 +47,72 @@ fn test_eval_blocks() -> Result<(), Box> { 41 ); + assert_eq!( + engine.eval::( + r#" + let foo = 42; + + eval("{ let foo = 123; }"); + + foo + "# + )?, + 42 + ); + + assert_eq!( + engine.eval::( + r#" + let foo = 42; + { { { + eval("let foo = 123"); + } } } + foo + "# + )?, + 42 + ); + + Ok(()) +} + +#[cfg(not(feature = "no_function"))] +#[cfg(not(feature = "no_module"))] +#[test] +fn test_eval_globals() -> Result<(), Box> { + let engine = Engine::new(); + + assert_eq!( + engine.eval::( + r#" + const XYZ = 123; + + fn foo() { global::XYZ } + { + eval("const XYZ = 42;"); + } + + foo() + "# + )?, + 123 + ); + + assert_eq!( + engine.eval::( + r#" + const XYZ = 123; + + fn foo() { global::XYZ } + + eval("const XYZ = 42;"); + + foo() + "# + )?, + 42 + ); + Ok(()) } diff --git a/tests/functions.rs b/tests/functions.rs index 491f9559..a81c56a3 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -181,5 +181,43 @@ fn test_functions_bang() -> Result<(), Box> { 123 ); + assert_eq!( + engine.eval::( + " + fn foo() { + let hello = bar + 42; + } + + let bar = 999; + let hello = 123; + + foo!(); + + hello + ", + )?, + 123 + ); + + assert_eq!( + engine.eval::( + r#" + fn foo(x) { + let hello = bar + 42 + x; + } + + let bar = 999; + let hello = 123; + + let f = Fn("foo"); + + call!(f, 1); + + hello + "#, + )?, + 123 + ); + Ok(()) } From 97900320e1d1839ef537ef50532a54652b388d12 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 30 Dec 2021 22:53:59 +0800 Subject: [PATCH 63/97] Fix benchmarks --- benches/eval_array.rs | 4 ++-- benches/eval_expression.rs | 12 ++++++------ benches/eval_module.rs | 12 ++++++------ benches/iterations.rs | 16 ++++++++-------- benches/parsing.rs | 6 +++--- benches/primes.rs | 32 ++++++++++++++++---------------- 6 files changed, 41 insertions(+), 41 deletions(-) diff --git a/benches/eval_array.rs b/benches/eval_array.rs index b89cc9d6..7287f222 100644 --- a/benches/eval_array.rs +++ b/benches/eval_array.rs @@ -64,7 +64,7 @@ fn bench_eval_array_large_set(bench: &mut Bencher) { #[bench] fn bench_eval_array_loop(bench: &mut Bencher) { - let script = r#" + let script = " let list = []; for i in 0..10_000 { @@ -76,7 +76,7 @@ fn bench_eval_array_loop(bench: &mut Bencher) { for i in list { sum += i; } - "#; + "; let mut engine = Engine::new(); engine.set_optimization_level(OptimizationLevel::None); diff --git a/benches/eval_expression.rs b/benches/eval_expression.rs index 73a2afb9..73bae9c0 100644 --- a/benches/eval_expression.rs +++ b/benches/eval_expression.rs @@ -108,12 +108,12 @@ fn bench_eval_call(bench: &mut Bencher) { #[bench] fn bench_eval_loop_number(bench: &mut Bencher) { - let script = r#" + let script = " let s = 0; for x in 0..10000 { s += 1; } - "#; + "; let mut engine = Engine::new(); engine.set_optimization_level(OptimizationLevel::None); @@ -159,7 +159,7 @@ fn bench_eval_loop_strings_no_build(bench: &mut Bencher) { #[bench] fn bench_eval_switch(bench: &mut Bencher) { - let script = r#" + let script = " let sum = 0; let rem = 0; @@ -179,7 +179,7 @@ fn bench_eval_switch(bench: &mut Bencher) { 9 => 1, } } - "#; + "; let mut engine = Engine::new(); engine.set_optimization_level(OptimizationLevel::None); @@ -191,7 +191,7 @@ fn bench_eval_switch(bench: &mut Bencher) { #[bench] fn bench_eval_nested_if(bench: &mut Bencher) { - let script = r#" + let script = " let sum = 0; let rem = 0; @@ -209,7 +209,7 @@ fn bench_eval_nested_if(bench: &mut Bencher) { else if rem == 8 { 42 } else if rem == 9 { 1 }; } - "#; + "; let mut engine = Engine::new(); engine.set_optimization_level(OptimizationLevel::None); diff --git a/benches/eval_module.rs b/benches/eval_module.rs index 3bb4e04e..de7843c0 100644 --- a/benches/eval_module.rs +++ b/benches/eval_module.rs @@ -8,10 +8,10 @@ use test::Bencher; #[bench] fn bench_eval_module(bench: &mut Bencher) { - let script = r#" + let script = " fn foo(x) { x + 1 } fn bar(x) { foo(x) } - "#; + "; let mut engine = Engine::new(); engine.set_optimization_level(OptimizationLevel::None); @@ -24,10 +24,10 @@ fn bench_eval_module(bench: &mut Bencher) { let ast = engine .compile( - r#" + " fn foo(x) { x - 1 } testing::bar(41) - "#, + ", ) .unwrap(); @@ -41,11 +41,11 @@ fn bench_eval_function_call(bench: &mut Bencher) { let ast = engine .compile( - r#" + " fn foo(x) { x - 1 } fn bar(x) { foo(x) } bar(41) - "#, + ", ) .unwrap(); diff --git a/benches/iterations.rs b/benches/iterations.rs index b322a95d..33ac414f 100644 --- a/benches/iterations.rs +++ b/benches/iterations.rs @@ -8,13 +8,13 @@ use test::Bencher; #[bench] fn bench_iterations_1000(bench: &mut Bencher) { - let script = r#" + let script = " let x = 1_000; while x > 0 { x -= 1; } - "#; + "; let mut engine = Engine::new(); engine.set_optimization_level(OptimizationLevel::None); @@ -26,7 +26,7 @@ fn bench_iterations_1000(bench: &mut Bencher) { #[bench] fn bench_iterations_fibonacci(bench: &mut Bencher) { - let script = r#" + let script = " fn fibonacci(n) { if n < 2 { n @@ -36,7 +36,7 @@ fn bench_iterations_fibonacci(bench: &mut Bencher) { } fibonacci(20) - "#; + "; let mut engine = Engine::new(); engine.set_optimization_level(OptimizationLevel::None); @@ -48,11 +48,11 @@ fn bench_iterations_fibonacci(bench: &mut Bencher) { #[bench] fn bench_iterations_array(bench: &mut Bencher) { - let script = r#" + let script = " let x = []; x.pad(1000, 0); for i in 0..1000 { x[i] = i % 256; } - "#; + "; let mut engine = Engine::new(); engine.set_optimization_level(OptimizationLevel::None); @@ -64,10 +64,10 @@ fn bench_iterations_array(bench: &mut Bencher) { #[bench] fn bench_iterations_blob(bench: &mut Bencher) { - let script = r#" + let script = " let x = blob(1000, 0); for i in 0..1000 { x[i] = i % 256; } - "#; + "; let mut engine = Engine::new(); engine.set_optimization_level(OptimizationLevel::None); diff --git a/benches/parsing.rs b/benches/parsing.rs index 907a7c71..d45b2252 100644 --- a/benches/parsing.rs +++ b/benches/parsing.rs @@ -64,7 +64,7 @@ fn bench_parse_map(bench: &mut Bencher) { #[bench] fn bench_parse_primes(bench: &mut Bencher) { - let script = r#" + let script = " // This script uses the Sieve of Eratosthenes to calculate prime numbers. let now = timestamp(); @@ -79,7 +79,7 @@ fn bench_parse_primes(bench: &mut Bencher) { let total_primes_found = 0; - for p in 2..=MAX_NUMBER_TO_CHECK { + for p in 2..MAX_NUMBER_TO_CHECK { if prime_mask[p] { print(p); @@ -95,7 +95,7 @@ fn bench_parse_primes(bench: &mut Bencher) { print(`Total ${total_primes_found} primes.`); print(`Run time = ${now.elapsed} seconds.`); - "#; + "; let mut engine = Engine::new(); engine.set_optimization_level(OptimizationLevel::None); diff --git a/benches/primes.rs b/benches/primes.rs index 8110c4f7..40c047a0 100644 --- a/benches/primes.rs +++ b/benches/primes.rs @@ -8,29 +8,29 @@ use test::Bencher; // This script uses the Sieve of Eratosthenes to calculate prime numbers. -const SCRIPT: &str = r#" -const MAX_NUMBER_TO_CHECK = 1_000; // 168 primes <= 1000 +const SCRIPT: &str = " + const MAX_NUMBER_TO_CHECK = 1_000; // 168 primes <= 1000 -let prime_mask = []; -prime_mask.pad(MAX_NUMBER_TO_CHECK, true); + let prime_mask = []; + prime_mask.pad(MAX_NUMBER_TO_CHECK, true); -prime_mask[0] = false; -prime_mask[1] = false; + prime_mask[0] = false; + prime_mask[1] = false; -let total_primes_found = 0; + let total_primes_found = 0; -for p in 2..=MAX_NUMBER_TO_CHECK { - if prime_mask[p] { - total_primes_found += 1; - let i = 2 * p; + for p in 2..MAX_NUMBER_TO_CHECK { + if prime_mask[p] { + total_primes_found += 1; + let i = 2 * p; - while i < MAX_NUMBER_TO_CHECK { - prime_mask[i] = false; - i += p; + while i < MAX_NUMBER_TO_CHECK { + prime_mask[i] = false; + i += p; + } } } -} -"#; +"; #[bench] fn bench_eval_primes(bench: &mut Bencher) { From e3aa2c19ce3ae98d993f5c2929a3157eeb1b9e9e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 31 Dec 2021 15:59:13 +0800 Subject: [PATCH 64/97] Make for loops for efficient. --- scripts/for3.rhai | 26 +++++++++++++++++++++++++ src/engine.rs | 49 ++++++++++++++++++++++++++++++----------------- tests/for.rs | 28 +++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 18 deletions(-) create mode 100644 scripts/for3.rhai diff --git a/scripts/for3.rhai b/scripts/for3.rhai new file mode 100644 index 00000000..de799684 --- /dev/null +++ b/scripts/for3.rhai @@ -0,0 +1,26 @@ +const MAX = 100; +const CHECK = ((MAX - 1) ** 2) * MAX; + +print("Ready... Go!"); + +let now = timestamp(); + +print(`Creating ${MAX} closures...`); + +let list = []; + +for i in 0..MAX { + list.push(|| i ** 2); +} + +print(`Time = ${now.elapsed} seconds...`); +print(`Summing ${MAX} closures...`); + +let sum = 0; + +for f in list { + sum += f.call(); +} + +print(`Sum = ${sum} (should be ${CHECK})`); +print(`Finished. Total run time = ${now.elapsed} seconds.`); diff --git a/src/engine.rs b/src/engine.rs index fd4104f8..90f06b62 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2892,16 +2892,18 @@ impl Engine { if let Some(func) = func { // Add the loop variables let orig_scope_len = scope.len(); - let counter_index = counter.as_ref().map(|Ident { name, .. }| { - scope.push(unsafe_cast_var_name_to_lifetime(name), 0 as INT); + let counter_index = if let Some(counter) = counter { + scope.push(unsafe_cast_var_name_to_lifetime(&counter.name), 0 as INT); scope.len() - 1 - }); + } else { + usize::MAX + }; scope.push(unsafe_cast_var_name_to_lifetime(name), ()); let index = scope.len() - 1; for (x, iter_value) in func(iter_obj).enumerate() { // Increment counter - if let Some(c) = counter_index { + if counter_index < usize::MAX { #[cfg(not(feature = "unchecked"))] if x > INT::MAX as usize { return Err(ERR::ErrorArithmetic( @@ -2911,26 +2913,37 @@ impl Engine { .into()); } - let mut counter_var = scope - .get_mut_by_index(c) - .write_lock::() - .expect("`INT`"); - *counter_var = x as INT; + let index_value = (x as INT).into(); + + #[cfg(not(feature = "no_closure"))] + { + let index_var = scope.get_mut_by_index(counter_index); + if index_var.is_shared() { + *index_var.write_lock().expect("`Dynamic`") = index_value; + } else { + *index_var = index_value; + } + } + #[cfg(feature = "no_closure")] + { + *scope.get_mut_by_index(counter_index) = index_value; + } } - let loop_var = scope.get_mut_by_index(index); let value = iter_value.flatten(); #[cfg(not(feature = "no_closure"))] - let loop_var_is_shared = loop_var.is_shared(); + { + let loop_var = scope.get_mut_by_index(index); + if loop_var.is_shared() { + *loop_var.write_lock().expect("`Dynamic`") = value; + } else { + *loop_var = value; + } + } #[cfg(feature = "no_closure")] - let loop_var_is_shared = false; - - if loop_var_is_shared { - let mut value_ref = loop_var.write_lock().expect("`Dynamic`"); - *value_ref = value; - } else { - *loop_var = value; + { + *scope.get_mut_by_index(index) = value; } #[cfg(not(feature = "unchecked"))] diff --git a/tests/for.rs b/tests/for.rs index 59e7223a..7cbf401a 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -371,3 +371,31 @@ fn test_for_module_iterator() -> Result<(), Box> { Ok(()) } + +#[test] +#[cfg(not(feature = "no_closure"))] +fn test_for_capture() -> Result<(), Box> { + let engine = Engine::new(); + + assert_eq!( + engine.eval::( + " + let a = []; + + for (x, i) in 100..110 { + a += || i + x; + } + + let sum = 0; + + for fp in a { + sum += fp.call(); + } + + sum + " + )?, + 1180 + ); + Ok(()) +} From 05baee042b8d1ed0df5781ca0016162ebb5ee50b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 31 Dec 2021 16:29:54 +0800 Subject: [PATCH 65/97] Add doc-comment example script. --- scripts/doc-comments.rhai | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 scripts/doc-comments.rhai diff --git a/scripts/doc-comments.rhai b/scripts/doc-comments.rhai new file mode 100644 index 00000000..9245065d --- /dev/null +++ b/scripts/doc-comments.rhai @@ -0,0 +1,20 @@ +/// The function `foo`, which prints `hello, world!` and a magic number, +/// accepts three parameters. +/// +/// # Parameters +/// +/// `x` - `i64` +/// `y` - `string` +/// `z` - `bool` +/// +/// # Notes +/// +/// This is a doc-comment. It can be obtained with the `metadata` feature. +/// +/// An example is the `rhai-doc` app. +/// +fn foo(x, y, z) { + print(`hello, world! ${if z { x + y.len() } else { x } }`); +} + +foo(39, "bar", true); From ce93f568131939c50f7a4afeeba561a09ec094d0 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 31 Dec 2021 17:49:19 +0800 Subject: [PATCH 66/97] Rename parameters. --- src/func/args.rs | 20 ++++++++++---------- src/packages/iter_basic.rs | 25 +++++++++++++------------ 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/func/args.rs b/src/func/args.rs index e2e21f6a..71941ee5 100644 --- a/src/func/args.rs +++ b/src/func/args.rs @@ -9,7 +9,7 @@ use std::prelude::v1::*; /// Trait that parses arguments to a function call. /// -/// Any data type can implement this trait in order to pass arguments to a function call. +/// Any data type can implement this trait in order to pass arguments to [`Engine::call_fn`][crate::Engine::call_fn]. pub trait FuncArgs { /// Parse function call arguments into a container. /// @@ -26,10 +26,10 @@ pub trait FuncArgs { /// } /// /// impl FuncArgs for Options { - /// fn parse>(self, container: &mut CONTAINER) { - /// container.extend(std::iter::once(self.foo.into())); - /// container.extend(std::iter::once(self.bar.into())); - /// container.extend(std::iter::once(self.baz.into())); + /// fn parse>(self, args: &mut ARGS) { + /// args.extend(std::iter::once(self.foo.into())); + /// args.extend(std::iter::once(self.bar.into())); + /// args.extend(std::iter::once(self.baz.into())); /// } /// } /// @@ -55,13 +55,13 @@ pub trait FuncArgs { /// # Ok(()) /// # } /// ``` - fn parse>(self, container: &mut CONTAINER); + fn parse>(self, args: &mut ARGS); } impl FuncArgs for Vec { #[inline] - fn parse>(self, container: &mut CONTAINER) { - container.extend(self.into_iter().map(Variant::into_dynamic)); + fn parse>(self, args: &mut ARGS) { + args.extend(self.into_iter().map(Variant::into_dynamic)); } } @@ -73,9 +73,9 @@ macro_rules! impl_args { { #[inline] #[allow(unused_variables)] - fn parse>(self, container: &mut CONTAINER) { + fn parse>(self, args: &mut ARGS) { let ($($p,)*) = self; - $(container.extend(Some($p.into_dynamic()));)* + $(args.extend(Some($p.into_dynamic()));)* } } diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index cd5579ef..bf675eac 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -1,10 +1,12 @@ use crate::plugin::*; use crate::types::dynamic::Variant; use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT}; -use std::iter::{ExactSizeIterator, FusedIterator}; -use std::ops::{Range, RangeInclusive}; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{ + iter::{ExactSizeIterator, FusedIterator}, + ops::{Range, RangeInclusive}, +}; #[cfg(not(feature = "unchecked"))] use num_traits::{CheckedAdd as Add, CheckedSub as Sub}; @@ -159,12 +161,10 @@ impl Iterator for BitRange { type Item = bool; fn next(&mut self) -> Option { - let Self(value, mask, len) = *self; - - if len == 0 { + if self.2 == 0 { None } else { - let r = (value & mask) != 0; + let r = (self.0 & self.1) != 0; self.1 <<= 1; self.2 -= 1; Some(r) @@ -263,7 +263,6 @@ macro_rules! reg_range { ($lib:ident | $x:expr => $( $y:ty ),*) => { $( $lib.set_iterator::>(); - $lib.set_iterator::>(); let _hash = $lib.set_native_fn($x, |from: $y, to: $y| Ok(from..to)); #[cfg(feature = "metadata")] @@ -272,6 +271,8 @@ macro_rules! reg_range { concat!("to: ", stringify!($y)), concat!("Iterator") ]); + + $lib.set_iterator::>(); )* }; ($lib:ident | step $x:expr => $( $y:ty ),*) => { @@ -450,7 +451,7 @@ def_package! { Ok(CharsStream::new(string, from, to - from)) }); #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["string: &str", "range: Range", "Iterator"]); + lib.update_fn_metadata(_hash, &["string: &str", "range: Range", "Iterator"]); let _hash = lib.set_native_fn("chars", |string, range: InclusiveRange| { let from = INT::max(*range.start(), 0); @@ -458,7 +459,7 @@ def_package! { Ok(CharsStream::new(string, from, to-from + 1)) }); #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["string: &str", "range: RangeInclusive", "Iterator"]); + lib.update_fn_metadata(_hash, &["string: &str", "range: RangeInclusive", "Iterator"]); let _hash = lib.set_native_fn("chars", |string, from, len| Ok(CharsStream::new(string, from, len))); #[cfg(feature = "metadata")] @@ -488,7 +489,7 @@ def_package! { BitRange::new(value, from, to - from) }); #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["value: INT", "range: Range", "Iterator"]); + lib.update_fn_metadata(_hash, &["value: INT", "range: Range", "Iterator"]); let _hash = lib.set_native_fn("bits", |value, range: InclusiveRange| { let from = INT::max(*range.start(), 0); @@ -496,7 +497,7 @@ def_package! { BitRange::new(value, from, to - from + 1) }); #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["value: INT", "range: RangeInclusive", "Iterator"]); + lib.update_fn_metadata(_hash, &["value: INT", "range: RangeInclusive", "Iterator"]); let _hash = lib.set_native_fn("bits", BitRange::new); #[cfg(feature = "metadata")] @@ -514,7 +515,7 @@ def_package! { { let _hash = lib.set_getter_fn("bits", |value: &mut INT| BitRange::new(*value, 0, INT::MAX) ); #[cfg(feature = "metadata")] - lib.update_fn_metadata(_hash, &["value: &mut INT", "range: Range", "Iterator"]); + lib.update_fn_metadata(_hash, &["value: &mut INT", "range: Range", "Iterator"]); } combine_with_exported_module!(lib, "range", range_functions); From f3a83649364b3d39e2e71510c67168b8719ac797 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 31 Dec 2021 17:49:38 +0800 Subject: [PATCH 67/97] Split blob write into write_utf8 and write_ascii. --- src/packages/blob_basic.rs | 52 +++++++++++++++++++++++++++++++------- tests/blobs.rs | 21 +++++++++++++-- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index ecd11b82..06e8ada8 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -645,8 +645,8 @@ pub mod blob_functions { pub fn write_be_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) { write_float(blob, start, len, value, false) } - #[rhai_fn(name = "write")] - pub fn write_string(blob: &mut Blob, start: INT, len: INT, string: &str) { + #[inline] + fn write_string(blob: &mut Blob, start: INT, len: INT, string: &str, ascii_only: bool) { if len <= 0 || blob.is_empty() || string.is_empty() { return; } @@ -670,18 +670,52 @@ pub mod blob_functions { let len = usize::min(len, string.len()); - blob[start..][..len].copy_from_slice(string[..len].as_bytes()); + if ascii_only { + string + .chars() + .filter(char::is_ascii) + .take(len) + .map(|ch| ch as u8) + .enumerate() + .for_each(|(i, x)| blob[start + i] = x); + } else { + blob[start..][..len].copy_from_slice(&string.as_bytes()[..len]); + } } - #[rhai_fn(name = "write")] - pub fn write_string_range(blob: &mut Blob, range: ExclusiveRange, string: &str) { + #[rhai_fn(name = "write_utf8")] + pub fn write_utf8_string(blob: &mut Blob, start: INT, len: INT, string: &str) { + write_string(blob, start, len, string, false) + } + #[rhai_fn(name = "write_utf8")] + pub fn write_utf8_string_range(blob: &mut Blob, range: ExclusiveRange, string: &str) { let start = INT::max(range.start, 0); let end = INT::max(range.end, start); - write_string(blob, start, end - start, string) + write_string(blob, start, end - start, string, false) } - #[rhai_fn(name = "write")] - pub fn write_string_range_inclusive(blob: &mut Blob, range: InclusiveRange, string: &str) { + #[rhai_fn(name = "write_utf8")] + pub fn write_utf8_string_range_inclusive(blob: &mut Blob, range: InclusiveRange, string: &str) { let start = INT::max(*range.start(), 0); let end = INT::max(*range.end(), start); - write_string(blob, start, end - start + 1, string) + write_string(blob, start, end - start + 1, string, false) + } + #[rhai_fn(name = "write_ascii")] + pub fn write_ascii_string(blob: &mut Blob, start: INT, len: INT, string: &str) { + write_string(blob, start, len, string, true) + } + #[rhai_fn(name = "write_ascii")] + pub fn write_ascii_string_range(blob: &mut Blob, range: ExclusiveRange, string: &str) { + let start = INT::max(range.start, 0); + let end = INT::max(range.end, start); + write_string(blob, start, end - start, string, true) + } + #[rhai_fn(name = "write_ascii")] + pub fn write_ascii_string_range_inclusive( + blob: &mut Blob, + range: InclusiveRange, + string: &str, + ) { + let start = INT::max(*range.start(), 0); + let end = INT::max(*range.end(), start); + write_string(blob, start, end - start + 1, string, true) } } diff --git a/tests/blobs.rs b/tests/blobs.rs index dcca26d3..f467be2c 100644 --- a/tests/blobs.rs +++ b/tests/blobs.rs @@ -170,15 +170,32 @@ fn test_blobs_parse() -> Result<(), Box> { ); assert_eq!( - engine.eval::(r#"let x = blob(16, 0); write(x, 0, 14, "hello, world!"); x"#)?, + engine.eval::(r#"let x = blob(16, 0); write_ascii(x, 0, 14, "hello, world!"); x"#)?, "hello, world!\0\0\0".as_bytes() ); assert_eq!( - engine.eval::(r#"let x = blob(10, 0); write(x, 3, 5, "hello, world!"); x"#)?, + engine.eval::(r#"let x = blob(10, 0); write_ascii(x, 3..8, "hello, world!"); x"#)?, "\0\0\0hello\0\0".as_bytes() ); + assert_eq!( + engine.eval::( + r#"let x = blob(10, 0); write_ascii(x, 0..9, "❤ hello, ❤ world! ❤❤❤"); x"# + )?, + " hello, \0".as_bytes() + ); + + assert_eq!( + engine.eval::(r#"let x = blob(10, 0); write_utf8(x, 3..9, "❤❤❤❤"); x"#)?, + "\0\0\0\u{2764}\u{2764}\0".as_bytes() + ); + + assert_eq!( + engine.eval::(r#"let x = blob(10, 0); write_utf8(x, 3..7, "❤❤❤❤"); x"#)?, + vec![0, 0, 0, 226, 157, 164, 226, 0, 0, 0] + ); + Ok(()) } From c4c4696964ffc01067230f993e181efe5e4d3d33 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 31 Dec 2021 18:55:48 +0800 Subject: [PATCH 68/97] Fix builds. --- tests/for.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/for.rs b/tests/for.rs index 7cbf401a..62112a12 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -373,6 +373,7 @@ fn test_for_module_iterator() -> Result<(), Box> { } #[test] +#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_closure"))] fn test_for_capture() -> Result<(), Box> { let engine = Engine::new(); @@ -389,7 +390,7 @@ fn test_for_capture() -> Result<(), Box> { let sum = 0; for fp in a { - sum += fp.call(); + sum += call(fp); } sum From 7ed91eadc0472b5425d6d5c48f524e3cc5a62b43 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 31 Dec 2021 19:34:43 +0800 Subject: [PATCH 69/97] Fix blob test. --- tests/blobs.rs | 55 ++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/tests/blobs.rs b/tests/blobs.rs index f467be2c..5653e1ef 100644 --- a/tests/blobs.rs +++ b/tests/blobs.rs @@ -169,33 +169,6 @@ fn test_blobs_parse() -> Result<(), Box> { vec![0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90] ); - assert_eq!( - engine.eval::(r#"let x = blob(16, 0); write_ascii(x, 0, 14, "hello, world!"); x"#)?, - "hello, world!\0\0\0".as_bytes() - ); - - assert_eq!( - engine.eval::(r#"let x = blob(10, 0); write_ascii(x, 3..8, "hello, world!"); x"#)?, - "\0\0\0hello\0\0".as_bytes() - ); - - assert_eq!( - engine.eval::( - r#"let x = blob(10, 0); write_ascii(x, 0..9, "❤ hello, ❤ world! ❤❤❤"); x"# - )?, - " hello, \0".as_bytes() - ); - - assert_eq!( - engine.eval::(r#"let x = blob(10, 0); write_utf8(x, 3..9, "❤❤❤❤"); x"#)?, - "\0\0\0\u{2764}\u{2764}\0".as_bytes() - ); - - assert_eq!( - engine.eval::(r#"let x = blob(10, 0); write_utf8(x, 3..7, "❤❤❤❤"); x"#)?, - vec![0, 0, 0, 226, 157, 164, 226, 0, 0, 0] - ); - Ok(()) } @@ -265,15 +238,39 @@ fn test_blobs_parse() -> Result<(), Box> { 0x1cf588 ); + Ok(()) +} + +#[test] +fn test_blobs_write_string() -> Result<(), Box> { + let engine = Engine::new(); + assert_eq!( - engine.eval::(r#"let x = blob(16, 0); write(x, 0, 14, "hello, world!"); x"#)?, + engine.eval::(r#"let x = blob(16, 0); write_ascii(x, 0, 14, "hello, world!"); x"#)?, "hello, world!\0\0\0".as_bytes() ); assert_eq!( - engine.eval::(r#"let x = blob(10, 0); write(x, 3, 5, "hello, world!"); x"#)?, + engine.eval::(r#"let x = blob(10, 0); write_ascii(x, 3..8, "hello, world!"); x"#)?, "\0\0\0hello\0\0".as_bytes() ); + assert_eq!( + engine.eval::( + r#"let x = blob(10, 0); write_ascii(x, 0..9, "❤ hello, ❤ world! ❤❤❤"); x"# + )?, + " hello, \0".as_bytes() + ); + + assert_eq!( + engine.eval::(r#"let x = blob(10, 0); write_utf8(x, 3..9, "❤❤❤❤"); x"#)?, + "\0\0\0\u{2764}\u{2764}\0".as_bytes() + ); + + assert_eq!( + engine.eval::(r#"let x = blob(10, 0); write_utf8(x, 3..7, "❤❤❤❤"); x"#)?, + vec![0, 0, 0, 226, 157, 164, 226, 0, 0, 0] + ); + Ok(()) } From 8329baea29246f127b0e145ce47479a75a431857 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 31 Dec 2021 23:01:34 +0800 Subject: [PATCH 70/97] Fix doc comments. --- src/api/custom_syntax.rs | 2 +- src/lib.rs | 20 +++++++++++++------- src/packages/math_basic.rs | 4 ++-- src/tokenizer.rs | 4 ++-- src/types/interner.rs | 7 +++---- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index 5331eb6a..c22503d2 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -353,7 +353,7 @@ impl Engine { /// /// * `Ok(None)`: parsing complete and there are no more symbols to match. /// * `Ok(Some(symbol))`: the next symbol to match, which can also be `$expr$`, `$ident$` or `$block$`. - /// * `Err(ParseError)`: error that is reflected back to the [`Engine`], normally `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate a syntax error, but it can be any [`ParseError`]. + /// * `Err(ParseError)`: error that is reflected back to the [`Engine`], normally `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate a syntax error, but it can be any [`ParseError`][crate::ParseError]. pub fn register_custom_syntax_raw( &mut self, key: impl Into, diff --git a/src/lib.rs b/src/lib.rs index d9e61697..1fcf4cec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,19 +105,19 @@ pub type INT = i64; #[cfg(feature = "only_i32")] pub type INT = i32; -/// The system base integer type. It is defined as [`u64`]. +/// The unsigned system base integer type. It is defined as [`u64`]. /// /// If the `only_i32` feature is enabled, this will be [`u32`] instead. #[cfg(not(feature = "only_i32"))] #[allow(non_camel_case_types)] -type INT_BASE = u64; -/// The system integer base type. +type UNSIGNED_INT = u64; +/// The unsigned system integer base type. /// It is defined as [`u32`] since the `only_i32` feature is used. /// /// If the `only_i32` feature is not used, this will be `u64` instead. #[cfg(feature = "only_i32")] #[allow(non_camel_case_types)] -type INT_BASE = u32; +type UNSIGNED_INT = u32; /// The system floating-point type. It is defined as [`f64`]. /// Not available under `no_float`. @@ -136,8 +136,11 @@ pub type FLOAT = f64; #[cfg(feature = "f32_float")] pub type FLOAT = f32; -pub type ExclusiveRange = std::ops::Range; -pub type InclusiveRange = std::ops::RangeInclusive; +/// An exclusive integer range. +type ExclusiveRange = std::ops::Range; + +/// An inclusive integer range. +type InclusiveRange = std::ops::RangeInclusive; pub use api::custom_syntax::Expression; pub use ast::{FnAccess, AST}; @@ -192,8 +195,11 @@ pub type Array = Vec; #[cfg(not(feature = "no_index"))] pub type Blob = Vec; -/// Hash map of [`Dynamic`] values with [`SmartString`](https://crates.io/crates/smartstring) keys. +/// A dictionary of [`Dynamic`] values with string keys. /// Not available under `no_object`. +/// +/// [`SmartString`](https://crates.io/crates/smartstring) is used as the key type because most +/// property names are ASCII and short, fewer than 23 characters, so they can be stored inline. #[cfg(not(feature = "no_object"))] pub type Map = std::collections::BTreeMap; diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 6b9e3186..0b902666 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use crate::plugin::*; -use crate::{def_package, Position, RhaiResultOf, ERR, INT, INT_BASE}; +use crate::{def_package, Position, RhaiResultOf, ERR, INT, UNSIGNED_INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -123,7 +123,7 @@ mod int_functions { .into()); } - INT_BASE::from_str_radix(string.trim(), radix as u32) + UNSIGNED_INT::from_str_radix(string.trim(), radix as u32) .map(|v| v as INT) .map_err(|err| { ERR::ErrorArithmetic( diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 9387bbee..696b05b7 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -5,7 +5,7 @@ use crate::engine::{ KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF, }; use crate::func::native::OnParseTokenCallback; -use crate::{Engine, LexError, StaticVec, INT, INT_BASE}; +use crate::{Engine, LexError, StaticVec, INT, UNSIGNED_INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -1535,7 +1535,7 @@ fn get_next_token_inner( .filter(|&&c| c != NUMBER_SEPARATOR) .collect(); - INT_BASE::from_str_radix(&out, radix) + UNSIGNED_INT::from_str_radix(&out, radix) .map(|v| v as INT) .map(Token::IntegerConstant) .unwrap_or_else(|_| { diff --git a/src/types/interner.rs b/src/types/interner.rs index f3680a65..a9908cd7 100644 --- a/src/types/interner.rs +++ b/src/types/interner.rs @@ -8,9 +8,8 @@ use std::prelude::v1::*; /// _(internals)_ A factory of identifiers from text strings. /// Exported under the `internals` feature only. /// -/// Normal identifiers are not interned since [`SmartString`][crate::SmartString] and thus copying -/// is relatively fast , this just returns a copy -/// because most identifiers in Rhai are short and ASCII-based. +/// Normal identifiers are not interned since they use `SmartString` because most identifiers in +/// Rhai are short and ASCII-based. Thus copying is relatively fast. /// /// Property getters and setters are interned separately. #[derive(Debug, Clone, Default, Hash)] @@ -24,7 +23,7 @@ pub struct StringsInterner { } impl StringsInterner { - /// Create a new [`IdentifierBuilder`]. + /// Create a new [`StringsInterner`]. #[inline] #[must_use] pub fn new() -> Self { From 135b1f54c29eed1a66a0089011640a34dd789fd2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 1 Jan 2022 17:20:00 +0800 Subject: [PATCH 71/97] Remove Option from source and use empty string as no source. --- src/api/eval.rs | 5 ++- src/api/run.rs | 5 ++- src/ast/ast.rs | 29 ++++++++-------- src/engine.rs | 40 ++++++++++------------ src/func/call.rs | 83 ++++++++++++++++++++++++++++------------------ src/func/native.rs | 4 +-- src/func/script.rs | 5 ++- src/lib.rs | 5 ++- src/module/mod.rs | 32 ++++++++++-------- tests/optimizer.rs | 8 ++--- 10 files changed, 118 insertions(+), 98 deletions(-) diff --git a/src/api/eval.rs b/src/api/eval.rs index 51dadfec..402b696b 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -209,9 +209,8 @@ impl Engine { level: usize, ) -> RhaiResult { let mut state = EvalState::new(); - if ast.source_raw().is_some() { - global.source = ast.source_raw().cloned(); - } + global.source = ast.source_raw().clone(); + #[cfg(not(feature = "no_module"))] { global.embedded_module_resolver = ast.resolver().cloned(); diff --git a/src/api/run.rs b/src/api/run.rs index aa27683f..ce33c049 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -46,9 +46,8 @@ impl Engine { pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> { let global = &mut GlobalRuntimeState::new(); let mut state = EvalState::new(); - if ast.source_raw().is_some() { - global.source = ast.source_raw().cloned(); - } + global.source = ast.source_raw().clone(); + #[cfg(not(feature = "no_module"))] { global.embedded_module_resolver = ast.resolver().cloned(); diff --git a/src/ast/ast.rs b/src/ast/ast.rs index 82ca8087..1f9ec131 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -17,7 +17,8 @@ use std::{ #[derive(Debug, Clone)] pub struct AST { /// Source of the [`AST`]. - source: Option, + /// No source if string is empty. + source: Identifier, /// Global statements. body: StmtBlock, /// Script-defined functions. @@ -45,7 +46,7 @@ impl AST { #[cfg(not(feature = "no_function"))] functions: impl Into>, ) -> Self { Self { - source: None, + source: Identifier::new_const(), body: StmtBlock::new(statements, Position::NONE), #[cfg(not(feature = "no_function"))] functions: functions.into(), @@ -63,7 +64,7 @@ impl AST { #[cfg(not(feature = "no_function"))] functions: impl Into>, ) -> Self { Self { - source: None, + source: Identifier::new_const(), body: StmtBlock::new(statements, Position::NONE), #[cfg(not(feature = "no_function"))] functions: functions.into(), @@ -111,7 +112,7 @@ impl AST { #[must_use] pub fn empty() -> Self { Self { - source: None, + source: Identifier::new_const(), body: StmtBlock::NONE, #[cfg(not(feature = "no_function"))] functions: crate::Module::new().into(), @@ -123,13 +124,17 @@ impl AST { #[inline(always)] #[must_use] pub fn source(&self) -> Option<&str> { - self.source.as_ref().map(|s| s.as_str()) + if self.source.is_empty() { + None + } else { + Some(&self.source) + } } /// Get a reference to the source. #[inline(always)] #[must_use] - pub(crate) fn source_raw(&self) -> Option<&Identifier> { - self.source.as_ref() + pub(crate) fn source_raw(&self) -> &Identifier { + &self.source } /// Set the source. #[inline] @@ -139,13 +144,13 @@ impl AST { crate::Shared::get_mut(&mut self.functions) .as_mut() .map(|m| m.set_id(source.clone())); - self.source = Some(source); + self.source = source; self } /// Clear the source. #[inline(always)] pub fn clear_source(&mut self) -> &mut Self { - self.source = None; + self.source.clear(); self } /// Get the statements. @@ -467,8 +472,6 @@ impl AST { (true, true) => StmtBlock::NONE, }; - let source = other.source.clone().or_else(|| self.source.clone()); - #[cfg(not(feature = "no_function"))] let functions = { let mut functions = self.functions.as_ref().clone(); @@ -476,12 +479,12 @@ impl AST { functions }; - if let Some(source) = source { + if !other.source.is_empty() { Self::new_with_source( merged, #[cfg(not(feature = "no_function"))] functions, - source, + other.source.clone(), ) } else { Self::new( diff --git a/src/engine.rs b/src/engine.rs index 90f06b62..0519a4b3 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2,6 +2,7 @@ use crate::api::custom_syntax::CustomSyntax; use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, Stmt, AST_OPTION_FLAGS::*}; +use crate::func::call::FnResolutionCache; use crate::func::native::{OnDebugCallback, OnParseTokenCallback, OnPrintCallback, OnVarCallback}; use crate::func::{get_hasher, CallableFunction, IteratorFn}; use crate::module::Namespace; @@ -507,7 +508,8 @@ pub struct GlobalRuntimeState { /// Stack of imported [modules][Module]. modules: StaticVec>, /// Source of the current context. - pub source: Option, + /// No source if the string is empty. + pub source: Identifier, /// Number of operations performed. pub num_operations: u64, /// Number of modules loaded. @@ -539,7 +541,7 @@ impl GlobalRuntimeState { Self { keys: StaticVec::new_const(), modules: StaticVec::new_const(), - source: None, + source: Identifier::new_const(), num_operations: 0, num_modules_loaded: 0, #[cfg(not(feature = "no_module"))] @@ -629,11 +631,11 @@ impl GlobalRuntimeState { /// Get the specified function via its hash key from the stack of globally-imported [modules][Module]. #[inline] #[must_use] - pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> { + pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&str>)> { self.modules .iter() .rev() - .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) + .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id()))) } /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of /// globally-imported [modules][Module]? @@ -746,20 +748,6 @@ impl fmt::Debug for GlobalRuntimeState { } } -/// _(internals)_ An entry in a function resolution cache. -/// Exported under the `internals` feature only. -#[derive(Debug, Clone)] -pub struct FnResolutionCacheEntry { - /// Function. - pub func: CallableFunction, - /// Optional source. - pub source: Option>, -} - -/// _(internals)_ A function resolution cache. -/// Exported under the `internals` feature only. -pub type FnResolutionCache = BTreeMap>>; - /// _(internals)_ A type that holds all the current states of the [`Engine`]. /// Exported under the `internals` feature only. #[derive(Debug, Clone)] @@ -844,7 +832,11 @@ impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> { #[inline(always)] #[must_use] pub fn source(&self) -> Option<&str> { - self.global.source.as_ref().map(|s| s.as_str()) + if self.global.source.is_empty() { + None + } else { + Some(&self.global.source) + } } /// The current [`Scope`]. #[inline(always)] @@ -3041,8 +3033,8 @@ impl Engine { err_map.insert("message".into(), err.to_string().into()); - if let Some(ref source) = global.source { - err_map.insert("source".into(), source.as_str().into()); + if !global.source.is_empty() { + err_map.insert("source".into(), global.source.clone().into()); } if err_pos.is_none() { @@ -3174,7 +3166,11 @@ impl Engine { { use crate::ModuleResolver; - let source = global.source.as_ref().map(|s| s.as_str()); + let source = if global.source.is_empty() { + None + } else { + Some(global.source.as_str()) + }; let path_pos = expr.position(); let module = global diff --git a/src/func/call.rs b/src/func/call.rs index fd20e4ad..99d241cc 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -6,9 +6,8 @@ use super::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; use crate::api::default_limits::MAX_DYNAMIC_PARAMETERS; use crate::ast::{Expr, FnCallHashes, Stmt}; use crate::engine::{ - EvalState, FnResolutionCacheEntry, GlobalRuntimeState, KEYWORD_DEBUG, KEYWORD_EVAL, - KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, - KEYWORD_TYPE_OF, + EvalState, GlobalRuntimeState, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, + KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::module::Namespace; use crate::tokenizer::Token; @@ -20,6 +19,7 @@ use crate::{ use std::prelude::v1::*; use std::{ any::{type_name, TypeId}, + collections::BTreeMap, convert::TryFrom, mem, }; @@ -125,6 +125,24 @@ pub fn ensure_no_data_race( Ok(()) } +/// _(internals)_ An entry in a function resolution cache. +/// Exported under the `internals` feature only. +#[derive(Debug, Clone)] +pub struct FnResolutionCacheEntry { + /// Function. + pub func: CallableFunction, + /// Optional source. + /// No source if the string is empty. + pub source: Identifier, +} + +/// _(internals)_ A function resolution cache. +/// Exported under the `internals` feature only. +/// +/// [`FnResolutionCacheEntry`] is [`Box`]ed in order to pack as many entries inside a single B-Tree +/// level as possible. +pub type FnResolutionCache = BTreeMap>>; + impl Engine { /// Generate the signature for a function call. #[inline] @@ -206,14 +224,14 @@ impl Engine { .find_map(|m| { m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry { func, - source: m.id_raw().map(|s| s.to_string().into_boxed_str()), + source: m.id_raw().clone(), }) }) .or_else(|| { self.global_modules.iter().find_map(|m| { m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry { func, - source: m.id_raw().map(|s| s.to_string().into_boxed_str()), + source: m.id_raw().clone(), }) }) }) @@ -222,7 +240,8 @@ impl Engine { .get_fn(hash) .map(|(func, source)| FnResolutionCacheEntry { func: func.clone(), - source: source.map(|s| s.to_string().into_boxed_str()), + source: source + .map_or_else(|| Identifier::new_const(), Into::into), }) }) .or_else(|| { @@ -230,7 +249,7 @@ impl Engine { m.get_qualified_fn(hash).cloned().map(|func| { FnResolutionCacheEntry { func, - source: m.id_raw().map(|s| s.to_string().into_boxed_str()), + source: m.id_raw().clone(), } }) }) @@ -253,7 +272,7 @@ impl Engine { func: CallableFunction::from_method( Box::new(f) as Box ), - source: None, + source: Identifier::new_const(), } }) } else { @@ -265,7 +284,7 @@ impl Engine { func: CallableFunction::from_method( Box::new(f) as Box ), - source: None, + source: Identifier::new_const(), }) } .map(Box::new) @@ -353,10 +372,15 @@ impl Engine { } // Run external function - let source = source - .as_ref() - .map(|s| &**s) - .or_else(|| parent_source.as_ref().map(|s| s.as_str())); + let source = if source.is_empty() { + if parent_source.is_empty() { + None + } else { + Some(parent_source.as_str()) + } + } else { + Some(source.as_str()) + }; let context = (self, name, source, &*global, lib, pos).into(); @@ -400,7 +424,11 @@ impl Engine { pos, ) })?; - let source = global.source.as_ref().map(|s| s.as_str()); + let source = if global.source.is_empty() { + None + } else { + Some(global.source.as_str()) + }; (debug(&text, source, pos).into(), false) } else { (Dynamic::UNIT, false) @@ -572,7 +600,7 @@ impl Engine { // Script-defined function call? #[cfg(not(feature = "no_function"))] - if let Some(FnResolutionCacheEntry { func, source }) = self + if let Some(FnResolutionCacheEntry { func, mut source }) = self .resolve_fn( global, state, @@ -603,13 +631,12 @@ impl Engine { } }; + mem::swap(&mut global.source, &mut source); + let result = if _is_method_call { // Method call of script function - map first argument to `this` let (first_arg, rest_args) = args.split_first_mut().expect("not empty"); - let orig_source = global.source.take(); - global.source = source.map(Into::into); - let level = _level + 1; let result = self.call_script_fn( @@ -625,9 +652,6 @@ impl Engine { level, ); - // Restore the original source - global.source = orig_source; - result? } else { // Normal call of script function @@ -641,18 +665,12 @@ impl Engine { .change_first_arg_to_copy(args); } - let orig_source = global.source.take(); - global.source = source.map(Into::into); - let level = _level + 1; let result = self.call_script_fn( scope, global, state, lib, &mut None, func, args, pos, true, level, ); - // Restore the original source - global.source = orig_source; - // Restore the original reference if let Some(bk) = backup { bk.restore_first_arg(args) @@ -661,6 +679,9 @@ impl Engine { result? }; + // Restore the original source + mem::swap(&mut global.source, &mut source); + return Ok((result, false)); } @@ -1058,11 +1079,7 @@ impl Engine { return result.map_err(|err| { ERR::ErrorInFunctionCall( KEYWORD_EVAL.to_string(), - global - .source - .as_ref() - .map(Identifier::to_string) - .unwrap_or_default(), + global.source.to_string(), err, pos, ) @@ -1265,7 +1282,7 @@ impl Engine { } else { let new_scope = &mut Scope::new(); - let mut source = module.id_raw().cloned(); + let mut source = module.id_raw().clone(); mem::swap(&mut global.source, &mut source); let level = level + 1; diff --git a/src/func/native.rs b/src/func/native.rs index f3d45d1d..457dc2f9 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -90,7 +90,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef + 'a + ?Sized> Self { engine: value.0, fn_name: value.1.as_ref(), - source: value.2.map(|v| v.as_ref()), + source: value.2.map(S::as_ref), global: Some(value.3), lib: value.4.as_ref(), pos: value.5, @@ -156,7 +156,7 @@ impl<'a> NativeCallContext<'a> { Self { engine, fn_name: fn_name.as_ref(), - source: source.map(|v| v.as_ref()), + source: source.map(S::as_ref), global: Some(global), lib, pos, diff --git a/src/func/script.rs b/src/func/script.rs index 774648d1..715fbcf7 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -47,9 +47,8 @@ impl Engine { fn_def .lib .as_ref() - .and_then(|m| m.id().map(|id| id.to_string())) - .or_else(|| global.source.as_ref().map(|s| s.to_string())) - .unwrap_or_default(), + .and_then(|m| m.id().map(str::to_string)) + .unwrap_or_else(|| global.source.to_string()), err, pos, ) diff --git a/src/lib.rs b/src/lib.rs index 1fcf4cec..0298da18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -247,7 +247,10 @@ pub use ast::{ pub use ast::FloatWrapper; #[cfg(feature = "internals")] -pub use engine::{EvalState, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState}; +pub use engine::{EvalState, GlobalRuntimeState}; + +#[cfg(feature = "internals")] +pub use func::call::{FnResolutionCache, FnResolutionCacheEntry}; #[cfg(feature = "internals")] pub use module::Namespace; diff --git a/src/module/mod.rs b/src/module/mod.rs index 0662e483..9ac02eaf 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -131,7 +131,8 @@ pub fn calc_native_fn_hash( #[derive(Clone)] pub struct Module { /// ID identifying the module. - id: Option, + /// No ID if string is empty. + id: Identifier, /// Is this module internal? pub(crate) internal: bool, /// Is this module part of a standard library? @@ -170,8 +171,9 @@ impl fmt::Debug for Module { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_struct("Module"); - self.id.as_ref().map(|id| d.field("id", id)); - + if !self.id.is_empty() { + d.field("id", &self.id); + } if !self.modules.is_empty() { d.field( "modules", @@ -241,7 +243,7 @@ impl Module { #[must_use] pub fn new() -> Self { Self { - id: None, + id: Identifier::new_const(), internal: false, standard: false, modules: BTreeMap::new(), @@ -270,18 +272,24 @@ impl Module { #[inline] #[must_use] pub fn id(&self) -> Option<&str> { - self.id_raw().map(|s| s.as_str()) + if self.id_raw().is_empty() { + None + } else { + Some(self.id_raw()) + } } /// Get the ID of the [`Module`] as an [`Identifier`], if any. #[inline(always)] #[must_use] - pub(crate) const fn id_raw(&self) -> Option<&Identifier> { - self.id.as_ref() + pub(crate) const fn id_raw(&self) -> &Identifier { + &self.id } /// Set the ID of the [`Module`]. /// + /// If the string is empty, it is equivalent to clearing the ID. + /// /// # Example /// /// ``` @@ -292,7 +300,7 @@ impl Module { /// ``` #[inline(always)] pub fn set_id(&mut self, id: impl Into) -> &mut Self { - self.id = Some(id.into()); + self.id = id.into(); self } /// Clear the ID of the [`Module`]. @@ -309,7 +317,7 @@ impl Module { /// ``` #[inline(always)] pub fn clear_id(&mut self) -> &mut Self { - self.id = None; + self.id.clear(); self } @@ -1616,11 +1624,7 @@ impl Module { }); } - if let Some(s) = ast.source_raw() { - module.set_id(s.clone()); - } else { - module.clear_id(); - } + module.set_id(ast.source_raw().clone()); module.build_index(); diff --git a/tests/optimizer.rs b/tests/optimizer.rs index 1a1c05db..b6d6a289 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -80,21 +80,21 @@ fn test_optimizer_parse() -> Result<(), Box> { assert_eq!( format!("{:?}", ast), - "AST { source: None, body: Block[Expr(123 @ 1:53)], functions: Module, resolver: None }" + "AST { source: \"\", body: Block[Expr(123 @ 1:53)], functions: Module, resolver: None }" ); let ast = engine.compile("const DECISION = false; if DECISION { 42 } else { 123 }")?; assert_eq!( format!("{:?}", ast), - r#"AST { source: None, body: Block[Var(false @ 1:18, "DECISION" @ 1:7, (Constant), 1:1), Expr(123 @ 1:51)], functions: Module, resolver: None }"# + r#"AST { source: "", body: Block[Var(false @ 1:18, "DECISION" @ 1:7, (Constant), 1:1), Expr(123 @ 1:51)], functions: Module, resolver: None }"# ); let ast = engine.compile("if 1 == 2 { 42 }")?; assert_eq!( format!("{:?}", ast), - "AST { source: None, body: Block[], functions: Module, resolver: None }" + "AST { source: \"\", body: Block[], functions: Module, resolver: None }" ); engine.set_optimization_level(OptimizationLevel::Full); @@ -103,7 +103,7 @@ fn test_optimizer_parse() -> Result<(), Box> { assert_eq!( format!("{:?}", ast), - "AST { source: None, body: Block[Expr(42 @ 1:1)], functions: Module, resolver: None }" + "AST { source: \"\", body: Block[Expr(42 @ 1:1)], functions: Module, resolver: None }" ); Ok(()) From a6ddb64596834bb2a61fa67d63b3cefb7fc3baea Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 1 Jan 2022 17:38:32 +0800 Subject: [PATCH 72/97] Fix builds. --- src/api/register.rs | 4 ++-- src/func/native.rs | 2 +- src/module/resolvers/collection.rs | 2 +- src/module/resolvers/file.rs | 2 +- src/parser.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/api/register.rs b/src/api/register.rs index 22276593..e369ab0c 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -79,7 +79,7 @@ impl Engine { name, FnNamespace::Global, FnAccess::Public, - param_type_names.as_ref().map(|v| v.as_ref()), + param_type_names.as_ref().map(<_>::as_ref), ¶m_types, func.into_callable_function(), ); @@ -137,7 +137,7 @@ impl Engine { name, FnNamespace::Global, FnAccess::Public, - param_type_names.as_ref().map(|v| v.as_ref()), + param_type_names.as_ref().map(<_>::as_ref), ¶m_types, func.into_callable_function(), ); diff --git a/src/func/native.rs b/src/func/native.rs index 457dc2f9..29317110 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -156,7 +156,7 @@ impl<'a> NativeCallContext<'a> { Self { engine, fn_name: fn_name.as_ref(), - source: source.map(S::as_ref), + source: source.map(<_>::as_ref), global: Some(global), lib, pos, diff --git a/src/module/resolvers/collection.rs b/src/module/resolvers/collection.rs index 835a8863..6deb7c3d 100644 --- a/src/module/resolvers/collection.rs +++ b/src/module/resolvers/collection.rs @@ -77,7 +77,7 @@ impl ModuleResolversCollection { /// Get an iterator of all the [module resolvers][ModuleResolver]. #[inline] pub fn iter(&self) -> impl Iterator { - self.0.iter().map(|v| v.as_ref()) + self.0.iter().map(<_>::as_ref) } /// Remove all [module resolvers][ModuleResolver]. #[inline(always)] diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index 1dcedd8e..ff6bfa88 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -225,7 +225,7 @@ impl FileModuleResolver { path: impl AsRef, source_path: Option>, ) -> Option> { - let file_path = self.get_file_path(path.as_ref(), source_path.as_ref().map(|v| v.as_ref())); + let file_path = self.get_file_path(path.as_ref(), source_path.as_ref().map(<_>::as_ref)); shared_write_lock(&self.cache) .remove_entry(&file_path) diff --git a/src/parser.rs b/src/parser.rs index 627a750c..348845d1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2623,7 +2623,7 @@ fn parse_export( pos: id_pos, }, Ident { - name: state.get_identifier("", rename.as_ref().map_or("", |s| s.as_ref())), + name: state.get_identifier("", rename.as_ref().map_or("", <_>::as_ref)), pos: rename_pos, }, )); From d60ed5a5028efd92f473f2e60d5d49f253448bf0 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 1 Jan 2022 19:54:46 +0800 Subject: [PATCH 73/97] Reduce size of Variant trait. --- src/engine.rs | 16 +++++++++++----- src/func/args.rs | 4 ++-- src/func/register.rs | 4 ++-- src/lib.rs | 4 +--- src/tokenizer.rs | 18 +++++++++--------- src/types/dynamic.rs | 38 +++++++++++++------------------------- 6 files changed, 38 insertions(+), 46 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 0519a4b3..8ae0a694 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -57,10 +57,19 @@ pub const FN_IDX_SET: &str = "index$set$"; pub const FN_ANONYMOUS: &str = "anon$"; /// Standard equality comparison operator. +/// +/// Some standard functions (e.g. searching an [`Array`][crate::Array]) implicitly call this +/// function to compare two [`Dynamic`] values. pub const OP_EQUALS: &str = Token::EqualsTo.literal_syntax(); -/// Standard method function for containment testing. -/// The `in` operator is implemented as a call to this method. +/// Standard concatenation operator. +/// +/// Used primarily to build up interpolated strings. +pub const OP_CONCAT: &str = Token::PlusAssign.literal_syntax(); + +/// Standard containment testing function. +/// +/// The `in` operator is implemented as a call to this function. pub const OP_CONTAINS: &str = "contains"; /// Standard exclusive range operator. @@ -69,9 +78,6 @@ pub const OP_EXCLUSIVE_RANGE: &str = Token::ExclusiveRange.literal_syntax(); /// Standard inclusive range operator. pub const OP_INCLUSIVE_RANGE: &str = Token::InclusiveRange.literal_syntax(); -/// Standard concatenation operator token. -pub const OP_CONCAT: &str = Token::PlusAssign.literal_syntax(); - /// Method of chaining. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] diff --git a/src/func/args.rs b/src/func/args.rs index 71941ee5..d612ad3f 100644 --- a/src/func/args.rs +++ b/src/func/args.rs @@ -61,7 +61,7 @@ pub trait FuncArgs { impl FuncArgs for Vec { #[inline] fn parse>(self, args: &mut ARGS) { - args.extend(self.into_iter().map(Variant::into_dynamic)); + args.extend(self.into_iter().map(Dynamic::from)); } } @@ -75,7 +75,7 @@ macro_rules! impl_args { #[allow(unused_variables)] fn parse>(self, args: &mut ARGS) { let ($($p,)*) = self; - $(args.extend(Some($p.into_dynamic()));)* + $(args.extend(Some(Dynamic::from($p)));)* } } diff --git a/src/func/register.rs b/src/func/register.rs index d71ca50b..daa93d41 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -135,7 +135,7 @@ macro_rules! def_register { let r = self($($arg),*); // Map the result - Ok(r.into_dynamic()) + Ok(Dynamic::from(r)) }) as Box) } } @@ -163,7 +163,7 @@ macro_rules! def_register { let r = self(ctx, $($arg),*); // Map the result - Ok(r.into_dynamic()) + Ok(Dynamic::from(r)) }) as Box) } } diff --git a/src/lib.rs b/src/lib.rs index 0298da18..b9210006 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,9 +144,7 @@ type InclusiveRange = std::ops::RangeInclusive; pub use api::custom_syntax::Expression; pub use ast::{FnAccess, AST}; -pub use engine::{ - Engine, EvalContext, OP_CONTAINS, OP_EQUALS, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE, -}; +pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS}; pub use func::{NativeCallContext, RegisterNativeFunction}; pub use module::{FnNamespace, Module}; pub use tokenizer::Position; diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 696b05b7..2a86927e 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1026,7 +1026,7 @@ pub trait InputStream { fn peek_next(&mut self) -> Option; } -/// _(internals)_ Parse a string literal ended by `termination_char`. +/// _(internals)_ Parse a string literal ended by a specified termination character. /// Exported under the `internals` feature only. /// /// Returns the parsed string and a boolean indicating whether the string is @@ -1034,7 +1034,7 @@ pub trait InputStream { /// /// # Returns /// -/// |Type |Return Value |`state.is_within_text_terminated_by`| +/// | Type | Return Value |`state.is_within_text_terminated_by`| /// |---------------------------------|:--------------------------:|:----------------------------------:| /// |`"hello"` |`StringConstant("hello")` |`None` | /// |`"hello`_{LF}_ or _{EOF}_ |`LexError` |`None` | @@ -1061,8 +1061,8 @@ pub fn parse_string_literal( state: &mut TokenizeState, pos: &mut Position, termination_char: char, - continuation: bool, verbatim: bool, + allow_line_continuation: bool, allow_interpolation: bool, ) -> Result<(Box, bool), (LexError, Position)> { let mut result = String::with_capacity(12); @@ -1091,7 +1091,7 @@ pub fn parse_string_literal( pos.advance(); break; } - None if continuation && !escape.is_empty() => { + None if allow_line_continuation && !escape.is_empty() => { assert_eq!(escape, "\\", "unexpected escape {} at end of line", escape); pos.advance(); break; @@ -1211,7 +1211,7 @@ pub fn parse_string_literal( } // Line continuation - '\n' if continuation && !escape.is_empty() => { + '\n' if allow_line_continuation && !escape.is_empty() => { assert_eq!(escape, "\\", "unexpected escape {} at end of line", escape); escape.clear(); pos.new_line(); @@ -1319,7 +1319,7 @@ fn scan_block_comment( level } -/// _(internals)_ Get the next token from the `stream`. +/// _(internals)_ Get the next token from the input stream. /// Exported under the `internals` feature only. #[inline] #[must_use] @@ -1400,7 +1400,7 @@ fn get_next_token_inner( if let Some(ch) = state.is_within_text_terminated_by.take() { let start_pos = *pos; - return parse_string_literal(stream, state, pos, ch, false, true, true).map_or_else( + return parse_string_literal(stream, state, pos, ch, true, false, true).map_or_else( |(err, err_pos)| Some((Token::LexError(err), err_pos)), |(result, interpolated)| { if interpolated { @@ -1584,7 +1584,7 @@ fn get_next_token_inner( // " - string literal ('"', _) => { - return parse_string_literal(stream, state, pos, c, true, false, false) + return parse_string_literal(stream, state, pos, c, false, true, false) .map_or_else( |(err, err_pos)| Some((Token::LexError(err), err_pos)), |(result, _)| Some((Token::StringConstant(result), start_pos)), @@ -1611,7 +1611,7 @@ fn get_next_token_inner( _ => (), } - return parse_string_literal(stream, state, pos, c, false, true, true).map_or_else( + return parse_string_literal(stream, state, pos, c, true, false, true).map_or_else( |(err, err_pos)| Some((Token::LexError(err), err_pos)), |(result, interpolated)| { if interpolated { diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 140903a8..eefcc2f4 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -52,7 +52,7 @@ pub trait Variant: Any + private::Sealed { #[must_use] fn as_mut_any(&mut self) -> &mut dyn Any; - /// Convert this [`Variant`] trait object to an [`Any`] trait object. + /// Convert this [`Variant`] trait object to [`Box`]. #[must_use] fn as_box_any(self: Box) -> Box; @@ -60,13 +60,9 @@ pub trait Variant: Any + private::Sealed { #[must_use] fn type_name(&self) -> &'static str; - /// Convert into [`Dynamic`]. + /// Clone this [`Variant`] trait object. #[must_use] - fn into_dynamic(self) -> Dynamic; - - /// Clone into [`Dynamic`]. - #[must_use] - fn clone_into_dynamic(&self) -> Dynamic; + fn clone_object(&self) -> Box; } /// _(internals)_ Trait to represent any type. @@ -83,7 +79,7 @@ pub trait Variant: Any + Send + Sync + private::Sealed { #[must_use] fn as_mut_any(&mut self) -> &mut dyn Any; - /// Convert this [`Variant`] trait object to an [`Any`] trait object. + /// Convert this [`Variant`] trait object to [`Box`]. #[must_use] fn as_box_any(self: Box) -> Box; @@ -91,13 +87,9 @@ pub trait Variant: Any + Send + Sync + private::Sealed { #[must_use] fn type_name(&self) -> &'static str; - /// Convert into [`Dynamic`]. + /// Clone this [`Variant`] trait object. #[must_use] - fn into_dynamic(self) -> Dynamic; - - /// Clone into [`Dynamic`]. - #[must_use] - fn clone_into_dynamic(&self) -> Dynamic; + fn clone_object(&self) -> Box; } impl Variant for T { @@ -118,12 +110,8 @@ impl Variant for T { type_name::() } #[inline(always)] - fn into_dynamic(self) -> Dynamic { - Dynamic::from(self) - } - #[inline(always)] - fn clone_into_dynamic(&self) -> Dynamic { - Dynamic::from(self.clone()) + fn clone_object(&self) -> Box { + Box::new(self.clone()) as Box } } @@ -818,11 +806,11 @@ impl Clone for Dynamic { Self(Union::TimeStamp(value.clone(), tag, ReadWrite)) } - Union::Variant(ref value, tag, _) => { - let mut x = value.as_ref().as_ref().clone_into_dynamic(); - x.set_tag(tag); - x - } + Union::Variant(ref value, tag, _) => Self(Union::Variant( + value.as_ref().as_ref().clone_object().into(), + tag, + ReadWrite, + )), #[cfg(not(feature = "no_closure"))] Union::Shared(ref cell, tag, _) => Self(Union::Shared(cell.clone(), tag, ReadWrite)), From 809feaf58d16f29fa18634d9a3b7d929dcf5d2ab Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 2 Jan 2022 15:14:55 +0800 Subject: [PATCH 74/97] Add doc comments. --- src/engine.rs | 7 +++++++ src/func/native.rs | 10 ++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 8ae0a694..a42290a4 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -818,12 +818,19 @@ impl EvalState { /// Context of a script evaluation process. #[derive(Debug)] pub struct EvalContext<'a, 'x, 'px, 'm, 's, 'b, 't, 'pt> { + /// The current [`Engine`]. pub(crate) engine: &'a Engine, + /// The current [`Scope`]. pub(crate) scope: &'x mut Scope<'px>, + /// The current [`GlobalRuntimeState`]. pub(crate) global: &'m mut GlobalRuntimeState, + /// The current [evaluation state][EvalState]. pub(crate) state: &'s mut EvalState, + /// The current stack of imported [modules][Module]. pub(crate) lib: &'b [&'b Module], + /// The current bound `this` pointer, if any. pub(crate) this_ptr: &'t mut Option<&'pt mut Dynamic>, + /// The current nesting level of function calls. pub(crate) level: usize, } diff --git a/src/func/native.rs b/src/func/native.rs index 29317110..1736bc8d 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -58,11 +58,17 @@ pub type LockGuard<'a, T> = std::sync::RwLockWriteGuard<'a, T>; /// Context of a native Rust function call. #[derive(Debug)] pub struct NativeCallContext<'a> { + /// The current [`Engine`]. engine: &'a Engine, + /// Name of function called. fn_name: &'a str, + /// Function source, if any. source: Option<&'a str>, + /// The current [`GlobalRuntimeState`], if any. global: Option<&'a GlobalRuntimeState>, + /// The current stack of loaded [modules][Module]. lib: &'a [&'a Module], + /// [Position] of the function call. pos: Position, } @@ -174,7 +180,7 @@ impl<'a> NativeCallContext<'a> { pub const fn fn_name(&self) -> &str { self.fn_name } - /// [Position][`Position`] of the function call. + /// [Position] of the function call. #[inline(always)] #[must_use] pub const fn position(&self) -> Position { @@ -204,7 +210,7 @@ impl<'a> NativeCallContext<'a> { ) -> impl Iterator)> { self.global.iter().flat_map(|&m| m.iter_modules_raw()) } - /// _(internals)_ The current [`GlobalRuntimeState`]. + /// _(internals)_ The current [`GlobalRuntimeState`], if any. /// Exported under the `internals` feature only. /// /// Not available under `no_module`. From 29f1328087604463843bf6966146672ac83e11c6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 2 Jan 2022 20:47:03 +0800 Subject: [PATCH 75/97] Simplify Dynamic::from for better inlining. --- src/api/custom_syntax.rs | 16 +++---- src/func/register.rs | 5 +-- src/types/dynamic.rs | 94 +++++++++++++++++----------------------- src/unsafe.rs | 45 ++++++++++++------- 4 files changed, 77 insertions(+), 83 deletions(-) diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index c22503d2..782b6034 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -95,41 +95,39 @@ impl Expression<'_> { if TypeId::of::() == TypeId::of::() { return match self.0 { - Expr::IntegerConstant(x, _) => unsafe_try_cast(*x).ok(), + Expr::IntegerConstant(x, _) => unsafe_try_cast(*x), _ => None, }; } #[cfg(not(feature = "no_float"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Expr::FloatConstant(x, _) => unsafe_try_cast(*x).ok(), + Expr::FloatConstant(x, _) => unsafe_try_cast(*x), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Expr::CharConstant(x, _) => unsafe_try_cast(*x).ok(), + Expr::CharConstant(x, _) => unsafe_try_cast(*x), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Expr::StringConstant(x, _) => unsafe_try_cast(x.clone()).ok(), - Expr::Variable(_, _, x) => { - unsafe_try_cast(Into::::into(&x.2)).ok() - } + Expr::StringConstant(x, _) => unsafe_try_cast(x.clone()), + Expr::Variable(_, _, x) => unsafe_try_cast(Into::::into(&x.2)), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Expr::BoolConstant(x, _) => unsafe_try_cast(*x).ok(), + Expr::BoolConstant(x, _) => unsafe_try_cast(*x), _ => None, }; } if TypeId::of::() == TypeId::of::<()>() { return match self.0 { - Expr::Unit(_) => unsafe_try_cast(()).ok(), + Expr::Unit(_) => unsafe_try_cast(()), _ => None, }; } diff --git a/src/func/register.rs b/src/func/register.rs index daa93d41..70085821 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -5,7 +5,7 @@ use super::call::FnCallArgs; use super::callable_function::CallableFunction; use super::native::{FnAny, SendSync}; -use crate::r#unsafe::unsafe_try_cast; +use crate::r#unsafe::unsafe_cast; use crate::tokenizer::Position; use crate::types::dynamic::{DynamicWriteLock, Variant}; use crate::{Dynamic, NativeCallContext, RhaiResultOf, ERR}; @@ -50,8 +50,7 @@ pub fn by_value(data: &mut Dynamic) -> T { ref_t.clone() } else if TypeId::of::() == TypeId::of::() { // If T is `String`, data must be `ImmutableString`, so map directly to it - let value = mem::take(data).into_string().expect("`ImmutableString`"); - unsafe_try_cast(value).expect("checked") + unsafe_cast(mem::take(data).into_string().expect("`ImmutableString`")) } else { // We consume the argument and then replace it with () - the argument is not supposed to be used again. // This way, we avoid having to clone the argument again, because it is already a clone when passed here. diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index eefcc2f4..d86422c8 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -1,7 +1,7 @@ //! Helper module which defines the [`Any`] trait to to allow dynamic value handling. use crate::func::native::SendSync; -use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast}; +use crate::r#unsafe::{unsafe_cast, unsafe_cast_box, unsafe_try_cast}; use crate::{ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -1209,11 +1209,11 @@ impl Dynamic { /// ``` #[inline] #[must_use] - pub fn from(mut value: T) -> Self { + pub fn from(value: T) -> Self { // Coded this way in order to maximally leverage potentials for dead-code removal. if TypeId::of::() == TypeId::of::() { - return unsafe_try_cast::<_, Dynamic>(value).ok().expect(CHECKED); + return unsafe_cast::<_, Dynamic>(value); } let val = value.as_any(); @@ -1249,52 +1249,36 @@ impl Dynamic { return ().into(); } - value = match unsafe_try_cast::<_, String>(value) { - Ok(s) => return s.into(), - Err(value) => value, - }; - #[cfg(not(feature = "no_index"))] - { - value = match unsafe_try_cast::<_, crate::Array>(value) { - Ok(array) => return array.into(), - Err(value) => value, - }; + if TypeId::of::() == TypeId::of::() { + return unsafe_cast::<_, String>(value).into(); + } + #[cfg(not(feature = "no_float"))] + if TypeId::of::() == TypeId::of::() { + return unsafe_cast::<_, crate::FLOAT>(value).into(); } #[cfg(not(feature = "no_index"))] - { - value = match unsafe_try_cast::<_, crate::Blob>(value) { - Ok(blob) => return Dynamic::from_blob(blob), // don't use blob.into() because it'll be converted into an Array - Err(value) => value, - }; + if TypeId::of::() == TypeId::of::() { + return unsafe_cast::<_, crate::Array>(value).into(); + } + #[cfg(not(feature = "no_index"))] + if TypeId::of::() == TypeId::of::() { + return Dynamic::from_blob(unsafe_cast::<_, crate::Blob>(value)); // don't use blob.into() because it'll be converted into an Array } - #[cfg(not(feature = "no_object"))] - { - value = match unsafe_try_cast::<_, crate::Map>(value) { - Ok(map) => return map.into(), - Err(value) => value, - }; + if TypeId::of::() == TypeId::of::() { + return unsafe_cast::<_, crate::Map>(value).into(); + } + if TypeId::of::() == TypeId::of::() { + return unsafe_cast::<_, FnPtr>(value).into(); } - - value = match unsafe_try_cast::<_, FnPtr>(value) { - Ok(fn_ptr) => return fn_ptr.into(), - Err(value) => value, - }; #[cfg(not(feature = "no_std"))] - { - value = match unsafe_try_cast::<_, Instant>(value) { - Ok(timestamp) => return timestamp.into(), - Err(value) => value, - }; + if TypeId::of::() == TypeId::of::() { + return unsafe_cast::<_, Instant>(value).into(); } - #[cfg(not(feature = "no_closure"))] - { - value = match unsafe_try_cast::<_, crate::Shared>>(value) { - Ok(value) => return value.into(), - Err(value) => value, - }; + if TypeId::of::() == TypeId::of::>>() { + return unsafe_cast::<_, crate::Shared>>(value).into(); } Self(Union::Variant( @@ -1365,12 +1349,12 @@ impl Dynamic { } if TypeId::of::() == TypeId::of::() { - return unsafe_try_cast::<_, T>(self).ok(); + return unsafe_try_cast::<_, T>(self); } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Int(value, _, _) => unsafe_try_cast(value).ok(), + Union::Int(value, _, _) => unsafe_try_cast(value), _ => None, }; } @@ -1378,7 +1362,7 @@ impl Dynamic { #[cfg(not(feature = "no_float"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Float(value, _, _) => unsafe_try_cast(*value).ok(), + Union::Float(value, _, _) => unsafe_try_cast(*value), _ => None, }; } @@ -1386,35 +1370,35 @@ impl Dynamic { #[cfg(feature = "decimal")] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Decimal(value, _, _) => unsafe_try_cast(*value).ok(), + Union::Decimal(value, _, _) => unsafe_try_cast(*value), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Bool(value, _, _) => unsafe_try_cast(value).ok(), + Union::Bool(value, _, _) => unsafe_try_cast(value), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Str(value, _, _) => unsafe_try_cast(value).ok(), + Union::Str(value, _, _) => unsafe_try_cast(value), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Str(value, _, _) => unsafe_try_cast(value.into_owned()).ok(), + Union::Str(value, _, _) => unsafe_try_cast(value.into_owned()), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Char(value, _, _) => unsafe_try_cast(value).ok(), + Union::Char(value, _, _) => unsafe_try_cast(value), _ => None, }; } @@ -1422,7 +1406,7 @@ impl Dynamic { #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Array(value, _, _) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), + Union::Array(value, _, _) => unsafe_cast_box::<_, T>(value).map(|v| *v), _ => None, }; } @@ -1430,7 +1414,7 @@ impl Dynamic { #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Blob(value, _, _) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), + Union::Blob(value, _, _) => unsafe_cast_box::<_, T>(value).map(|v| *v), _ => None, }; } @@ -1438,14 +1422,14 @@ impl Dynamic { #[cfg(not(feature = "no_object"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Map(value, _, _) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), + Union::Map(value, _, _) => unsafe_cast_box::<_, T>(value).map(|v| *v), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::FnPtr(value, _, _) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), + Union::FnPtr(value, _, _) => unsafe_cast_box::<_, T>(value).map(|v| *v), _ => None, }; } @@ -1453,20 +1437,20 @@ impl Dynamic { #[cfg(not(feature = "no_std"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::TimeStamp(value, _, _) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), + Union::TimeStamp(value, _, _) => unsafe_cast_box::<_, T>(value).map(|v| *v), _ => None, }; } if TypeId::of::() == TypeId::of::<()>() { return match self.0 { - Union::Unit(value, _, _) => unsafe_try_cast(value).ok(), + Union::Unit(value, _, _) => unsafe_try_cast(value), _ => None, }; } match self.0 { - Union::Variant(value, _, _) => (*value).as_box_any().downcast().map(|x| *x).ok(), + Union::Variant(value, _, _) => (*value).as_box_any().downcast().ok().map(|x| *x), #[cfg(not(feature = "no_closure"))] Union::Shared(_, _, _) => unreachable!("Union::Shared case should be already handled"), _ => None, diff --git a/src/unsafe.rs b/src/unsafe.rs index 3ce5b4c9..e7c81cbe 100644 --- a/src/unsafe.rs +++ b/src/unsafe.rs @@ -8,35 +8,48 @@ use std::{ }; /// Cast a type into another type. -#[inline] -pub fn unsafe_try_cast(a: A) -> Result { +/// +/// # Undefined Behavior +/// +/// It is UB if the types are not compatible. +#[inline(always)] +#[must_use] +pub fn unsafe_cast(a: A) -> B { + unsafe { + let ret: B = ptr::read(&a as *const _ as *const B); + // We explicitly forget the value immediately after moving out, + // removing any chance of a destructor running or value otherwise + // being used again. + mem::forget(a); + ret + } +} + +/// Cast a type into another type. +#[inline(always)] +#[must_use] +pub fn unsafe_try_cast(a: A) -> Option { if TypeId::of::() == a.type_id() { - // SAFETY: Just checked we have the right type. We explicitly forget the - // value immediately after moving out, removing any chance of a destructor - // running or value otherwise being used again. - unsafe { - let ret: B = ptr::read(&a as *const _ as *const B); - mem::forget(a); - Ok(ret) - } + // SAFETY: Just checked we have the right type. + Some(unsafe_cast(a)) } else { - Err(a) + None } } /// Cast a Boxed type into another type. -#[inline] -pub fn unsafe_cast_box(item: Box) -> Result, Box> { +#[inline(always)] +#[must_use] +pub fn unsafe_cast_box(item: Box) -> Option> { // Only allow casting to the exact same type if TypeId::of::() == TypeId::of::() { // SAFETY: just checked whether we are pointing to the correct type unsafe { let raw: *mut dyn Any = Box::into_raw(item as Box); - Ok(Box::from_raw(raw as *mut T)) + Some(Box::from_raw(raw as *mut T)) } } else { - // Return the consumed item for chaining. - Err(item) + None } } From 3984a29d6be96fd70931d5a72f8d3654c97833ee Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 2 Jan 2022 21:54:21 +0800 Subject: [PATCH 76/97] Fix test output. --- codegen/ui_tests/rhai_fn_non_clonable_return.stderr | 2 +- codegen/ui_tests/rhai_mod_non_clonable_return.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/codegen/ui_tests/rhai_fn_non_clonable_return.stderr b/codegen/ui_tests/rhai_fn_non_clonable_return.stderr index ba165394..9d946726 100644 --- a/codegen/ui_tests/rhai_fn_non_clonable_return.stderr +++ b/codegen/ui_tests/rhai_fn_non_clonable_return.stderr @@ -10,5 +10,5 @@ error[E0277]: the trait bound `NonClonable: Clone` is not satisfied note: required by a bound in `rhai::Dynamic::from` --> $WORKSPACE/src/types/dynamic.rs | - | pub fn from(mut value: T) -> Self { + | pub fn from(value: T) -> Self { | ^^^^^ required by this bound in `rhai::Dynamic::from` diff --git a/codegen/ui_tests/rhai_mod_non_clonable_return.stderr b/codegen/ui_tests/rhai_mod_non_clonable_return.stderr index 8cd59893..1d664c65 100644 --- a/codegen/ui_tests/rhai_mod_non_clonable_return.stderr +++ b/codegen/ui_tests/rhai_mod_non_clonable_return.stderr @@ -10,5 +10,5 @@ error[E0277]: the trait bound `NonClonable: Clone` is not satisfied note: required by a bound in `rhai::Dynamic::from` --> $WORKSPACE/src/types/dynamic.rs | - | pub fn from(mut value: T) -> Self { + | pub fn from(value: T) -> Self { | ^^^^^ required by this bound in `rhai::Dynamic::from` From bccdf70521d99b37c26fa4064ee54d2d0be9070a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 2 Jan 2022 23:26:38 +0800 Subject: [PATCH 77/97] Change tuples into fields. --- src/engine.rs | 238 +++++++++++++++++++++++++++++++------------------- 1 file changed, 150 insertions(+), 88 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index a42290a4..edfb6b70 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -90,6 +90,20 @@ enum ChainType { Dotting, } +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] +impl From<&Expr> for ChainType { + #[inline] + fn from(expr: &Expr) -> Self { + match expr { + #[cfg(not(feature = "no_index"))] + Expr::Index(_, _, _) => Self::Indexing, + #[cfg(not(feature = "no_object"))] + Expr::Dot(_, _, _) => Self::Dotting, + expr => unreachable!("Expr::Index or Expr::Dot expected but gets {:?}", expr), + } + } +} + /// Value of a chaining argument. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[derive(Debug, Clone, Hash)] @@ -173,50 +187,67 @@ impl ChainArgument { } } -/// Get the chaining type for an [`Expr`]. -#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -fn match_chaining_type(expr: &Expr) -> ChainType { - match expr { - #[cfg(not(feature = "no_index"))] - Expr::Index(_, _, _) => ChainType::Indexing, - #[cfg(not(feature = "no_object"))] - Expr::Dot(_, _, _) => ChainType::Dotting, - expr => unreachable!("Expr::Index or Expr::Dot expected but gets {:?}", expr), - } -} - /// A type that encapsulates a mutation target for an expression with side effects. #[derive(Debug)] pub enum Target<'a> { - /// The target is a mutable reference to a `Dynamic` value somewhere. + /// The target is a mutable reference to a [`Dynamic`]. RefMut(&'a mut Dynamic), - /// The target is a mutable reference to a Shared `Dynamic` value. - /// It holds both the access guard and the original shared value. + /// The target is a mutable reference to a _shared_ [`Dynamic`]. #[cfg(not(feature = "no_closure"))] - LockGuard( - ( - crate::types::dynamic::DynamicWriteLock<'a, Dynamic>, - Dynamic, - ), - ), - /// The target is a temporary `Dynamic` value (i.e. its mutation can cause no side effects). + SharedValue { + /// Lock guard to the shared [`Dynamic`]. + source: crate::types::dynamic::DynamicWriteLock<'a, Dynamic>, + /// Copy of the value. + value: Dynamic, + }, + /// The target is a temporary [`Dynamic`] value (i.e. its mutation can cause no side effects). TempValue(Dynamic), /// The target is a bit inside an [`INT`][crate::INT]. /// This is necessary because directly pointing to a bit inside an [`INT`][crate::INT] is impossible. #[cfg(not(feature = "no_index"))] - Bit(&'a mut Dynamic, Dynamic, u8), + Bit { + /// Mutable reference to the source [`Dynamic`]. + source: &'a mut Dynamic, + /// Copy of the boolean bit, as a [`Dynamic`]. + value: Dynamic, + /// Bit offset. + bit: u8, + }, /// The target is a range of bits inside an [`INT`][crate::INT]. /// This is necessary because directly pointing to a range of bits inside an [`INT`][crate::INT] is impossible. #[cfg(not(feature = "no_index"))] - BitField(&'a mut Dynamic, INT, Dynamic, u8), - /// The target is a byte inside a Blob. + BitField { + /// Mutable reference to the source [`Dynamic`]. + source: &'a mut Dynamic, + /// Copy of the integer value of the bits, as a [`Dynamic`]. + value: Dynamic, + /// Bitmask to apply to the source value (i.e. shifted) + mask: INT, + /// Number of bits to right-shift the source value. + shift: u8, + }, + /// The target is a byte inside a [`Blob`][crate::Blob]. /// This is necessary because directly pointing to a byte (in [`Dynamic`] form) inside a blob is impossible. #[cfg(not(feature = "no_index"))] - BlobByte(&'a mut Dynamic, usize, Dynamic), - /// The target is a character inside a String. + BlobByte { + /// Mutable reference to the source [`Dynamic`]. + source: &'a mut Dynamic, + /// Copy of the byte at the index, as a [`Dynamic`]. + value: Dynamic, + /// Offset index. + index: usize, + }, + /// The target is a character inside a string. /// This is necessary because directly pointing to a char inside a String is impossible. #[cfg(not(feature = "no_index"))] - StringChar(&'a mut Dynamic, usize, Dynamic), + StringChar { + /// Mutable reference to the source [`Dynamic`]. + source: &'a mut Dynamic, + /// Copy of the character at the offset, as a [`Dynamic`]. + value: Dynamic, + /// Offset index. + index: usize, + }, } impl<'a> Target<'a> { @@ -228,13 +259,13 @@ impl<'a> Target<'a> { match self { Self::RefMut(_) => true, #[cfg(not(feature = "no_closure"))] - Self::LockGuard(_) => true, + Self::SharedValue { .. } => true, Self::TempValue(_) => false, #[cfg(not(feature = "no_index"))] - Self::Bit(_, _, _) - | Self::BitField(_, _, _, _) - | Self::BlobByte(_, _, _) - | Self::StringChar(_, _, _) => false, + Self::Bit { .. } + | Self::BitField { .. } + | Self::BlobByte { .. } + | Self::StringChar { .. } => false, } } /// Is the `Target` a temp value? @@ -244,13 +275,13 @@ impl<'a> Target<'a> { match self { Self::RefMut(_) => false, #[cfg(not(feature = "no_closure"))] - Self::LockGuard(_) => false, + Self::SharedValue { .. } => false, Self::TempValue(_) => true, #[cfg(not(feature = "no_index"))] - Self::Bit(_, _, _) - | Self::BitField(_, _, _, _) - | Self::BlobByte(_, _, _) - | Self::StringChar(_, _, _) => false, + Self::Bit { .. } + | Self::BitField { .. } + | Self::BlobByte { .. } + | Self::StringChar { .. } => false, } } /// Is the `Target` a shared value? @@ -261,13 +292,13 @@ impl<'a> Target<'a> { match self { Self::RefMut(r) => r.is_shared(), #[cfg(not(feature = "no_closure"))] - Self::LockGuard(_) => true, + Self::SharedValue { .. } => true, Self::TempValue(r) => r.is_shared(), #[cfg(not(feature = "no_index"))] - Self::Bit(_, _, _) - | Self::BitField(_, _, _, _) - | Self::BlobByte(_, _, _) - | Self::StringChar(_, _, _) => false, + Self::Bit { .. } + | Self::BitField { .. } + | Self::BlobByte { .. } + | Self::StringChar { .. } => false, } } /// Is the `Target` a specific type? @@ -278,16 +309,16 @@ impl<'a> Target<'a> { match self { Self::RefMut(r) => r.is::(), #[cfg(not(feature = "no_closure"))] - Self::LockGuard((r, _)) => r.is::(), + Self::SharedValue { source, .. } => source.is::(), Self::TempValue(r) => r.is::(), #[cfg(not(feature = "no_index"))] - Self::Bit(_, _, _) => TypeId::of::() == TypeId::of::(), + Self::Bit { .. } => TypeId::of::() == TypeId::of::(), #[cfg(not(feature = "no_index"))] - Self::BitField(_, _, _, _) => TypeId::of::() == TypeId::of::(), + Self::BitField { .. } => TypeId::of::() == TypeId::of::(), #[cfg(not(feature = "no_index"))] - Self::BlobByte(_, _, _) => TypeId::of::() == TypeId::of::(), + Self::BlobByte { .. } => TypeId::of::() == TypeId::of::(), #[cfg(not(feature = "no_index"))] - Self::StringChar(_, _, _) => TypeId::of::() == TypeId::of::(), + Self::StringChar { .. } => TypeId::of::() == TypeId::of::(), } } /// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary. @@ -297,16 +328,16 @@ impl<'a> Target<'a> { match self { Self::RefMut(r) => r.clone(), // Referenced value is cloned #[cfg(not(feature = "no_closure"))] - Self::LockGuard((_, shared)) => shared, // Original shared value is simply taken + Self::SharedValue { value, .. } => value, // Original shared value is simply taken Self::TempValue(v) => v, // Owned value is simply taken #[cfg(not(feature = "no_index"))] - Self::Bit(_, value, _) => value, // Boolean is taken + Self::Bit { value, .. } => value, // boolean is taken #[cfg(not(feature = "no_index"))] - Self::BitField(_, _, value, _) => value, // INT is taken + Self::BitField { value, .. } => value, // INT is taken #[cfg(not(feature = "no_index"))] - Self::BlobByte(_, _, value) => value, // Byte is taken + Self::BlobByte { value, .. } => value, // byte is taken #[cfg(not(feature = "no_index"))] - Self::StringChar(_, _, ch) => ch, // Character is taken + Self::StringChar { value, .. } => value, // char is taken } } /// Take a `&mut Dynamic` reference from the `Target`. @@ -331,11 +362,11 @@ impl<'a> Target<'a> { match self { Self::RefMut(_) | Self::TempValue(_) => (), #[cfg(not(feature = "no_closure"))] - Self::LockGuard(_) => (), + Self::SharedValue { .. } => (), #[cfg(not(feature = "no_index"))] - Self::Bit(value, new_val, index) => { + Self::Bit { source, value, bit } => { // Replace the bit at the specified index position - let new_bit = new_val.as_bool().map_err(|err| { + let new_bit = value.as_bool().map_err(|err| { Box::new(ERR::ErrorMismatchDataType( "bool".to_string(), err.to_string(), @@ -343,9 +374,9 @@ impl<'a> Target<'a> { )) })?; - let value = &mut *value.write_lock::().expect("`INT`"); + let value = &mut *source.write_lock::().expect("`INT`"); - let index = *index; + let index = *bit; let mask = 1 << index; if new_bit { @@ -355,12 +386,17 @@ impl<'a> Target<'a> { } } #[cfg(not(feature = "no_index"))] - Self::BitField(value, mask, new_val, shift) => { + Self::BitField { + source, + value, + mask, + shift, + } => { let shift = *shift; let mask = *mask; // Replace the bit at the specified index position - let new_value = new_val.as_int().map_err(|err| { + let new_value = value.as_int().map_err(|err| { Box::new(ERR::ErrorMismatchDataType( "integer".to_string(), err.to_string(), @@ -369,15 +405,19 @@ impl<'a> Target<'a> { })?; let new_value = (new_value << shift) & mask; - let value = &mut *value.write_lock::().expect("`INT`"); + let value = &mut *source.write_lock::().expect("`INT`"); *value &= !mask; *value |= new_value; } #[cfg(not(feature = "no_index"))] - Self::BlobByte(value, index, new_val) => { + Self::BlobByte { + source, + value, + index, + } => { // Replace the byte at the specified index position - let new_byte = new_val.as_int().map_err(|err| { + let new_byte = value.as_int().map_err(|err| { Box::new(ERR::ErrorMismatchDataType( "INT".to_string(), err.to_string(), @@ -385,7 +425,7 @@ impl<'a> Target<'a> { )) })?; - let value = &mut *value.write_lock::().expect("`Blob`"); + let value = &mut *source.write_lock::().expect("`Blob`"); let index = *index; @@ -396,9 +436,13 @@ impl<'a> Target<'a> { } } #[cfg(not(feature = "no_index"))] - Self::StringChar(s, index, new_val) => { + Self::StringChar { + source, + value, + index, + } => { // Replace the character at the specified index position - let new_ch = new_val.as_char().map_err(|err| { + let new_ch = value.as_char().map_err(|err| { Box::new(ERR::ErrorMismatchDataType( "char".to_string(), err.to_string(), @@ -406,7 +450,7 @@ impl<'a> Target<'a> { )) })?; - let s = &mut *s + let s = &mut *source .write_lock::() .expect("`ImmutableString`"); @@ -430,8 +474,9 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> { #[cfg(not(feature = "no_closure"))] if value.is_shared() { // Cloning is cheap for a shared value - let container = value.clone(); - return Self::LockGuard((value.write_lock::().expect("`Dynamic`"), container)); + let val = value.clone(); + let source = value.write_lock::().expect("`Dynamic`"); + return Self::SharedValue { source, value: val }; } Self::RefMut(value) @@ -446,13 +491,13 @@ impl Deref for Target<'_> { match self { Self::RefMut(r) => *r, #[cfg(not(feature = "no_closure"))] - Self::LockGuard((r, _)) => &**r, + Self::SharedValue { source, .. } => &**source, Self::TempValue(ref r) => r, #[cfg(not(feature = "no_index"))] - Self::Bit(_, ref r, _) - | Self::BitField(_, _, ref r, _) - | Self::BlobByte(_, _, ref r) - | Self::StringChar(_, _, ref r) => r, + Self::Bit { ref value, .. } + | Self::BitField { ref value, .. } + | Self::BlobByte { ref value, .. } + | Self::StringChar { ref value, .. } => value, } } } @@ -470,13 +515,13 @@ impl DerefMut for Target<'_> { match self { Self::RefMut(r) => *r, #[cfg(not(feature = "no_closure"))] - Self::LockGuard((r, _)) => &mut *r, + Self::SharedValue { source, .. } => &mut *source, Self::TempValue(ref mut r) => r, #[cfg(not(feature = "no_index"))] - Self::Bit(_, ref mut r, _) - | Self::BitField(_, _, ref mut r, _) - | Self::BlobByte(_, _, ref mut r) - | Self::StringChar(_, _, ref mut r) => r, + Self::Bit { ref mut value, .. } + | Self::BitField { ref mut value, .. } + | Self::BlobByte { ref mut value, .. } + | Self::StringChar { ref mut value, .. } => value, } } } @@ -1345,7 +1390,7 @@ impl Engine { { let mut idx_val_for_setter = idx_val.clone(); let idx_pos = x.lhs.position(); - let rhs_chain = match_chaining_type(rhs); + let rhs_chain = rhs.into(); let (try_setter, result) = { let mut obj = self.get_indexed_mut( @@ -1623,7 +1668,7 @@ impl Engine { // Others - syntax error ref expr => unreachable!("invalid dot expression: {:?}", expr), }; - let rhs_chain = match_chaining_type(rhs); + let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( global, state, lib, this_ptr, val_target, root, &x.rhs, *term, @@ -1638,7 +1683,7 @@ impl Engine { Expr::Property(ref p) => { let ((getter, hash_get), (setter, hash_set), (name, pos)) = p.as_ref(); - let rhs_chain = match_chaining_type(rhs); + let rhs_chain = rhs.into(); let hash_get = crate::ast::FnCallHashes::from_native(*hash_get); let hash_set = crate::ast::FnCallHashes::from_native(*hash_set); let mut arg_values = [target.as_mut(), &mut Dynamic::UNIT.clone()]; @@ -1732,7 +1777,7 @@ impl Engine { // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr Expr::FnCall(ref f, pos) if !f.is_qualified() => { let FnCallExpr { name, hashes, .. } = f.as_ref(); - let rhs_chain = match_chaining_type(rhs); + let rhs_chain = rhs.into(); let args = &mut idx_val.into_fn_call_args(); let (mut val, _) = self.make_method_call( global, state, lib, name, *hashes, target, args, pos, level, @@ -1931,7 +1976,7 @@ impl Engine { }; // Push in reverse order - let chain_type = match_chaining_type(expr); + let chain_type = expr.into(); self.eval_dot_index_chain_arguments( scope, global, state, lib, this_ptr, rhs, *term, chain_type, idx_values, size, @@ -2055,7 +2100,11 @@ impl Engine { .get(arr_idx) .map(|&v| (v as INT).into()) .ok_or_else(|| Box::new(ERR::ErrorArrayBounds(arr_len, index, idx_pos)))?; - Ok(Target::BlobByte(target, arr_idx, value)) + Ok(Target::BlobByte { + source: target, + value, + index: arr_idx, + }) } #[cfg(not(feature = "no_object"))] @@ -2133,7 +2182,12 @@ impl Engine { let field_value = (*value & mask) >> shift; - Ok(Target::BitField(target, mask, field_value.into(), shift)) + Ok(Target::BitField { + source: target, + value: field_value.into(), + mask, + shift, + }) } #[cfg(not(feature = "no_index"))] @@ -2170,7 +2224,11 @@ impl Engine { return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into()); }; - Ok(Target::Bit(target, bit_value.into(), offset)) + Ok(Target::Bit { + source: target, + value: bit_value.into(), + bit: offset, + }) } #[cfg(not(feature = "no_index"))] @@ -2204,7 +2262,11 @@ impl Engine { return Err(ERR::ErrorStringBounds(chars_len, index, idx_pos).into()); }; - Ok(Target::StringChar(target, offset, ch.into())) + Ok(Target::StringChar { + source: target, + value: ch.into(), + index: offset, + }) } _ if use_indexers => { From 8961f36e8e0869d933a5459527fcdc4be203ee9c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 3 Jan 2022 23:10:20 +0800 Subject: [PATCH 78/97] Move bit-fields into new package. --- CHANGELOG.md | 6 ++ src/packages/bit_field.rs | 198 ++++++++++++++++++++++++++++++++++++++ src/packages/lang_core.rs | 4 + src/packages/logic.rs | 195 +------------------------------------ src/packages/mod.rs | 2 + src/packages/pkg_core.rs | 2 - src/packages/pkg_std.rs | 4 + 7 files changed, 215 insertions(+), 196 deletions(-) create mode 100644 src/packages/bit_field.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c0d1ee4..878bdc07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ Script-breaking changes * `is` is (pun intended) now a reserved keyword to prepare for possible future type checking expressions (e.g. `x is "string"`). +Breaking changes +---------------- + +* `LogicPackage` is removed from `CorePackage`. +* Bit-field functions are moved into a new `BitFieldPackage` (used to be in `LogicPackage`) which makes more sense. + New features ------------ diff --git a/src/packages/bit_field.rs b/src/packages/bit_field.rs new file mode 100644 index 00000000..57155f68 --- /dev/null +++ b/src/packages/bit_field.rs @@ -0,0 +1,198 @@ +#![allow(non_snake_case)] + +use crate::plugin::*; +use crate::{def_package, ExclusiveRange, InclusiveRange, Position, RhaiResultOf, ERR, INT}; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; + +def_package! { + /// Package of basic bit-field utilities. + crate::BitFieldPackage => |lib| { + lib.standard = true; + + combine_with_exported_module!(lib, "bit_field", bit_field_functions); + } +} + +#[export_module] +mod bit_field_functions { + const BITS: usize = std::mem::size_of::() * 8; + + #[rhai_fn(return_raw)] + pub fn get_bit(value: INT, index: INT) -> RhaiResultOf { + if index >= 0 { + let offset = index as usize; + + if offset >= BITS { + Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) + } else { + Ok((value & (1 << offset)) != 0) + } + } else if let Some(abs_index) = index.checked_abs() { + let offset = abs_index as usize; + + // Count from end if negative + if offset > BITS { + Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) + } else { + Ok((value & (1 << (BITS - offset))) != 0) + } + } else { + Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) + } + } + #[rhai_fn(return_raw)] + pub fn set_bit(value: &mut INT, index: INT, new_value: bool) -> RhaiResultOf<()> { + if index >= 0 { + let offset = index as usize; + + if offset >= BITS { + Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) + } else { + let mask = 1 << offset; + if new_value { + *value |= mask; + } else { + *value &= !mask; + } + Ok(()) + } + } else if let Some(abs_index) = index.checked_abs() { + let offset = abs_index as usize; + + // Count from end if negative + if offset > BITS { + Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) + } else { + let mask = 1 << offset; + if new_value { + *value |= mask; + } else { + *value &= !mask; + } + Ok(()) + } + } else { + Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) + } + } + #[rhai_fn(name = "get_bits", return_raw)] + pub fn get_bits_range(value: INT, range: ExclusiveRange) -> RhaiResultOf { + let from = INT::max(range.start, 0); + let to = INT::max(range.end, from); + get_bits(value, from, to - from) + } + #[rhai_fn(name = "get_bits", return_raw)] + pub fn get_bits_range_inclusive(value: INT, range: InclusiveRange) -> RhaiResultOf { + let from = INT::max(*range.start(), 0); + let to = INT::max(*range.end(), from - 1); + get_bits(value, from, to - from + 1) + } + #[rhai_fn(return_raw)] + pub fn get_bits(value: INT, index: INT, bits: INT) -> RhaiResultOf { + if bits < 1 { + return Ok(0); + } + + let offset = if index >= 0 { + let offset = index as usize; + + if offset >= BITS { + return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); + } + + offset + } else if let Some(abs_index) = index.checked_abs() { + let offset = abs_index as usize; + + // Count from end if negative + if offset > BITS { + return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); + } + BITS - offset + } else { + return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); + }; + + let bits = if offset + bits as usize > BITS { + BITS - offset + } else { + bits as usize + }; + + let mut base = 1; + let mut mask = 0; + + for _ in 0..bits { + mask |= base; + base <<= 1; + } + + Ok(((value & (mask << index)) >> index) & mask) + } + #[rhai_fn(name = "set_bits", return_raw)] + pub fn set_bits_range( + value: &mut INT, + range: ExclusiveRange, + new_value: INT, + ) -> RhaiResultOf<()> { + let from = INT::max(range.start, 0); + let to = INT::max(range.end, from); + set_bits(value, from, to - from, new_value) + } + #[rhai_fn(name = "set_bits", return_raw)] + pub fn set_bits_range_inclusive( + value: &mut INT, + range: InclusiveRange, + new_value: INT, + ) -> RhaiResultOf<()> { + let from = INT::max(*range.start(), 0); + let to = INT::max(*range.end(), from - 1); + set_bits(value, from, to - from + 1, new_value) + } + #[rhai_fn(return_raw)] + pub fn set_bits(value: &mut INT, index: INT, bits: INT, new_value: INT) -> RhaiResultOf<()> { + if bits < 1 { + return Ok(()); + } + + let offset = if index >= 0 { + let offset = index as usize; + + if offset >= BITS { + return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); + } + + offset + } else if let Some(abs_index) = index.checked_abs() { + let offset = abs_index as usize; + + // Count from end if negative + if offset > BITS { + return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); + } + BITS - offset + } else { + return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); + }; + + let bits = if offset + bits as usize > BITS { + BITS - offset + } else { + bits as usize + }; + + let mut base = 1; + let mut mask = 0; + + for _ in 0..bits { + mask |= base; + base <<= 1; + } + + *value &= !(mask << index); + *value |= (new_value & mask) << index; + + Ok(()) + } +} diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index 400b1332..a84b6628 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -16,6 +16,10 @@ def_package! { #[export_module] mod core_functions { + #[rhai_fn(name = "!")] + pub fn not(x: bool) -> bool { + !x + } #[rhai_fn(name = "tag", get = "tag", pure)] pub fn get_tag(value: &mut Dynamic) -> INT { value.tag() as INT diff --git a/src/packages/logic.rs b/src/packages/logic.rs index fb87a5f9..860adcda 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use crate::plugin::*; -use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, ERR, INT}; +use crate::{def_package, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -61,19 +61,9 @@ def_package! { reg_functions!(lib += float; f64); combine_with_exported_module!(lib, "f64", f64_functions); } - - set_exported_fn!(lib, "!", not); - - combine_with_exported_module!(lib, "bit_field", bit_field_functions); } } -// Logic operators -#[export_fn] -fn not(x: bool) -> bool { - !x -} - #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] gen_cmp_functions!(numbers => i8, u8, i16, u16, i32, u32, u64); @@ -196,186 +186,3 @@ mod f64_functions { (x as f64) <= (y as f64) } } - -#[export_module] -mod bit_field_functions { - const BITS: usize = std::mem::size_of::() * 8; - - #[rhai_fn(return_raw)] - pub fn get_bit(value: INT, index: INT) -> RhaiResultOf { - if index >= 0 { - let offset = index as usize; - - if offset >= BITS { - Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) - } else { - Ok((value & (1 << offset)) != 0) - } - } else if let Some(abs_index) = index.checked_abs() { - let offset = abs_index as usize; - - // Count from end if negative - if offset > BITS { - Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) - } else { - Ok((value & (1 << (BITS - offset))) != 0) - } - } else { - Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) - } - } - #[rhai_fn(return_raw)] - pub fn set_bit(value: &mut INT, index: INT, new_value: bool) -> RhaiResultOf<()> { - if index >= 0 { - let offset = index as usize; - - if offset >= BITS { - Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) - } else { - let mask = 1 << offset; - if new_value { - *value |= mask; - } else { - *value &= !mask; - } - Ok(()) - } - } else if let Some(abs_index) = index.checked_abs() { - let offset = abs_index as usize; - - // Count from end if negative - if offset > BITS { - Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) - } else { - let mask = 1 << offset; - if new_value { - *value |= mask; - } else { - *value &= !mask; - } - Ok(()) - } - } else { - Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()) - } - } - #[rhai_fn(name = "get_bits", return_raw)] - pub fn get_bits_range(value: INT, range: ExclusiveRange) -> RhaiResultOf { - let from = INT::max(range.start, 0); - let to = INT::max(range.end, from); - get_bits(value, from, to - from) - } - #[rhai_fn(name = "get_bits", return_raw)] - pub fn get_bits_range_inclusive(value: INT, range: InclusiveRange) -> RhaiResultOf { - let from = INT::max(*range.start(), 0); - let to = INT::max(*range.end(), from - 1); - get_bits(value, from, to - from + 1) - } - #[rhai_fn(return_raw)] - pub fn get_bits(value: INT, index: INT, bits: INT) -> RhaiResultOf { - if bits < 1 { - return Ok(0); - } - - let offset = if index >= 0 { - let offset = index as usize; - - if offset >= BITS { - return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); - } - - offset - } else if let Some(abs_index) = index.checked_abs() { - let offset = abs_index as usize; - - // Count from end if negative - if offset > BITS { - return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); - } - BITS - offset - } else { - return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); - }; - - let bits = if offset + bits as usize > BITS { - BITS - offset - } else { - bits as usize - }; - - let mut base = 1; - let mut mask = 0; - - for _ in 0..bits { - mask |= base; - base <<= 1; - } - - Ok(((value & (mask << index)) >> index) & mask) - } - #[rhai_fn(name = "set_bits", return_raw)] - pub fn set_bits_range( - value: &mut INT, - range: ExclusiveRange, - new_value: INT, - ) -> RhaiResultOf<()> { - let from = INT::max(range.start, 0); - let to = INT::max(range.end, from); - set_bits(value, from, to - from, new_value) - } - #[rhai_fn(name = "set_bits", return_raw)] - pub fn set_bits_range_inclusive( - value: &mut INT, - range: InclusiveRange, - new_value: INT, - ) -> RhaiResultOf<()> { - let from = INT::max(*range.start(), 0); - let to = INT::max(*range.end(), from - 1); - set_bits(value, from, to - from + 1, new_value) - } - #[rhai_fn(return_raw)] - pub fn set_bits(value: &mut INT, index: INT, bits: INT, new_value: INT) -> RhaiResultOf<()> { - if bits < 1 { - return Ok(()); - } - - let offset = if index >= 0 { - let offset = index as usize; - - if offset >= BITS { - return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); - } - - offset - } else if let Some(abs_index) = index.checked_abs() { - let offset = abs_index as usize; - - // Count from end if negative - if offset > BITS { - return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); - } - BITS - offset - } else { - return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into()); - }; - - let bits = if offset + bits as usize > BITS { - BITS - offset - } else { - bits as usize - }; - - let mut base = 1; - let mut mask = 0; - - for _ in 0..bits { - mask |= base; - base <<= 1; - } - - *value &= !(mask << index); - *value |= (new_value & mask) << index; - - Ok(()) - } -} diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 7f73e774..0b8bc6f3 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -4,6 +4,7 @@ use crate::{Module, Shared}; pub(crate) mod arithmetic; pub(crate) mod array_basic; +mod bit_field; pub(crate) mod blob_basic; mod fn_basic; mod iter_basic; @@ -20,6 +21,7 @@ mod time_basic; pub use arithmetic::ArithmeticPackage; #[cfg(not(feature = "no_index"))] pub use array_basic::BasicArrayPackage; +pub use bit_field::BitFieldPackage; #[cfg(not(feature = "no_index"))] pub use blob_basic::BasicBlobPackage; pub use fn_basic::BasicFnPackage; diff --git a/src/packages/pkg_core.rs b/src/packages/pkg_core.rs index dc106dee..c006b0a1 100644 --- a/src/packages/pkg_core.rs +++ b/src/packages/pkg_core.rs @@ -10,7 +10,6 @@ def_package! { /// /// * [`LanguageCorePackage`][super::LanguageCorePackage] /// * [`ArithmeticPackage`][super::ArithmeticPackage] - /// * [`LogicPackage`][super::LogicPackage] /// * [`BasicStringPackage`][super::BasicStringPackage] /// * [`BasicIteratorPackage`][super::BasicIteratorPackage] /// * [`BasicFnPackage`][super::BasicFnPackage] @@ -19,7 +18,6 @@ def_package! { super::LanguageCorePackage::init(lib); super::ArithmeticPackage::init(lib); - super::LogicPackage::init(lib); super::BasicStringPackage::init(lib); super::BasicIteratorPackage::init(lib); super::BasicFnPackage::init(lib); diff --git a/src/packages/pkg_std.rs b/src/packages/pkg_std.rs index 012aa7d8..b388b379 100644 --- a/src/packages/pkg_std.rs +++ b/src/packages/pkg_std.rs @@ -9,6 +9,8 @@ def_package! { /// # Contents /// /// * [`CorePackage`][super::CorePackage] + /// * [`BitFieldPackage`][super::BitFieldPackage] + /// * [`LogicPackage`][super::LogicPackage] /// * [`BasicMathPackage`][super::BasicMathPackage] /// * [`BasicArrayPackage`][super::BasicArrayPackage] /// * [`BasicBlobPackage`][super::BasicBlobPackage] @@ -19,6 +21,8 @@ def_package! { lib.standard = true; super::CorePackage::init(lib); + super::BitFieldPackage::init(lib); + super::LogicPackage::init(lib); super::BasicMathPackage::init(lib); #[cfg(not(feature = "no_index"))] super::BasicArrayPackage::init(lib); From d60f14ec27c17ac45d19ec1891a65556b2c7472e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 3 Jan 2022 23:11:06 +0800 Subject: [PATCH 79/97] Streamline hot code. --- src/types/dynamic.rs | 207 ++++++++++++++++++++----------------------- src/unsafe.rs | 4 +- 2 files changed, 100 insertions(+), 111 deletions(-) diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index d86422c8..1ef19dae 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -50,11 +50,11 @@ pub trait Variant: Any + private::Sealed { /// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any]. #[must_use] - fn as_mut_any(&mut self) -> &mut dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; /// Convert this [`Variant`] trait object to [`Box`]. #[must_use] - fn as_box_any(self: Box) -> Box; + fn as_boxed_any(self: Box) -> Box; /// Get the name of this type. #[must_use] @@ -77,11 +77,11 @@ pub trait Variant: Any + Send + Sync + private::Sealed { /// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any]. #[must_use] - fn as_mut_any(&mut self) -> &mut dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; /// Convert this [`Variant`] trait object to [`Box`]. #[must_use] - fn as_box_any(self: Box) -> Box; + fn as_boxed_any(self: Box) -> Box; /// Get the name of this type. #[must_use] @@ -98,11 +98,11 @@ impl Variant for T { self } #[inline(always)] - fn as_mut_any(&mut self) -> &mut dyn Any { + fn as_any_mut(&mut self) -> &mut dyn Any { self } #[inline(always)] - fn as_box_any(self: Box) -> Box { + fn as_boxed_any(self: Box) -> Box { self } #[inline(always)] @@ -400,7 +400,7 @@ impl Dynamic { #[cfg(not(feature = "no_std"))] Union::TimeStamp(_, _, _) => TypeId::of::(), - Union::Variant(ref value, _, _) => (***value).type_id(), + Union::Variant(ref v, _, _) => (***v).type_id(), #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] @@ -439,7 +439,7 @@ impl Dynamic { #[cfg(not(feature = "no_std"))] Union::TimeStamp(_, _, _) => "timestamp", - Union::Variant(ref value, _, _) => (***value).type_name(), + Union::Variant(ref v, _, _) => (***v).type_name(), #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] @@ -489,11 +489,13 @@ impl Hash for Dynamic { #[cfg(feature = "sync")] Union::Shared(ref cell, _, _) => (*cell.read().unwrap()).hash(state), - Union::Variant(ref _value, _, _) => { + Union::Variant(ref v, _, _) => { + let _v = v; + #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] { - let value_any = (***_value).as_any(); + let value_any = (***_v).as_any(); let type_id = value_any.type_id(); if type_id == TypeId::of::() { @@ -591,26 +593,26 @@ impl fmt::Display for Dynamic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { Union::Unit(_, _, _) => write!(f, ""), - Union::Bool(ref value, _, _) => fmt::Display::fmt(value, f), - Union::Str(ref value, _, _) => fmt::Display::fmt(value, f), - Union::Char(ref value, _, _) => fmt::Display::fmt(value, f), - Union::Int(ref value, _, _) => fmt::Display::fmt(value, f), + Union::Bool(ref v, _, _) => fmt::Display::fmt(v, f), + Union::Str(ref v, _, _) => fmt::Display::fmt(v, f), + Union::Char(ref v, _, _) => fmt::Display::fmt(v, f), + Union::Int(ref v, _, _) => fmt::Display::fmt(v, f), #[cfg(not(feature = "no_float"))] - Union::Float(ref value, _, _) => fmt::Display::fmt(value, f), + Union::Float(ref v, _, _) => fmt::Display::fmt(v, f), #[cfg(feature = "decimal")] - Union::Decimal(ref value, _, _) => fmt::Display::fmt(value, f), + Union::Decimal(ref v, _, _) => fmt::Display::fmt(v, f), #[cfg(not(feature = "no_index"))] Union::Array(_, _, _) => fmt::Debug::fmt(self, f), #[cfg(not(feature = "no_index"))] Union::Blob(_, _, _) => fmt::Debug::fmt(self, f), #[cfg(not(feature = "no_object"))] Union::Map(_, _, _) => fmt::Debug::fmt(self, f), - Union::FnPtr(ref value, _, _) => fmt::Display::fmt(value, f), + Union::FnPtr(ref v, _, _) => fmt::Display::fmt(v, f), #[cfg(not(feature = "no_std"))] Union::TimeStamp(_, _, _) => f.write_str(""), - Union::Variant(ref value, _, _) => { - let _value_any = (***value).as_any(); + Union::Variant(ref v, _, _) => { + let _value_any = (***v).as_any(); let _type_id = _value_any.type_id(); #[cfg(not(feature = "only_i32"))] @@ -655,7 +657,7 @@ impl fmt::Display for Dynamic { return write!(f, "{}..={}", range.start(), range.end()); } - f.write_str((***value).type_name()) + f.write_str((***v).type_name()) } #[cfg(not(feature = "no_closure"))] @@ -677,21 +679,21 @@ impl fmt::Display for Dynamic { impl fmt::Debug for Dynamic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { - Union::Unit(ref value, _, _) => fmt::Debug::fmt(value, f), - Union::Bool(ref value, _, _) => fmt::Debug::fmt(value, f), - Union::Str(ref value, _, _) => fmt::Debug::fmt(value, f), - Union::Char(ref value, _, _) => fmt::Debug::fmt(value, f), - Union::Int(ref value, _, _) => fmt::Debug::fmt(value, f), + Union::Unit(ref v, _, _) => fmt::Debug::fmt(v, f), + Union::Bool(ref v, _, _) => fmt::Debug::fmt(v, f), + Union::Str(ref v, _, _) => fmt::Debug::fmt(v, f), + Union::Char(ref v, _, _) => fmt::Debug::fmt(v, f), + Union::Int(ref v, _, _) => fmt::Debug::fmt(v, f), #[cfg(not(feature = "no_float"))] - Union::Float(ref value, _, _) => fmt::Debug::fmt(value, f), + Union::Float(ref v, _, _) => fmt::Debug::fmt(v, f), #[cfg(feature = "decimal")] - Union::Decimal(ref value, _, _) => fmt::Debug::fmt(value, f), + Union::Decimal(ref v, _, _) => fmt::Debug::fmt(v, f), #[cfg(not(feature = "no_index"))] - Union::Array(ref value, _, _) => fmt::Debug::fmt(value, f), + Union::Array(ref v, _, _) => fmt::Debug::fmt(v, f), #[cfg(not(feature = "no_index"))] - Union::Blob(ref value, _, _) => { + Union::Blob(ref v, _, _) => { f.write_str("[")?; - value.iter().enumerate().try_for_each(|(i, v)| { + v.iter().enumerate().try_for_each(|(i, v)| { if i > 0 && i % 8 == 0 { f.write_str(" ")?; } @@ -700,16 +702,16 @@ impl fmt::Debug for Dynamic { f.write_str("]") } #[cfg(not(feature = "no_object"))] - Union::Map(ref value, _, _) => { + Union::Map(ref v, _, _) => { f.write_str("#")?; - fmt::Debug::fmt(value, f) + fmt::Debug::fmt(v, f) } - Union::FnPtr(ref value, _, _) => fmt::Debug::fmt(value, f), + Union::FnPtr(ref v, _, _) => fmt::Debug::fmt(v, f), #[cfg(not(feature = "no_std"))] Union::TimeStamp(_, _, _) => write!(f, ""), - Union::Variant(ref value, _, _) => { - let _value_any = (***value).as_any(); + Union::Variant(ref v, _, _) => { + let _value_any = (***v).as_any(); let _type_id = _value_any.type_id(); #[cfg(not(feature = "only_i32"))] @@ -754,7 +756,7 @@ impl fmt::Debug for Dynamic { return write!(f, "{}..={}", range.start(), range.end()); } - f.write_str((***value).type_name()) + f.write_str((***v).type_name()) } #[cfg(not(feature = "no_closure"))] @@ -783,31 +785,27 @@ impl Clone for Dynamic { /// The cloned copy is marked read-write even if the original is read-only. fn clone(&self) -> Self { match self.0 { - Union::Unit(value, tag, _) => Self(Union::Unit(value, tag, ReadWrite)), - Union::Bool(value, tag, _) => Self(Union::Bool(value, tag, ReadWrite)), - Union::Str(ref value, tag, _) => Self(Union::Str(value.clone(), tag, ReadWrite)), - Union::Char(value, tag, _) => Self(Union::Char(value, tag, ReadWrite)), - Union::Int(value, tag, _) => Self(Union::Int(value, tag, ReadWrite)), + Union::Unit(v, tag, _) => Self(Union::Unit(v, tag, ReadWrite)), + Union::Bool(v, tag, _) => Self(Union::Bool(v, tag, ReadWrite)), + Union::Str(ref v, tag, _) => Self(Union::Str(v.clone(), tag, ReadWrite)), + Union::Char(v, tag, _) => Self(Union::Char(v, tag, ReadWrite)), + Union::Int(v, tag, _) => Self(Union::Int(v, tag, ReadWrite)), #[cfg(not(feature = "no_float"))] - Union::Float(value, tag, _) => Self(Union::Float(value, tag, ReadWrite)), + Union::Float(v, tag, _) => Self(Union::Float(v, tag, ReadWrite)), #[cfg(feature = "decimal")] - Union::Decimal(ref value, tag, _) => { - Self(Union::Decimal(value.clone(), tag, ReadWrite)) - } + Union::Decimal(ref v, tag, _) => Self(Union::Decimal(v.clone(), tag, ReadWrite)), #[cfg(not(feature = "no_index"))] - Union::Array(ref value, tag, _) => Self(Union::Array(value.clone(), tag, ReadWrite)), + Union::Array(ref v, tag, _) => Self(Union::Array(v.clone(), tag, ReadWrite)), #[cfg(not(feature = "no_index"))] - Union::Blob(ref value, tag, _) => Self(Union::Blob(value.clone(), tag, ReadWrite)), + Union::Blob(ref v, tag, _) => Self(Union::Blob(v.clone(), tag, ReadWrite)), #[cfg(not(feature = "no_object"))] - Union::Map(ref value, tag, _) => Self(Union::Map(value.clone(), tag, ReadWrite)), - Union::FnPtr(ref value, tag, _) => Self(Union::FnPtr(value.clone(), tag, ReadWrite)), + Union::Map(ref v, tag, _) => Self(Union::Map(v.clone(), tag, ReadWrite)), + Union::FnPtr(ref v, tag, _) => Self(Union::FnPtr(v.clone(), tag, ReadWrite)), #[cfg(not(feature = "no_std"))] - Union::TimeStamp(ref value, tag, _) => { - Self(Union::TimeStamp(value.clone(), tag, ReadWrite)) - } + Union::TimeStamp(ref v, tag, _) => Self(Union::TimeStamp(v.clone(), tag, ReadWrite)), - Union::Variant(ref value, tag, _) => Self(Union::Variant( - value.as_ref().as_ref().clone_object().into(), + Union::Variant(ref v, tag, _) => Self(Union::Variant( + v.as_ref().as_ref().clone_object().into(), tag, ReadWrite, )), @@ -1354,7 +1352,7 @@ impl Dynamic { if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Int(value, _, _) => unsafe_try_cast(value), + Union::Int(v, _, _) => unsafe_try_cast(v), _ => None, }; } @@ -1362,7 +1360,7 @@ impl Dynamic { #[cfg(not(feature = "no_float"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Float(value, _, _) => unsafe_try_cast(*value), + Union::Float(v, _, _) => unsafe_try_cast(*v), _ => None, }; } @@ -1370,35 +1368,35 @@ impl Dynamic { #[cfg(feature = "decimal")] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Decimal(value, _, _) => unsafe_try_cast(*value), + Union::Decimal(v, _, _) => unsafe_try_cast(*v), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Bool(value, _, _) => unsafe_try_cast(value), + Union::Bool(v, _, _) => unsafe_try_cast(v), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Str(value, _, _) => unsafe_try_cast(value), + Union::Str(v, _, _) => unsafe_try_cast(v), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Str(value, _, _) => unsafe_try_cast(value.into_owned()), + Union::Str(v, _, _) => unsafe_try_cast(v.to_string()), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Char(value, _, _) => unsafe_try_cast(value), + Union::Char(v, _, _) => unsafe_try_cast(v), _ => None, }; } @@ -1406,7 +1404,7 @@ impl Dynamic { #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Array(value, _, _) => unsafe_cast_box::<_, T>(value).map(|v| *v), + Union::Array(v, _, _) => unsafe_cast_box::<_, T>(v), _ => None, }; } @@ -1414,7 +1412,7 @@ impl Dynamic { #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Blob(value, _, _) => unsafe_cast_box::<_, T>(value).map(|v| *v), + Union::Blob(v, _, _) => unsafe_cast_box::<_, T>(v), _ => None, }; } @@ -1422,14 +1420,14 @@ impl Dynamic { #[cfg(not(feature = "no_object"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Map(value, _, _) => unsafe_cast_box::<_, T>(value).map(|v| *v), + Union::Map(v, _, _) => unsafe_cast_box::<_, T>(v), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::FnPtr(value, _, _) => unsafe_cast_box::<_, T>(value).map(|v| *v), + Union::FnPtr(v, _, _) => unsafe_cast_box::<_, T>(v), _ => None, }; } @@ -1437,20 +1435,20 @@ impl Dynamic { #[cfg(not(feature = "no_std"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::TimeStamp(value, _, _) => unsafe_cast_box::<_, T>(value).map(|v| *v), + Union::TimeStamp(v, _, _) => unsafe_cast_box::<_, T>(v), _ => None, }; } if TypeId::of::() == TypeId::of::<()>() { return match self.0 { - Union::Unit(value, _, _) => unsafe_try_cast(value), + Union::Unit(v, _, _) => unsafe_try_cast(v), _ => None, }; } match self.0 { - Union::Variant(value, _, _) => (*value).as_box_any().downcast().ok().map(|x| *x), + Union::Variant(v, _, _) => (*v).as_boxed_any().downcast().ok().map(|x| *x), #[cfg(not(feature = "no_closure"))] Union::Shared(_, _, _) => unreachable!("Union::Shared case should be already handled"), _ => None, @@ -1622,7 +1620,6 @@ impl Dynamic { Union::Shared(ref _cell, _, _) => { #[cfg(not(feature = "sync"))] return _cell.try_borrow().is_err(); - #[cfg(feature = "sync")] return false; } @@ -1663,7 +1660,8 @@ impl Dynamic { } self.downcast_ref() - .map(|r| DynamicReadLock(DynamicReadLockInner::Reference(r))) + .map(DynamicReadLockInner::Reference) + .map(DynamicReadLock) } /// Get a mutable reference of a specific type to the [`Dynamic`]. /// Casting to [`Dynamic`] just returns a mutable reference to it. @@ -1694,7 +1692,8 @@ impl Dynamic { } self.downcast_mut() - .map(|r| DynamicWriteLock(DynamicWriteLockInner::Reference(r))) + .map(DynamicWriteLockInner::Reference) + .map(DynamicWriteLock) } /// Get a reference of a specific type to the [`Dynamic`]. /// Casting to [`Dynamic`] just returns a reference to it. @@ -1707,79 +1706,79 @@ impl Dynamic { if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Int(ref value, _, _) => value.as_any().downcast_ref::(), + Union::Int(ref v, _, _) => v.as_any().downcast_ref::(), _ => None, }; } #[cfg(not(feature = "no_float"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Float(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), + Union::Float(ref v, _, _) => v.as_ref().as_any().downcast_ref::(), _ => None, }; } #[cfg(feature = "decimal")] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Decimal(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), + Union::Decimal(ref v, _, _) => v.as_ref().as_any().downcast_ref::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Bool(ref value, _, _) => value.as_any().downcast_ref::(), + Union::Bool(ref v, _, _) => v.as_any().downcast_ref::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Str(ref value, _, _) => value.as_any().downcast_ref::(), + Union::Str(ref v, _, _) => v.as_any().downcast_ref::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Char(ref value, _, _) => value.as_any().downcast_ref::(), + Union::Char(ref v, _, _) => v.as_any().downcast_ref::(), _ => None, }; } #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Array(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), + Union::Array(ref v, _, _) => v.as_ref().as_any().downcast_ref::(), _ => None, }; } #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Blob(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), + Union::Blob(ref v, _, _) => v.as_ref().as_any().downcast_ref::(), _ => None, }; } #[cfg(not(feature = "no_object"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Map(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), + Union::Map(ref v, _, _) => v.as_ref().as_any().downcast_ref::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::FnPtr(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), + Union::FnPtr(ref v, _, _) => v.as_ref().as_any().downcast_ref::(), _ => None, }; } #[cfg(not(feature = "no_std"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::TimeStamp(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), + Union::TimeStamp(ref v, _, _) => v.as_ref().as_any().downcast_ref::(), _ => None, }; } if TypeId::of::() == TypeId::of::<()>() { return match self.0 { - Union::Unit(ref value, _, _) => value.as_any().downcast_ref::(), + Union::Unit(ref v, _, _) => v.as_any().downcast_ref::(), _ => None, }; } @@ -1788,7 +1787,7 @@ impl Dynamic { } match self.0 { - Union::Variant(ref value, _, _) => (***value).as_any().downcast_ref::(), + Union::Variant(ref v, _, _) => (***v).as_any().downcast_ref::(), #[cfg(not(feature = "no_closure"))] Union::Shared(_, _, _) => None, _ => None, @@ -1805,98 +1804,88 @@ impl Dynamic { if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Int(ref mut value, _, _) => value.as_mut_any().downcast_mut::(), + Union::Int(ref mut v, _, _) => v.as_any_mut().downcast_mut::(), _ => None, }; } #[cfg(not(feature = "no_float"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Float(ref mut value, _, _) => { - value.as_mut().as_mut_any().downcast_mut::() - } + Union::Float(ref mut v, _, _) => v.as_mut().as_any_mut().downcast_mut::(), _ => None, }; } #[cfg(feature = "decimal")] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Decimal(ref mut value, _, _) => { - value.as_mut().as_mut_any().downcast_mut::() - } + Union::Decimal(ref mut v, _, _) => v.as_mut().as_any_mut().downcast_mut::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Bool(ref mut value, _, _) => value.as_mut_any().downcast_mut::(), + Union::Bool(ref mut v, _, _) => v.as_any_mut().downcast_mut::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Str(ref mut value, _, _) => value.as_mut_any().downcast_mut::(), + Union::Str(ref mut v, _, _) => v.as_any_mut().downcast_mut::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Char(ref mut value, _, _) => value.as_mut_any().downcast_mut::(), + Union::Char(ref mut v, _, _) => v.as_any_mut().downcast_mut::(), _ => None, }; } #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Array(ref mut value, _, _) => { - value.as_mut().as_mut_any().downcast_mut::() - } + Union::Array(ref mut v, _, _) => v.as_mut().as_any_mut().downcast_mut::(), _ => None, }; } #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Blob(ref mut value, _, _) => value.as_mut().as_mut_any().downcast_mut::(), + Union::Blob(ref mut v, _, _) => v.as_mut().as_any_mut().downcast_mut::(), _ => None, }; } #[cfg(not(feature = "no_object"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Map(ref mut value, _, _) => value.as_mut().as_mut_any().downcast_mut::(), + Union::Map(ref mut v, _, _) => v.as_mut().as_any_mut().downcast_mut::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::FnPtr(ref mut value, _, _) => { - value.as_mut().as_mut_any().downcast_mut::() - } + Union::FnPtr(ref mut v, _, _) => v.as_mut().as_any_mut().downcast_mut::(), _ => None, }; } #[cfg(not(feature = "no_std"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::TimeStamp(ref mut value, _, _) => { - value.as_mut().as_mut_any().downcast_mut::() - } + Union::TimeStamp(ref mut v, _, _) => v.as_mut().as_any_mut().downcast_mut::(), _ => None, }; } if TypeId::of::() == TypeId::of::<()>() { return match self.0 { - Union::Unit(ref mut value, _, _) => value.as_mut_any().downcast_mut::(), + Union::Unit(ref mut v, _, _) => v.as_any_mut().downcast_mut::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { - return self.as_mut_any().downcast_mut::(); + return self.as_any_mut().downcast_mut::(); } match self.0 { - Union::Variant(ref mut value, _, _) => (***value).as_mut_any().downcast_mut::(), + Union::Variant(ref mut v, _, _) => (***v).as_any_mut().downcast_mut::(), #[cfg(not(feature = "no_closure"))] Union::Shared(_, _, _) => None, _ => None, @@ -1907,7 +1896,7 @@ impl Dynamic { #[inline] pub fn as_unit(&self) -> Result<(), &'static str> { match self.0 { - Union::Unit(value, _, _) => Ok(value), + Union::Unit(v, _, _) => Ok(v), #[cfg(not(feature = "no_closure"))] Union::Shared(_, _, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()), _ => Err(self.type_name()), diff --git a/src/unsafe.rs b/src/unsafe.rs index e7c81cbe..ab272fa8 100644 --- a/src/unsafe.rs +++ b/src/unsafe.rs @@ -40,13 +40,13 @@ pub fn unsafe_try_cast(a: A) -> Option { /// Cast a Boxed type into another type. #[inline(always)] #[must_use] -pub fn unsafe_cast_box(item: Box) -> Option> { +pub fn unsafe_cast_box(item: Box) -> Option { // Only allow casting to the exact same type if TypeId::of::() == TypeId::of::() { // SAFETY: just checked whether we are pointing to the correct type unsafe { let raw: *mut dyn Any = Box::into_raw(item as Box); - Some(Box::from_raw(raw as *mut T)) + Some(*Box::from_raw(raw as *mut T)) } } else { None From 1d1e473ac45f5fb1418ddbbf3188ac6a534f4151 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 3 Jan 2022 23:16:47 +0800 Subject: [PATCH 80/97] Minor refactor. --- src/ast/ast.rs | 7 +++---- src/engine.rs | 14 ++++++-------- src/func/call.rs | 20 +++++++------------- src/func/callable_function.rs | 2 +- src/packages/arithmetic.rs | 2 +- src/packages/fn_basic.rs | 13 ++++++++----- src/packages/math_basic.rs | 11 ++--------- src/packages/time_basic.rs | 12 ++++++------ 8 files changed, 34 insertions(+), 47 deletions(-) diff --git a/src/ast/ast.rs b/src/ast/ast.rs index 1f9ec131..3a7b96b9 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -124,10 +124,9 @@ impl AST { #[inline(always)] #[must_use] pub fn source(&self) -> Option<&str> { - if self.source.is_empty() { - None - } else { - Some(&self.source) + match self.source.as_str() { + "" => None, + s => Some(s), } } /// Get a reference to the source. diff --git a/src/engine.rs b/src/engine.rs index edfb6b70..c6d44849 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -890,10 +890,9 @@ impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> { #[inline(always)] #[must_use] pub fn source(&self) -> Option<&str> { - if self.global.source.is_empty() { - None - } else { - Some(&self.global.source) + match self.global.source.as_str() { + "" => None, + s => Some(s), } } /// The current [`Scope`]. @@ -3241,10 +3240,9 @@ impl Engine { { use crate::ModuleResolver; - let source = if global.source.is_empty() { - None - } else { - Some(global.source.as_str()) + let source = match global.source.as_str() { + "" => None, + s => Some(s), }; let path_pos = expr.position(); diff --git a/src/func/call.rs b/src/func/call.rs index 99d241cc..fcc252fa 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -155,7 +155,7 @@ impl Engine { ) -> String { format!( "{}{}{} ({})", - namespace.map_or(String::new(), |ns| ns.to_string()), + namespace.map_or_else(|| String::new(), |ns| ns.to_string()), if namespace.is_some() { Token::DoubleColon.literal_syntax() } else { @@ -372,14 +372,9 @@ impl Engine { } // Run external function - let source = if source.is_empty() { - if parent_source.is_empty() { - None - } else { - Some(parent_source.as_str()) - } - } else { - Some(source.as_str()) + let source = match (source.as_str(), parent_source.as_str()) { + ("", "") => None, + ("", s) | (s, _) => Some(s), }; let context = (self, name, source, &*global, lib, pos).into(); @@ -424,10 +419,9 @@ impl Engine { pos, ) })?; - let source = if global.source.is_empty() { - None - } else { - Some(global.source.as_str()) + let source = match global.source.as_str() { + "" => None, + s => Some(s), }; (debug(&text, source, pos).into(), false) } else { diff --git a/src/func/callable_function.rs b/src/func/callable_function.rs index 65504c42..0402ba87 100644 --- a/src/func/callable_function.rs +++ b/src/func/callable_function.rs @@ -50,7 +50,7 @@ impl fmt::Display for CallableFunction { Self::Plugin(_) => write!(f, "PluginFunction"), #[cfg(not(feature = "no_function"))] - CallableFunction::Script(s) => fmt::Display::fmt(s, f), + Self::Script(s) => fmt::Display::fmt(s, f), } } } diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index 72f441f7..8c700ed4 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -9,7 +9,7 @@ use std::prelude::v1::*; #[cfg(not(feature = "no_float"))] use num_traits::Float; -#[inline(never)] +#[inline] pub fn make_err(msg: impl Into) -> RhaiError { ERR::ErrorArithmetic(msg.into(), Position::NONE).into() } diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 6037761f..66be2772 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -44,7 +44,7 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array { fn make_metadata( dict: &BTreeSet, namespace: Option, - f: &ScriptFnDef, + func: &ScriptFnDef, ) -> Map { const DICT: &str = "key exists"; @@ -53,10 +53,13 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array { if let Some(ns) = namespace { map.insert(dict.get("namespace").expect(DICT).clone(), ns.into()); } - map.insert(dict.get("name").expect(DICT).clone(), f.name.clone().into()); + map.insert( + dict.get("name").expect(DICT).clone(), + func.name.clone().into(), + ); map.insert( dict.get("access").expect(DICT).clone(), - match f.access { + match func.access { FnAccess::Public => dict.get("public").expect(DICT).clone(), FnAccess::Private => dict.get("private").expect(DICT).clone(), } @@ -64,11 +67,11 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array { ); map.insert( dict.get("is_anonymous").expect(DICT).clone(), - f.name.starts_with(crate::engine::FN_ANONYMOUS).into(), + func.name.starts_with(crate::engine::FN_ANONYMOUS).into(), ); map.insert( dict.get("params").expect(DICT).clone(), - f.params + func.params .iter() .cloned() .map(Into::into) diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 0b902666..d664033e 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -18,13 +18,6 @@ use rust_decimal::Decimal; #[cfg(feature = "decimal")] use super::arithmetic::make_err; -#[allow(dead_code)] -#[cfg(feature = "only_i32")] -pub const MAX_INT: INT = i32::MAX; -#[allow(dead_code)] -#[cfg(not(feature = "only_i32"))] -pub const MAX_INT: INT = i64::MAX; - macro_rules! gen_conversion_as_functions { ($root:ident => $func_name:ident ( $($arg_type:ident),+ ) -> $result_type:ty) => { pub mod $root { $(pub mod $arg_type { @@ -264,7 +257,7 @@ mod float_functions { } #[rhai_fn(name = "to_int", return_raw)] pub fn f32_to_int(x: f32) -> RhaiResultOf { - if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) { + if cfg!(not(feature = "unchecked")) && x > (INT::MAX as f32) { Err( ERR::ErrorArithmetic(format!("Integer overflow: to_int({})", x), Position::NONE) .into(), @@ -275,7 +268,7 @@ mod float_functions { } #[rhai_fn(name = "to_int", return_raw)] pub fn f64_to_int(x: f64) -> RhaiResultOf { - if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) { + if cfg!(not(feature = "unchecked")) && x > (INT::MAX as f64) { Err( ERR::ErrorArithmetic(format!("Integer overflow: to_int({})", x), Position::NONE) .into(), diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index f4a6e7df..270b89a7 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -1,6 +1,6 @@ #![cfg(not(feature = "no_std"))] -use super::{arithmetic::make_err as make_arithmetic_err, math_basic::MAX_INT}; +use super::arithmetic::make_err as make_arithmetic_err; use crate::plugin::*; use crate::{def_package, Dynamic, EvalAltResult, RhaiResult, RhaiResultOf, INT}; @@ -42,7 +42,7 @@ mod time_functions { { let seconds = timestamp.elapsed().as_secs(); - if cfg!(not(feature = "unchecked")) && seconds > (MAX_INT as u64) { + if cfg!(not(feature = "unchecked")) && seconds > (INT::MAX as u64) { Err(make_arithmetic_err(format!( "Integer overflow for timestamp.elapsed: {}", seconds @@ -69,7 +69,7 @@ mod time_functions { if timestamp2 > timestamp1 { let seconds = (timestamp2 - timestamp1).as_secs(); - if cfg!(not(feature = "unchecked")) && seconds > (MAX_INT as u64) { + if cfg!(not(feature = "unchecked")) && seconds > (INT::MAX as u64) { Err(make_arithmetic_err(format!( "Integer overflow for timestamp duration: -{}", seconds @@ -80,7 +80,7 @@ mod time_functions { } else { let seconds = (timestamp1 - timestamp2).as_secs(); - if cfg!(not(feature = "unchecked")) && seconds > (MAX_INT as u64) { + if cfg!(not(feature = "unchecked")) && seconds > (INT::MAX as u64) { Err(make_arithmetic_err(format!( "Integer overflow for timestamp duration: {}", seconds @@ -97,7 +97,7 @@ mod time_functions { if seconds < 0.0 { subtract_impl(timestamp, -seconds) } else if cfg!(not(feature = "unchecked")) { - if seconds > (MAX_INT as FLOAT) { + if seconds > (INT::MAX as FLOAT) { Err(make_arithmetic_err(format!( "Integer overflow for timestamp add: {}", seconds @@ -120,7 +120,7 @@ mod time_functions { if seconds < 0.0 { add_impl(timestamp, -seconds) } else if cfg!(not(feature = "unchecked")) { - if seconds > (MAX_INT as FLOAT) { + if seconds > (INT::MAX as FLOAT) { Err(make_arithmetic_err(format!( "Integer overflow for timestamp add: {}", seconds From d99953c101dfd39bfe0586ff7173399305d5c8d7 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 4 Jan 2022 15:22:48 +0800 Subject: [PATCH 81/97] Fixup AsRef vs &str. --- src/api/compile.rs | 14 ++++--- src/api/custom_syntax.rs | 6 +-- src/api/mod.rs | 5 ++- src/api/register.rs | 20 +++++----- src/engine.rs | 25 +++++++----- src/func/call.rs | 37 ++++++++---------- src/func/hashing.rs | 23 +++++------ src/func/native.rs | 7 ++-- src/module/mod.rs | 69 ++++++++++++++++++++------------- src/optimizer.rs | 16 ++++---- src/parser.rs | 41 ++++++++------------ src/tokenizer.rs | 16 +++----- src/types/interner.rs | 83 +++++++++++++++++++--------------------- 13 files changed, 181 insertions(+), 181 deletions(-) diff --git a/src/api/compile.rs b/src/api/compile.rs index a23efc11..5accd0bc 100644 --- a/src/api/compile.rs +++ b/src/api/compile.rs @@ -193,10 +193,10 @@ impl Engine { /// # } /// ``` #[inline(always)] - pub fn compile_scripts_with_scope( + pub fn compile_scripts_with_scope>( &self, scope: &Scope, - scripts: &[impl AsRef], + scripts: impl AsRef<[S]>, ) -> ParseResult { self.compile_with_scope_and_optimization_level( scope, @@ -213,14 +213,16 @@ impl Engine { /// throughout the script _including_ functions. This allows functions to be optimized based on /// dynamic global constants. #[inline] - pub(crate) fn compile_with_scope_and_optimization_level( + pub(crate) fn compile_with_scope_and_optimization_level>( &self, scope: &Scope, - scripts: &[impl AsRef], + scripts: impl AsRef<[S]>, #[cfg(not(feature = "no_optimize"))] optimization_level: crate::OptimizationLevel, ) -> ParseResult { - let (stream, tokenizer_control) = - self.lex_raw(scripts, self.token_mapper.as_ref().map(Box::as_ref)); + let (stream, tokenizer_control) = self.lex_raw( + scripts.as_ref(), + self.token_mapper.as_ref().map(Box::as_ref), + ); let mut state = ParseState::new(self, tokenizer_control); self.parse( &mut stream.peekable(), diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index 782b6034..0558673d 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -209,9 +209,9 @@ impl Engine { /// Replacing one variable with another (i.e. adding a new variable and removing one variable at /// the same time so that the total _size_ of the [`Scope`][crate::Scope] is unchanged) also /// does NOT count, so `false` should be passed. - pub fn register_custom_syntax( + pub fn register_custom_syntax + Into>( &mut self, - symbols: &[impl AsRef + Into], + symbols: impl AsRef<[S]>, scope_may_be_changed: bool, func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static, ) -> ParseResult<&mut Self> { @@ -219,7 +219,7 @@ impl Engine { let mut segments = StaticVec::::new(); - for s in symbols { + for s in symbols.as_ref() { let s = s.as_ref().trim(); // Skip empty symbols diff --git a/src/api/mod.rs b/src/api/mod.rs index e42a489a..39045da7 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -213,7 +213,7 @@ impl Engine { /// ``` pub fn register_custom_operator( &mut self, - keyword: impl AsRef + Into, + keyword: impl AsRef, precedence: u8, ) -> Result<&mut Self, String> { let precedence = Precedence::new(precedence); @@ -247,7 +247,8 @@ impl Engine { } // Add to custom keywords - self.custom_keywords.insert(keyword.into(), precedence); + self.custom_keywords + .insert(keyword.as_ref().into(), precedence); Ok(self) } diff --git a/src/api/register.rs b/src/api/register.rs index e369ab0c..605192a7 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -165,7 +165,7 @@ impl Engine { pub fn register_raw_fn( &mut self, name: N, - arg_types: &[TypeId], + arg_types: impl AsRef<[TypeId]>, func: impl Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResultOf + SendSync + 'static, ) -> &mut Self where @@ -346,7 +346,7 @@ impl Engine { name: impl AsRef, get_fn: impl Fn(&mut T) -> V + SendSync + 'static, ) -> &mut Self { - self.register_fn(&crate::engine::make_getter(name.as_ref()), get_fn) + self.register_fn(crate::engine::make_getter(name.as_ref()).as_str(), get_fn) } /// Register a getter function for a member of a registered type with the [`Engine`]. /// @@ -395,7 +395,7 @@ impl Engine { name: impl AsRef, get_fn: impl Fn(&mut T) -> RhaiResultOf + SendSync + 'static, ) -> &mut Self { - self.register_result_fn(&crate::engine::make_getter(name.as_ref()), get_fn) + self.register_result_fn(crate::engine::make_getter(name.as_ref()).as_str(), get_fn) } /// Register a setter function for a member of a registered type with the [`Engine`]. /// @@ -445,7 +445,7 @@ impl Engine { name: impl AsRef, set_fn: impl Fn(&mut T, V) + SendSync + 'static, ) -> &mut Self { - self.register_fn(&crate::engine::make_setter(name.as_ref()), set_fn) + self.register_fn(crate::engine::make_setter(name.as_ref()).as_str(), set_fn) } /// Register a setter function for a member of a registered type with the [`Engine`]. /// @@ -496,7 +496,7 @@ impl Engine { name: impl AsRef, set_fn: impl Fn(&mut T, V) -> RhaiResultOf<()> + SendSync + 'static, ) -> &mut Self { - self.register_result_fn(&crate::engine::make_setter(name.as_ref()), set_fn) + self.register_result_fn(crate::engine::make_setter(name.as_ref()).as_str(), set_fn) } /// Short-hand for registering both getter and setter functions /// of a registered type with the [`Engine`]. @@ -975,17 +975,17 @@ impl Engine { #[cfg(not(feature = "no_module"))] pub fn register_static_module( &mut self, - name: impl AsRef + Into, + name: impl AsRef, module: Shared, ) -> &mut Self { fn register_static_module_raw( root: &mut std::collections::BTreeMap>, - name: impl AsRef + Into, + name: &str, module: Shared, ) { let separator = crate::tokenizer::Token::DoubleColon.syntax(); - if !name.as_ref().contains(separator.as_ref()) { + if !name.contains(separator.as_ref()) { if !module.is_indexed() { // Index the module (making a clone copy if necessary) if it is not indexed let mut module = crate::func::native::shared_take_or_clone(module); @@ -995,7 +995,7 @@ impl Engine { root.insert(name.into(), module); } } else { - let mut iter = name.as_ref().splitn(2, separator.as_ref()); + let mut iter = name.splitn(2, separator.as_ref()); let sub_module = iter.next().expect("contains separator").trim(); let remainder = iter.next().expect("contains separator").trim(); @@ -1014,7 +1014,7 @@ impl Engine { } } - register_static_module_raw(&mut self.global_sub_modules, name, module); + register_static_module_raw(&mut self.global_sub_modules, name.as_ref(), module); self } /// _(metadata)_ Generate a list of all registered functions. diff --git a/src/engine.rs b/src/engine.rs index c6d44849..6754b9e9 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -626,8 +626,7 @@ impl GlobalRuntimeState { /// Get the index of a globally-imported [module][Module] by name. #[inline] #[must_use] - pub fn find_module(&self, name: impl AsRef) -> Option { - let name = name.as_ref(); + pub fn find_module(&self, name: &str) -> Option { let len = self.keys.len(); self.keys.iter().rev().enumerate().find_map(|(i, key)| { @@ -1041,26 +1040,32 @@ impl Default for Engine { /// Make getter function #[cfg(not(feature = "no_object"))] -#[inline] +#[inline(always)] #[must_use] -pub fn make_getter(id: &str) -> String { - format!("{}{}", FN_GET, id) +pub fn make_getter(id: &str) -> Identifier { + let mut buf = Identifier::new_const(); + buf.push_str(FN_GET); + buf.push_str(id); + buf } /// Make setter function #[cfg(not(feature = "no_object"))] -#[inline] +#[inline(always)] #[must_use] -pub fn make_setter(id: &str) -> String { - format!("{}{}", FN_SET, id) +pub fn make_setter(id: &str) -> Identifier { + let mut buf = Identifier::new_const(); + buf.push_str(FN_SET); + buf.push_str(id); + buf } /// Is this function an anonymous function? #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] -pub fn is_anonymous_fn(fn_name: impl AsRef) -> bool { - fn_name.as_ref().starts_with(FN_ANONYMOUS) +pub fn is_anonymous_fn(fn_name: &str) -> bool { + fn_name.starts_with(FN_ANONYMOUS) } /// Print to `stdout` diff --git a/src/func/call.rs b/src/func/call.rs index fcc252fa..3bbe0e74 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -105,7 +105,7 @@ impl Drop for ArgBackup<'_> { #[cfg(not(feature = "no_closure"))] #[inline] pub fn ensure_no_data_race( - fn_name: impl AsRef, + fn_name: &str, args: &FnCallArgs, is_method_call: bool, ) -> RhaiResultOf<()> { @@ -116,7 +116,7 @@ pub fn ensure_no_data_race( .find(|(_, a)| a.is_locked()) { return Err(ERR::ErrorDataRace( - format!("argument #{} of function '{}'", n + 1, fn_name.as_ref()), + format!("argument #{} of function '{}'", n + 1, fn_name), Position::NONE, ) .into()); @@ -150,7 +150,7 @@ impl Engine { fn gen_call_signature( &self, namespace: Option<&Namespace>, - fn_name: impl AsRef, + fn_name: &str, args: &[&mut Dynamic], ) -> String { format!( @@ -161,7 +161,7 @@ impl Engine { } else { "" }, - fn_name.as_ref(), + fn_name, args.iter() .map(|a| if a.is::() { "&str | ImmutableString | String" @@ -187,7 +187,7 @@ impl Engine { global: &GlobalRuntimeState, state: &'s mut EvalState, lib: &[&Module], - fn_name: impl AsRef, + fn_name: &str, hash_script: u64, args: Option<&mut FnCallArgs>, allow_dynamic: bool, @@ -197,8 +197,6 @@ impl Engine { return None; } - let fn_name = fn_name.as_ref(); - let mut hash = args.as_ref().map_or(hash_script, |args| { combine_hashes( hash_script, @@ -332,7 +330,7 @@ impl Engine { global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], - name: impl AsRef, + name: &str, hash: u64, args: &mut FnCallArgs, is_ref_mut: bool, @@ -342,7 +340,6 @@ impl Engine { #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, pos)?; - let name = name.as_ref(); let parent_source = global.source.clone(); // Check if function access already in the cache @@ -521,7 +518,7 @@ impl Engine { global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], - fn_name: impl AsRef, + fn_name: &str, hashes: FnCallHashes, args: &mut FnCallArgs, is_ref_mut: bool, @@ -535,8 +532,6 @@ impl Engine { Err(ERR::ErrorRuntime(msg.into(), pos).into()) } - let fn_name = fn_name.as_ref(); - // Check for data race. #[cfg(not(feature = "no_closure"))] ensure_no_data_race(fn_name, args, is_ref_mut)?; @@ -717,14 +712,13 @@ impl Engine { global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], - fn_name: impl AsRef, + fn_name: &str, mut hash: FnCallHashes, target: &mut crate::engine::Target, (call_args, call_arg_pos): &mut (FnArgsVec, Position), pos: Position, level: usize, ) -> RhaiResultOf<(Dynamic, bool)> { - let fn_name = fn_name.as_ref(); let is_ref_mut = target.is_ref(); let (result, updated) = match fn_name { @@ -902,7 +896,7 @@ impl Engine { state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, - fn_name: impl AsRef, + fn_name: &str, args_expr: &[Expr], constants: &[Dynamic], hashes: FnCallHashes, @@ -910,7 +904,6 @@ impl Engine { capture_scope: bool, level: usize, ) -> RhaiResult { - let fn_name = fn_name.as_ref(); let mut a_expr = args_expr; let mut total_args = a_expr.len(); let mut curry = FnArgsVec::new_const(); @@ -1179,14 +1172,13 @@ impl Engine { lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, namespace: &Namespace, - fn_name: impl AsRef, + fn_name: &str, args_expr: &[Expr], constants: &[Dynamic], hash: u64, pos: Position, level: usize, ) -> RhaiResult { - let fn_name = fn_name.as_ref(); let mut arg_values = FnArgsVec::with_capacity(args_expr.len()); let mut args = FnArgsVec::with_capacity(args_expr.len()); let mut first_arg_value = None; @@ -1324,14 +1316,17 @@ impl Engine { global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], - script: impl AsRef, - _pos: Position, + script: &str, + pos: Position, level: usize, ) -> RhaiResult { + let _pos = pos; + #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, _pos)?; - let script = script.as_ref().trim(); + let script = script.trim(); + if script.is_empty() { return Ok(Dynamic::UNIT); } diff --git a/src/func/hashing.rs b/src/func/hashing.rs index 38a8b636..96bdba56 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -87,10 +87,7 @@ pub fn get_hasher() -> ahash::AHasher { /// The first module name is skipped. Hashing starts from the _second_ module in the chain. #[inline] #[must_use] -pub fn calc_qualified_var_hash<'a>( - modules: impl Iterator + 'a>, - var_name: impl AsRef, -) -> u64 { +pub fn calc_qualified_var_hash<'a>(modules: impl Iterator, var_name: &str) -> u64 { let s = &mut get_hasher(); // We always skip the first module @@ -98,9 +95,9 @@ pub fn calc_qualified_var_hash<'a>( modules .inspect(|_| len += 1) .skip(1) - .for_each(|m| m.as_ref().hash(s)); + .for_each(|m| m.hash(s)); len.hash(s); - var_name.as_ref().hash(s); + var_name.hash(s); match s.finish() { 0 => ALT_ZERO_HASH, @@ -123,9 +120,9 @@ pub fn calc_qualified_var_hash<'a>( /// The first module name is skipped. Hashing starts from the _second_ module in the chain. #[inline] #[must_use] -pub fn calc_qualified_fn_hash( - modules: impl Iterator>, - fn_name: impl AsRef, +pub fn calc_qualified_fn_hash<'a>( + modules: impl Iterator, + fn_name: &str, num: usize, ) -> u64 { let s = &mut get_hasher(); @@ -135,9 +132,9 @@ pub fn calc_qualified_fn_hash( modules .inspect(|_| len += 1) .skip(1) - .for_each(|m| m.as_ref().hash(s)); + .for_each(|m| m.hash(s)); len.hash(s); - fn_name.as_ref().hash(s); + fn_name.hash(s); num.hash(s); match s.finish() { @@ -156,8 +153,8 @@ pub fn calc_qualified_fn_hash( /// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. #[inline(always)] #[must_use] -pub fn calc_fn_hash(fn_name: impl AsRef, num: usize) -> u64 { - calc_qualified_fn_hash(empty::<&str>(), fn_name, num) +pub fn calc_fn_hash(fn_name: &str, num: usize) -> u64 { + calc_qualified_fn_hash(empty(), fn_name, num) } /// Calculate a non-zero [`u64`] hash key from a list of parameter types. diff --git a/src/func/native.rs b/src/func/native.rs index 1736bc8d..7d49fc84 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -287,15 +287,16 @@ impl<'a> NativeCallContext<'a> { args: &mut [&mut Dynamic], ) -> RhaiResult { let fn_name = fn_name.as_ref(); + let len = args.len(); let hash = if is_method_call { FnCallHashes::from_all( #[cfg(not(feature = "no_function"))] - calc_fn_hash(fn_name, args.len() - 1), - calc_fn_hash(fn_name, args.len()), + calc_fn_hash(fn_name, len - 1), + calc_fn_hash(fn_name, len), ) } else { - calc_fn_hash(fn_name, args.len()).into() + calc_fn_hash(fn_name, len).into() }; self.engine() diff --git a/src/module/mod.rs b/src/module/mod.rs index 9ac02eaf..3842c9ba 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -116,12 +116,12 @@ impl FuncInfo { /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. #[inline] -pub fn calc_native_fn_hash( - modules: impl Iterator>, - fn_name: impl AsRef, +pub fn calc_native_fn_hash<'a>( + modules: impl Iterator, + fn_name: &str, params: &[TypeId], ) -> u64 { - let hash_script = calc_qualified_fn_hash(modules, fn_name.as_ref(), params.len()); + let hash_script = calc_qualified_fn_hash(modules, fn_name, params.len()); let hash_params = calc_fn_params_hash(params.iter().cloned()); combine_hashes(hash_script, hash_params) } @@ -644,8 +644,13 @@ impl Module { /// In other words, the number of entries should be one larger than the number of parameters. #[cfg(feature = "metadata")] #[inline] - pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[impl AsRef]) -> &mut Self { + pub fn update_fn_metadata>( + &mut self, + hash_fn: u64, + arg_names: impl AsRef<[S]>, + ) -> &mut Self { let mut param_names: StaticVec<_> = arg_names + .as_ref() .iter() .map(|name| self.interner.get("", name.as_ref()).into()) .collect(); @@ -689,17 +694,23 @@ impl Module { /// doc-comment leader: `///` or `/**`. #[cfg(feature = "metadata")] #[inline] - pub fn update_fn_metadata_with_comments( + pub fn update_fn_metadata_with_comments, C: AsRef>( &mut self, hash_fn: u64, - arg_names: &[impl AsRef], - comments: &[impl AsRef], + arg_names: impl AsRef<[A]>, + comments: impl AsRef<[C]>, ) -> &mut Self { self.update_fn_metadata(hash_fn, arg_names); - if !comments.is_empty() { + if !comments.as_ref().is_empty() { let f = self.functions.get_mut(&hash_fn).expect("exists"); - f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect()); + f.comments = Some( + comments + .as_ref() + .iter() + .map(|s| s.as_ref().into()) + .collect(), + ); } self @@ -756,17 +767,18 @@ impl Module { #[inline] pub fn set_fn( &mut self, - name: impl AsRef + Into, + name: impl AsRef, namespace: FnNamespace, access: FnAccess, arg_names: Option<&[&str]>, - arg_types: &[TypeId], + arg_types: impl AsRef<[TypeId]>, func: CallableFunction, ) -> u64 { let _arg_names = arg_names; let is_method = func.is_method(); let mut param_types: StaticVec<_> = arg_types + .as_ref() .iter() .cloned() .enumerate() @@ -781,7 +793,7 @@ impl Module { .flat_map(|&p| p.iter()) .map(|&arg| self.interner.get("", arg).into()) .collect::>(); - let return_type = if names.len() > arg_types.len() { + let return_type = if names.len() > arg_types.as_ref().len() { names.pop().expect("exists") } else { Default::default() @@ -795,7 +807,7 @@ impl Module { self.functions.insert( hash_fn, FuncInfo { - name: self.interner.get("", name.as_ref()).into(), + name: self.interner.get("", name).into(), namespace, access, params: param_types.len(), @@ -845,21 +857,27 @@ impl Module { /// doc-comment leader: `///` or `/**`. #[cfg(feature = "metadata")] #[inline] - pub fn set_fn_with_comments( + pub fn set_fn_with_comments>( &mut self, - name: impl AsRef + Into, + name: impl AsRef, namespace: FnNamespace, access: FnAccess, arg_names: Option<&[&str]>, - arg_types: &[TypeId], - comments: &[impl AsRef], + arg_types: impl AsRef<[TypeId]>, + comments: impl AsRef<[S]>, func: CallableFunction, ) -> u64 { let hash = self.set_fn(name, namespace, access, arg_names, arg_types, func); - if !comments.is_empty() { + if !comments.as_ref().is_empty() { let f = self.functions.get_mut(&hash).expect("exists"); - f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect()); + f.comments = Some( + comments + .as_ref() + .iter() + .map(|s| s.as_ref().into()) + .collect(), + ); } hash @@ -932,16 +950,15 @@ impl Module { /// assert!(module.contains_fn(hash)); /// ``` #[inline(always)] - pub fn set_raw_fn( + pub fn set_raw_fn( &mut self, - name: N, + name: impl AsRef, namespace: FnNamespace, access: FnAccess, - arg_types: &[TypeId], + arg_types: impl AsRef<[TypeId]>, func: F, ) -> u64 where - N: AsRef + Into, T: Variant + Clone, F: Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResultOf + SendSync + 'static, { @@ -1025,7 +1042,7 @@ impl Module { F: Fn(&mut A) -> RhaiResultOf + SendSync + 'static, { self.set_fn( - &crate::engine::make_getter(name.as_ref()), + crate::engine::make_getter(name.as_ref()).as_str(), FnNamespace::Global, FnAccess::Public, None, @@ -1067,7 +1084,7 @@ impl Module { F: Fn(&mut A, B) -> RhaiResultOf<()> + SendSync + 'static, { self.set_fn( - &crate::engine::make_setter(name.as_ref()), + crate::engine::make_setter(name.as_ref()).as_str(), FnNamespace::Global, FnAccess::Public, None, diff --git a/src/optimizer.rs b/src/optimizer.rs index 64d3037d..95c5c2d8 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -110,13 +110,11 @@ impl<'a> OptimizerState<'a> { } /// Look up a constant from the list. #[inline] - pub fn find_constant(&self, name: impl AsRef) -> Option<&Dynamic> { + pub fn find_constant(&self, name: &str) -> Option<&Dynamic> { if !self.propagate_constants { return None; } - let name = name.as_ref(); - for (n, access, value) in self.variables.iter().rev() { if n == name { return match access { @@ -132,7 +130,7 @@ impl<'a> OptimizerState<'a> { #[inline] pub fn call_fn_with_constant_arguments( &self, - fn_name: impl AsRef, + fn_name: &str, arg_values: &mut [Dynamic], ) -> Option { #[cfg(not(feature = "no_function"))] @@ -145,7 +143,7 @@ impl<'a> OptimizerState<'a> { &mut GlobalRuntimeState::new(), &mut EvalState::new(), lib, - &fn_name, + fn_name, calc_fn_hash(&fn_name, arg_values.len()), &mut arg_values.iter_mut().collect::>(), false, @@ -158,8 +156,12 @@ impl<'a> OptimizerState<'a> { } // Has a system function a Rust-native override? -fn has_native_fn_override(engine: &Engine, hash_script: u64, arg_types: &[TypeId]) -> bool { - let hash_params = calc_fn_params_hash(arg_types.iter().cloned()); +fn has_native_fn_override( + engine: &Engine, + hash_script: u64, + arg_types: impl AsRef<[TypeId]>, +) -> bool { + let hash_params = calc_fn_params_hash(arg_types.as_ref().iter().cloned()); let hash = combine_hashes(hash_script, hash_params); // First check the global namespace and packages, but skip modules that are standard because diff --git a/src/parser.rs b/src/parser.rs index 348845d1..b3a1a9f1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -108,8 +108,7 @@ impl<'e> ParseState<'e> { /// Return `None` when the variable name is not found in the `stack`. #[inline] #[must_use] - pub fn access_var(&mut self, name: impl AsRef, pos: Position) -> Option { - let name = name.as_ref(); + pub fn access_var(&mut self, name: &str, pos: Position) -> Option { let mut barrier = false; let _pos = pos; @@ -158,9 +157,7 @@ impl<'e> ParseState<'e> { #[cfg(not(feature = "no_module"))] #[inline] #[must_use] - pub fn find_module(&self, name: impl AsRef) -> Option { - let name = name.as_ref(); - + pub fn find_module(&self, name: &str) -> Option { self.modules .iter() .rev() @@ -172,11 +169,7 @@ impl<'e> ParseState<'e> { /// Get an interned identifier, creating one if it is not yet interned. #[inline(always)] #[must_use] - pub fn get_identifier( - &mut self, - prefix: &'static str, - text: impl AsRef + Into + Into, - ) -> Identifier { + pub fn get_identifier(&mut self, prefix: impl AsRef, text: impl AsRef) -> Identifier { self.interned_strings.get(prefix, text).into() } @@ -186,8 +179,8 @@ impl<'e> ParseState<'e> { #[must_use] pub fn get_interned_string( &mut self, - prefix: &'static str, - text: impl AsRef + Into + Into, + prefix: impl AsRef, + text: impl AsRef, ) -> ImmutableString { self.interned_strings.get(prefix, text) } @@ -259,15 +252,15 @@ impl Expr { match self { Self::Variable(_, pos, x) if x.1.is_none() => { let ident = x.2; - let getter = state.get_identifier(crate::engine::FN_GET, ident.as_str()); + let getter = state.get_identifier(crate::engine::FN_GET, &ident); let hash_get = calc_fn_hash(&getter, 1); - let setter = state.get_identifier(crate::engine::FN_SET, ident.as_str()); + let setter = state.get_identifier(crate::engine::FN_SET, &ident); let hash_set = calc_fn_hash(&setter, 2); Self::Property(Box::new(( (getter, hash_get), (setter, hash_set), - (state.get_interned_string("", ident.as_str()), pos), + (state.get_interned_string("", &ident), pos), ))) } _ => self, @@ -1159,7 +1152,7 @@ fn parse_primary( Token::IntegerConstant(x) => Expr::IntegerConstant(x, settings.pos), Token::CharConstant(c) => Expr::CharConstant(c, settings.pos), Token::StringConstant(s) => { - Expr::StringConstant(state.get_identifier("", s).into(), settings.pos) + Expr::StringConstant(state.get_interned_string("", s), settings.pos) } Token::True => Expr::BoolConstant(true, settings.pos), Token::False => Expr::BoolConstant(false, settings.pos), @@ -1998,7 +1991,7 @@ fn parse_binary_op( let hash = calc_fn_hash(&op, 2); let op_base = FnCallExpr { - name: state.get_identifier("", op.as_ref()), + name: state.get_identifier("", op), hashes: FnCallHashes::from_native(hash), ..Default::default() }; @@ -2125,7 +2118,7 @@ fn parse_custom_syntax( && seg.len() > CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT.len() => { inputs.push(Expr::StringConstant( - state.get_identifier("", seg).into(), + state.get_interned_string("", seg), pos, )); break; @@ -2145,7 +2138,7 @@ fn parse_custom_syntax( } CUSTOM_SYNTAX_MARKER_SYMBOL => { let (symbol, pos) = parse_symbol(input)?; - let symbol: ImmutableString = state.get_identifier("", symbol).into(); + let symbol = state.get_interned_string("", symbol); segments.push(symbol.clone()); tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_SYMBOL)); inputs.push(Expr::StringConstant(symbol, pos)); @@ -2168,7 +2161,7 @@ fn parse_custom_syntax( CUSTOM_SYNTAX_MARKER_BOOL => match input.next().expect(NEVER_ENDS) { (b @ Token::True, pos) | (b @ Token::False, pos) => { inputs.push(Expr::BoolConstant(b == Token::True, pos)); - segments.push(state.get_identifier("", b.literal_syntax()).into()); + segments.push(state.get_interned_string("", b.literal_syntax())); tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_BOOL)); } (_, pos) => { @@ -2207,7 +2200,7 @@ fn parse_custom_syntax( }, CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) { (Token::StringConstant(s), pos) => { - let s: ImmutableString = state.get_identifier("", s).into(); + let s = state.get_interned_string("", s); inputs.push(Expr::StringConstant(s.clone(), pos)); segments.push(s); tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_STRING)); @@ -3268,11 +3261,7 @@ fn parse_anon_fn( params.iter().for_each(|p| p.hash(hasher)); body.hash(hasher); let hash = hasher.finish(); - - let fn_name = state.get_identifier( - "", - &(format!("{}{:016x}", crate::engine::FN_ANONYMOUS, hash)), - ); + let fn_name = state.get_identifier("", format!("{}{:016x}", crate::engine::FN_ANONYMOUS, hash)); // Define the function let script = ScriptFnDef { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 2a86927e..b3410186 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -694,11 +694,9 @@ impl Token { /// Reverse lookup a token from a piece of syntax. #[must_use] - pub fn lookup_from_syntax(syntax: impl AsRef) -> Option { + pub fn lookup_from_syntax(syntax: &str) -> Option { use Token::*; - let syntax = syntax.as_ref(); - Some(match syntax { "{" => LeftBrace, "}" => RightBrace, @@ -1355,9 +1353,7 @@ fn is_numeric_digit(c: char) -> bool { #[cfg(feature = "metadata")] #[inline] #[must_use] -pub fn is_doc_comment(comment: impl AsRef) -> bool { - let comment = comment.as_ref(); - +pub fn is_doc_comment(comment: &str) -> bool { (comment.starts_with("///") && !comment.starts_with("////")) || (comment.starts_with("/**") && !comment.starts_with("/***")) } @@ -2018,8 +2014,8 @@ fn get_identifier( /// Is this keyword allowed as a function? #[inline] #[must_use] -pub fn is_keyword_function(name: impl AsRef) -> bool { - match name.as_ref() { +pub fn is_keyword_function(name: &str) -> bool { + match name { KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_DEF_VAR => true, @@ -2051,8 +2047,8 @@ pub fn is_valid_identifier(name: impl Iterator) -> bool { /// Is a text string a valid script-defined function name? #[inline(always)] #[must_use] -pub fn is_valid_function_name(name: impl AsRef) -> bool { - is_valid_identifier(name.as_ref().chars()) +pub fn is_valid_function_name(name: &str) -> bool { + is_valid_identifier(name.chars()) } /// Is a character valid to start an identifier? diff --git a/src/types/interner.rs b/src/types/interner.rs index a9908cd7..b6617829 100644 --- a/src/types/interner.rs +++ b/src/types/interner.rs @@ -8,12 +8,11 @@ use std::prelude::v1::*; /// _(internals)_ A factory of identifiers from text strings. /// Exported under the `internals` feature only. /// -/// Normal identifiers are not interned since they use `SmartString` because most identifiers in -/// Rhai are short and ASCII-based. Thus copying is relatively fast. -/// -/// Property getters and setters are interned separately. +/// Normal identifiers, property getters and setters are interned separately. #[derive(Debug, Clone, Default, Hash)] pub struct StringsInterner { + /// Normal strings. + strings: std::collections::BTreeMap, /// Property getters. #[cfg(not(feature = "no_object"))] getters: std::collections::BTreeMap, @@ -28,6 +27,7 @@ impl StringsInterner { #[must_use] pub fn new() -> Self { Self { + strings: std::collections::BTreeMap::new(), #[cfg(not(feature = "no_object"))] getters: std::collections::BTreeMap::new(), #[cfg(not(feature = "no_object"))] @@ -36,39 +36,37 @@ impl StringsInterner { } /// Get an identifier from a text string and prefix, adding it to the interner if necessary. /// + /// # Prefix + /// + /// Currently recognized prefixes are: + /// + /// * `""` - None (normal string) + /// * `"get$"` - Property getter, not available under `no_object` + /// * `"set$"` - Property setter, not available under `no_object` + /// /// # Panics /// /// Panics if the prefix is not recognized. #[inline] #[must_use] - pub fn get( - &mut self, - prefix: &'static str, - text: impl AsRef + Into + Into, - ) -> ImmutableString { - #[cfg(not(feature = "no_object"))] - { - let (dict, mapper) = match prefix { - "" => return text.into(), - FN_GET => (&mut self.getters, make_getter as fn(&str) -> String), - FN_SET => (&mut self.setters, make_setter as fn(&str) -> String), - _ => unreachable!("unsupported prefix {}", prefix), - }; + pub fn get(&mut self, prefix: impl AsRef, text: impl AsRef) -> ImmutableString { + let (dict, mapper): (_, fn(&str) -> Identifier) = match prefix.as_ref() { + "" => (&mut self.strings, |s| s.into()), - if dict.contains_key(text.as_ref()) { - self.getters.get(text.as_ref()).expect("exists").clone() - } else { - let value: ImmutableString = mapper(text.as_ref()).into(); - let text = text.into(); - dict.insert(text, value.clone()); - value - } - } + #[cfg(not(feature = "no_object"))] + FN_GET => (&mut self.getters, |s| make_getter(s)), + #[cfg(not(feature = "no_object"))] + FN_SET => (&mut self.setters, |s| make_setter(s)), - #[cfg(feature = "no_object")] - match prefix { - "" => return text.into(), - _ => unreachable!("unsupported prefix {}", prefix), + _ => unreachable!("unsupported prefix {}", prefix.as_ref()), + }; + + if dict.contains_key(text.as_ref()) { + dict.get(text.as_ref()).expect("exists").clone() + } else { + let value: ImmutableString = mapper(text.as_ref()).into(); + dict.insert(text.as_ref().into(), value.clone()); + value } } } @@ -76,27 +74,24 @@ impl StringsInterner { impl AddAssign for StringsInterner { #[inline(always)] fn add_assign(&mut self, rhs: Self) { - let _rhs = rhs; - + self.strings.extend(rhs.strings.into_iter()); #[cfg(not(feature = "no_object"))] - { - self.getters.extend(_rhs.getters.into_iter()); - self.setters.extend(_rhs.setters.into_iter()); - } + self.getters.extend(rhs.getters.into_iter()); + #[cfg(not(feature = "no_object"))] + self.setters.extend(rhs.setters.into_iter()); } } impl AddAssign<&Self> for StringsInterner { #[inline(always)] fn add_assign(&mut self, rhs: &Self) { - let _rhs = rhs; - + self.strings + .extend(rhs.strings.iter().map(|(k, v)| (k.clone(), v.clone()))); #[cfg(not(feature = "no_object"))] - { - self.getters - .extend(_rhs.getters.iter().map(|(k, v)| (k.clone(), v.clone()))); - self.setters - .extend(_rhs.setters.iter().map(|(k, v)| (k.clone(), v.clone()))); - } + self.getters + .extend(rhs.getters.iter().map(|(k, v)| (k.clone(), v.clone()))); + #[cfg(not(feature = "no_object"))] + self.setters + .extend(rhs.setters.iter().map(|(k, v)| (k.clone(), v.clone()))); } } From 328f6910b651a5f29be807e4838e167ec6a6a1dd Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 4 Jan 2022 22:16:20 +0800 Subject: [PATCH 82/97] Refactor wasm feature gates. --- src/api/deprecated.rs | 6 ++++-- src/api/files.rs | 7 ++----- src/ast/flags.rs | 8 ++++---- src/engine.rs | 18 +++++++++++++----- src/func/builtin.rs | 12 ++++++++++-- src/func/call.rs | 4 ++-- src/lib.rs | 9 ++++++--- src/module/mod.rs | 24 ++++++++---------------- src/module/resolvers/file.rs | 3 ++- src/module/resolvers/mod.rs | 3 ++- src/packages/arithmetic.rs | 9 ++++++--- src/packages/iter_basic.rs | 2 ++ src/packages/logic.rs | 6 ++++-- src/packages/math_basic.rs | 12 ++++++++---- src/packages/string_basic.rs | 3 ++- src/packages/time_basic.rs | 3 ++- src/serde/ser.rs | 9 ++++++--- src/types/dynamic.rs | 29 ++++++++++++++++++++++------- tests/plugins.rs | 3 ++- tests/plugins_unroll.rs | 3 ++- tests/time.rs | 1 + 21 files changed, 110 insertions(+), 64 deletions(-) diff --git a/src/api/deprecated.rs b/src/api/deprecated.rs index 7320451b..166daf4f 100644 --- a/src/api/deprecated.rs +++ b/src/api/deprecated.rs @@ -20,7 +20,8 @@ impl Engine { /// This method will be removed in the next major version. #[deprecated(since = "1.1.0", note = "use `run_file` instead")] #[cfg(not(feature = "no_std"))] - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] #[inline(always)] pub fn consume_file(&self, path: std::path::PathBuf) -> RhaiResultOf<()> { self.run_file(path) @@ -38,7 +39,8 @@ impl Engine { /// This method will be removed in the next major version. #[deprecated(since = "1.1.0", note = "use `run_file_with_scope` instead")] #[cfg(not(feature = "no_std"))] - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] #[inline(always)] pub fn consume_file_with_scope( &self, diff --git a/src/api/files.rs b/src/api/files.rs index 004daf89..82267f01 100644 --- a/src/api/files.rs +++ b/src/api/files.rs @@ -1,6 +1,7 @@ //! Module that defines the public file-based API of [`Engine`]. #![cfg(not(feature = "no_std"))] -#![cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +#![cfg(not(target_arch = "wasm32"))] +#![cfg(not(target_arch = "wasm64"))] use crate::types::dynamic::Variant; use crate::{Engine, RhaiResultOf, Scope, AST, ERR}; @@ -165,8 +166,6 @@ impl Engine { /// Evaluate a file, returning any error (if any). /// /// Not available under `no_std` or `WASM`. - #[cfg(not(feature = "no_std"))] - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[inline] pub fn run_file(&self, path: std::path::PathBuf) -> RhaiResultOf<()> { Self::read_file(path).and_then(|contents| self.run(&contents)) @@ -180,8 +179,6 @@ impl Engine { /// If not [`OptimizationLevel::None`][crate::OptimizationLevel::None], constants defined within /// the scope are propagated throughout the script _including_ functions. This allows functions /// to be optimized based on dynamic global constants. - #[cfg(not(feature = "no_std"))] - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[inline] pub fn run_file_with_scope( &self, diff --git a/src/ast/flags.rs b/src/ast/flags.rs index 4b5bc19d..0b548135 100644 --- a/src/ast/flags.rs +++ b/src/ast/flags.rs @@ -1,5 +1,9 @@ //! Module defining script options. +use std::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Not, Sub, SubAssign}; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; + /// A type representing the access mode of a function. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum FnAccess { @@ -9,10 +13,6 @@ pub enum FnAccess { Private, } -use std::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Not, Sub, SubAssign}; -#[cfg(feature = "no_std")] -use std::prelude::v1::*; - /// A type that holds a configuration option with bit-flags. /// Exported under the `internals` feature only. #[derive(PartialEq, Eq, Copy, Clone, Hash, Default)] diff --git a/src/engine.rs b/src/engine.rs index 6754b9e9..1f2c1345 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -352,8 +352,13 @@ impl<'a> Target<'a> { /// Convert a shared or reference `Target` into a target with an owned value. #[inline(always)] #[must_use] - pub fn into_owned(self) -> Target<'static> { - self.take_or_clone().into() + pub fn into_owned(self) -> Self { + match self { + Self::RefMut(r) => Self::TempValue(r.clone()), + #[cfg(not(feature = "no_closure"))] + Self::SharedValue { value, .. } => Self::TempValue(value), + _ => self, + } } /// Propagate a changed value back to the original source. /// This has no effect for direct references. @@ -1073,7 +1078,8 @@ pub fn is_anonymous_fn(fn_name: &str) -> bool { #[allow(unused_variables)] fn print_to_stdout(s: &str) { #[cfg(not(feature = "no_std"))] - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] println!("{}", s); } @@ -1082,7 +1088,8 @@ fn print_to_stdout(s: &str) { #[allow(unused_variables)] fn debug_to_stdout(s: &str, source: Option<&str>, pos: Position) { #[cfg(not(feature = "no_std"))] - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] if let Some(source) = source { println!("{}{:?} | {}", source, pos, s); } else if pos.is_none() { @@ -1102,7 +1109,8 @@ impl Engine { #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_std"))] - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] { engine.module_resolver = Some(Box::new(crate::module::resolvers::FileModuleResolver::new())); diff --git a/src/func/builtin.rs b/src/func/builtin.rs index e1320e3d..a84b437c 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -25,7 +25,12 @@ const BUILTIN: &str = "data type was checked"; #[inline] #[must_use] fn is_numeric(type_id: TypeId) -> bool { - let result = type_id == TypeId::of::() + let result = false; + + #[cfg(not(feature = "only_i64"))] + #[cfg(not(feature = "only_i32"))] + let result = result + || type_id == TypeId::of::() || type_id == TypeId::of::() || type_id == TypeId::of::() || type_id == TypeId::of::() @@ -34,7 +39,10 @@ fn is_numeric(type_id: TypeId) -> bool { || type_id == TypeId::of::() || type_id == TypeId::of::(); - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(feature = "only_i64"))] + #[cfg(not(feature = "only_i32"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] let result = result || type_id == TypeId::of::() || type_id == TypeId::of::(); #[cfg(not(feature = "no_float"))] diff --git a/src/func/call.rs b/src/func/call.rs index 3bbe0e74..512cf845 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1142,7 +1142,7 @@ impl Engine { } else { // Turn it into a method call only if the object is not shared and not a simple value is_ref_mut = true; - let obj_ref = target.take_ref().expect("reference"); + let obj_ref = target.take_ref().expect("ref"); args.push(obj_ref); args.extend(arg_values.iter_mut()); } @@ -1217,7 +1217,7 @@ impl Engine { // Turn it into a method call only if the object is not shared and not a simple value let (first, rest) = arg_values.split_first_mut().expect("not empty"); first_arg_value = Some(first); - let obj_ref = target.take_ref().expect("reference"); + let obj_ref = target.take_ref().expect("ref"); args.push(obj_ref); args.extend(rest.iter_mut()); } diff --git a/src/lib.rs b/src/lib.rs index b9210006..f56a18ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,8 @@ //! engine.register_fn("compute", compute_something); //! //! # #[cfg(not(feature = "no_std"))] -//! # #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +//! # #[cfg(not(target_arch = "wasm32"))] +//! # #[cfg(not(target_arch = "wasm64"))] //! // Evaluate the script, expecting a 'bool' result //! let result = engine.eval_file::("my_script.rhai".into())?; //! @@ -366,11 +367,13 @@ compile_error!("`stdweb` cannot be used with `no-std`"); #[cfg(feature = "no_std")] compile_error!("`no_std` cannot be used for WASM target"); -#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_arch = "wasm64"))] #[cfg(feature = "wasm-bindgen")] compile_error!("`wasm-bindgen` cannot be used for non-WASM target"); -#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_arch = "wasm64"))] #[cfg(feature = "stdweb")] compile_error!("`stdweb` cannot be used non-WASM target"); diff --git a/src/module/mod.rs b/src/module/mod.rs index 3842c9ba..68ac6518 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -702,15 +702,11 @@ impl Module { ) -> &mut Self { self.update_fn_metadata(hash_fn, arg_names); - if !comments.as_ref().is_empty() { + let comments = comments.as_ref(); + + if !comments.is_empty() { let f = self.functions.get_mut(&hash_fn).expect("exists"); - f.comments = Some( - comments - .as_ref() - .iter() - .map(|s| s.as_ref().into()) - .collect(), - ); + f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect()); } self @@ -869,15 +865,11 @@ impl Module { ) -> u64 { let hash = self.set_fn(name, namespace, access, arg_names, arg_types, func); - if !comments.as_ref().is_empty() { + let comments = comments.as_ref(); + + if !comments.is_empty() { let f = self.functions.get_mut(&hash).expect("exists"); - f.comments = Some( - comments - .as_ref() - .iter() - .map(|s| s.as_ref().into()) - .collect(), - ); + f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect()); } hash diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index ff6bfa88..6d65a063 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -1,5 +1,6 @@ #![cfg(not(feature = "no_std"))] -#![cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +#![cfg(not(target_arch = "wasm32"))] +#![cfg(not(target_arch = "wasm64"))] use crate::func::native::shared_write_lock; use crate::{ diff --git a/src/module/resolvers/mod.rs b/src/module/resolvers/mod.rs index 33f3d6e8..82d350c8 100644 --- a/src/module/resolvers/mod.rs +++ b/src/module/resolvers/mod.rs @@ -11,7 +11,8 @@ mod stat; pub use collection::ModuleResolversCollection; pub use dummy::DummyModuleResolver; #[cfg(not(feature = "no_std"))] -#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_arch = "wasm64"))] pub use file::FileModuleResolver; pub use stat::StaticModuleResolver; diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index 8c700ed4..b908cb49 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -193,7 +193,8 @@ def_package! { reg_functions!(lib += arith_numbers; i8, u8, i16, u16, i32, u32, u64); reg_functions!(lib += signed_numbers; i8, i16, i32); - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] { reg_functions!(lib += arith_num_128; i128, u128); reg_functions!(lib += signed_num_128; i128); @@ -237,7 +238,8 @@ gen_arithmetic_functions!(arith_numbers => i8, u8, i16, u16, i32, u32, u64); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] -#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_arch = "wasm64"))] gen_arithmetic_functions!(arith_num_128 => i128, u128); gen_signed_functions!(signed_basic => INT); @@ -248,7 +250,8 @@ gen_signed_functions!(signed_numbers => i8, i16, i32); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] -#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_arch = "wasm64"))] gen_signed_functions!(signed_num_128 => i128); #[cfg(not(feature = "no_float"))] diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index bf675eac..6dbd8eaf 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -304,6 +304,7 @@ def_package! { reg_range!(lib | "range" => i8, u8, i16, u16, i32, u32, i64, u64); #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] reg_range!(lib | "range" => i128, u128); } @@ -315,6 +316,7 @@ def_package! { reg_range!(lib | step "range" => i8, u8, i16, u16, i32, u32, i64, u64); #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] reg_range!(lib | step "range" => i128, u128); } diff --git a/src/packages/logic.rs b/src/packages/logic.rs index 860adcda..9d51db0e 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -47,7 +47,8 @@ def_package! { { reg_functions!(lib += numbers; i8, u8, i16, u16, i32, u32, u64); - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] reg_functions!(lib += num_128; i128, u128); } @@ -70,7 +71,8 @@ gen_cmp_functions!(numbers => i8, u8, i16, u16, i32, u32, u64); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] -#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_arch = "wasm64"))] gen_cmp_functions!(num_128 => i128, u128); #[cfg(not(feature = "no_float"))] diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index d664033e..a6dfa939 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -66,7 +66,8 @@ def_package! { { reg_functions!(lib += numbers_to_int::to_int(i8, u8, i16, u16, i32, u32, i64, u64)); - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] reg_functions!(lib += num_128_to_int::to_int(i128, u128)); } @@ -85,7 +86,8 @@ def_package! { { reg_functions!(lib += numbers_to_float::to_float(i8, u8, i16, u16, i32, u32, i64, u32)); - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] reg_functions!(lib += num_128_to_float::to_float(i128, u128)); } } @@ -520,7 +522,8 @@ gen_conversion_as_functions!(numbers_to_float => to_float (i8, u8, i16, u16, i32 #[cfg(not(feature = "no_float"))] #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] -#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_arch = "wasm64"))] gen_conversion_as_functions!(num_128_to_float => to_float (i128, u128) -> FLOAT); gen_conversion_as_functions!(basic_to_int => to_int (char) -> INT); @@ -531,7 +534,8 @@ gen_conversion_as_functions!(numbers_to_int => to_int (i8, u8, i16, u16, i32, u3 #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] -#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_arch = "wasm64"))] gen_conversion_as_functions!(num_128_to_int => to_int (i128, u128) -> INT); #[cfg(feature = "decimal")] diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index 33048147..736fdd95 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -274,7 +274,8 @@ mod number_formatting { to_binary(value) } - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] pub mod num_128 { #[rhai_fn(name = "to_hex")] pub fn u128_to_hex(value: u128) -> ImmutableString { diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index 270b89a7..5f6721bf 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -7,7 +7,8 @@ use crate::{def_package, Dynamic, EvalAltResult, RhaiResult, RhaiResultOf, INT}; #[cfg(not(feature = "no_float"))] use crate::FLOAT; -#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_arch = "wasm64"))] use std::time::{Duration, Instant}; #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] diff --git a/src/serde/ser.rs b/src/serde/ser.rs index a9083243..d85cea90 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -93,7 +93,8 @@ impl Serializer for &mut DynamicSerializer { type SerializeSeq = DynamicSerializer; type SerializeTuple = DynamicSerializer; type SerializeTupleStruct = DynamicSerializer; - #[cfg(not(any(feature = "no_object", feature = "no_index")))] + #[cfg(not(feature = "no_object"))] + #[cfg(not(feature = "no_index"))] type SerializeTupleVariant = TupleVariantSerializer; #[cfg(any(feature = "no_object", feature = "no_index"))] type SerializeTupleVariant = serde::ser::Impossible; @@ -629,13 +630,15 @@ impl SerializeStruct for DynamicSerializer { } } -#[cfg(not(any(feature = "no_object", feature = "no_index")))] +#[cfg(not(feature = "no_object"))] +#[cfg(not(feature = "no_index"))] struct TupleVariantSerializer { variant: &'static str, array: crate::Array, } -#[cfg(not(any(feature = "no_object", feature = "no_index")))] +#[cfg(not(feature = "no_object"))] +#[cfg(not(feature = "no_index"))] impl serde::ser::SerializeTupleVariant for TupleVariantSerializer { type Ok = Dynamic; type Error = RhaiError; diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 1ef19dae..a6dbc5c9 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -15,7 +15,8 @@ use std::{ }; #[cfg(not(feature = "no_std"))] -#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_arch = "wasm64"))] use std::time::Instant; #[cfg(not(feature = "no_std"))] @@ -524,7 +525,8 @@ impl Hash for Dynamic { value_any.downcast_ref::().expect(CHECKED).hash(state); } - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] if type_id == TypeId::of::() { TypeId::of::().hash(state); value_any.downcast_ref::().expect(CHECKED).hash(state); @@ -636,13 +638,20 @@ impl fmt::Display for Dynamic { } #[cfg(not(feature = "no_float"))] + #[cfg(not(feature = "f32_float"))] if _type_id == TypeId::of::() { return fmt::Display::fmt(_value_any.downcast_ref::().expect(CHECKED), f); - } else if _type_id == TypeId::of::() { + } + #[cfg(not(feature = "no_float"))] + #[cfg(feature = "f32_float")] + if _type_id == TypeId::of::() { return fmt::Display::fmt(_value_any.downcast_ref::().expect(CHECKED), f); } - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] if _type_id == TypeId::of::() { return fmt::Display::fmt(_value_any.downcast_ref::().expect(CHECKED), f); } else if _type_id == TypeId::of::() { @@ -735,13 +744,20 @@ impl fmt::Debug for Dynamic { } #[cfg(not(feature = "no_float"))] + #[cfg(not(feature = "f32_float"))] if _type_id == TypeId::of::() { return fmt::Debug::fmt(_value_any.downcast_ref::().expect(CHECKED), f); - } else if _type_id == TypeId::of::() { + } + #[cfg(not(feature = "no_float"))] + #[cfg(feature = "f32_float")] + if _type_id == TypeId::of::() { return fmt::Debug::fmt(_value_any.downcast_ref::().expect(CHECKED), f); } - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] if _type_id == TypeId::of::() { return fmt::Debug::fmt(_value_any.downcast_ref::().expect(CHECKED), f); } else if _type_id == TypeId::of::() { @@ -942,7 +958,6 @@ impl Dynamic { /// /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] - #[cfg(not(feature = "f32_float"))] pub const FLOAT_PI: Self = Self::from_float(FloatConstants::PI); /// A [`Dynamic`] containing π/2. /// diff --git a/tests/plugins.rs b/tests/plugins.rs index 40e2a31a..5d0099f2 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -1,4 +1,5 @@ -#![cfg(not(any(feature = "no_index", feature = "no_module")))] +#![cfg(not(feature = "no_index"))] +#![cfg(not(feature = "no_module"))] use rhai::plugin::*; use rhai::{Engine, EvalAltResult, INT}; diff --git a/tests/plugins_unroll.rs b/tests/plugins_unroll.rs index d94a7282..366f6dcb 100644 --- a/tests/plugins_unroll.rs +++ b/tests/plugins_unroll.rs @@ -1,4 +1,5 @@ -#![cfg(not(any(feature = "no_index", feature = "no_module")))] +#![cfg(not(feature = "no_index"))] +#![cfg(not(feature = "no_module"))] use rhai::plugin::*; use rhai::{Engine, EvalAltResult, Module, INT}; diff --git a/tests/time.rs b/tests/time.rs index b2f0294b..03df04bc 100644 --- a/tests/time.rs +++ b/tests/time.rs @@ -1,5 +1,6 @@ #![cfg(not(feature = "no_std"))] #![cfg(not(target_arch = "wasm32"))] +#![cfg(not(target_arch = "wasm64"))] use rhai::{Engine, EvalAltResult}; From f35c9c1ac44be3439e1a4cf78d18c5192b698cb5 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Jan 2022 12:52:56 +0800 Subject: [PATCH 83/97] Refactor imports. --- src/types/interner.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/types/interner.rs b/src/types/interner.rs index b6617829..bcc6b955 100644 --- a/src/types/interner.rs +++ b/src/types/interner.rs @@ -1,9 +1,7 @@ -#[cfg(not(feature = "no_object"))] -use crate::engine::{make_getter, make_setter, FN_GET, FN_SET}; use crate::{Identifier, ImmutableString}; -use std::ops::AddAssign; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{collections::BTreeMap, ops::AddAssign}; /// _(internals)_ A factory of identifiers from text strings. /// Exported under the `internals` feature only. @@ -12,13 +10,13 @@ use std::prelude::v1::*; #[derive(Debug, Clone, Default, Hash)] pub struct StringsInterner { /// Normal strings. - strings: std::collections::BTreeMap, + strings: BTreeMap, /// Property getters. #[cfg(not(feature = "no_object"))] - getters: std::collections::BTreeMap, + getters: BTreeMap, /// Property setters. #[cfg(not(feature = "no_object"))] - setters: std::collections::BTreeMap, + setters: BTreeMap, } impl StringsInterner { @@ -27,11 +25,11 @@ impl StringsInterner { #[must_use] pub fn new() -> Self { Self { - strings: std::collections::BTreeMap::new(), + strings: BTreeMap::new(), #[cfg(not(feature = "no_object"))] - getters: std::collections::BTreeMap::new(), + getters: BTreeMap::new(), #[cfg(not(feature = "no_object"))] - setters: std::collections::BTreeMap::new(), + setters: BTreeMap::new(), } } /// Get an identifier from a text string and prefix, adding it to the interner if necessary. @@ -54,9 +52,9 @@ impl StringsInterner { "" => (&mut self.strings, |s| s.into()), #[cfg(not(feature = "no_object"))] - FN_GET => (&mut self.getters, |s| make_getter(s)), + crate::engine::FN_GET => (&mut self.getters, crate::engine::make_getter), #[cfg(not(feature = "no_object"))] - FN_SET => (&mut self.setters, |s| make_setter(s)), + crate::engine::FN_SET => (&mut self.setters, crate::engine::make_setter), _ => unreachable!("unsupported prefix {}", prefix.as_ref()), }; From a0531129df26f90071d3a52e6aa5f6385a3650b7 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Jan 2022 13:14:18 +0800 Subject: [PATCH 84/97] Restructure expression dispatch. --- src/engine.rs | 446 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 265 insertions(+), 181 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 1f2c1345..1c995221 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1440,8 +1440,7 @@ impl Engine { } } - self.check_data_size(target.as_ref()) - .map_err(|err| err.fill_position(root.1))?; + self.check_data_size(target.as_ref(), root.1)?; Ok(result) } @@ -1482,8 +1481,7 @@ impl Engine { )?; } - self.check_data_size(target.as_ref()) - .map_err(|err| err.fill_position(root.1))?; + self.check_data_size(target.as_ref(), root.1)?; Ok((Dynamic::UNIT, true)) } @@ -1529,8 +1527,7 @@ impl Engine { ) .map_err(|err| err.fill_position(new_pos))?; } - self.check_data_size(target.as_ref()) - .map_err(|err| err.fill_position(root.1))?; + self.check_data_size(target.as_ref(), root.1)?; Ok((Dynamic::UNIT, true)) } // {xxx:map}.id @@ -1586,8 +1583,7 @@ impl Engine { ) .map_err(|err| err.fill_position(new_pos))?; - self.check_data_size(target.as_ref()) - .map_err(|err| err.fill_position(root.1))?; + self.check_data_size(target.as_ref(), root.1)?; new_val = orig_val; } @@ -1780,8 +1776,7 @@ impl Engine { _ => Err(err), }, )?; - self.check_data_size(target.as_ref()) - .map_err(|err| err.fill_position(root.1))?; + self.check_data_size(target.as_ref(), root.1)?; } Ok((result, may_be_changed)) @@ -1845,7 +1840,9 @@ impl Engine { scope, global, state, lib, this_ptr, rhs, term, chain_type, idx_values, 0, level, )?; - match lhs { + let is_assignment = new_val.is_some(); + + let result = match lhs { // id.??? or id[???] Expr::Variable(_, var_pos, x) => { #[cfg(not(feature = "unchecked"))] @@ -1865,7 +1862,7 @@ impl Engine { .map_err(|err| err.fill_position(op_pos)) } // {expr}.??? = ??? or {expr}[???] = ??? - _ if new_val.is_some() => unreachable!("cannot assign to an expression"), + _ if is_assignment => unreachable!("cannot assign to an expression"), // {expr}.??? or {expr}[???] expr => { let value = self.eval_expr(scope, global, state, lib, this_ptr, expr, level)?; @@ -1875,9 +1872,15 @@ impl Engine { global, state, lib, this_ptr, obj_ptr, root, rhs, term, idx_values, chain_type, level, new_val, ) - .map(|(v, _)| v) + .map(|(v, _)| if is_assignment { Dynamic::UNIT } else { v }) .map_err(|err| err.fill_position(op_pos)) } + }; + + if is_assignment { + result.map(|_| Dynamic::UNIT) + } else { + self.check_return_value(result, expr.position()) } } @@ -2305,8 +2308,23 @@ impl Engine { } } - /// Evaluate an expression. - pub(crate) fn eval_expr( + /// Evaluate a constant expression. + fn eval_constant_expr(expr: &Expr) -> RhaiResult { + Ok(match expr { + Expr::DynamicConstant(x, _) => x.as_ref().clone(), + Expr::IntegerConstant(x, _) => (*x).into(), + #[cfg(not(feature = "no_float"))] + Expr::FloatConstant(x, _) => (*x).into(), + Expr::StringConstant(x, _) => x.clone().into(), + Expr::CharConstant(x, _) => (*x).into(), + Expr::BoolConstant(x, _) => (*x).into(), + Expr::Unit(_) => Dynamic::UNIT, + _ => unreachable!("constant expression expected but gets {:?}", expr), + }) + } + + /// Evaluate a literal expression. + fn eval_literal_expr( &self, scope: &mut Scope, global: &mut GlobalRuntimeState, @@ -2316,49 +2334,13 @@ impl Engine { expr: &Expr, level: usize, ) -> RhaiResult { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, expr.position())?; - - let result = match expr { - Expr::DynamicConstant(x, _) => Ok(x.as_ref().clone()), - Expr::IntegerConstant(x, _) => Ok((*x).into()), - #[cfg(not(feature = "no_float"))] - Expr::FloatConstant(x, _) => Ok((*x).into()), - Expr::StringConstant(x, _) => Ok(x.clone().into()), - Expr::CharConstant(x, _) => Ok((*x).into()), - - Expr::Variable(None, var_pos, x) if x.0.is_none() && x.2 == KEYWORD_THIS => this_ptr - .as_deref() - .cloned() - .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()), - Expr::Variable(_, _, _) => self - .search_namespace(scope, global, state, lib, this_ptr, expr) - .map(|(val, _)| val.take_or_clone()), - - // Statement block - Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), - Expr::Stmt(x) => { - self.eval_stmt_block(scope, global, state, lib, this_ptr, x, true, level) - } - - // lhs[idx_expr] - #[cfg(not(feature = "no_index"))] - Expr::Index(_, _, _) => { - self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None) - } - - // lhs.dot_rhs - #[cfg(not(feature = "no_object"))] - Expr::Dot(_, _, _) => { - self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None) - } - + match expr { // `... ${...} ...` Expr::InterpolatedString(x, pos) => { let mut pos = *pos; let mut result: Dynamic = self.const_empty_string().into(); - x.iter().try_for_each(|expr| { + for expr in x.iter() { let item = self.eval_expr(scope, global, state, lib, this_ptr, expr, level)?; self.eval_op_assignment( @@ -2375,83 +2357,67 @@ impl Engine { pos = expr.position(); - self.check_data_size(&result) - .map_err(|err| err.fill_position(pos)) - })?; + self.check_data_size(&result, pos)?; + } assert!( result.is::(), "interpolated string must be a string" ); - Ok(result) + self.check_return_value(Ok(result), expr.position()) } #[cfg(not(feature = "no_index"))] - Expr::Array(x, _) => Ok(x - .iter() - .try_fold( - crate::Array::with_capacity(x.len()), - |mut arr, item| -> RhaiResultOf<_> { - arr.push( - self.eval_expr(scope, global, state, lib, this_ptr, item, level)? - .flatten(), - ); - Ok(arr) - }, - )? - .into()), + Expr::Array(x, _) => { + let mut arr = Dynamic::from_array(crate::Array::with_capacity(x.len())); + + for item_expr in x.iter() { + arr.write_lock::().expect("`Array`").push( + self.eval_expr(scope, global, state, lib, this_ptr, item_expr, level)? + .flatten(), + ); + + self.check_data_size(&arr, item_expr.position())?; + } + + Ok(arr) + } #[cfg(not(feature = "no_object"))] - Expr::Map(x, _) => Ok(x - .0 - .iter() - .try_fold( - x.1.clone(), - |mut map, (Ident { name: key, .. }, expr)| -> RhaiResultOf<_> { - let value_ref = map.get_mut(key.as_str()).expect("contains all keys"); - *value_ref = self - .eval_expr(scope, global, state, lib, this_ptr, expr, level)? - .flatten(); - Ok(map) - }, - )? - .into()), + Expr::Map(x, _) => { + let mut map = Dynamic::from_map(x.1.clone()); - // Namespace-qualified function call - Expr::FnCall(x, pos) if x.is_qualified() => { - let FnCallExpr { - name, - namespace, - hashes, - args, - constants, - .. - } = x.as_ref(); - let namespace = namespace.as_ref().expect("qualified function call"); - let hash = hashes.native; - self.make_qualified_function_call( - scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, - *pos, level, - ) + for (Ident { name, .. }, value_expr) in x.0.iter() { + *map.write_lock::() + .expect("`Map`") + .get_mut(name.as_str()) + .expect("exists") = self + .eval_expr(scope, global, state, lib, this_ptr, value_expr, level)? + .flatten(); + + self.check_data_size(&map, value_expr.position())?; + } + + Ok(map) } - // Normal function call - Expr::FnCall(x, pos) => { - let FnCallExpr { - name, - capture_parent_scope: capture, - hashes, - args, - constants, - .. - } = x.as_ref(); - self.make_function_call( - scope, global, state, lib, this_ptr, name, args, constants, *hashes, *pos, - *capture, level, - ) - } + _ => unreachable!("literal expression expected but gets {:?}", expr), + } + } + /// Evaluate a simple expression. + fn eval_simple_expr( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + state: &mut EvalState, + lib: &[&Module], + this_ptr: &mut Option<&mut Dynamic>, + expr: &Expr, + level: usize, + ) -> RhaiResult { + match expr { Expr::And(x, _) => { Ok((self .eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level)? @@ -2478,9 +2444,6 @@ impl Engine { .into()) } - Expr::BoolConstant(x, _) => Ok((*x).into()), - Expr::Unit(_) => Ok(Dynamic::UNIT), - Expr::Custom(custom, _) => { let expressions: StaticVec<_> = custom.inputs.iter().map(Into::into).collect(); let key_token = custom.tokens.first().expect("not empty"); @@ -2494,14 +2457,166 @@ impl Engine { this_ptr, level, }; - (custom_def.func)(&mut context, &expressions) + + let result = (custom_def.func)(&mut context, &expressions); + + self.check_return_value(result, expr.position()) + } + + _ => unreachable!("simple expression expected but gets {:?}", expr), + } + } + + /// Evaluate a variable expression. + fn eval_variable_expr( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + state: &mut EvalState, + lib: &[&Module], + this_ptr: &mut Option<&mut Dynamic>, + expr: &Expr, + _level: usize, + ) -> RhaiResult { + let result = match expr { + Expr::Variable(None, var_pos, x) if x.0.is_none() && x.2 == KEYWORD_THIS => this_ptr + .as_deref() + .cloned() + .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()), + + Expr::Variable(_, _, _) => self + .search_namespace(scope, global, state, lib, this_ptr, expr) + .map(|(val, _)| val.take_or_clone()), + + _ => unreachable!("Expr::Variable expected but gets {:?}", expr), + }; + + self.check_return_value(result, expr.position()) + } + + /// Evaluate a function call expression. + fn eval_fn_call_expr( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + state: &mut EvalState, + lib: &[&Module], + this_ptr: &mut Option<&mut Dynamic>, + expr: &FnCallExpr, + pos: Position, + level: usize, + ) -> RhaiResult { + let result = if expr.is_qualified() { + // Qualified function call + let FnCallExpr { + name, + namespace, + hashes, + args, + constants, + .. + } = expr; + let namespace = namespace.as_ref().expect("qualified function call"); + let hash = hashes.native; + + self.make_qualified_function_call( + scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, pos, + level, + ) + } else { + // Normal function call + let FnCallExpr { + name, + capture_parent_scope: capture, + hashes, + args, + constants, + .. + } = expr; + + self.make_function_call( + scope, global, state, lib, this_ptr, name, args, constants, *hashes, pos, *capture, + level, + ) + }; + + self.check_return_value(result, pos) + } + + /// Evaluate an expression. + pub(crate) fn eval_expr( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + state: &mut EvalState, + lib: &[&Module], + this_ptr: &mut Option<&mut Dynamic>, + expr: &Expr, + level: usize, + ) -> RhaiResult { + #[cfg(not(feature = "unchecked"))] + self.inc_operations(&mut global.num_operations, expr.position())?; + + match expr { + Expr::DynamicConstant(_, _) + | Expr::IntegerConstant(_, _) + | Expr::StringConstant(_, _) + | Expr::CharConstant(_, _) + | Expr::BoolConstant(_, _) + | Expr::Unit(_) => Self::eval_constant_expr(expr), + #[cfg(not(feature = "no_float"))] + Expr::FloatConstant(_, _) => Self::eval_constant_expr(expr), + + // Variable + Expr::Variable(_, _, _) => { + self.eval_variable_expr(scope, global, state, lib, this_ptr, expr, level) + } + + // Statement block + Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), + Expr::Stmt(x) => { + self.eval_stmt_block(scope, global, state, lib, this_ptr, x, true, level) + } + + // lhs[idx_expr] + #[cfg(not(feature = "no_index"))] + Expr::Index(_, _, _) => { + self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None) + } + + // lhs.dot_rhs + #[cfg(not(feature = "no_object"))] + Expr::Dot(_, _, _) => { + self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None) + } + + // `... ${...} ...` + Expr::InterpolatedString(_, _) => { + self.eval_literal_expr(scope, global, state, lib, this_ptr, expr, level) + } + + #[cfg(not(feature = "no_index"))] + Expr::Array(_, _) => { + self.eval_literal_expr(scope, global, state, lib, this_ptr, expr, level) + } + + #[cfg(not(feature = "no_object"))] + Expr::Map(_, _) => { + self.eval_literal_expr(scope, global, state, lib, this_ptr, expr, level) + } + + // Function call + Expr::FnCall(x, pos) => { + self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, *pos, level) + } + + // Simple expressions + Expr::And(_, _) | Expr::Or(_, _) | Expr::Custom(_, _) => { + self.eval_simple_expr(scope, global, state, lib, this_ptr, expr, level) } _ => unreachable!("expression cannot be evaluated: {:?}", expr), - }; - - self.check_return_value(result) - .map_err(|err| err.fill_position(expr.position())) + } } /// Evaluate a statements block. @@ -2529,10 +2644,12 @@ impl Engine { state.scope_level += 1; } - let result = statements.iter().try_fold(Dynamic::UNIT, |_, stmt| { + let mut result = Dynamic::UNIT; + + for stmt in statements { let _mods_len = global.num_imported_modules(); - let r = self.eval_stmt( + result = self.eval_stmt( scope, global, state, @@ -2566,9 +2683,7 @@ impl Engine { } } } - - Ok(r) - }); + } // If imports list is modified, pop the functions lookup cache state.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); @@ -2583,7 +2698,7 @@ impl Engine { state.always_search_scope = orig_always_search_scope; } - result + Ok(result) } /// Evaluate an op-assignment statement. @@ -2715,8 +2830,7 @@ impl Engine { .map_err(|err| err.fill_position(rhs_expr.position()))?; if op_info.is_some() { - self.check_data_size(lhs_ptr.as_ref()) - .map_err(|err| err.fill_position(lhs_expr.position()))?; + self.check_data_size(lhs_ptr.as_ref(), lhs_expr.position())?; } Ok(Dynamic::UNIT) @@ -3058,38 +3172,9 @@ impl Engine { Err(ERR::LoopBreak(options.contains(AST_OPTION_BREAK_OUT), *pos).into()) } - // Namespace-qualified function call - Stmt::FnCall(x, pos) if x.is_qualified() => { - let FnCallExpr { - name, - namespace, - hashes, - args, - constants, - .. - } = x.as_ref(); - let namespace = namespace.as_ref().expect("qualified function call"); - let hash = hashes.native; - self.make_qualified_function_call( - scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, - *pos, level, - ) - } - - // Normal function call + // Function call Stmt::FnCall(x, pos) => { - let FnCallExpr { - name, - capture_parent_scope: capture, - hashes, - args, - constants, - .. - } = x.as_ref(); - self.make_function_call( - scope, global, state, lib, this_ptr, name, args, constants, *hashes, *pos, - *capture, level, - ) + self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, *pos, level) } // Try/Catch statement @@ -3329,12 +3414,11 @@ impl Engine { } }; - self.check_return_value(result) - .map_err(|err| err.fill_position(stmt.position())) + self.check_return_value(result, stmt.position()) } /// Check a result to ensure that the data size is within allowable limit. - fn check_return_value(&self, mut result: RhaiResult) -> RhaiResult { + fn check_return_value(&self, mut result: RhaiResult, pos: Position) -> RhaiResult { if let Ok(ref mut r) = result { // Concentrate all empty strings into one instance to save memory if let Dynamic(crate::types::dynamic::Union::Str(s, _, _)) = r { @@ -3347,7 +3431,7 @@ impl Engine { } #[cfg(not(feature = "unchecked"))] - self.check_data_size(&r)?; + self.check_data_size(&r, pos)?; } result @@ -3355,27 +3439,27 @@ impl Engine { #[cfg(feature = "unchecked")] #[inline(always)] - fn check_data_size(&self, _value: &Dynamic) -> RhaiResultOf<()> { + fn check_data_size(&self, _value: &Dynamic, _pos: Position) -> RhaiResultOf<()> { Ok(()) } #[cfg(not(feature = "unchecked"))] - fn check_data_size(&self, value: &Dynamic) -> RhaiResultOf<()> { + fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> { // Recursively calculate the size of a value (especially `Array` and `Map`) - fn calc_size(value: &Dynamic) -> (usize, usize, usize) { + fn calc_size(value: &Dynamic, top: bool) -> (usize, usize, usize) { match value.0 { #[cfg(not(feature = "no_index"))] Union::Array(ref arr, _, _) => { arr.iter() .fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 { Union::Array(_, _, _) => { - let (a, m, s) = calc_size(value); + let (a, m, s) = calc_size(value, false); (arrays + a + 1, maps + m, strings + s) } Union::Blob(ref a, _, _) => (arrays + 1 + a.len(), maps, strings), #[cfg(not(feature = "no_object"))] Union::Map(_, _, _) => { - let (a, m, s) = calc_size(value); + let (a, m, s) = calc_size(value, false); (arrays + a + 1, maps + m, strings + s) } Union::Str(ref s, _, _) => (arrays + 1, maps, strings + s.len()), @@ -3390,13 +3474,13 @@ impl Engine { .fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 { #[cfg(not(feature = "no_index"))] Union::Array(_, _, _) => { - let (a, m, s) = calc_size(value); + let (a, m, s) = calc_size(value, false); (arrays + a, maps + m + 1, strings + s) } #[cfg(not(feature = "no_index"))] Union::Blob(ref a, _, _) => (arrays + a.len(), maps, strings), Union::Map(_, _, _) => { - let (a, m, s) = calc_size(value); + let (a, m, s) = calc_size(value, false); (arrays + a, maps + m + 1, strings + s) } Union::Str(ref s, _, _) => (arrays, maps + 1, strings + s.len()), @@ -3404,6 +3488,10 @@ impl Engine { }) } Union::Str(ref s, _, _) => (0, 0, s.len()), + #[cfg(not(feature = "no_closure"))] + Union::Shared(_, _, _) if !top => { + unreachable!("shared values discovered within data: {}", value) + } _ => (0, 0, 0), } } @@ -3423,16 +3511,14 @@ impl Engine { return Ok(()); } - let (_arr, _map, s) = calc_size(value); + let (_arr, _map, s) = calc_size(value, true); if s > self .limits .max_string_size .map_or(usize::MAX, NonZeroUsize::get) { - return Err( - ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(), - ); + return Err(ERR::ErrorDataTooLarge("Length of string".to_string(), pos).into()); } #[cfg(not(feature = "no_index"))] @@ -3442,7 +3528,7 @@ impl Engine { .max_array_size .map_or(usize::MAX, NonZeroUsize::get) { - return Err(ERR::ErrorDataTooLarge("Size of array".to_string(), Position::NONE).into()); + return Err(ERR::ErrorDataTooLarge("Size of array".to_string(), pos).into()); } #[cfg(not(feature = "no_object"))] @@ -3452,9 +3538,7 @@ impl Engine { .max_map_size .map_or(usize::MAX, NonZeroUsize::get) { - return Err( - ERR::ErrorDataTooLarge("Size of object map".to_string(), Position::NONE).into(), - ); + return Err(ERR::ErrorDataTooLarge("Size of object map".to_string(), pos).into()); } Ok(()) From 97295a66dac258cef3cc1779c30da0ee9365bc32 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Jan 2022 13:24:52 +0800 Subject: [PATCH 85/97] Fix test. --- CHANGELOG.md | 6 ++++++ tests/ops.rs | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 878bdc07..3b2d71ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,12 @@ Breaking changes * `LogicPackage` is removed from `CorePackage`. * Bit-field functions are moved into a new `BitFieldPackage` (used to be in `LogicPackage`) which makes more sense. +Bug fixes +--------- + +* Constructing a literal array or object map now checks for size limits for each item instead of at the very end when it is already too late. +* Non-`INT` integer types are now treated exactly as custom types under `only_i64` and `only_i32`. + New features ------------ diff --git a/tests/ops.rs b/tests/ops.rs index 290c9ada..d50bf517 100644 --- a/tests/ops.rs +++ b/tests/ops.rs @@ -10,8 +10,10 @@ fn test_ops() -> Result<(), Box> { Ok(()) } +#[cfg(not(feature = "only_i32"))] +#[cfg(not(feature = "only_i64"))] #[test] -fn test_ops_numbers() -> Result<(), Box> { +fn test_ops_other_number_types() -> Result<(), Box> { let engine = Engine::new(); let mut scope = Scope::new(); From 2b613fdff3dc5a09ca6720c3164abad1a7aeca93 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Jan 2022 13:40:49 +0800 Subject: [PATCH 86/97] Remove unneeded strings interner for modules. --- src/module/mod.rs | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/module/mod.rs b/src/module/mod.rs index 68ac6518..330ef8a9 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -7,7 +7,6 @@ use crate::func::{ }; use crate::tokenizer::Token; use crate::types::dynamic::Variant; -use crate::types::StringsInterner; use crate::{ calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, Identifier, ImmutableString, NativeCallContext, RhaiResultOf, Shared, StaticVec, @@ -156,8 +155,6 @@ pub struct Module { indexed: bool, /// Does the [`Module`] contain indexed functions that have been exposed to the global namespace? contains_indexed_global_functions: bool, - /// Interned strings. - interner: StringsInterner, } impl Default for Module { @@ -255,7 +252,6 @@ impl Module { all_type_iterators: BTreeMap::new(), indexed: true, contains_indexed_global_functions: false, - interner: StringsInterner::new(), } } @@ -482,11 +478,7 @@ impl Module { let num_params = fn_def.params.len(); let hash_script = crate::calc_fn_hash(&fn_def.name, num_params); #[cfg(feature = "metadata")] - let param_names_and_types = fn_def - .params - .iter() - .map(|v| self.interner.get("", v.as_str()).into()) - .collect(); + let param_names_and_types = fn_def.params.iter().cloned().collect(); self.functions.insert( hash_script, FuncInfo { @@ -498,7 +490,7 @@ impl Module { #[cfg(feature = "metadata")] param_names_and_types, #[cfg(feature = "metadata")] - return_type_name: self.interner.get("", "Dynamic").into(), + return_type_name: "Dynamic".into(), #[cfg(feature = "metadata")] comments: None, func: Into::::into(fn_def).into(), @@ -652,7 +644,7 @@ impl Module { let mut param_names: StaticVec<_> = arg_names .as_ref() .iter() - .map(|name| self.interner.get("", name.as_ref()).into()) + .map(|s| s.as_ref().into()) .collect(); if let Some(f) = self.functions.get_mut(&hash_fn) { @@ -787,7 +779,7 @@ impl Module { let mut names = _arg_names .iter() .flat_map(|&p| p.iter()) - .map(|&arg| self.interner.get("", arg).into()) + .map(|&s| s.into()) .collect::>(); let return_type = if names.len() > arg_types.as_ref().len() { names.pop().expect("exists") @@ -803,7 +795,7 @@ impl Module { self.functions.insert( hash_fn, FuncInfo { - name: self.interner.get("", name).into(), + name: name.as_ref().into(), namespace, access, params: param_types.len(), @@ -1302,7 +1294,6 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; - self.interner += other.interner; self } @@ -1322,7 +1313,6 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; - self.interner += other.interner; self } @@ -1351,7 +1341,6 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; - self.interner += &other.interner; self } @@ -1401,7 +1390,6 @@ impl Module { self.all_type_iterators.clear(); self.indexed = false; self.contains_indexed_global_functions = false; - self.interner += &other.interner; self } From 123e8b1b486ac8c6c57af41af46ac96c7b70e372 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 5 Jan 2022 13:48:25 +0800 Subject: [PATCH 87/97] Improve FnPtr debug display. --- src/types/fn_ptr.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 9b707e39..249e50a0 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -24,7 +24,12 @@ impl fmt::Debug for FnPtr { if !self.is_curried() { write!(f, "Fn({})", self.fn_name()) } else { - f.debug_tuple("Fn").field(&self.0).field(&self.1).finish() + self.1 + .iter() + .fold(f.debug_tuple("Fn").field(&self.0), |f, curry| { + f.field(curry) + }) + .finish() } } } From 7bf517b2423b86ebafb0cb3c2555e0bf5b95751d Mon Sep 17 00:00:00 2001 From: magnus Date: Wed, 5 Jan 2022 16:08:02 +0100 Subject: [PATCH 88/97] Fix documentation --- src/types/dynamic.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index a6dbc5c9..5bed4a5d 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -1474,7 +1474,6 @@ impl Dynamic { /// Casting to a [`Dynamic`] just returns as is, but if it contains a shared value, /// it is cloned into a [`Dynamic`] with a normal value. /// - /// Returns [`None`] if types mismatched. /// /// # Panics or Deadlocks /// From b1b4361d08170b652eaa3c452cf0cc104b834f40 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 6 Jan 2022 10:19:25 +0800 Subject: [PATCH 89/97] Fix documentation. --- src/types/dynamic.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 5bed4a5d..3cb2f857 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -1474,11 +1474,9 @@ impl Dynamic { /// Casting to a [`Dynamic`] just returns as is, but if it contains a shared value, /// it is cloned into a [`Dynamic`] with a normal value. /// - /// /// # Panics or Deadlocks /// - /// Panics if the cast fails (e.g. the type of the actual value is not the - /// same as the specified type). + /// Panics if the cast fails (e.g. the type of the actual value is not the same as the specified type). /// /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). /// Otherwise, this call panics if the data is currently borrowed for write. From bc6bf6c6baf3614c2be1e8585fb79562290d8e1f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 6 Jan 2022 11:07:52 +0800 Subject: [PATCH 90/97] Change expect("exists") to unwrap(). --- src/api/compile.rs | 22 ++++++++++----------- src/api/register.rs | 34 ++++++++++++++++++-------------- src/ast/expr.rs | 11 ++++------- src/ast/stmt.rs | 2 +- src/engine.rs | 33 +++++++++++++++++-------------- src/func/call.rs | 9 ++++----- src/module/mod.rs | 12 ++++++------ src/optimizer.rs | 39 +++++++++++++++++-------------------- src/packages/string_more.rs | 4 ++-- src/parser.rs | 22 +++++++-------------- src/serde/de.rs | 2 +- src/tokenizer.rs | 7 +++---- src/types/error.rs | 2 +- src/types/interner.rs | 2 +- src/types/scope.rs | 8 ++++---- 15 files changed, 99 insertions(+), 110 deletions(-) diff --git a/src/api/compile.rs b/src/api/compile.rs index 5accd0bc..d10ede04 100644 --- a/src/api/compile.rs +++ b/src/api/compile.rs @@ -97,18 +97,16 @@ impl Engine { resolver: &StaticModuleResolver, imports: &mut BTreeSet, ) { - ast.walk( - &mut |path| match path.last().expect("contains current node") { - // Collect all `import` statements with a string constant path - ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _)) - if !resolver.contains_path(s) && !imports.contains(s.as_str()) => - { - imports.insert(s.clone().into()); - true - } - _ => true, - }, - ); + ast.walk(&mut |path| match path.last().unwrap() { + // Collect all `import` statements with a string constant path + ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _)) + if !resolver.contains_path(s) && !imports.contains(s.as_str()) => + { + imports.insert(s.clone().into()); + true + } + _ => true, + }); } let mut ast = self.compile_scripts_with_scope(scope, &[script])?; diff --git a/src/api/register.rs b/src/api/register.rs index 605192a7..71ef20e9 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -4,7 +4,6 @@ use crate::func::{FnCallArgs, RegisterNativeFunction, SendSync}; use crate::types::dynamic::Variant; use crate::{ Engine, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, RhaiResultOf, Shared, - SmartString, }; use std::any::{type_name, TypeId}; #[cfg(feature = "no_std")] @@ -15,13 +14,14 @@ impl Engine { #[inline(always)] #[allow(dead_code)] pub(crate) fn global_namespace(&self) -> &Module { - self.global_modules.first().expect("not empty") + self.global_modules.first().unwrap() } /// Get a mutable reference to the global namespace module /// (which is the first module in `global_modules`). #[inline(always)] pub(crate) fn global_namespace_mut(&mut self) -> &mut Module { - Shared::get_mut(self.global_modules.first_mut().expect("not empty")).expect("not shared") + let module = self.global_modules.first_mut().unwrap(); + Shared::get_mut(module).expect("not shared") } /// Register a custom function with the [`Engine`]. /// @@ -69,18 +69,20 @@ impl Engine { } #[cfg(feature = "metadata")] - let param_type_names: Option> = - Some(param_type_names.iter().map(|ty| ty.as_str()).collect()); + let param_type_names: crate::StaticVec<_> = + param_type_names.iter().map(|ty| ty.as_str()).collect(); + #[cfg(feature = "metadata")] + let param_type_names = Some(param_type_names.as_ref()); #[cfg(not(feature = "metadata"))] - let param_type_names: Option<[&str; 0]> = None; + let param_type_names: Option<&[&str]> = None; self.global_namespace_mut().set_fn( name, FnNamespace::Global, FnAccess::Public, - param_type_names.as_ref().map(<_>::as_ref), - ¶m_types, + param_type_names, + param_types, func.into_callable_function(), ); self @@ -127,18 +129,20 @@ impl Engine { .collect(); #[cfg(feature = "metadata")] - let param_type_names: Option> = - Some(param_type_names.iter().map(|ty| ty.as_str()).collect()); + let param_type_names: crate::StaticVec<_> = + param_type_names.iter().map(|ty| ty.as_str()).collect(); + #[cfg(feature = "metadata")] + let param_type_names = Some(param_type_names.as_ref()); #[cfg(not(feature = "metadata"))] - let param_type_names: Option<[&str; 0]> = None; + let param_type_names: Option<&[&str]> = None; self.global_namespace_mut().set_fn( name, FnNamespace::Global, FnAccess::Public, - param_type_names.as_ref().map(<_>::as_ref), - ¶m_types, + param_type_names, + param_types, func.into_callable_function(), ); self @@ -280,8 +284,8 @@ impl Engine { #[inline(always)] pub fn register_type_with_name_raw( &mut self, - fully_qualified_type_path: impl Into, - name: impl Into, + fully_qualified_type_path: impl Into, + name: impl Into, ) -> &mut Self { // Add the pretty-print type name into the map self.type_names diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 137d5214..f59efa87 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -509,18 +509,15 @@ impl Expr { #[cfg(not(feature = "no_index"))] Self::Array(x, _) if self.is_constant() => { let mut arr = crate::Array::with_capacity(x.len()); - arr.extend( - x.iter() - .map(|v| v.get_literal_value().expect("constant value")), - ); + arr.extend(x.iter().map(|v| v.get_literal_value().unwrap())); Dynamic::from_array(arr) } #[cfg(not(feature = "no_object"))] Self::Map(x, _) if self.is_constant() => { Dynamic::from_map(x.0.iter().fold(x.1.clone(), |mut map, (k, v)| { - let value_ref = map.get_mut(k.name.as_str()).expect("contains all keys"); - *value_ref = v.get_literal_value().expect("constant value"); + let value_ref = map.get_mut(k.name.as_str()).unwrap(); + *value_ref = v.get_literal_value().unwrap(); map })) } @@ -827,7 +824,7 @@ impl Expr { _ => (), } - path.pop().expect("contains current node"); + path.pop().unwrap(); true } diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index cda7bad5..f41d566f 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -660,7 +660,7 @@ impl Stmt { _ => (), } - path.pop().expect("contains current node"); + path.pop().unwrap(); true } diff --git a/src/engine.rs b/src/engine.rs index 1c995221..2c6ea87b 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -849,7 +849,7 @@ impl EvalState { // Push a new function resolution cache if the stack is empty self.push_fn_resolution_cache(); } - self.fn_resolution_caches.last_mut().expect("not empty") + self.fn_resolution_caches.last_mut().unwrap() } /// Push an empty function resolution cache onto the stack and make it current. #[allow(dead_code)] @@ -1200,11 +1200,11 @@ impl Engine { if let Some(index) = index { let offset = global.num_imported_modules() - index.get(); - Some(global.get_shared_module(offset).expect("within range")) + Some(global.get_shared_module(offset).unwrap()) } else { global .find_module(root) - .map(|n| global.get_shared_module(n).expect("valid index")) + .map(|n| global.get_shared_module(n).unwrap()) .or_else(|| self.global_sub_modules.get(root).cloned()) } } @@ -1335,7 +1335,7 @@ impl Engine { level: 0, }; match resolve_var( - expr.get_variable_name(true).expect("`Variable`"), + expr.get_variable_name(true).expect("`Expr::Variable`"), index, &context, ) { @@ -1352,7 +1352,7 @@ impl Engine { scope.len() - index } else { // Find the variable in the scope - let var_name = expr.get_variable_name(true).expect("`Variable`"); + let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); scope .get_index(var_name) .ok_or_else(|| ERR::ErrorVariableNotFound(var_name.to_string(), var_pos))? @@ -1386,7 +1386,7 @@ impl Engine { let _terminate_chaining = terminate_chaining; // Pop the last index value - let idx_val = idx_values.pop().expect("not empty"); + let idx_val = idx_values.pop().unwrap(); match chain_type { #[cfg(not(feature = "no_index"))] @@ -2389,13 +2389,16 @@ impl Engine { let mut map = Dynamic::from_map(x.1.clone()); for (Ident { name, .. }, value_expr) in x.0.iter() { - *map.write_lock::() - .expect("`Map`") - .get_mut(name.as_str()) - .expect("exists") = self + let key = name.as_str(); + let value = self .eval_expr(scope, global, state, lib, this_ptr, value_expr, level)? .flatten(); + *map.write_lock::() + .expect("`Map`") + .get_mut(key) + .unwrap() = value; + self.check_data_size(&map, value_expr.position())?; } @@ -2446,8 +2449,8 @@ impl Engine { Expr::Custom(custom, _) => { let expressions: StaticVec<_> = custom.inputs.iter().map(Into::into).collect(); - let key_token = custom.tokens.first().expect("not empty"); - let custom_def = self.custom_syntax.get(key_token).expect("must match"); + let key_token = custom.tokens.first().unwrap(); + let custom_def = self.custom_syntax.get(key_token).unwrap(); let mut context = EvalContext { engine: self, scope, @@ -2808,7 +2811,7 @@ impl Engine { let (mut lhs_ptr, pos) = self.search_namespace(scope, global, state, lib, this_ptr, lhs_expr)?; - let var_name = lhs_expr.get_variable_name(false).expect("`Variable`"); + let var_name = lhs_expr.get_variable_name(false).expect("`Expr::Variable`"); if !lhs_ptr.is_ref() { return Err(ERR::ErrorAssignmentToConstant(var_name.to_string(), pos).into()); @@ -3212,11 +3215,11 @@ impl Engine { if err_pos.is_none() { // No position info } else { - let line = err_pos.line().expect("line number") as INT; + let line = err_pos.line().unwrap() as INT; let position = if err_pos.is_beginning_of_line() { 0 } else { - err_pos.position().expect("character position") + err_pos.position().unwrap() } as INT; err_map.insert("line".into(), line.into()); err_map.insert("position".into(), position.into()); diff --git a/src/func/call.rs b/src/func/call.rs index 512cf845..e7c10cad 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -274,8 +274,7 @@ impl Engine { } }) } else { - let (first_arg, rest_args) = - args.split_first().expect("two arguments"); + let (first_arg, rest_args) = args.split_first().unwrap(); get_builtin_op_assignment_fn(fn_name, *first_arg, rest_args[0]) .map(|f| FnResolutionCacheEntry { @@ -624,7 +623,7 @@ impl Engine { let result = if _is_method_call { // Method call of script function - map first argument to `this` - let (first_arg, rest_args) = args.split_first_mut().expect("not empty"); + let (first_arg, rest_args) = args.split_first_mut().unwrap(); let level = _level + 1; @@ -1114,7 +1113,7 @@ impl Engine { // avoid cloning the value if curry.is_empty() && !a_expr.is_empty() && a_expr[0].is_variable_access(false) { // func(x, ...) -> x.func(...) - let (first_expr, rest_expr) = a_expr.split_first().expect("not empty"); + let (first_expr, rest_expr) = a_expr.split_first().unwrap(); rest_expr.iter().try_for_each(|expr| { self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants) @@ -1215,7 +1214,7 @@ impl Engine { args.extend(arg_values.iter_mut()); } else { // Turn it into a method call only if the object is not shared and not a simple value - let (first, rest) = arg_values.split_first_mut().expect("not empty"); + let (first, rest) = arg_values.split_first_mut().unwrap(); first_arg_value = Some(first); let obj_ref = target.take_ref().expect("ref"); args.push(obj_ref); diff --git a/src/module/mod.rs b/src/module/mod.rs index 330ef8a9..4de4b678 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -649,7 +649,7 @@ impl Module { if let Some(f) = self.functions.get_mut(&hash_fn) { let (param_names, return_type_name) = if param_names.len() > f.params { - let return_type = param_names.pop().expect("exists"); + let return_type = param_names.pop().unwrap(); (param_names, return_type) } else { (param_names, Default::default()) @@ -697,7 +697,7 @@ impl Module { let comments = comments.as_ref(); if !comments.is_empty() { - let f = self.functions.get_mut(&hash_fn).expect("exists"); + let f = self.functions.get_mut(&hash_fn).unwrap(); f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect()); } @@ -782,7 +782,7 @@ impl Module { .map(|&s| s.into()) .collect::>(); let return_type = if names.len() > arg_types.as_ref().len() { - names.pop().expect("exists") + names.pop().unwrap() } else { Default::default() }; @@ -860,7 +860,7 @@ impl Module { let comments = comments.as_ref(); if !comments.is_empty() { - let f = self.functions.get_mut(&hash).expect("exists"); + let f = self.functions.get_mut(&hash).unwrap(); f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect()); } @@ -1569,11 +1569,11 @@ impl Module { match aliases.len() { 0 => (), 1 => { - let alias = aliases.pop().expect("not empty"); + let alias = aliases.pop().unwrap(); module.set_var(alias, value); } _ => { - let last_alias = aliases.pop().expect("not empty"); + let last_alias = aliases.pop().unwrap(); aliases.into_iter().for_each(|alias| { module.set_var(alias, value.clone()); }); diff --git a/src/optimizer.rs b/src/optimizer.rs index 95c5c2d8..bc6e0aeb 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -326,14 +326,14 @@ fn optimize_stmt_block( && !last_stmt.returns_value() => { state.set_dirty(); - statements.pop().expect(">= 2 elements"); + statements.pop().unwrap(); } // { ...; return val; } -> { ...; val } [.., Stmt::Return(options, ref mut expr, pos)] if reduce_return && !options.contains(AST_OPTION_BREAK_OUT) => { state.set_dirty(); - *statements.last_mut().expect(">= 2 elements") = expr + *statements.last_mut().unwrap() = expr .as_mut() .map_or_else(|| Stmt::Noop(pos), |e| Stmt::Expr(mem::take(e))); } @@ -350,10 +350,9 @@ fn optimize_stmt_block( { state.set_dirty(); if second_last_stmt.returns_value() { - *statements.last_mut().expect(">= 2 elements") = - Stmt::Noop(last_stmt.position()); + *statements.last_mut().unwrap() = Stmt::Noop(last_stmt.position()); } else { - statements.pop().expect(">= 2 elements"); + statements.pop().unwrap(); } } _ => break, @@ -371,7 +370,7 @@ fn optimize_stmt_block( if reduce_return && !options.contains(AST_OPTION_BREAK_OUT) => { state.set_dirty(); - statements.pop().expect(">= 2 elements"); + statements.pop().unwrap(); } // { ...; return pure_val; } -> { ... } [.., Stmt::Return(options, Some(ref expr), _)] @@ -380,11 +379,11 @@ fn optimize_stmt_block( && expr.is_pure() => { state.set_dirty(); - statements.pop().expect(">= 2 elements"); + statements.pop().unwrap(); } [.., ref last_stmt] if is_pure(last_stmt) => { state.set_dirty(); - statements.pop().expect("not empty"); + statements.pop().unwrap(); } _ => break, } @@ -431,10 +430,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b let value = mem::take(&mut x2.args[1]); if let Expr::Stack(slot, pos) = value { - x.2 = Expr::from_dynamic( - mem::take(x2.constants.get_mut(slot).expect("valid slot")), - pos, - ); + x.2 = + Expr::from_dynamic(mem::take(x2.constants.get_mut(slot).unwrap()), pos); } else { x.2 = value; } @@ -502,7 +499,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b // switch const { ... } Stmt::Switch(match_expr, x, pos) if match_expr.is_constant() => { - let value = match_expr.get_literal_value().expect("constant"); + let value = match_expr.get_literal_value().unwrap(); let hasher = &mut get_hasher(); value.hash(hasher); let hash = hasher.finish(); @@ -870,13 +867,13 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { (Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if *i >= 0 && (*i as usize) < s.chars().count() => { // String literal indexing - get the character state.set_dirty(); - *expr = Expr::CharConstant(s.chars().nth(*i as usize).expect("valid index"), *pos); + *expr = Expr::CharConstant(s.chars().nth(*i as usize).unwrap(), *pos); } // string[-int] (Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if *i < 0 && i.checked_abs().map(|n| n as usize <= s.chars().count()).unwrap_or(false) => { // String literal indexing - get the character state.set_dirty(); - *expr = Expr::CharConstant(s.chars().rev().nth(i.abs() as usize - 1).expect("valid index"), *pos); + *expr = Expr::CharConstant(s.chars().rev().nth(i.abs() as usize - 1).unwrap(), *pos); } // var[rhs] (Expr::Variable(_, _, _), rhs) => optimize_expr(rhs, state, true), @@ -920,7 +917,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { #[cfg(not(feature = "no_index"))] Expr::Array(_, _) if expr.is_constant() => { state.set_dirty(); - *expr = Expr::DynamicConstant(expr.get_literal_value().expect("constant").into(), expr.position()); + *expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position()); } // [ items .. ] #[cfg(not(feature = "no_index"))] @@ -929,7 +926,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { #[cfg(not(feature = "no_object"))] Expr::Map(_, _) if expr.is_constant() => { state.set_dirty(); - *expr = Expr::DynamicConstant(expr.get_literal_value().expect("constant").into(), expr.position()); + *expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position()); } // #{ key:value, .. } #[cfg(not(feature = "no_object"))] @@ -997,7 +994,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { => { let arg_values = &mut x.args.iter().map(|e| match e { Expr::Stack(slot, _) => x.constants[*slot].clone(), - _ => e.get_literal_value().expect("constant") + _ => e.get_literal_value().unwrap() }).collect::>(); let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); @@ -1024,7 +1021,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { let lib = &[]; let context = (state.engine, x.name.as_str(), lib).into(); - let (first, second) = arg_values.split_first_mut().expect("not empty"); + let (first, second) = arg_values.split_first_mut().unwrap(); (f)(context, &mut [ first, &mut second[0] ]).ok() }) { state.set_dirty(); @@ -1063,7 +1060,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { if !has_script_fn { let arg_values = &mut x.args.iter().map(|e| match e { Expr::Stack(slot, _) => x.constants[*slot].clone(), - _ => e.get_literal_value().expect("constant") + _ => e.get_literal_value().unwrap() }).collect::>(); let result = match x.name.as_str() { @@ -1098,7 +1095,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { // constant-name Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => { // Replace constant with value - *expr = Expr::from_dynamic(state.find_constant(&x.2).expect("exists").clone(), *pos); + *expr = Expr::from_dynamic(state.find_constant(&x.2).unwrap().clone(), *pos); state.set_dirty(); } diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 7ec7af76..515dda99 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -178,7 +178,7 @@ mod string_functions { #[rhai_fn(name = "to_upper")] pub fn to_upper_char(character: char) -> char { let mut stream = character.to_uppercase(); - let ch = stream.next().expect("not empty"); + let ch = stream.next().unwrap(); if stream.next().is_some() { character } else { @@ -192,7 +192,7 @@ mod string_functions { #[rhai_fn(name = "to_lower")] pub fn to_lower_char(character: char) -> char { let mut stream = character.to_lowercase(); - let ch = stream.next().expect("not empty"); + let ch = stream.next().unwrap(); if stream.next().is_some() { character } else { diff --git a/src/parser.rs b/src/parser.rs index b3a1a9f1..c4c5311f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1304,11 +1304,7 @@ fn parse_primary( Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key) if state.engine.custom_syntax.contains_key(&**key) => { - let (key, syntax) = state - .engine - .custom_syntax - .get_key_value(&**key) - .expect("exists"); + let (key, syntax) = state.engine.custom_syntax.get_key_value(&**key).unwrap(); let (_, pos) = input.next().expect(NEVER_ENDS); let settings2 = settings.level_up(); parse_custom_syntax(input, state, lib, settings2, key, syntax, pos)? @@ -1714,11 +1710,7 @@ fn make_assignment_stmt( Expr::Variable(i, var_pos, ref x) => { let (index, _, name) = x.as_ref(); let index = i.map_or_else( - || { - index - .expect("the long index is `Some` when the short index is `None`") - .get() - }, + || index.expect("either long or short index is `None`").get(), |n| n.get() as usize, ); match state.stack[state.stack.len() - index].1 { @@ -2013,8 +2005,8 @@ fn parse_binary_op( | Token::GreaterThanEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos), Token::Or => { - let rhs = args.pop().expect("two arguments"); - let current_lhs = args.pop().expect("two arguments"); + let rhs = args.pop().unwrap(); + let current_lhs = args.pop().unwrap(); Expr::Or( BinaryExpr { lhs: current_lhs.ensure_bool_expr()?, @@ -2025,8 +2017,8 @@ fn parse_binary_op( ) } Token::And => { - let rhs = args.pop().expect("two arguments"); - let current_lhs = args.pop().expect("two arguments"); + let rhs = args.pop().unwrap(); + let current_lhs = args.pop().unwrap(); Expr::And( BinaryExpr { lhs: current_lhs.ensure_bool_expr()?, @@ -3026,7 +3018,7 @@ fn parse_try_catch( if err_var.is_some() { // Remove the error variable from the stack - state.stack.pop().expect("not empty"); + state.stack.pop().unwrap(); } Ok(Stmt::TryCatch( diff --git a/src/serde/de.rs b/src/serde/de.rs index 0574f877..eb158707 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -565,7 +565,7 @@ where ) -> RhaiResultOf { // Deserialize each value item coming out of the iterator. seed.deserialize(&mut DynamicDeserializer::from_dynamic( - self.values.next().expect("exists"), + self.values.next().unwrap(), )) } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index b3410186..d4827dfe 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1216,7 +1216,7 @@ pub fn parse_string_literal( #[cfg(not(feature = "no_position"))] { - let start_position = start.position().expect("start position"); + let start_position = start.position().unwrap(); skip_whitespace_until = start_position + 1; } } @@ -1237,8 +1237,7 @@ pub fn parse_string_literal( // Whitespace to skip #[cfg(not(feature = "no_position"))] - _ if next_char.is_whitespace() - && pos.position().expect("position") < skip_whitespace_until => {} + _ if next_char.is_whitespace() && pos.position().unwrap() < skip_whitespace_until => {} // All other characters _ => { @@ -1632,7 +1631,7 @@ fn get_next_token_inner( |(err, err_pos)| (Token::LexError(err), err_pos), |(result, _)| { let mut chars = result.chars(); - let first = chars.next().expect("not empty"); + let first = chars.next().unwrap(); if chars.next().is_some() { ( diff --git a/src/types/error.rs b/src/types/error.rs index 90e9d834..93e7f3d1 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -300,7 +300,7 @@ impl EvalAltResult { format!("{:?}", self) .split('(') .next() - .expect("debug format of error is `ErrorXXX(...)`") + .expect("`ErrorXXX(...)`") .into(), ); diff --git a/src/types/interner.rs b/src/types/interner.rs index bcc6b955..05e19fc5 100644 --- a/src/types/interner.rs +++ b/src/types/interner.rs @@ -60,7 +60,7 @@ impl StringsInterner { }; if dict.contains_key(text.as_ref()) { - dict.get(text.as_ref()).expect("exists").clone() + dict.get(text.as_ref()).unwrap().clone() } else { let value: ImmutableString = mapper(text.as_ref()).into(); dict.insert(text.as_ref().into(), value.clone()); diff --git a/src/types/scope.rs b/src/types/scope.rs index 50bf3c69..84c4c446 100644 --- a/src/types/scope.rs +++ b/src/types/scope.rs @@ -406,7 +406,7 @@ impl<'a> Scope<'a> { self.push(name, value); } Some((index, AccessMode::ReadWrite)) => { - let value_ref = self.values.get_mut(index).expect("valid index"); + let value_ref = self.values.get_mut(index).unwrap(); *value_ref = Dynamic::from(value); } } @@ -446,7 +446,7 @@ impl<'a> Scope<'a> { } Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name.as_ref()), Some((index, AccessMode::ReadWrite)) => { - let value_ref = self.values.get_mut(index).expect("valid index"); + let value_ref = self.values.get_mut(index).unwrap(); *value_ref = Dynamic::from(value); } } @@ -492,7 +492,7 @@ impl<'a> Scope<'a> { #[inline] #[must_use] pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic { - self.values.get_mut(index).expect("valid index") + self.values.get_mut(index).unwrap() } /// Add an alias to an entry in the [`Scope`]. /// @@ -502,7 +502,7 @@ impl<'a> Scope<'a> { #[cfg(not(feature = "no_module"))] #[inline] pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self { - let (_, aliases) = self.names.get_mut(index).expect("valid index"); + let (_, aliases) = self.names.get_mut(index).unwrap(); match aliases { None => { let mut list = StaticVec::new_const(); From ed89817d0c05939e86ee8e310eefed25ee74ff21 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 6 Jan 2022 12:31:46 +0800 Subject: [PATCH 91/97] Reverse tail call optimization. --- src/engine.rs | 252 ++++++++++++++++---------------------------------- 1 file changed, 78 insertions(+), 174 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 2c6ea87b..2b0a116f 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2308,23 +2308,49 @@ impl Engine { } } - /// Evaluate a constant expression. - fn eval_constant_expr(expr: &Expr) -> RhaiResult { - Ok(match expr { - Expr::DynamicConstant(x, _) => x.as_ref().clone(), - Expr::IntegerConstant(x, _) => (*x).into(), - #[cfg(not(feature = "no_float"))] - Expr::FloatConstant(x, _) => (*x).into(), - Expr::StringConstant(x, _) => x.clone().into(), - Expr::CharConstant(x, _) => (*x).into(), - Expr::BoolConstant(x, _) => (*x).into(), - Expr::Unit(_) => Dynamic::UNIT, - _ => unreachable!("constant expression expected but gets {:?}", expr), - }) + /// Evaluate a function call expression. + fn eval_fn_call_expr( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + state: &mut EvalState, + lib: &[&Module], + this_ptr: &mut Option<&mut Dynamic>, + expr: &FnCallExpr, + pos: Position, + level: usize, + ) -> RhaiResult { + let FnCallExpr { + name, + namespace, + capture_parent_scope: capture, + hashes, + args, + constants, + .. + } = expr; + + let result = if let Some(namespace) = namespace.as_ref() { + // Qualified function call + let hash = hashes.native; + + self.make_qualified_function_call( + scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, pos, + level, + ) + } else { + // Normal function call + self.make_function_call( + scope, global, state, lib, this_ptr, name, args, constants, *hashes, pos, *capture, + level, + ) + }; + + self.check_return_value(result, pos) } - /// Evaluate a literal expression. - fn eval_literal_expr( + /// Evaluate an expression. + pub(crate) fn eval_expr( &self, scope: &mut Scope, global: &mut GlobalRuntimeState, @@ -2334,7 +2360,31 @@ impl Engine { expr: &Expr, level: usize, ) -> RhaiResult { + #[cfg(not(feature = "unchecked"))] + self.inc_operations(&mut global.num_operations, expr.position())?; + match expr { + // Constants + Expr::DynamicConstant(x, _) => Ok(x.as_ref().clone()), + Expr::IntegerConstant(x, _) => Ok((*x).into()), + #[cfg(not(feature = "no_float"))] + Expr::FloatConstant(x, _) => Ok((*x).into()), + Expr::StringConstant(x, _) => Ok(x.clone().into()), + Expr::CharConstant(x, _) => Ok((*x).into()), + Expr::BoolConstant(x, _) => Ok((*x).into()), + Expr::Unit(_) => Ok(Dynamic::UNIT), + + // `this` + Expr::Variable(None, var_pos, x) if x.0.is_none() && x.2 == KEYWORD_THIS => this_ptr + .as_deref() + .cloned() + .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()), + + // Normal variable + Expr::Variable(_, _, _) => self + .search_namespace(scope, global, state, lib, this_ptr, expr) + .map(|(val, _)| val.take_or_clone()), + // `... ${...} ...` Expr::InterpolatedString(x, pos) => { let mut pos = *pos; @@ -2405,22 +2455,6 @@ impl Engine { Ok(map) } - _ => unreachable!("literal expression expected but gets {:?}", expr), - } - } - - /// Evaluate a simple expression. - fn eval_simple_expr( - &self, - scope: &mut Scope, - global: &mut GlobalRuntimeState, - state: &mut EvalState, - lib: &[&Module], - this_ptr: &mut Option<&mut Dynamic>, - expr: &Expr, - level: usize, - ) -> RhaiResult { - match expr { Expr::And(x, _) => { Ok((self .eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level)? @@ -2466,158 +2500,25 @@ impl Engine { self.check_return_value(result, expr.position()) } - _ => unreachable!("simple expression expected but gets {:?}", expr), - } - } - - /// Evaluate a variable expression. - fn eval_variable_expr( - &self, - scope: &mut Scope, - global: &mut GlobalRuntimeState, - state: &mut EvalState, - lib: &[&Module], - this_ptr: &mut Option<&mut Dynamic>, - expr: &Expr, - _level: usize, - ) -> RhaiResult { - let result = match expr { - Expr::Variable(None, var_pos, x) if x.0.is_none() && x.2 == KEYWORD_THIS => this_ptr - .as_deref() - .cloned() - .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()), - - Expr::Variable(_, _, _) => self - .search_namespace(scope, global, state, lib, this_ptr, expr) - .map(|(val, _)| val.take_or_clone()), - - _ => unreachable!("Expr::Variable expected but gets {:?}", expr), - }; - - self.check_return_value(result, expr.position()) - } - - /// Evaluate a function call expression. - fn eval_fn_call_expr( - &self, - scope: &mut Scope, - global: &mut GlobalRuntimeState, - state: &mut EvalState, - lib: &[&Module], - this_ptr: &mut Option<&mut Dynamic>, - expr: &FnCallExpr, - pos: Position, - level: usize, - ) -> RhaiResult { - let result = if expr.is_qualified() { - // Qualified function call - let FnCallExpr { - name, - namespace, - hashes, - args, - constants, - .. - } = expr; - let namespace = namespace.as_ref().expect("qualified function call"); - let hash = hashes.native; - - self.make_qualified_function_call( - scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, pos, - level, - ) - } else { - // Normal function call - let FnCallExpr { - name, - capture_parent_scope: capture, - hashes, - args, - constants, - .. - } = expr; - - self.make_function_call( - scope, global, state, lib, this_ptr, name, args, constants, *hashes, pos, *capture, - level, - ) - }; - - self.check_return_value(result, pos) - } - - /// Evaluate an expression. - pub(crate) fn eval_expr( - &self, - scope: &mut Scope, - global: &mut GlobalRuntimeState, - state: &mut EvalState, - lib: &[&Module], - this_ptr: &mut Option<&mut Dynamic>, - expr: &Expr, - level: usize, - ) -> RhaiResult { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, expr.position())?; - - match expr { - Expr::DynamicConstant(_, _) - | Expr::IntegerConstant(_, _) - | Expr::StringConstant(_, _) - | Expr::CharConstant(_, _) - | Expr::BoolConstant(_, _) - | Expr::Unit(_) => Self::eval_constant_expr(expr), - #[cfg(not(feature = "no_float"))] - Expr::FloatConstant(_, _) => Self::eval_constant_expr(expr), - - // Variable - Expr::Variable(_, _, _) => { - self.eval_variable_expr(scope, global, state, lib, this_ptr, expr, level) - } - - // Statement block Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), Expr::Stmt(x) => { self.eval_stmt_block(scope, global, state, lib, this_ptr, x, true, level) } - // lhs[idx_expr] #[cfg(not(feature = "no_index"))] Expr::Index(_, _, _) => { self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None) } - // lhs.dot_rhs #[cfg(not(feature = "no_object"))] Expr::Dot(_, _, _) => { self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None) } - // `... ${...} ...` - Expr::InterpolatedString(_, _) => { - self.eval_literal_expr(scope, global, state, lib, this_ptr, expr, level) - } - - #[cfg(not(feature = "no_index"))] - Expr::Array(_, _) => { - self.eval_literal_expr(scope, global, state, lib, this_ptr, expr, level) - } - - #[cfg(not(feature = "no_object"))] - Expr::Map(_, _) => { - self.eval_literal_expr(scope, global, state, lib, this_ptr, expr, level) - } - - // Function call Expr::FnCall(x, pos) => { self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, *pos, level) } - // Simple expressions - Expr::And(_, _) | Expr::Or(_, _) | Expr::Custom(_, _) => { - self.eval_simple_expr(scope, global, state, lib, this_ptr, expr, level) - } - _ => unreachable!("expression cannot be evaluated: {:?}", expr), } } @@ -3422,19 +3323,22 @@ impl Engine { /// Check a result to ensure that the data size is within allowable limit. fn check_return_value(&self, mut result: RhaiResult, pos: Position) -> RhaiResult { - if let Ok(ref mut r) = result { - // Concentrate all empty strings into one instance to save memory - if let Dynamic(crate::types::dynamic::Union::Str(s, _, _)) = r { - if s.is_empty() { - if !s.ptr_eq(&self.empty_string) { - *s = self.const_empty_string(); + match result { + Ok(ref mut r) => { + // Concentrate all empty strings into one instance to save memory + if let Dynamic(Union::Str(s, _, _)) = r { + if s.is_empty() { + if !s.ptr_eq(&self.empty_string) { + *s = self.const_empty_string(); + } + return result; } - return result; } - } - #[cfg(not(feature = "unchecked"))] - self.check_data_size(&r, pos)?; + #[cfg(not(feature = "unchecked"))] + self.check_data_size(&r, pos)?; + } + _ => (), } result From 2e050f8f8807a10d3860dc6b421da0be82f58465 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 6 Jan 2022 13:40:03 +0800 Subject: [PATCH 92/97] Reduce data size checking. --- src/engine.rs | 212 +++++++++++++++++++++++++++++++------------------- 1 file changed, 134 insertions(+), 78 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 2b0a116f..8ac60e4c 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2407,28 +2407,40 @@ impl Engine { pos = expr.position(); - self.check_data_size(&result, pos)?; + result = self.check_return_value(Ok(result), pos)?; } - assert!( - result.is::(), - "interpolated string must be a string" - ); - - self.check_return_value(Ok(result), expr.position()) + Ok(result) } #[cfg(not(feature = "no_index"))] Expr::Array(x, _) => { let mut arr = Dynamic::from_array(crate::Array::with_capacity(x.len())); - for item_expr in x.iter() { - arr.write_lock::().expect("`Array`").push( - self.eval_expr(scope, global, state, lib, this_ptr, item_expr, level)? - .flatten(), - ); + #[cfg(not(feature = "unchecked"))] + let mut sizes = (0, 0, 0); - self.check_data_size(&arr, item_expr.position())?; + for item_expr in x.iter() { + let value = self + .eval_expr(scope, global, state, lib, this_ptr, item_expr, level)? + .flatten(); + + #[cfg(not(feature = "unchecked"))] + let val_sizes = Self::calc_data_sizes(&value, true); + + arr.write_lock::() + .expect("`Array`") + .push(value); + + #[cfg(not(feature = "unchecked"))] + if self.has_data_size_limit() { + sizes = ( + sizes.0 + val_sizes.0, + sizes.1 + val_sizes.1, + sizes.2 + val_sizes.2, + ); + self.raise_err_if_over_data_size_limit(sizes, item_expr.position())?; + } } Ok(arr) @@ -2438,18 +2450,32 @@ impl Engine { Expr::Map(x, _) => { let mut map = Dynamic::from_map(x.1.clone()); + #[cfg(not(feature = "unchecked"))] + let mut sizes = (0, 0, 0); + for (Ident { name, .. }, value_expr) in x.0.iter() { let key = name.as_str(); let value = self .eval_expr(scope, global, state, lib, this_ptr, value_expr, level)? .flatten(); + #[cfg(not(feature = "unchecked"))] + let val_sizes = Self::calc_data_sizes(&value, true); + *map.write_lock::() .expect("`Map`") .get_mut(key) .unwrap() = value; - self.check_data_size(&map, value_expr.position())?; + #[cfg(not(feature = "unchecked"))] + if self.has_data_size_limit() { + sizes = ( + sizes.0 + val_sizes.0, + sizes.1 + val_sizes.1, + sizes.2 + val_sizes.2, + ); + self.raise_err_if_over_data_size_limit(sizes, value_expr.position())?; + } } Ok(map) @@ -3323,6 +3349,8 @@ impl Engine { /// Check a result to ensure that the data size is within allowable limit. fn check_return_value(&self, mut result: RhaiResult, pos: Position) -> RhaiResult { + let _pos = pos; + match result { Ok(ref mut r) => { // Concentrate all empty strings into one instance to save memory @@ -3336,7 +3364,7 @@ impl Engine { } #[cfg(not(feature = "unchecked"))] - self.check_data_size(&r, pos)?; + self.check_data_size(&r, _pos)?; } _ => (), } @@ -3344,81 +3372,89 @@ impl Engine { result } - #[cfg(feature = "unchecked")] - #[inline(always)] - fn check_data_size(&self, _value: &Dynamic, _pos: Position) -> RhaiResultOf<()> { - Ok(()) + /// Recursively calculate the sizes of a value. + /// + /// Sizes returned are `(`[`Array`][crate::Array], [`Map`][crate::Map] and `String)`. + /// + /// # Panics + /// + /// Panics if any interior data is shared (should never happen). + #[cfg(not(feature = "unchecked"))] + fn calc_data_sizes(value: &Dynamic, top: bool) -> (usize, usize, usize) { + match value.0 { + #[cfg(not(feature = "no_index"))] + Union::Array(ref arr, _, _) => { + arr.iter() + .fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 { + Union::Array(_, _, _) => { + let (a, m, s) = Self::calc_data_sizes(value, false); + (arrays + a + 1, maps + m, strings + s) + } + Union::Blob(ref a, _, _) => (arrays + 1 + a.len(), maps, strings), + #[cfg(not(feature = "no_object"))] + Union::Map(_, _, _) => { + let (a, m, s) = Self::calc_data_sizes(value, false); + (arrays + a + 1, maps + m, strings + s) + } + Union::Str(ref s, _, _) => (arrays + 1, maps, strings + s.len()), + _ => (arrays + 1, maps, strings), + }) + } + #[cfg(not(feature = "no_index"))] + Union::Blob(ref arr, _, _) => (arr.len(), 0, 0), + #[cfg(not(feature = "no_object"))] + Union::Map(ref map, _, _) => { + map.values() + .fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 { + #[cfg(not(feature = "no_index"))] + Union::Array(_, _, _) => { + let (a, m, s) = Self::calc_data_sizes(value, false); + (arrays + a, maps + m + 1, strings + s) + } + #[cfg(not(feature = "no_index"))] + Union::Blob(ref a, _, _) => (arrays + a.len(), maps, strings), + Union::Map(_, _, _) => { + let (a, m, s) = Self::calc_data_sizes(value, false); + (arrays + a, maps + m + 1, strings + s) + } + Union::Str(ref s, _, _) => (arrays, maps + 1, strings + s.len()), + _ => (arrays, maps + 1, strings), + }) + } + Union::Str(ref s, _, _) => (0, 0, s.len()), + #[cfg(not(feature = "no_closure"))] + Union::Shared(_, _, _) if !top => { + unreachable!("shared values discovered within data: {}", value) + } + _ => (0, 0, 0), + } } + /// Is there a data size limit set? #[cfg(not(feature = "unchecked"))] - fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> { - // Recursively calculate the size of a value (especially `Array` and `Map`) - fn calc_size(value: &Dynamic, top: bool) -> (usize, usize, usize) { - match value.0 { - #[cfg(not(feature = "no_index"))] - Union::Array(ref arr, _, _) => { - arr.iter() - .fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 { - Union::Array(_, _, _) => { - let (a, m, s) = calc_size(value, false); - (arrays + a + 1, maps + m, strings + s) - } - Union::Blob(ref a, _, _) => (arrays + 1 + a.len(), maps, strings), - #[cfg(not(feature = "no_object"))] - Union::Map(_, _, _) => { - let (a, m, s) = calc_size(value, false); - (arrays + a + 1, maps + m, strings + s) - } - Union::Str(ref s, _, _) => (arrays + 1, maps, strings + s.len()), - _ => (arrays + 1, maps, strings), - }) - } - #[cfg(not(feature = "no_index"))] - Union::Blob(ref arr, _, _) => (arr.len(), 0, 0), - #[cfg(not(feature = "no_object"))] - Union::Map(ref map, _, _) => { - map.values() - .fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 { - #[cfg(not(feature = "no_index"))] - Union::Array(_, _, _) => { - let (a, m, s) = calc_size(value, false); - (arrays + a, maps + m + 1, strings + s) - } - #[cfg(not(feature = "no_index"))] - Union::Blob(ref a, _, _) => (arrays + a.len(), maps, strings), - Union::Map(_, _, _) => { - let (a, m, s) = calc_size(value, false); - (arrays + a, maps + m + 1, strings + s) - } - Union::Str(ref s, _, _) => (arrays, maps + 1, strings + s.len()), - _ => (arrays, maps + 1, strings), - }) - } - Union::Str(ref s, _, _) => (0, 0, s.len()), - #[cfg(not(feature = "no_closure"))] - Union::Shared(_, _, _) if !top => { - unreachable!("shared values discovered within data: {}", value) - } - _ => (0, 0, 0), - } - } + fn has_data_size_limit(&self) -> bool { + let mut _limited = self.limits.max_string_size.is_some(); - // If no data size limits, just return - let mut _has_limit = self.limits.max_string_size.is_some(); #[cfg(not(feature = "no_index"))] { - _has_limit = _has_limit || self.limits.max_array_size.is_some(); + _limited = _limited || self.limits.max_array_size.is_some(); } #[cfg(not(feature = "no_object"))] { - _has_limit = _has_limit || self.limits.max_map_size.is_some(); + _limited = _limited || self.limits.max_map_size.is_some(); } - if !_has_limit { - return Ok(()); - } + _limited + } - let (_arr, _map, s) = calc_size(value, true); + /// Raise an error if any data size exceeds limit. + #[cfg(not(feature = "unchecked"))] + fn raise_err_if_over_data_size_limit( + &self, + sizes: (usize, usize, usize), + pos: Position, + ) -> RhaiResultOf<()> { + let (_arr, _map, s) = sizes; if s > self .limits @@ -3451,6 +3487,26 @@ impl Engine { Ok(()) } + /// Check whether the size of a [`Dynamic`] is within limits. + #[cfg(not(feature = "unchecked"))] + fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> { + // If no data size limits, just return + if !self.has_data_size_limit() { + return Ok(()); + } + + let sizes = Self::calc_data_sizes(value, true); + + self.raise_err_if_over_data_size_limit(sizes, pos) + } + + /// Check whether the size of a [`Dynamic`] is within limits. + #[cfg(feature = "unchecked")] + #[inline(always)] + fn check_data_size(&self, _value: &Dynamic, _pos: Position) -> RhaiResultOf<()> { + Ok(()) + } + /// Check if the number of operations stay within limit. #[cfg(not(feature = "unchecked"))] pub(crate) fn inc_operations( From 0a63e1732bf770af72ae77886ad5160715779252 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 6 Jan 2022 15:28:53 +0800 Subject: [PATCH 93/97] Optimize hot path in Engine::eval_expr. --- src/engine.rs | 63 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 8ac60e4c..68a78def 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1440,6 +1440,7 @@ impl Engine { } } + #[cfg(not(feature = "unchecked"))] self.check_data_size(target.as_ref(), root.1)?; Ok(result) @@ -1481,6 +1482,7 @@ impl Engine { )?; } + #[cfg(not(feature = "unchecked"))] self.check_data_size(target.as_ref(), root.1)?; Ok((Dynamic::UNIT, true)) @@ -1527,6 +1529,7 @@ impl Engine { ) .map_err(|err| err.fill_position(new_pos))?; } + #[cfg(not(feature = "unchecked"))] self.check_data_size(target.as_ref(), root.1)?; Ok((Dynamic::UNIT, true)) } @@ -1583,6 +1586,7 @@ impl Engine { ) .map_err(|err| err.fill_position(new_pos))?; + #[cfg(not(feature = "unchecked"))] self.check_data_size(target.as_ref(), root.1)?; new_val = orig_val; @@ -1776,6 +1780,8 @@ impl Engine { _ => Err(err), }, )?; + + #[cfg(not(feature = "unchecked"))] self.check_data_size(target.as_ref(), root.1)?; } @@ -2360,6 +2366,36 @@ impl Engine { expr: &Expr, level: usize, ) -> RhaiResult { + // Coded this way for better branch prediction. + // Popular branches are lifted out of the `match` statement into their own branches. + + // Function calls should account for a relatively larger portion of expressions because + // binary operators are also function calls. + if let Expr::FnCall(x, pos) = expr { + #[cfg(not(feature = "unchecked"))] + self.inc_operations(&mut global.num_operations, expr.position())?; + + return self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, *pos, level); + } + + // Then variable access. + // We shouldn't do this for too many variants because, soon or later, the added comparisons + // will cost more than the mis-predicted `match` branch. + if let Expr::Variable(index, var_pos, x) = expr { + #[cfg(not(feature = "unchecked"))] + self.inc_operations(&mut global.num_operations, expr.position())?; + + return if index.is_none() && x.0.is_none() && x.2 == KEYWORD_THIS { + this_ptr + .as_deref() + .cloned() + .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()) + } else { + self.search_namespace(scope, global, state, lib, this_ptr, expr) + .map(|(val, _)| val.take_or_clone()) + }; + } + #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, expr.position())?; @@ -2374,17 +2410,6 @@ impl Engine { Expr::BoolConstant(x, _) => Ok((*x).into()), Expr::Unit(_) => Ok(Dynamic::UNIT), - // `this` - Expr::Variable(None, var_pos, x) if x.0.is_none() && x.2 == KEYWORD_THIS => this_ptr - .as_deref() - .cloned() - .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()), - - // Normal variable - Expr::Variable(_, _, _) => self - .search_namespace(scope, global, state, lib, this_ptr, expr) - .map(|(val, _)| val.take_or_clone()), - // `... ${...} ...` Expr::InterpolatedString(x, pos) => { let mut pos = *pos; @@ -2541,10 +2566,6 @@ impl Engine { self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None) } - Expr::FnCall(x, pos) => { - self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, *pos, level) - } - _ => unreachable!("expression cannot be evaluated: {:?}", expr), } } @@ -2759,6 +2780,7 @@ impl Engine { ) .map_err(|err| err.fill_position(rhs_expr.position()))?; + #[cfg(not(feature = "unchecked"))] if op_info.is_some() { self.check_data_size(lhs_ptr.as_ref(), lhs_expr.position())?; } @@ -3489,6 +3511,7 @@ impl Engine { /// Check whether the size of a [`Dynamic`] is within limits. #[cfg(not(feature = "unchecked"))] + #[inline] fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> { // If no data size limits, just return if !self.has_data_size_limit() { @@ -3500,11 +3523,13 @@ impl Engine { self.raise_err_if_over_data_size_limit(sizes, pos) } - /// Check whether the size of a [`Dynamic`] is within limits. - #[cfg(feature = "unchecked")] + /// Raise an error if the size of a [`Dynamic`] is out of limits (if any). + /// + /// Not available under `unchecked`. + #[cfg(not(feature = "unchecked"))] #[inline(always)] - fn check_data_size(&self, _value: &Dynamic, _pos: Position) -> RhaiResultOf<()> { - Ok(()) + pub fn ensure_data_size_within_limits(&self, value: &Dynamic) -> RhaiResultOf<()> { + self.check_data_size(value, Position::NONE) } /// Check if the number of operations stay within limit. From 80edb1200d00260f6091843efd58f83d286707c1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 6 Jan 2022 15:30:17 +0800 Subject: [PATCH 94/97] Fix Array::pad limits. --- CHANGELOG.md | 1 + src/packages/array_basic.rs | 31 +++++++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b2d71ec..f801b08d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Bug fixes * Constructing a literal array or object map now checks for size limits for each item instead of at the very end when it is already too late. * Non-`INT` integer types are now treated exactly as custom types under `only_i64` and `only_i32`. +* Calling `pad` on an array now checks for total size over limit after each item added. New features ------------ diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 6c07f94d..72c40e87 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -3,6 +3,7 @@ use crate::engine::OP_EQUALS; use crate::plugin::*; +use crate::types::dynamic::Union; use crate::{ def_package, Array, Dynamic, ExclusiveRange, FnPtr, InclusiveRange, NativeCallContext, Position, RhaiResultOf, ERR, INT, @@ -81,20 +82,42 @@ pub mod array_functions { len: INT, item: Dynamic, ) -> RhaiResultOf<()> { - if len <= 0 { + if len <= 0 || (len as usize) <= array.len() { return Ok(()); } let _ctx = ctx; + let len = len as usize; // Check if array will be over max size limit #[cfg(not(feature = "unchecked"))] - if _ctx.engine().max_array_size() > 0 && (len as usize) > _ctx.engine().max_array_size() { + if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { return Err(ERR::ErrorDataTooLarge("Size of array".to_string(), Position::NONE).into()); } - if len as usize > array.len() { - array.resize(len as usize, item); + #[cfg(not(feature = "unchecked"))] + let check_sizes = match item.0 { + Union::Array(_, _, _) | Union::Str(_, _, _) => true, + Union::Map(_, _, _) => true, + _ => false, + }; + #[cfg(feature = "unchecked")] + let check_sizes = false; + + if check_sizes { + let arr = mem::take(array); + let mut arr = Dynamic::from_array(arr); + + while array.len() < len { + arr.write_lock::().unwrap().push(item.clone()); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine().ensure_data_size_within_limits(&arr)?; + } + + *array = arr.into_array().unwrap(); + } else { + array.resize(len, item); } Ok(()) From de6cb3650397e13f8fec21b9df347805207ddfdc Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 6 Jan 2022 16:06:31 +0800 Subject: [PATCH 95/97] Lift function calls out of match block in eval_stmt. --- src/engine.rs | 60 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 68a78def..e5211627 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -251,7 +251,7 @@ pub enum Target<'a> { } impl<'a> Target<'a> { - /// Is the `Target` a reference pointing to other data? + /// Is the [`Target`] a reference pointing to other data? #[allow(dead_code)] #[inline] #[must_use] @@ -268,7 +268,7 @@ impl<'a> Target<'a> { | Self::StringChar { .. } => false, } } - /// Is the `Target` a temp value? + /// Is the [`Target`] a temp value? #[inline] #[must_use] pub const fn is_temp_value(&self) -> bool { @@ -284,7 +284,7 @@ impl<'a> Target<'a> { | Self::StringChar { .. } => false, } } - /// Is the `Target` a shared value? + /// Is the [`Target`] a shared value? #[cfg(not(feature = "no_closure"))] #[inline] #[must_use] @@ -301,7 +301,7 @@ impl<'a> Target<'a> { | Self::StringChar { .. } => false, } } - /// Is the `Target` a specific type? + /// Is the [`Target`] a specific type? #[allow(dead_code)] #[inline] #[must_use] @@ -321,7 +321,7 @@ impl<'a> Target<'a> { Self::StringChar { .. } => TypeId::of::() == TypeId::of::(), } } - /// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary. + /// Get the value of the [`Target`] as a [`Dynamic`], cloning a referenced value if necessary. #[inline] #[must_use] pub fn take_or_clone(self) -> Dynamic { @@ -349,7 +349,7 @@ impl<'a> Target<'a> { _ => None, } } - /// Convert a shared or reference `Target` into a target with an owned value. + /// Convert a shared or reference [`Target`] into a target with an owned value. #[inline(always)] #[must_use] pub fn into_owned(self) -> Self { @@ -360,6 +360,25 @@ impl<'a> Target<'a> { _ => self, } } + /// Get the source [`Dynamic`] of the [`Target`]. + #[inline] + #[must_use] + pub fn source(&self) -> &Dynamic { + match self { + Self::RefMut(r) => *r, + #[cfg(not(feature = "no_closure"))] + Self::SharedValue { source, .. } => source, + Self::TempValue(v) => v, + #[cfg(not(feature = "no_index"))] + Self::Bit { source, .. } => source, + #[cfg(not(feature = "no_index"))] + Self::BitField { source, .. } => source, + #[cfg(not(feature = "no_index"))] + Self::BlobByte { source, .. } => source, + #[cfg(not(feature = "no_index"))] + Self::StringChar { source, .. } => source, + } + } /// Propagate a changed value back to the original source. /// This has no effect for direct references. #[inline] @@ -2431,8 +2450,6 @@ impl Engine { .map_err(|err| err.fill_position(expr.position()))?; pos = expr.position(); - - result = self.check_return_value(Ok(result), pos)?; } Ok(result) @@ -2718,7 +2735,9 @@ impl Engine { *target.as_mut() = new_val; } - target.propagate_changed_value() + target.propagate_changed_value()?; + + self.check_data_size(target.source(), Position::NONE) } /// Evaluate a statement. @@ -2738,10 +2757,21 @@ impl Engine { rewind_scope: bool, level: usize, ) -> RhaiResult { + // Coded this way for better branch prediction. + // Popular branches are lifted out of the `match` statement into their own branches. + + // Function calls should account for a relatively larger portion of statements. + if let Stmt::FnCall(x, pos) = stmt { + #[cfg(not(feature = "unchecked"))] + self.inc_operations(&mut global.num_operations, stmt.position())?; + + return self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, *pos, level); + } + #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, stmt.position())?; - let result = match stmt { + match stmt { // No-op Stmt::Noop(_) => Ok(Dynamic::UNIT), @@ -3124,11 +3154,6 @@ impl Engine { Err(ERR::LoopBreak(options.contains(AST_OPTION_BREAK_OUT), *pos).into()) } - // Function call - Stmt::FnCall(x, pos) => { - self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, *pos, level) - } - // Try/Catch statement Stmt::TryCatch(x, _) => { let (try_stmt, err_var, catch_stmt) = x.as_ref(); @@ -3364,9 +3389,9 @@ impl Engine { } Ok(Dynamic::UNIT) } - }; - self.check_return_value(result, stmt.position()) + _ => unreachable!("statement cannot be evaluated: {:?}", stmt), + } } /// Check a result to ensure that the data size is within allowable limit. @@ -3511,7 +3536,6 @@ impl Engine { /// Check whether the size of a [`Dynamic`] is within limits. #[cfg(not(feature = "unchecked"))] - #[inline] fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> { // If no data size limits, just return if !self.has_data_size_limit() { From c75d51ae880267f567cc09adb4d041094d0bdaf1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 6 Jan 2022 22:10:16 +0800 Subject: [PATCH 96/97] Reduce unnecessary data size checking. --- src/engine.rs | 150 ++++++++++++++++-------------------- src/func/call.rs | 19 +++-- src/packages/array_basic.rs | 6 +- tests/data_size.rs | 32 +++++--- 4 files changed, 105 insertions(+), 102 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index e5211627..00dadfeb 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -361,6 +361,7 @@ impl<'a> Target<'a> { } } /// Get the source [`Dynamic`] of the [`Target`]. + #[allow(dead_code)] #[inline] #[must_use] pub fn source(&self) -> &Dynamic { @@ -1459,9 +1460,6 @@ impl Engine { } } - #[cfg(not(feature = "unchecked"))] - self.check_data_size(target.as_ref(), root.1)?; - Ok(result) } // xxx[rhs] op= new_val @@ -1478,6 +1476,8 @@ impl Engine { global, state, lib, op_info, op_pos, obj_ptr, root, new_val, ) .map_err(|err| err.fill_position(new_pos))?; + #[cfg(not(feature = "unchecked"))] + self.check_data_size(obj_ptr, new_pos)?; None } // Can't index - try to call an index setter @@ -1501,9 +1501,6 @@ impl Engine { )?; } - #[cfg(not(feature = "unchecked"))] - self.check_data_size(target.as_ref(), root.1)?; - Ok((Dynamic::UNIT, true)) } // xxx[rhs] @@ -1549,7 +1546,7 @@ impl Engine { .map_err(|err| err.fill_position(new_pos))?; } #[cfg(not(feature = "unchecked"))] - self.check_data_size(target.as_ref(), root.1)?; + self.check_data_size(target.source(), new_pos)?; Ok((Dynamic::UNIT, true)) } // {xxx:map}.id @@ -1605,9 +1602,6 @@ impl Engine { ) .map_err(|err| err.fill_position(new_pos))?; - #[cfg(not(feature = "unchecked"))] - self.check_data_size(target.as_ref(), root.1)?; - new_val = orig_val; } @@ -1799,9 +1793,6 @@ impl Engine { _ => Err(err), }, )?; - - #[cfg(not(feature = "unchecked"))] - self.check_data_size(target.as_ref(), root.1)?; } Ok((result, may_be_changed)) @@ -1867,7 +1858,7 @@ impl Engine { let is_assignment = new_val.is_some(); - let result = match lhs { + match lhs { // id.??? or id[???] Expr::Variable(_, var_pos, x) => { #[cfg(not(feature = "unchecked"))] @@ -1900,12 +1891,6 @@ impl Engine { .map(|(v, _)| if is_assignment { Dynamic::UNIT } else { v }) .map_err(|err| err.fill_position(op_pos)) } - }; - - if is_assignment { - result.map(|_| Dynamic::UNIT) - } else { - self.check_return_value(result, expr.position()) } } @@ -2355,7 +2340,7 @@ impl Engine { .. } = expr; - let result = if let Some(namespace) = namespace.as_ref() { + if let Some(namespace) = namespace.as_ref() { // Qualified function call let hash = hashes.native; @@ -2369,9 +2354,7 @@ impl Engine { scope, global, state, lib, this_ptr, name, args, constants, *hashes, pos, *capture, level, ) - }; - - self.check_return_value(result, pos) + } } /// Evaluate an expression. @@ -2695,49 +2678,45 @@ impl Engine { op, }) = op_info { - { - let mut lock_guard; - let lhs_ptr_inner; + let mut lock_guard; + let lhs_ptr_inner; - #[cfg(not(feature = "no_closure"))] - let target_is_shared = target.is_shared(); - #[cfg(feature = "no_closure")] - let target_is_shared = false; + #[cfg(not(feature = "no_closure"))] + let target_is_shared = target.is_shared(); + #[cfg(feature = "no_closure")] + let target_is_shared = false; - if target_is_shared { - lock_guard = target.write_lock::().expect("`Dynamic`"); - lhs_ptr_inner = &mut *lock_guard; - } else { - lhs_ptr_inner = &mut *target; - } - - let hash = hash_op_assign; - let args = &mut [lhs_ptr_inner, &mut new_val]; - - match self.call_native_fn(global, state, lib, op, hash, args, true, true, op_pos) { - Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, _) if f.starts_with(op)) => - { - // Expand to `var = var op rhs` - let op = &op[..op.len() - 1]; // extract operator without = - - // Run function - let (value, _) = self.call_native_fn( - global, state, lib, op, hash_op, args, true, false, op_pos, - )?; - - *args[0] = value.flatten(); - } - err => return err.map(|_| ()), + if target_is_shared { + lock_guard = target.write_lock::().expect("`Dynamic`"); + lhs_ptr_inner = &mut *lock_guard; + } else { + lhs_ptr_inner = &mut *target; + } + + let hash = hash_op_assign; + let args = &mut [lhs_ptr_inner, &mut new_val]; + + match self.call_native_fn(global, state, lib, op, hash, args, true, true, op_pos) { + Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, _) if f.starts_with(op)) => + { + // Expand to `var = var op rhs` + let op = &op[..op.len() - 1]; // extract operator without = + + // Run function + let (value, _) = self.call_native_fn( + global, state, lib, op, hash_op, args, true, false, op_pos, + )?; + + *args[0] = value.flatten(); } + err => return err.map(|_| ()), } } else { // Normal assignment *target.as_mut() = new_val; } - target.propagate_changed_value()?; - - self.check_data_size(target.source(), Position::NONE) + target.propagate_changed_value() } /// Evaluate a statement. @@ -2768,20 +2747,14 @@ impl Engine { return self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, *pos, level); } - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, stmt.position())?; + // Then assignments. + // We shouldn't do this for too many variants because, soon or later, the added comparisons + // will cost more than the mis-predicted `match` branch. + if let Stmt::Assignment(x, op_pos) = stmt { + #[cfg(not(feature = "unchecked"))] + self.inc_operations(&mut global.num_operations, stmt.position())?; - match stmt { - // No-op - Stmt::Noop(_) => Ok(Dynamic::UNIT), - - // Expression as statement - Stmt::Expr(expr) => Ok(self - .eval_expr(scope, global, state, lib, this_ptr, expr, level)? - .flatten()), - - // var op= rhs - Stmt::Assignment(x, op_pos) if x.0.is_variable_access(false) => { + return if x.0.is_variable_access(false) { let (lhs_expr, op_info, rhs_expr) = x.as_ref(); let rhs_val = self .eval_expr(scope, global, state, lib, this_ptr, rhs_expr, level)? @@ -2810,16 +2783,8 @@ impl Engine { ) .map_err(|err| err.fill_position(rhs_expr.position()))?; - #[cfg(not(feature = "unchecked"))] - if op_info.is_some() { - self.check_data_size(lhs_ptr.as_ref(), lhs_expr.position())?; - } - Ok(Dynamic::UNIT) - } - - // lhs op= rhs - Stmt::Assignment(x, op_pos) => { + } else { let (lhs_expr, op_info, rhs_expr) = x.as_ref(); let rhs_val = self .eval_expr(scope, global, state, lib, this_ptr, rhs_expr, level)? @@ -2850,7 +2815,20 @@ impl Engine { } _ => unreachable!("cannot assign to expression: {:?}", lhs_expr), } - } + }; + } + + #[cfg(not(feature = "unchecked"))] + self.inc_operations(&mut global.num_operations, stmt.position())?; + + match stmt { + // No-op + Stmt::Noop(_) => Ok(Dynamic::UNIT), + + // Expression as statement + Stmt::Expr(expr) => Ok(self + .eval_expr(scope, global, state, lib, this_ptr, expr, level)? + .flatten()), // Block scope Stmt::Block(statements, _) if statements.is_empty() => Ok(Dynamic::UNIT), @@ -3395,7 +3373,7 @@ impl Engine { } /// Check a result to ensure that the data size is within allowable limit. - fn check_return_value(&self, mut result: RhaiResult, pos: Position) -> RhaiResult { + pub(crate) fn check_return_value(&self, mut result: RhaiResult, pos: Position) -> RhaiResult { let _pos = pos; match result { @@ -3470,7 +3448,11 @@ impl Engine { } Union::Str(ref s, _, _) => (0, 0, s.len()), #[cfg(not(feature = "no_closure"))] - Union::Shared(_, _, _) if !top => { + Union::Shared(_, _, _) if top => { + Self::calc_data_sizes(&*value.read_lock::().unwrap(), true) + } + #[cfg(not(feature = "no_closure"))] + Union::Shared(_, _, _) => { unreachable!("shared values discovered within data: {}", value) } _ => (0, 0, 0), @@ -3536,7 +3518,7 @@ impl Engine { /// Check whether the size of a [`Dynamic`] is within limits. #[cfg(not(feature = "unchecked"))] - fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> { + pub(crate) fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> { // If no data size limits, just return if !self.has_data_size_limit() { return Ok(()); diff --git a/src/func/call.rs b/src/func/call.rs index e7c10cad..2af8dc00 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -388,7 +388,14 @@ impl Engine { bk.restore_first_arg(args) } - let result = result.map_err(|err| err.fill_position(pos))?; + // Check the return value (including data sizes) + let result = self.check_return_value(result, pos)?; + + // Check the data size of any `&mut` object, which may be changed. + #[cfg(not(feature = "unchecked"))] + if is_ref_mut && args.len() > 0 { + self.check_data_size(&args[0], pos)?; + } // See if the function match print/debug (which requires special processing) return Ok(match name { @@ -1285,17 +1292,19 @@ impl Engine { Some(f) if f.is_plugin_fn() => { let context = (self, fn_name, module.id(), &*global, lib, pos).into(); - f.get_plugin_fn() + let result = f + .get_plugin_fn() .expect("plugin function") .clone() - .call(context, &mut args) - .map_err(|err| err.fill_position(pos)) + .call(context, &mut args); + self.check_return_value(result, pos) } Some(f) if f.is_native() => { let func = f.get_native_fn().expect("native function"); let context = (self, fn_name, module.id(), &*global, lib, pos).into(); - func(context, &mut args).map_err(|err| err.fill_position(pos)) + let result = func(context, &mut args); + self.check_return_value(result, pos) } Some(f) => unreachable!("unknown function type: {:?}", f), diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 72c40e87..2ad195ac 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -3,7 +3,6 @@ use crate::engine::OP_EQUALS; use crate::plugin::*; -use crate::types::dynamic::Union; use crate::{ def_package, Array, Dynamic, ExclusiveRange, FnPtr, InclusiveRange, NativeCallContext, Position, RhaiResultOf, ERR, INT, @@ -97,8 +96,9 @@ pub mod array_functions { #[cfg(not(feature = "unchecked"))] let check_sizes = match item.0 { - Union::Array(_, _, _) | Union::Str(_, _, _) => true, - Union::Map(_, _, _) => true, + crate::types::dynamic::Union::Array(_, _, _) + | crate::types::dynamic::Union::Str(_, _, _) => true, + crate::types::dynamic::Union::Map(_, _, _) => true, _ => false, }; #[cfg(feature = "unchecked")] diff --git a/tests/data_size.rs b/tests/data_size.rs index 303496f8..a4dc1818 100644 --- a/tests/data_size.rs +++ b/tests/data_size.rs @@ -30,7 +30,7 @@ fn test_max_string_size() -> Result<(), Box> { assert!(matches!( *engine - .eval::( + .run( r#" let x = "hello, "; let y = "world!"; @@ -44,7 +44,7 @@ fn test_max_string_size() -> Result<(), Box> { #[cfg(not(feature = "no_object"))] assert!(matches!( *engine - .eval::( + .run( r#" let x = "hello"; x.pad(100, '!'); @@ -90,7 +90,7 @@ fn test_max_array_size() -> Result<(), Box> { assert!(matches!( *engine - .eval::( + .run( " let x = [1,2,3,4,5,6]; let y = [7,8,9,10,11,12]; @@ -101,10 +101,22 @@ fn test_max_array_size() -> Result<(), Box> { EvalAltResult::ErrorDataTooLarge(_, _) )); + assert!(matches!( + *engine + .run( + " + let x = [ 42 ]; + loop { x[0] = x; } + " + ) + .expect_err("should error"), + EvalAltResult::ErrorDataTooLarge(_, _) + )); + #[cfg(not(feature = "no_object"))] assert!(matches!( *engine - .eval::( + .run( " let x = [1,2,3,4,5,6]; x.pad(100, 42); @@ -117,7 +129,7 @@ fn test_max_array_size() -> Result<(), Box> { assert!(matches!( *engine - .eval::( + .run( " let x = [1,2,3]; [x, x, x, x] @@ -130,7 +142,7 @@ fn test_max_array_size() -> Result<(), Box> { #[cfg(not(feature = "no_object"))] assert!(matches!( *engine - .eval::( + .run( " let x = #{a:1, b:2, c:3}; [x, x, x, x] @@ -142,7 +154,7 @@ fn test_max_array_size() -> Result<(), Box> { assert!(matches!( *engine - .eval::( + .run( " let x = [1]; let y = [x, x]; @@ -220,7 +232,7 @@ fn test_max_map_size() -> Result<(), Box> { assert!(matches!( *engine - .eval::( + .run( " let x = #{a:1,b:2,c:3,d:4,e:5,f:6}; let y = #{g:7,h:8,i:9,j:10,k:11,l:12}; @@ -233,7 +245,7 @@ fn test_max_map_size() -> Result<(), Box> { assert!(matches!( *engine - .eval::( + .run( " let x = #{a:1,b:2,c:3}; #{u:x, v:x, w:x, z:x} @@ -246,7 +258,7 @@ fn test_max_map_size() -> Result<(), Box> { #[cfg(not(feature = "no_index"))] assert!(matches!( *engine - .eval::( + .run( " let x = [1, 2, 3]; #{u:x, v:x, w:x, z:x} From 33bfff372251c5fc648d00dd51a2fa2707278645 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 6 Jan 2022 23:29:11 +0800 Subject: [PATCH 97/97] Fix build. --- src/packages/array_basic.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 2ad195ac..c2ad47af 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -98,6 +98,7 @@ pub mod array_functions { let check_sizes = match item.0 { crate::types::dynamic::Union::Array(_, _, _) | crate::types::dynamic::Union::Str(_, _, _) => true, + #[cfg(not(feature = "no_object"))] crate::types::dynamic::Union::Map(_, _, _) => true, _ => false, };