Code refactor, bug fixes, code docs.

This commit is contained in:
Stephen Chung
2020-03-04 22:00:01 +08:00
parent b4d56accd4
commit bb56a7a843
14 changed files with 949 additions and 676 deletions

View File

@@ -1,10 +1,95 @@
use crate::any::{Any, AnyExt};
use crate::engine::{Engine, EvalAltResult, FnIntExt, FnSpec};
use crate::parser::{lex, parse, ParseError, Position, AST};
use crate::any::{Any, AnyExt, Dynamic};
use crate::call::FuncArgs;
use crate::engine::{Engine, FnAny, FnIntExt, FnSpec};
use crate::error::ParseError;
use crate::fn_register::RegisterFn;
use crate::parser::{lex, parse, Position, AST};
use crate::result::EvalAltResult;
use crate::scope::Scope;
use std::any::TypeId;
use std::sync::Arc;
impl Engine {
impl<'a> Engine<'a> {
pub(crate) fn register_fn_raw(
&mut self,
fn_name: &str,
args: Option<Vec<TypeId>>,
f: Box<FnAny>,
) {
debug_println!(
"Register function: {} ({})",
fn_name,
args.iter()
.map(|x| (*x).type_name())
.map(|name| self.map_type_name(name))
.collect::<Vec<_>>()
.join(", ")
);
let spec = FnSpec {
name: fn_name.to_string().into(),
args,
};
self.fns.insert(spec, Arc::new(FnIntExt::Ext(f)));
}
/// Register a custom type for use with the `Engine`.
/// The type must be `Clone`.
pub fn register_type<T: Any + Clone>(&mut self) {
self.register_type_with_name::<T>(std::any::type_name::<T>());
}
/// Register a custom type for use with the `Engine` with a name for the `type_of` function.
/// The type must be `Clone`.
pub fn register_type_with_name<T: Any + Clone>(&mut self, type_name: &str) {
// Add the pretty-print type name into the map
self.type_names.insert(
std::any::type_name::<T>().to_string(),
type_name.to_string(),
);
}
/// Register an iterator adapter for a type with the `Engine`.
pub fn register_iterator<T: Any, F>(&mut self, f: F)
where
F: Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + 'static,
{
self.type_iterators.insert(TypeId::of::<T>(), Arc::new(f));
}
/// Register a getter function for a member of a registered type with the `Engine`.
pub fn register_get<T: Any + Clone, U: Any + Clone>(
&mut self,
name: &str,
get_fn: impl Fn(&mut T) -> U + 'static,
) {
let get_name = "get$".to_string() + name;
self.register_fn(&get_name, get_fn);
}
/// Register a setter function for a member of a registered type with the `Engine`.
pub fn register_set<T: Any + Clone, U: Any + Clone>(
&mut self,
name: &str,
set_fn: impl Fn(&mut T, U) -> () + 'static,
) {
let set_name = "set$".to_string() + name;
self.register_fn(&set_name, set_fn);
}
/// Shorthand for registering both getter and setter functions
/// of a registered type with the `Engine`.
pub fn register_get_set<T: Any + Clone, U: Any + Clone>(
&mut self,
name: &str,
get_fn: impl Fn(&mut T) -> U + 'static,
set_fn: impl Fn(&mut T, U) -> () + 'static,
) {
self.register_get(name, get_fn);
self.register_set(name, set_fn);
}
/// Compile a string into an AST
pub fn compile(input: &str) -> Result<AST, ParseError> {
let tokens = lex(input);
@@ -17,12 +102,12 @@ impl Engine {
use std::io::prelude::*;
let mut f = File::open(filename)
.map_err(|err| EvalAltResult::ErrorCantOpenScriptFile(filename.into(), err))?;
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?;
let mut contents = String::new();
f.read_to_string(&mut contents)
.map_err(|err| EvalAltResult::ErrorCantOpenScriptFile(filename.into(), err))
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))
.and_then(|_| Self::compile(&contents).map_err(EvalAltResult::ErrorParsing))
}
@@ -32,12 +117,12 @@ impl Engine {
use std::io::prelude::*;
let mut f = File::open(filename)
.map_err(|err| EvalAltResult::ErrorCantOpenScriptFile(filename.into(), err))?;
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?;
let mut contents = String::new();
f.read_to_string(&mut contents)
.map_err(|err| EvalAltResult::ErrorCantOpenScriptFile(filename.into(), err))
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))
.and_then(|_| self.eval::<T>(&contents))
}
@@ -74,7 +159,7 @@ impl Engine {
fns.iter().for_each(|f| {
self.script_fns.insert(
FnSpec {
ident: f.name.clone(),
name: f.name.clone().into(),
args: None,
},
Arc::new(FnIntExt::Int(f.clone())),
@@ -106,7 +191,7 @@ impl Engine {
}
}
/// Evaluate a file, but only return errors, if there are any.
/// Evaluate a file, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need
/// to keep track of possible errors
pub fn consume_file(&mut self, filename: &str) -> Result<(), EvalAltResult> {
@@ -114,23 +199,23 @@ impl Engine {
use std::io::prelude::*;
let mut f = File::open(filename)
.map_err(|err| EvalAltResult::ErrorCantOpenScriptFile(filename.into(), err))?;
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?;
let mut contents = String::new();
f.read_to_string(&mut contents)
.map_err(|err| EvalAltResult::ErrorCantOpenScriptFile(filename.into(), err))
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))
.and_then(|_| self.consume(&contents))
}
/// Evaluate a string, but only return errors, if there are any.
/// Evaluate a string, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need
/// to keep track of possible errors
pub fn consume(&mut self, input: &str) -> Result<(), EvalAltResult> {
self.consume_with_scope(&mut Scope::new(), input)
}
/// Evaluate a string with own scope, but only return errors, if there are any.
/// Evaluate a string with own scope, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need
/// to keep track of possible errors
pub fn consume_with_scope(
@@ -146,7 +231,7 @@ impl Engine {
for f in fns {
self.script_fns.insert(
FnSpec {
ident: f.name.clone(),
name: f.name.clone().into(),
args: None,
},
Arc::new(FnIntExt::Int(f.clone())),
@@ -164,13 +249,94 @@ impl Engine {
})
}
/// Overrides `on_print`
pub fn on_print(&mut self, callback: impl Fn(&str) + 'static) {
/// Call a script function defined in a compiled AST.
///
/// # Example
///
/// ```rust
/// # use rhai::{Engine, EvalAltResult};
/// # fn main() -> Result<(), EvalAltResult> {
/// let mut engine = Engine::new();
///
/// let ast = Engine::compile("fn add(x, y) { x.len() + y }")?;
///
/// let result: i64 = engine.call_fn("add", ast, (&mut String::from("abc"), &mut 123_i64))?;
///
/// assert_eq!(result, 126);
/// # Ok(())
/// # }
/// ```
pub fn call_fn<'f, A: FuncArgs<'f>, T: Any + Clone>(
&mut self,
name: &str,
ast: AST,
args: A,
) -> Result<T, EvalAltResult> {
let pos = Default::default();
ast.1.iter().for_each(|f| {
self.script_fns.insert(
FnSpec {
name: f.name.clone().into(),
args: None,
},
Arc::new(FnIntExt::Int(f.clone())),
);
});
let result = self
.call_fn_raw(name, args.into_vec(), None, pos)
.and_then(|b| {
b.downcast().map(|b| *b).map_err(|a| {
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).into(),
pos,
)
})
});
self.script_fns.clear(); // Clean up engine
result
}
/// Override default action of `print` (print to stdout using `println!`)
///
/// # Example
///
/// ```rust
/// # use rhai::Engine;
/// let mut result = String::from("");
/// {
/// let mut engine = Engine::new();
///
/// // Override action of 'print' function
/// engine.on_print(|s| result.push_str(s));
/// engine.consume("print(40 + 2);").unwrap();
/// }
/// assert_eq!(result, "42");
/// ```
pub fn on_print(&mut self, callback: impl FnMut(&str) + 'a) {
self.on_print = Box::new(callback);
}
/// Overrides `on_debug`
pub fn on_debug(&mut self, callback: impl Fn(&str) + 'static) {
/// Override default action of `debug` (print to stdout using `println!`)
///
/// # Example
///
/// ```rust
/// # use rhai::Engine;
/// let mut result = String::from("");
/// {
/// let mut engine = Engine::new();
///
/// // Override action of 'debug' function
/// engine.on_debug(|s| result.push_str(s));
/// engine.consume(r#"debug("hello");"#).unwrap();
/// }
/// assert_eq!(result, "\"hello\"");
/// ```
pub fn on_debug(&mut self, callback: impl FnMut(&str) + 'a) {
self.on_debug = Box::new(callback);
}
}