Allow engine to retain functions across runs.
This commit is contained in:
parent
55e7af7b04
commit
bae9946291
@ -4,9 +4,9 @@ fn main() -> Result<(), EvalAltResult> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5")?;
|
engine.eval_with_scope::<()>(&mut scope, false, "let x = 4 + 5")?;
|
||||||
|
|
||||||
let result = engine.eval_with_scope::<i64>(&mut scope, "x")?;
|
let result = engine.eval_with_scope::<i64>(&mut scope, false, "x")?;
|
||||||
|
|
||||||
println!("result: {}", result);
|
println!("result: {}", result);
|
||||||
|
|
||||||
|
55
src/api.rs
55
src/api.rs
@ -94,13 +94,13 @@ impl<'e> Engine<'e> {
|
|||||||
self.register_set(name, set_fn);
|
self.register_set(name, set_fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compile a string into an AST
|
/// Compile a string into an AST.
|
||||||
pub fn compile(&self, input: &str) -> Result<AST, ParseError> {
|
pub fn compile(&self, input: &str) -> Result<AST, ParseError> {
|
||||||
let tokens = lex(input);
|
let tokens = lex(input);
|
||||||
parse(&mut tokens.peekable(), self.optimize)
|
parse(&mut tokens.peekable(), self.optimize)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compile a file into an AST
|
/// Compile a file into an AST.
|
||||||
pub fn compile_file(&self, filename: &str) -> Result<AST, EvalAltResult> {
|
pub fn compile_file(&self, filename: &str) -> Result<AST, EvalAltResult> {
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
@ -115,7 +115,7 @@ impl<'e> Engine<'e> {
|
|||||||
.and_then(|_| self.compile(&contents).map_err(EvalAltResult::ErrorParsing))
|
.and_then(|_| self.compile(&contents).map_err(EvalAltResult::ErrorParsing))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a file
|
/// Evaluate a file.
|
||||||
pub fn eval_file<T: Any + Clone>(&mut self, filename: &str) -> Result<T, EvalAltResult> {
|
pub fn eval_file<T: Any + Clone>(&mut self, filename: &str) -> Result<T, EvalAltResult> {
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
@ -130,32 +130,40 @@ impl<'e> Engine<'e> {
|
|||||||
.and_then(|_| self.eval::<T>(&contents))
|
.and_then(|_| self.eval::<T>(&contents))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a string
|
/// Evaluate a string.
|
||||||
pub fn eval<T: Any + Clone>(&mut self, input: &str) -> Result<T, EvalAltResult> {
|
pub fn eval<T: Any + Clone>(&mut self, input: &str) -> Result<T, EvalAltResult> {
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
self.eval_with_scope(&mut scope, input)
|
self.eval_with_scope(&mut scope, false, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a string with own scope
|
/// Evaluate a string with own scope.
|
||||||
|
///
|
||||||
|
/// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_
|
||||||
|
/// and not cleared from run to run.
|
||||||
pub fn eval_with_scope<T: Any + Clone>(
|
pub fn eval_with_scope<T: Any + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
|
retain_functions: bool,
|
||||||
input: &str,
|
input: &str,
|
||||||
) -> Result<T, EvalAltResult> {
|
) -> Result<T, EvalAltResult> {
|
||||||
let ast = self.compile(input).map_err(EvalAltResult::ErrorParsing)?;
|
let ast = self.compile(input).map_err(EvalAltResult::ErrorParsing)?;
|
||||||
self.eval_ast_with_scope(scope, &ast)
|
self.eval_ast_with_scope(scope, retain_functions, &ast)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an AST
|
/// Evaluate an AST.
|
||||||
pub fn eval_ast<T: Any + Clone>(&mut self, ast: &AST) -> Result<T, EvalAltResult> {
|
pub fn eval_ast<T: Any + Clone>(&mut self, ast: &AST) -> Result<T, EvalAltResult> {
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
self.eval_ast_with_scope(&mut scope, ast)
|
self.eval_ast_with_scope(&mut scope, false, ast)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an AST with own scope
|
/// Evaluate an AST with own scope.
|
||||||
|
///
|
||||||
|
/// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_
|
||||||
|
/// and not cleared from run to run.
|
||||||
pub fn eval_ast_with_scope<T: Any + Clone>(
|
pub fn eval_ast_with_scope<T: Any + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
|
retain_functions: bool,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
) -> Result<T, EvalAltResult> {
|
) -> Result<T, EvalAltResult> {
|
||||||
let AST(statements, functions) = ast;
|
let AST(statements, functions) = ast;
|
||||||
@ -174,7 +182,9 @@ impl<'e> Engine<'e> {
|
|||||||
.iter()
|
.iter()
|
||||||
.try_fold(().into_dynamic(), |_, stmt| self.eval_stmt(scope, stmt));
|
.try_fold(().into_dynamic(), |_, stmt| self.eval_stmt(scope, stmt));
|
||||||
|
|
||||||
self.script_functions.clear(); // Clean up engine
|
if !retain_functions {
|
||||||
|
self.clear_functions();
|
||||||
|
}
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Err(EvalAltResult::Return(out, pos)) => out.downcast::<T>().map(|v| *v).map_err(|a| {
|
Err(EvalAltResult::Return(out, pos)) => out.downcast::<T>().map(|v| *v).map_err(|a| {
|
||||||
@ -196,8 +206,7 @@ impl<'e> Engine<'e> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a file, but throw away the result and only return error (if 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
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||||
/// to keep track of possible errors
|
|
||||||
pub fn consume_file(&mut self, filename: &str) -> Result<(), EvalAltResult> {
|
pub fn consume_file(&mut self, filename: &str) -> Result<(), EvalAltResult> {
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
@ -213,18 +222,20 @@ impl<'e> Engine<'e> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a string, but throw away the result and only return error (if 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
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||||
/// to keep track of possible errors
|
|
||||||
pub fn consume(&mut self, input: &str) -> Result<(), EvalAltResult> {
|
pub fn consume(&mut self, input: &str) -> Result<(), EvalAltResult> {
|
||||||
self.consume_with_scope(&mut Scope::new(), input)
|
self.consume_with_scope(&mut Scope::new(), false, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a string with own scope, but throw away the result and only return error (if 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
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||||
/// to keep track of possible errors
|
///
|
||||||
|
/// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_
|
||||||
|
/// and not cleared from run to run.
|
||||||
pub fn consume_with_scope(
|
pub fn consume_with_scope(
|
||||||
&mut self,
|
&mut self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
|
retain_functions: bool,
|
||||||
input: &str,
|
input: &str,
|
||||||
) -> Result<(), EvalAltResult> {
|
) -> Result<(), EvalAltResult> {
|
||||||
let tokens = lex(input);
|
let tokens = lex(input);
|
||||||
@ -247,7 +258,9 @@ impl<'e> Engine<'e> {
|
|||||||
.try_fold(().into_dynamic(), |_, o| self.eval_stmt(scope, o))
|
.try_fold(().into_dynamic(), |_, o| self.eval_stmt(scope, o))
|
||||||
.map(|_| ());
|
.map(|_| ());
|
||||||
|
|
||||||
self.script_functions.clear(); // Clean up engine
|
if !retain_functions {
|
||||||
|
self.clear_functions();
|
||||||
|
}
|
||||||
|
|
||||||
val
|
val
|
||||||
})
|
})
|
||||||
@ -300,7 +313,7 @@ impl<'e> Engine<'e> {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
self.script_functions.clear(); // Clean up engine
|
self.clear_functions();
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -1030,6 +1030,11 @@ impl Engine<'_> {
|
|||||||
.map(|s| s.as_str())
|
.map(|s| s.as_str())
|
||||||
.unwrap_or(name)
|
.unwrap_or(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clean up all script-defined functions within the `Engine`.
|
||||||
|
pub fn clear_functions(&mut self) {
|
||||||
|
self.script_functions.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print/debug to stdout
|
/// Print/debug to stdout
|
||||||
|
@ -81,7 +81,7 @@ pub enum ParseErrorType {
|
|||||||
|
|
||||||
/// Error when parsing a script.
|
/// Error when parsing a script.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct ParseError(ParseErrorType, Position);
|
pub struct ParseError(pub(crate) ParseErrorType, pub(crate) Position);
|
||||||
|
|
||||||
impl ParseError {
|
impl ParseError {
|
||||||
/// Create a new `ParseError`.
|
/// Create a new `ParseError`.
|
||||||
|
@ -174,29 +174,54 @@ impl From<ParseError> for EvalAltResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EvalAltResult {
|
impl EvalAltResult {
|
||||||
|
pub fn position(&self) -> Position {
|
||||||
|
match self {
|
||||||
|
Self::ErrorReadingScriptFile(_, _) | Self::LoopBreak => Position::none(),
|
||||||
|
|
||||||
|
Self::ErrorParsing(err) => err.position(),
|
||||||
|
|
||||||
|
Self::ErrorFunctionNotFound(_, pos)
|
||||||
|
| Self::ErrorFunctionArgsMismatch(_, _, _, pos)
|
||||||
|
| Self::ErrorBooleanArgMismatch(_, pos)
|
||||||
|
| Self::ErrorCharMismatch(pos)
|
||||||
|
| Self::ErrorArrayBounds(_, _, pos)
|
||||||
|
| Self::ErrorStringBounds(_, _, pos)
|
||||||
|
| Self::ErrorIndexingType(_, pos)
|
||||||
|
| Self::ErrorIndexExpr(pos)
|
||||||
|
| Self::ErrorIfGuard(pos)
|
||||||
|
| Self::ErrorFor(pos)
|
||||||
|
| Self::ErrorVariableNotFound(_, pos)
|
||||||
|
| Self::ErrorAssignmentToUnknownLHS(pos)
|
||||||
|
| Self::ErrorMismatchOutputType(_, pos)
|
||||||
|
| Self::ErrorDotExpr(_, pos)
|
||||||
|
| Self::ErrorArithmetic(_, pos)
|
||||||
|
| Self::ErrorRuntime(_, pos)
|
||||||
|
| Self::Return(_, pos) => *pos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn set_position(&mut self, new_position: Position) {
|
pub(crate) fn set_position(&mut self, new_position: Position) {
|
||||||
match self {
|
match self {
|
||||||
EvalAltResult::ErrorReadingScriptFile(_, _)
|
Self::ErrorReadingScriptFile(_, _) | Self::LoopBreak => (),
|
||||||
| EvalAltResult::LoopBreak
|
|
||||||
| EvalAltResult::ErrorParsing(_) => (),
|
|
||||||
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, ref mut pos)
|
Self::ErrorParsing(ParseError(_, ref mut pos))
|
||||||
| EvalAltResult::ErrorFunctionArgsMismatch(_, _, _, ref mut pos)
|
| Self::ErrorFunctionNotFound(_, ref mut pos)
|
||||||
| EvalAltResult::ErrorBooleanArgMismatch(_, ref mut pos)
|
| Self::ErrorFunctionArgsMismatch(_, _, _, ref mut pos)
|
||||||
| EvalAltResult::ErrorCharMismatch(ref mut pos)
|
| Self::ErrorBooleanArgMismatch(_, ref mut pos)
|
||||||
| EvalAltResult::ErrorArrayBounds(_, _, ref mut pos)
|
| Self::ErrorCharMismatch(ref mut pos)
|
||||||
| EvalAltResult::ErrorStringBounds(_, _, ref mut pos)
|
| Self::ErrorArrayBounds(_, _, ref mut pos)
|
||||||
| EvalAltResult::ErrorIndexingType(_, ref mut pos)
|
| Self::ErrorStringBounds(_, _, ref mut pos)
|
||||||
| EvalAltResult::ErrorIndexExpr(ref mut pos)
|
| Self::ErrorIndexingType(_, ref mut pos)
|
||||||
| EvalAltResult::ErrorIfGuard(ref mut pos)
|
| Self::ErrorIndexExpr(ref mut pos)
|
||||||
| EvalAltResult::ErrorFor(ref mut pos)
|
| Self::ErrorIfGuard(ref mut pos)
|
||||||
| EvalAltResult::ErrorVariableNotFound(_, ref mut pos)
|
| Self::ErrorFor(ref mut pos)
|
||||||
| EvalAltResult::ErrorAssignmentToUnknownLHS(ref mut pos)
|
| Self::ErrorVariableNotFound(_, ref mut pos)
|
||||||
| EvalAltResult::ErrorMismatchOutputType(_, ref mut pos)
|
| Self::ErrorAssignmentToUnknownLHS(ref mut pos)
|
||||||
| EvalAltResult::ErrorDotExpr(_, ref mut pos)
|
| Self::ErrorMismatchOutputType(_, ref mut pos)
|
||||||
| EvalAltResult::ErrorArithmetic(_, ref mut pos)
|
| Self::ErrorDotExpr(_, ref mut pos)
|
||||||
| EvalAltResult::ErrorRuntime(_, ref mut pos)
|
| Self::ErrorArithmetic(_, ref mut pos)
|
||||||
| EvalAltResult::Return(_, ref mut pos) => *pos = new_position,
|
| Self::ErrorRuntime(_, ref mut pos)
|
||||||
|
| Self::Return(_, ref mut pos) => *pos = new_position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use crate::any::{Any, Dynamic};
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
/// A type containing information about current scope.
|
/// A type containing information about current scope.
|
||||||
/// Useful for keeping state between `Engine` runs
|
/// Useful for keeping state between `Engine` runs.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -15,9 +15,9 @@ use std::borrow::Cow;
|
|||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
/// let mut my_scope = Scope::new();
|
/// let mut my_scope = Scope::new();
|
||||||
///
|
///
|
||||||
/// engine.eval_with_scope::<()>(&mut my_scope, "let x = 5;")?;
|
/// engine.eval_with_scope::<()>(&mut my_scope, false, "let x = 5;")?;
|
||||||
///
|
///
|
||||||
/// assert_eq!(engine.eval_with_scope::<i64>(&mut my_scope, "x + 1")?, 6);
|
/// assert_eq!(engine.eval_with_scope::<i64>(&mut my_scope, false, "x + 1")?, 6);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -5,12 +5,15 @@ fn test_var_scope() -> Result<(), EvalAltResult> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5")?;
|
engine.eval_with_scope::<()>(&mut scope, false, "let x = 4 + 5")?;
|
||||||
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, "x")?, 9);
|
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, false, "x")?, 9);
|
||||||
engine.eval_with_scope::<()>(&mut scope, "x = x + 1; x = x + 2;")?;
|
engine.eval_with_scope::<()>(&mut scope, false, "x = x + 1; x = x + 2;")?;
|
||||||
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, "x")?, 12);
|
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, false, "x")?, 12);
|
||||||
assert_eq!(engine.eval_with_scope::<()>(&mut scope, "{let x = 3}")?, ());
|
assert_eq!(
|
||||||
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, "x")?, 12);
|
engine.eval_with_scope::<()>(&mut scope, false, "{let x = 3}")?,
|
||||||
|
()
|
||||||
|
);
|
||||||
|
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, false, "x")?, 12);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -30,17 +33,11 @@ fn test_scope_eval() -> Result<(), EvalAltResult> {
|
|||||||
|
|
||||||
// First invocation
|
// First invocation
|
||||||
engine
|
engine
|
||||||
.eval_with_scope::<()>(
|
.eval_with_scope::<()>(&mut scope, false, " let x = 4 + 5 - y + z; y = 1;")
|
||||||
&mut scope,
|
|
||||||
r"
|
|
||||||
let x = 4 + 5 - y + z;
|
|
||||||
y = 1;
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.expect("y and z not found?");
|
.expect("y and z not found?");
|
||||||
|
|
||||||
// Second invocation using the same state
|
// Second invocation using the same state
|
||||||
let result = engine.eval_with_scope::<i64>(&mut scope, "x")?;
|
let result = engine.eval_with_scope::<i64>(&mut scope, false, "x")?;
|
||||||
|
|
||||||
println!("result: {}", result); // should print 966
|
println!("result: {}", result); // should print 966
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user