Add no_inidex feature to disable arrays and indexing.

This commit is contained in:
Stephen Chung 2020-03-10 17:10:33 +08:00
parent f3bcb2a10d
commit 52b5732bcb
10 changed files with 225 additions and 145 deletions

View File

@ -18,6 +18,12 @@ include = [
num-traits = "*"
[features]
default = []
debug_msgs = []
no_stdlib = []
unchecked = []
no_stdlib = []
no_index = []
[profile.release]
lto = "fat"
codegen-units = 1

View File

@ -43,6 +43,7 @@ Optional features
| `debug_msgs` | Print debug messages to stdout (using `println!`) related to function registrations and function calls. |
| `no_stdlib` | Exclude the standard library of utility functions in the build, and only include the minimum necessary functionalities. |
| `unchecked` | Exclude arithmetic checking in the standard library. Beware that a bad script may panic the entire system! |
| `no_index` | Disable arrays and indexing features |
Related
-------
@ -606,7 +607,7 @@ let booly = !true;
Numeric functions
-----------------
The following standard functions (defined in the standard library but excluded if `no_stdlib`) operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
| Function | Description |
| ---------- | ----------------------------------- |
@ -617,7 +618,7 @@ The following standard functions (defined in the standard library but excluded i
Floating-point functions
------------------------
The following standard functions (defined in the standard library but excluded if `no_stdlib`) operate on `f64` only:
The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on `f64` only:
| Category | Functions |
| ---------------- | ------------------------------------------------------------ |
@ -645,7 +646,7 @@ let age = 42;
let record = full_name + ": age " + age;
record == "Bob C. Davis: age 42";
// Strings can be indexed to get a character
// Strings can be indexed to get a character (disabled with the 'no_index' feature)
let c = record[4];
c == 'C';
@ -669,7 +670,7 @@ record[4] = '\x58'; // 0x58 = 'X'
record == "Bob X. Davis: age 42 ❤\n";
```
The following standard functions (defined in the standard library but excluded if `no_stdlib`) operate on strings:
The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on strings:
| Function | Description |
| ---------- | ------------------------------------------------------------------------ |
@ -716,7 +717,7 @@ Arrays
You can create arrays of values, and then access them with numeric indices.
The following functions (defined in the standard library but excluded if `no_stdlib`) operate on arrays:
The following functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on arrays:
| Function | Description |
| ---------- | ------------------------------------------------------------------------------------- |
@ -788,6 +789,8 @@ engine.register_fn("push",
The type of a Rhai array is `rhai::Array`. `type_of()` returns `"array"`.
Arrays are disabled via the [`no_index`](#optional-features) feature.
Comparison operators
--------------------

View File

@ -104,8 +104,7 @@ impl<'e> Engine<'e> {
parse(&mut tokens.peekable(), self.optimize)
}
/// Compile a file into an AST.
pub fn compile_file(&self, filename: &str) -> Result<AST, EvalAltResult> {
fn read_file(filename: &str) -> Result<String, EvalAltResult> {
let mut f = File::open(filename)
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?;
@ -113,19 +112,18 @@ impl<'e> Engine<'e> {
f.read_to_string(&mut contents)
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))
.and_then(|_| self.compile(&contents).map_err(EvalAltResult::ErrorParsing))
.map(|_| contents)
}
/// Compile a file into an AST.
pub fn compile_file(&self, filename: &str) -> Result<AST, EvalAltResult> {
Self::read_file(filename)
.and_then(|contents| self.compile(&contents).map_err(|err| err.into()))
}
/// Evaluate a file.
pub fn eval_file<T: Any + Clone>(&mut self, filename: &str) -> Result<T, EvalAltResult> {
let mut f = File::open(filename)
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?;
let mut contents = String::new();
f.read_to_string(&mut contents)
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))
.and_then(|_| self.eval::<T>(&contents))
Self::read_file(filename).and_then(|contents| self.eval::<T>(&contents))
}
/// Evaluate a string.
@ -164,10 +162,16 @@ impl<'e> Engine<'e> {
retain_functions: bool,
ast: &AST,
) -> Result<T, EvalAltResult> {
fn eval_ast_internal(
engine: &mut Engine,
scope: &mut Scope,
retain_functions: bool,
ast: &AST,
) -> Result<Dynamic, EvalAltResult> {
let AST(statements, functions) = ast;
functions.iter().for_each(|f| {
self.script_functions.insert(
engine.script_functions.insert(
FnSpec {
name: f.name.clone().into(),
args: None,
@ -178,13 +182,16 @@ impl<'e> Engine<'e> {
let result = statements
.iter()
.try_fold(().into_dynamic(), |_, stmt| self.eval_stmt(scope, stmt));
.try_fold(().into_dynamic(), |_, stmt| engine.eval_stmt(scope, stmt));
if !retain_functions {
self.clear_functions();
engine.clear_functions();
}
match result {
result
}
match eval_ast_internal(self, scope, retain_functions, ast) {
Err(EvalAltResult::Return(out, pos)) => out.downcast::<T>().map(|v| *v).map_err(|a| {
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).to_string(),
@ -285,25 +292,14 @@ impl<'e> Engine<'e> {
ast: &AST,
args: A,
) -> Result<T, EvalAltResult> {
let mut arg_values = args.into_vec();
self.call_fn_internal(
name,
ast,
arg_values.iter_mut().map(|v| v.as_mut()).collect(),
)
}
pub(crate) fn call_fn_internal<T: Any + Clone>(
&mut self,
fn call_fn_internal(
engine: &mut Engine,
name: &str,
ast: &AST,
args: FnCallArgs,
) -> Result<T, EvalAltResult> {
let pos = Default::default();
) -> Result<Dynamic, EvalAltResult> {
ast.1.iter().for_each(|f| {
self.script_functions.insert(
engine.script_functions.insert(
FnSpec {
name: f.name.clone().into(),
args: None,
@ -312,18 +308,29 @@ impl<'e> Engine<'e> {
);
});
let result = self.call_fn_raw(name, args, None, pos).and_then(|b| {
let result = engine.call_fn_raw(name, args, None, Position::none());
engine.clear_functions();
result
}
let mut arg_values = args.into_vec();
call_fn_internal(
self,
name,
ast,
arg_values.iter_mut().map(|v| v.as_mut()).collect(),
)
.and_then(|b| {
b.downcast().map(|b| *b).map_err(|a| {
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).into(),
pos,
Position::none(),
)
})
});
self.clear_functions();
result
})
}
/// Override default action of `print` (print to stdout using `println!`)

View File

@ -2,8 +2,12 @@
//! _standard library_ of utility functions.
use crate::any::Any;
use crate::engine::{Array, Engine};
use crate::engine::Engine;
use crate::fn_register::RegisterFn;
#[cfg(not(feature = "no_index"))]
use crate::engine::Array;
use std::{
fmt::{Debug, Display},
i32, i64,
@ -15,14 +19,13 @@ use std::{
use std::ops::{Shl, Shr};
#[cfg(not(feature = "unchecked"))]
use crate::{parser::Position, result::EvalAltResult, RegisterResultFn};
#[cfg(not(feature = "unchecked"))]
use std::convert::TryFrom;
#[cfg(not(feature = "unchecked"))]
use num_traits::{
CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub,
use {
crate::{parser::Position, result::EvalAltResult, RegisterResultFn},
num_traits::{
CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr,
CheckedSub,
},
std::convert::TryFrom,
};
macro_rules! reg_op {
@ -102,6 +105,7 @@ macro_rules! reg_func2y {
}
#[cfg(not(feature = "no_stdlib"))]
#[cfg(not(feature = "no_index"))]
macro_rules! reg_func3 {
($self:expr, $x:expr, $op:expr, $v:ty, $w:ty, $r:ty, $( $y:ty ),*) => (
$(
@ -438,19 +442,23 @@ impl Engine<'_> {
reg_func1!(self, "print", print, String, i8, u8, i16, u16);
reg_func1!(self, "print", print, String, i32, i64, u32, u64);
reg_func1!(self, "print", print, String, f32, f64, bool, char, String);
reg_func1!(self, "print", print_debug, String, Array);
self.register_fn("print", || "".to_string());
self.register_fn("print", |_: ()| "".to_string());
reg_func1!(self, "debug", print_debug, String, i8, u8, i16, u16);
reg_func1!(self, "debug", print_debug, String, i32, i64, u32, u64);
reg_func1!(self, "debug", print_debug, String, f32, f64, bool, char);
#[cfg(not(feature = "no_index"))]
{
reg_func1!(self, "print", print_debug, String, Array);
reg_func1!(self, "debug", print_debug, String, String, Array, ());
// Register array iterator
self.register_iterator::<Array, _>(|a| {
Box::new(a.downcast_ref::<Array>().unwrap().clone().into_iter())
});
}
// Register range function
self.register_iterator::<Range<i64>, _>(|a| {
@ -468,6 +476,7 @@ impl Engine<'_> {
/// Register the built-in library.
#[cfg(not(feature = "no_stdlib"))]
pub(crate) fn register_stdlib(&mut self) {
#[cfg(not(feature = "no_index"))]
use crate::fn_register::RegisterDynamicFn;
// Advanced math functions
@ -547,6 +556,8 @@ impl Engine<'_> {
self.register_fn("to_int", |x: f64| x as i64);
}
#[cfg(not(feature = "no_index"))]
{
// Register array utility functions
fn push<T: Any>(list: &mut Array, item: T) {
list.push(Box::new(item));
@ -583,6 +594,7 @@ impl Engine<'_> {
list.truncate(len as usize);
}
});
}
// Register string concatenate functions
fn prepend<T: Display>(x: T, y: String) -> String {
@ -596,16 +608,20 @@ impl Engine<'_> {
self, "+", append, String, String, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64,
bool, char
);
self.register_fn("+", |x: String, y: Array| format!("{}{:?}", x, y));
self.register_fn("+", |x: String, _: ()| format!("{}", x));
reg_func2y!(
self, "+", prepend, String, String, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64,
bool, char
);
self.register_fn("+", |x: Array, y: String| format!("{:?}{}", x, y));
self.register_fn("+", |_: (), y: String| format!("{}", y));
#[cfg(not(feature = "no_index"))]
{
self.register_fn("+", |x: String, y: Array| format!("{}{:?}", x, y));
self.register_fn("+", |x: Array, y: String| format!("{:?}{}", x, y));
}
// Register string utility functions
self.register_fn("len", |s: &mut String| s.chars().count() as i64);
self.register_fn("contains", |s: &mut String, ch: char| s.contains(ch));

View File

@ -14,6 +14,7 @@ use std::{
};
/// An dynamic array of `Dynamic` values.
#[cfg(not(feature = "no_index"))]
pub type Array = Vec<Dynamic>;
pub type FnCallArgs<'a> = Vec<&'a mut Variant>;
@ -29,6 +30,7 @@ pub(crate) const FUNC_GETTER: &'static str = "get$";
pub(crate) const FUNC_SETTER: &'static str = "set$";
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[cfg(not(feature = "no_index"))]
enum IndexSourceType {
Array,
String,
@ -82,8 +84,9 @@ impl Engine<'_> {
// User-friendly names for built-in types
let type_names = [
(type_name::<String>(), "string"),
(type_name::<Array>(), "array"),
(type_name::<Dynamic>(), "dynamic"),
#[cfg(not(feature = "no_index"))]
(type_name::<Array>(), "array"),
]
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
@ -251,7 +254,7 @@ impl Engine<'_> {
match dot_rhs {
// xxx.fn_name(args)
Expr::FunctionCall(fn_name, args, def_val, pos) => {
let mut args: Array = args
let mut args = args
.iter()
.map(|arg| self.eval_expr(scope, arg))
.collect::<Result<Vec<_>, _>>()?;
@ -271,6 +274,7 @@ impl Engine<'_> {
}
// xxx.idx_lhs[idx_expr]
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, idx_pos) => {
let (expr, _) = match idx_lhs.as_ref() {
// xxx.id[idx_expr]
@ -309,6 +313,7 @@ impl Engine<'_> {
.and_then(|mut v| self.get_dot_val_helper(scope, v.as_mut(), rhs))
}
// xxx.idx_lhs[idx_expr].rhs
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, idx_pos) => {
let (expr, _) = match idx_lhs.as_ref() {
// xxx.id[idx_expr].rhs
@ -371,6 +376,7 @@ impl Engine<'_> {
}
// idx_lhs[idx_expr].???
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, idx_pos) => {
let (src_type, src, idx, mut target) =
self.eval_index_expr(scope, idx_lhs, idx_expr, *idx_pos)?;
@ -414,6 +420,7 @@ impl Engine<'_> {
}
/// Evaluate the value of an index (must evaluate to i64)
#[cfg(not(feature = "no_index"))]
fn eval_index_value(
&mut self,
scope: &mut Scope,
@ -426,6 +433,7 @@ impl Engine<'_> {
}
/// Get the value at the indexed position of a base type
#[cfg(not(feature = "no_index"))]
fn get_indexed_value(
&self,
val: Dynamic,
@ -473,6 +481,7 @@ impl Engine<'_> {
}
/// Evaluate an index expression
#[cfg(not(feature = "no_index"))]
fn eval_index_expr<'a>(
&mut self,
scope: &mut Scope,
@ -505,6 +514,7 @@ impl Engine<'_> {
}
/// Replace a character at an index position in a mutable string
#[cfg(not(feature = "no_index"))]
fn str_replace_char(s: &mut String, idx: usize, new_ch: char) {
let mut chars: Vec<char> = s.chars().collect();
let ch = *chars.get(idx).expect("string index out of bounds");
@ -518,6 +528,7 @@ impl Engine<'_> {
}
/// Update the value at an index position in a variable inside the scope
#[cfg(not(feature = "no_index"))]
fn update_indexed_var_in_scope(
src_type: IndexSourceType,
scope: &mut Scope,
@ -550,6 +561,7 @@ impl Engine<'_> {
}
/// Update the value at an index position
#[cfg(not(feature = "no_index"))]
fn update_indexed_value(
mut target: Dynamic,
idx: usize,
@ -593,6 +605,7 @@ impl Engine<'_> {
// xxx.lhs[idx_expr]
// TODO - Allow chaining of indexing!
#[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, idx_pos) => match lhs.as_ref() {
// xxx.id[idx_expr]
Expr::Identifier(id, pos) => {
@ -636,6 +649,7 @@ impl Engine<'_> {
// xxx.lhs[idx_expr].rhs
// TODO - Allow chaining of indexing!
#[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, idx_pos) => match lhs.as_ref() {
// xxx.id[idx_expr].rhs
Expr::Identifier(id, pos) => {
@ -720,6 +734,7 @@ impl Engine<'_> {
// lhs[idx_expr].???
// TODO - Allow chaining of indexing!
#[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, idx_pos) => {
let (src_type, src, idx, mut target) =
self.eval_index_expr(scope, lhs, idx_expr, *idx_pos)?;
@ -762,6 +777,7 @@ impl Engine<'_> {
}
// lhs[idx_expr]
#[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, idx_pos) => self
.eval_index_expr(scope, lhs, idx_expr, *idx_pos)
.map(|(_, _, _, x)| x),
@ -785,6 +801,7 @@ impl Engine<'_> {
}
// idx_lhs[idx_expr] = rhs
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, idx_pos) => {
let (src_type, src, idx, _) =
self.eval_index_expr(scope, idx_lhs, idx_expr, *idx_pos)?;
@ -818,6 +835,7 @@ impl Engine<'_> {
Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, lhs, rhs),
#[cfg(not(feature = "no_index"))]
Expr::Array(contents, _) => {
let mut arr = Vec::new();
@ -836,7 +854,7 @@ impl Engine<'_> {
let mut args = args
.iter()
.map(|expr| self.eval_expr(scope, expr))
.collect::<Result<Array, _>>()?;
.collect::<Result<Vec<Dynamic>, _>>()?;
self.call_fn_raw(
fn_name,

View File

@ -75,9 +75,12 @@ mod scope;
pub use any::{Any, AnyExt, Dynamic, Variant};
pub use call::FuncArgs;
pub use engine::{Array, Engine};
pub use engine::Engine;
pub use error::{ParseError, ParseErrorType};
pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn};
pub use parser::{Position, AST};
pub use result::EvalAltResult;
pub use scope::Scope;
#[cfg(not(feature = "no_index"))]
pub use engine::Array;

View File

@ -139,11 +139,24 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr {
Box::new(optimize_expr(*rhs, changed)),
pos,
),
Expr::Index(lhs, rhs, pos) => Expr::Index(
Box::new(optimize_expr(*lhs, changed)),
Box::new(optimize_expr(*rhs, changed)),
#[cfg(not(feature = "no_index"))]
Expr::Index(lhs, rhs, pos) => match (*lhs, *rhs) {
(Expr::Array(mut items, _), Expr::IntegerConstant(i, _))
if i >= 0
&& (i as usize) < items.len()
&& !items.iter().any(|x| x.is_constant()) =>
{
// Array where everything is a constant - promote the item
*changed = true;
items.remove(i as usize)
}
(lhs, rhs) => Expr::Index(
Box::new(optimize_expr(lhs, changed)),
Box::new(optimize_expr(rhs, changed)),
pos,
),
},
#[cfg(not(feature = "no_index"))]
Expr::Array(items, pos) => {
let original_len = items.len();

View File

@ -167,7 +167,9 @@ pub enum Expr {
FunctionCall(String, Vec<Expr>, Option<Dynamic>, Position),
Assignment(Box<Expr>, Box<Expr>, Position),
Dot(Box<Expr>, Box<Expr>, Position),
#[cfg(not(feature = "no_index"))]
Index(Box<Expr>, Box<Expr>, Position),
#[cfg(not(feature = "no_index"))]
Array(Vec<Expr>, Position),
And(Box<Expr>, Box<Expr>),
Or(Box<Expr>, Box<Expr>),
@ -186,16 +188,19 @@ impl Expr {
| Expr::StringConstant(_, pos)
| Expr::FunctionCall(_, _, _, pos)
| Expr::Stmt(_, pos)
| Expr::Array(_, pos)
| Expr::True(pos)
| Expr::False(pos)
| Expr::Unit(pos) => *pos,
Expr::Index(e, _, _)
| Expr::Assignment(e, _, _)
| Expr::Dot(e, _, _)
| Expr::And(e, _)
| Expr::Or(e, _) => e.position(),
Expr::Assignment(e, _, _) | Expr::Dot(e, _, _) | Expr::And(e, _) | Expr::Or(e, _) => {
e.position()
}
#[cfg(not(feature = "no_index"))]
Expr::Index(e, _, _) => e.position(),
#[cfg(not(feature = "no_index"))]
Expr::Array(_, pos) => *pos,
}
}
@ -1166,6 +1171,7 @@ fn parse_call_expr<'a>(
}
}
#[cfg(not(feature = "no_index"))]
fn parse_index_expr<'a>(
lhs: Box<Expr>,
input: &mut Peekable<TokenIterator<'a>>,
@ -1260,6 +1266,7 @@ fn parse_ident_expr<'a>(
input.next();
parse_call_expr(id, input, begin)
}
#[cfg(not(feature = "no_index"))]
Some(&(Token::LeftBracket, pos)) => {
input.next();
parse_index_expr(Box::new(Expr::Identifier(id, begin)), input, pos)
@ -1269,6 +1276,7 @@ fn parse_ident_expr<'a>(
}
}
#[cfg(not(feature = "no_index"))]
fn parse_array_expr<'a>(
input: &mut Peekable<TokenIterator<'a>>,
begin: Position,
@ -1320,26 +1328,28 @@ fn parse_primary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pa
let token = input.next();
let mut follow_on = false;
let mut can_be_indexed = false;
#[allow(unused_mut)]
let mut root_expr = match token {
Some((Token::IntegerConstant(x), pos)) => Ok(Expr::IntegerConstant(x, pos)),
Some((Token::FloatConstant(x), pos)) => Ok(Expr::FloatConstant(x, pos)),
Some((Token::CharConstant(c), pos)) => Ok(Expr::CharConstant(c, pos)),
Some((Token::StringConst(s), pos)) => {
follow_on = true;
can_be_indexed = true;
Ok(Expr::StringConstant(s, pos))
}
Some((Token::Identifier(s), pos)) => {
follow_on = true;
can_be_indexed = true;
parse_ident_expr(s, input, pos)
}
Some((Token::LeftParen, pos)) => {
follow_on = true;
can_be_indexed = true;
parse_paren_expr(input, pos)
}
#[cfg(not(feature = "no_index"))]
Some((Token::LeftBracket, pos)) => {
follow_on = true;
can_be_indexed = true;
parse_array_expr(input, pos)
}
Some((Token::True, pos)) => Ok(Expr::True(pos)),
@ -1354,15 +1364,14 @@ fn parse_primary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pa
None => Err(ParseError::new(PERR::InputPastEndOfFile, Position::eof())),
}?;
if !follow_on {
return Ok(root_expr);
}
if can_be_indexed {
// Tail processing all possible indexing
#[cfg(not(feature = "no_index"))]
while let Some(&(Token::LeftBracket, pos)) = input.peek() {
input.next();
root_expr = parse_index_expr(Box::new(root_expr), input, pos)?;
}
}
Ok(root_expr)
}
@ -1408,6 +1417,7 @@ fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, ParseEr
match expr {
Expr::Identifier(_, pos) => (true, *pos),
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() {
Expr::Identifier(_, _) => (true, idx_lhs.position()),
_ => (false, idx_lhs.position()),
@ -1415,10 +1425,13 @@ fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, ParseEr
Expr::Dot(dot_lhs, dot_rhs, _) => match dot_lhs.as_ref() {
Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs),
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() {
Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs),
_ => (false, idx_lhs.position()),
},
_ => (false, dot_lhs.position()),
},

View File

@ -173,6 +173,12 @@ impl From<ParseError> for EvalAltResult {
}
}
impl<T: AsRef<str>> From<T> for EvalAltResult {
fn from(err: T) -> Self {
Self::ErrorRuntime(err.as_ref().to_string(), Position::none())
}
}
impl EvalAltResult {
pub fn position(&self) -> Position {
match self {
@ -225,9 +231,3 @@ impl EvalAltResult {
}
}
}
impl<T: AsRef<str>> From<T> for EvalAltResult {
fn from(err: T) -> Self {
Self::ErrorRuntime(err.as_ref().to_string(), Position::none())
}
}

View File

@ -93,6 +93,7 @@ impl<'a> Scope<'a> {
}
/// Get a mutable reference to a variable in the Scope and downcast it to a specific type
#[cfg(not(feature = "no_index"))]
pub(crate) fn get_mut_by_type<T: Any + Clone>(&mut self, key: &str, index: usize) -> &mut T {
self.get_mut(key, index)
.downcast_mut::<T>()