Avoid string copying.
This commit is contained in:
parent
ea82ee81d6
commit
024133ae2d
@ -46,22 +46,22 @@ type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Engine<'a> {
|
||||
pub struct Engine<'e> {
|
||||
/// A hashmap containing all compiled functions known to the engine
|
||||
pub(crate) external_functions: HashMap<FnSpec<'a>, Arc<FnIntExt>>,
|
||||
pub(crate) external_functions: HashMap<FnSpec<'e>, Arc<FnIntExt<'e>>>,
|
||||
/// A hashmap containing all script-defined functions
|
||||
pub(crate) script_functions: HashMap<FnSpec<'a>, Arc<FnIntExt>>,
|
||||
pub(crate) script_functions: HashMap<FnSpec<'e>, Arc<FnIntExt<'e>>>,
|
||||
/// A hashmap containing all iterators known to the engine
|
||||
pub(crate) type_iterators: HashMap<TypeId, Arc<IteratorFn>>,
|
||||
pub(crate) type_names: HashMap<String, String>,
|
||||
|
||||
pub(crate) on_print: Box<dyn FnMut(&str) + 'a>,
|
||||
pub(crate) on_debug: Box<dyn FnMut(&str) + 'a>,
|
||||
pub(crate) on_print: Box<dyn FnMut(&str) + 'e>,
|
||||
pub(crate) on_debug: Box<dyn FnMut(&str) + 'e>,
|
||||
}
|
||||
|
||||
pub enum FnIntExt {
|
||||
pub enum FnIntExt<'a> {
|
||||
Ext(Box<FnAny>),
|
||||
Int(FnDef),
|
||||
Int(FnDef<'a>),
|
||||
}
|
||||
|
||||
pub type FnAny = dyn Fn(FnCallArgs, Position) -> Result<Dynamic, EvalAltResult>;
|
||||
@ -104,27 +104,27 @@ impl Engine<'_> {
|
||||
|
||||
if let Some(f) = fn_def {
|
||||
match *f {
|
||||
FnIntExt::Ext(ref f) => {
|
||||
let r = f(args, pos)?;
|
||||
FnIntExt::Ext(ref func) => {
|
||||
let result = func(args, pos)?;
|
||||
|
||||
let callback = match spec.name.as_ref() {
|
||||
KEYWORD_PRINT => self.on_print.as_mut(),
|
||||
KEYWORD_DEBUG => self.on_debug.as_mut(),
|
||||
_ => return Ok(r),
|
||||
_ => return Ok(result),
|
||||
};
|
||||
|
||||
Ok(callback(
|
||||
&r.downcast::<String>()
|
||||
.map(|s| *s)
|
||||
.unwrap_or("error: not a string".into()),
|
||||
)
|
||||
.into_dynamic())
|
||||
let val = &result
|
||||
.downcast::<String>()
|
||||
.map(|s| *s)
|
||||
.unwrap_or("error: not a string".into());
|
||||
|
||||
Ok(callback(val).into_dynamic())
|
||||
}
|
||||
FnIntExt::Int(ref f) => {
|
||||
if f.params.len() != args.len() {
|
||||
FnIntExt::Int(ref func) => {
|
||||
if func.params.len() != args.len() {
|
||||
return Err(EvalAltResult::ErrorFunctionArgsMismatch(
|
||||
spec.name.into(),
|
||||
f.params.len(),
|
||||
func.params.len(),
|
||||
args.len(),
|
||||
pos,
|
||||
));
|
||||
@ -133,13 +133,13 @@ impl Engine<'_> {
|
||||
let mut scope = Scope::new();
|
||||
|
||||
scope.extend(
|
||||
f.params
|
||||
func.params
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|s| s.clone())
|
||||
.zip(args.iter().map(|x| (*x).into_dynamic())),
|
||||
);
|
||||
|
||||
match self.eval_stmt(&mut scope, &*f.body) {
|
||||
match self.eval_stmt(&mut scope, &*func.body) {
|
||||
Err(EvalAltResult::Return(x, _)) => Ok(x),
|
||||
other => other,
|
||||
}
|
||||
@ -319,12 +319,12 @@ impl Engine<'_> {
|
||||
}
|
||||
|
||||
/// Evaluate an index expression
|
||||
fn eval_index_expr(
|
||||
fn eval_index_expr<'a>(
|
||||
&mut self,
|
||||
scope: &mut Scope,
|
||||
lhs: &Expr,
|
||||
lhs: &'a Expr,
|
||||
idx_expr: &Expr,
|
||||
) -> Result<(IndexSourceType, Option<(String, usize)>, usize, Dynamic), EvalAltResult> {
|
||||
) -> Result<(IndexSourceType, Option<(&'a str, usize)>, usize, Dynamic), EvalAltResult> {
|
||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
||||
|
||||
match lhs {
|
||||
@ -336,7 +336,7 @@ impl Engine<'_> {
|
||||
lhs.position(),
|
||||
)
|
||||
.map(|(src_idx, (val, src_type))| {
|
||||
(src_type, Some((id.clone(), src_idx)), idx as usize, val)
|
||||
(src_type, Some((id.as_str(), src_idx)), idx as usize, val)
|
||||
}),
|
||||
|
||||
// (expr)[idx_expr]
|
||||
@ -371,7 +371,8 @@ impl Engine<'_> {
|
||||
// array_id[idx] = val
|
||||
IndexSourceType::Array => {
|
||||
let arr = scope.get_mut_by_type::<Array>(id, src_idx);
|
||||
(arr[idx as usize] = val).into_dynamic()
|
||||
arr[idx as usize] = val;
|
||||
().into_dynamic()
|
||||
}
|
||||
|
||||
// string_id[idx] = val
|
||||
@ -381,7 +382,8 @@ impl Engine<'_> {
|
||||
let ch = *val
|
||||
.downcast::<char>()
|
||||
.expect("char value expected to update an index position in a string");
|
||||
Self::str_replace_char(s, idx as usize, ch).into_dynamic()
|
||||
Self::str_replace_char(s, idx as usize, ch);
|
||||
().into_dynamic()
|
||||
}
|
||||
|
||||
// All other variable types should be an error
|
||||
@ -419,7 +421,7 @@ impl Engine<'_> {
|
||||
// of the above `clone`.
|
||||
if let Some((id, src_idx)) = src {
|
||||
Self::update_indexed_variable_in_scope(
|
||||
src_type, scope, &id, src_idx, idx, target,
|
||||
src_type, scope, id, src_idx, idx, target,
|
||||
);
|
||||
}
|
||||
|
||||
@ -510,7 +512,7 @@ impl Engine<'_> {
|
||||
|
||||
if let Some((id, src_idx)) = src {
|
||||
Self::update_indexed_variable_in_scope(
|
||||
src_type, scope, &id, src_idx, idx, target,
|
||||
src_type, scope, id, src_idx, idx, target,
|
||||
);
|
||||
}
|
||||
|
||||
@ -525,10 +527,10 @@ impl Engine<'_> {
|
||||
/// Evaluate an expression
|
||||
fn eval_expr(&mut self, scope: &mut Scope, expr: &Expr) -> Result<Dynamic, EvalAltResult> {
|
||||
match expr {
|
||||
Expr::IntegerConstant(i, _) => Ok((*i).into_dynamic()),
|
||||
Expr::FloatConstant(i, _) => Ok((*i).into_dynamic()),
|
||||
Expr::IntegerConstant(i, _) => Ok(i.into_dynamic()),
|
||||
Expr::FloatConstant(f, _) => Ok(f.into_dynamic()),
|
||||
Expr::StringConstant(s, _) => Ok(s.into_dynamic()),
|
||||
Expr::CharConstant(c, _) => Ok((*c).into_dynamic()),
|
||||
Expr::CharConstant(c, _) => Ok(c.into_dynamic()),
|
||||
Expr::Identifier(id, pos) => {
|
||||
Self::search_scope(scope, id, Ok, *pos).map(|(_, val)| val)
|
||||
}
|
||||
|
@ -75,6 +75,8 @@ pub enum ParseErrorType {
|
||||
FnMissingName,
|
||||
/// A function definition is missing the parameters list. Wrapped value is the function name.
|
||||
FnMissingParams(String),
|
||||
/// Assignment to an inappropriate LHS (left-hand-side) expression.
|
||||
AssignmentToInvalidLHS,
|
||||
}
|
||||
|
||||
/// Error when parsing a script.
|
||||
@ -114,6 +116,7 @@ impl Error for ParseError {
|
||||
ParseErrorType::FnMissingName => "Expecting name in function declaration",
|
||||
ParseErrorType::FnMissingParams(_) => "Expecting parameters in function declaration",
|
||||
ParseErrorType::WrongFnDefinition => "Function definitions must be at top level and cannot be inside a block or another function",
|
||||
ParseErrorType::AssignmentToInvalidLHS => "Assignment to an unsupported left-hand side expression"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ use crate::any::Dynamic;
|
||||
use crate::error::{LexError, ParseError, ParseErrorType};
|
||||
use std::char;
|
||||
use std::iter::Peekable;
|
||||
use std::str::Chars;
|
||||
use std::{borrow::Cow, str::Chars};
|
||||
|
||||
type LERR = LexError;
|
||||
type PERR = ParseErrorType;
|
||||
@ -99,12 +99,12 @@ impl std::fmt::Debug for Position {
|
||||
}
|
||||
|
||||
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
||||
pub struct AST(pub(crate) Vec<Stmt>, pub(crate) Vec<FnDef>);
|
||||
pub struct AST(pub(crate) Vec<Stmt>, pub(crate) Vec<FnDef<'static>>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FnDef {
|
||||
pub name: String,
|
||||
pub params: Vec<String>,
|
||||
pub struct FnDef<'a> {
|
||||
pub name: Cow<'a, str>,
|
||||
pub params: Vec<Cow<'a, str>>,
|
||||
pub body: Box<Stmt>,
|
||||
pub pos: Position,
|
||||
}
|
||||
@ -232,14 +232,14 @@ pub enum Token {
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub fn syntax(&self) -> std::borrow::Cow<'static, str> {
|
||||
pub fn syntax<'a>(&'a self) -> Cow<'a, str> {
|
||||
use self::Token::*;
|
||||
|
||||
match *self {
|
||||
IntegerConstant(ref s) => s.to_string().into(),
|
||||
FloatConstant(ref s) => s.to_string().into(),
|
||||
Identifier(ref s) => s.to_string().into(),
|
||||
CharConstant(ref s) => s.to_string().into(),
|
||||
IntegerConstant(ref i) => i.to_string().into(),
|
||||
FloatConstant(ref f) => f.to_string().into(),
|
||||
Identifier(ref s) => s.into(),
|
||||
CharConstant(ref c) => c.to_string().into(),
|
||||
LexError(ref err) => err.to_string().into(),
|
||||
|
||||
ref token => (match token {
|
||||
@ -301,7 +301,7 @@ impl Token {
|
||||
PowerOfAssign => "~=",
|
||||
For => "for",
|
||||
In => "in",
|
||||
_ => panic!(),
|
||||
_ => panic!("operator should be match in outer scope"),
|
||||
})
|
||||
.into(),
|
||||
}
|
||||
@ -538,8 +538,7 @@ impl<'a> TokenIterator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let out: String = result.iter().collect();
|
||||
Ok(out)
|
||||
Ok(result.iter().collect())
|
||||
}
|
||||
|
||||
fn inner_next(&mut self) -> Option<(Token, Position)> {
|
||||
@ -640,20 +639,20 @@ impl<'a> TokenIterator<'a> {
|
||||
},
|
||||
pos,
|
||||
));
|
||||
} else {
|
||||
let out: String = result.iter().filter(|&&c| c != '_').collect();
|
||||
|
||||
return Some((
|
||||
if let Ok(val) = out.parse::<i64>() {
|
||||
Token::IntegerConstant(val)
|
||||
} else if let Ok(val) = out.parse::<f64>() {
|
||||
Token::FloatConstant(val)
|
||||
} else {
|
||||
Token::LexError(LERR::MalformedNumber(result.iter().collect()))
|
||||
},
|
||||
pos,
|
||||
));
|
||||
}
|
||||
|
||||
let out: String = result.iter().filter(|&&c| c != '_').collect();
|
||||
|
||||
return Some((
|
||||
if let Ok(val) = out.parse::<i64>() {
|
||||
Token::IntegerConstant(val)
|
||||
} else if let Ok(val) = out.parse::<f64>() {
|
||||
Token::FloatConstant(val)
|
||||
} else {
|
||||
Token::LexError(LERR::MalformedNumber(result.iter().collect()))
|
||||
},
|
||||
pos,
|
||||
));
|
||||
}
|
||||
'A'..='Z' | 'a'..='z' | '_' => {
|
||||
let mut result = Vec::new();
|
||||
@ -687,7 +686,7 @@ impl<'a> TokenIterator<'a> {
|
||||
"fn" => Token::Fn,
|
||||
"for" => Token::For,
|
||||
"in" => Token::In,
|
||||
x => Token::Identifier(x.into()),
|
||||
_ => Token::Identifier(out),
|
||||
},
|
||||
pos,
|
||||
));
|
||||
@ -1264,6 +1263,21 @@ fn parse_unary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pars
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_assignment(lhs: Expr, rhs: Expr) -> Result<Expr, ParseError> {
|
||||
match lhs {
|
||||
// Only assignments to a variable, and index erxpression and a dot expression is valid LHS
|
||||
Expr::Identifier(_, _) | Expr::Index(_, _) | Expr::Dot(_, _) => {
|
||||
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs)))
|
||||
}
|
||||
|
||||
// All other LHS cannot be assigned to
|
||||
_ => Err(ParseError::new(
|
||||
PERR::AssignmentToInvalidLHS,
|
||||
lhs.position(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_binary_op<'a>(
|
||||
input: &mut Peekable<TokenIterator<'a>>,
|
||||
precedence: i8,
|
||||
@ -1308,7 +1322,7 @@ fn parse_binary_op<'a>(
|
||||
}
|
||||
Token::Divide => Expr::FunctionCall("/".into(), vec![current_lhs, rhs], None, pos),
|
||||
|
||||
Token::Equals => Expr::Assignment(Box::new(current_lhs), Box::new(rhs)),
|
||||
Token::Equals => parse_assignment(current_lhs, rhs)?,
|
||||
Token::PlusAssign => {
|
||||
let lhs_copy = current_lhs.clone();
|
||||
Expr::Assignment(
|
||||
@ -1687,7 +1701,7 @@ fn parse_stmt<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Parse
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_fn<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<FnDef, ParseError> {
|
||||
fn parse_fn<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<FnDef<'static>, ParseError> {
|
||||
let pos = match input.next() {
|
||||
Some((_, tok_pos)) => tok_pos,
|
||||
_ => return Err(ParseError::new(PERR::InputPastEndOfFile, Position::eof())),
|
||||
@ -1723,7 +1737,7 @@ fn parse_fn<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<FnDef, ParseE
|
||||
Some((Token::RightParen, _)) => break,
|
||||
Some((Token::Comma, _)) => (),
|
||||
Some((Token::Identifier(s), _)) => {
|
||||
params.push(s);
|
||||
params.push(s.into());
|
||||
}
|
||||
Some((_, pos)) => return Err(ParseError::new(PERR::MalformedCallExpr, pos)),
|
||||
None => return Err(ParseError::new(PERR::MalformedCallExpr, Position::eof())),
|
||||
@ -1734,7 +1748,7 @@ fn parse_fn<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<FnDef, ParseE
|
||||
let body = parse_block(input)?;
|
||||
|
||||
Ok(FnDef {
|
||||
name: name,
|
||||
name: name.into(),
|
||||
params: params,
|
||||
body: Box::new(body),
|
||||
pos: pos,
|
||||
|
35
src/scope.rs
35
src/scope.rs
@ -1,4 +1,5 @@
|
||||
use crate::any::{Any, Dynamic};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// A type containing information about current scope.
|
||||
/// Useful for keeping state between `Engine` runs
|
||||
@ -17,9 +18,9 @@ use crate::any::{Any, Dynamic};
|
||||
///
|
||||
/// When searching for variables, newly-added variables are found before similarly-named but older variables,
|
||||
/// allowing for automatic _shadowing_ of variables.
|
||||
pub struct Scope(Vec<(String, Dynamic)>);
|
||||
pub struct Scope<'a>(Vec<(Cow<'a, str>, Dynamic)>);
|
||||
|
||||
impl Scope {
|
||||
impl<'a> Scope<'a> {
|
||||
/// Create a new Scope.
|
||||
pub fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
@ -36,18 +37,18 @@ impl Scope {
|
||||
}
|
||||
|
||||
/// Add (push) a new variable to the Scope.
|
||||
pub fn push<T: Any>(&mut self, key: String, value: T) {
|
||||
self.0.push((key, Box::new(value)));
|
||||
pub fn push<K: Into<Cow<'a, str>>, T: Any>(&mut self, key: K, value: T) {
|
||||
self.0.push((key.into(), Box::new(value)));
|
||||
}
|
||||
|
||||
/// Add (push) a new variable to the Scope.
|
||||
pub(crate) fn push_dynamic(&mut self, key: String, value: Dynamic) {
|
||||
self.0.push((key, value));
|
||||
pub(crate) fn push_dynamic<K: Into<Cow<'a, str>>>(&mut self, key: K, value: Dynamic) {
|
||||
self.0.push((key.into(), value));
|
||||
}
|
||||
|
||||
/// Remove (pop) the last variable from the Scope.
|
||||
pub fn pop(&mut self) -> Option<(String, Dynamic)> {
|
||||
self.0.pop()
|
||||
self.0.pop().map(|(key, value)| (key.to_string(), value))
|
||||
}
|
||||
|
||||
/// Truncate (rewind) the Scope to a previous size.
|
||||
@ -56,13 +57,13 @@ impl Scope {
|
||||
}
|
||||
|
||||
/// Find a variable in the Scope, starting from the last.
|
||||
pub fn get(&self, key: &str) -> Option<(usize, String, Dynamic)> {
|
||||
pub fn get(&self, key: &str) -> Option<(usize, &str, Dynamic)> {
|
||||
self.0
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev() // Always search a Scope in reverse order
|
||||
.find(|(_, (name, _))| name == key)
|
||||
.map(|(i, (name, value))| (i, name.clone(), value.clone()))
|
||||
.map(|(i, (name, value))| (i, name.as_ref(), value.clone()))
|
||||
}
|
||||
|
||||
/// Get the value of a variable in the Scope, starting from the last.
|
||||
@ -97,20 +98,26 @@ impl Scope {
|
||||
self.0
|
||||
.iter()
|
||||
.rev() // Always search a Scope in reverse order
|
||||
.map(|(key, value)| (key.as_str(), value))
|
||||
.map(|(key, value)| (key.as_ref(), value))
|
||||
}
|
||||
|
||||
/*
|
||||
/// Get a mutable iterator to variables in the Scope.
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Dynamic)> {
|
||||
self.0
|
||||
.iter_mut()
|
||||
.rev() // Always search a Scope in reverse order
|
||||
.map(|(key, value)| (key.as_str(), value))
|
||||
.map(|(key, value)| (key.as_ref(), value))
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
impl std::iter::Extend<(String, Dynamic)> for Scope {
|
||||
fn extend<T: IntoIterator<Item = (String, Dynamic)>>(&mut self, iter: T) {
|
||||
self.0.extend(iter);
|
||||
impl<'a, K> std::iter::Extend<(K, Dynamic)> for Scope<'a>
|
||||
where
|
||||
K: Into<Cow<'a, str>>,
|
||||
{
|
||||
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
|
||||
self.0
|
||||
.extend(iter.into_iter().map(|(key, value)| (key.into(), value)));
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,8 @@ fn test_scope_eval() -> Result<(), EvalAltResult> {
|
||||
// Then push some initialized variables into the state
|
||||
// NOTE: Remember the default numbers used by Rhai are i64 and f64.
|
||||
// Better stick to them or it gets hard to work with other variables in the script.
|
||||
scope.push("y".into(), 42_i64);
|
||||
scope.push("z".into(), 999_i64);
|
||||
scope.push("y", 42_i64);
|
||||
scope.push("z", 999_i64);
|
||||
|
||||
// First invocation
|
||||
engine
|
||||
|
Loading…
Reference in New Issue
Block a user