Autocurry fixes and test

This commit is contained in:
Ilya Lakhin 2020-07-29 18:52:54 +07:00
parent 8e51988b66
commit 48356abc83
2 changed files with 48 additions and 56 deletions

View File

@ -2,10 +2,7 @@
use crate::any::{Dynamic, Union}; use crate::any::{Dynamic, Union};
use crate::calc_fn_hash; use crate::calc_fn_hash;
use crate::engine::{ use crate::engine::{Engine, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
Engine, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT,
KEYWORD_FN_PTR_CURRY,
};
use crate::error::{LexError, ParseError, ParseErrorType}; use crate::error::{LexError, ParseError, ParseErrorType};
use crate::fn_native::Shared; use crate::fn_native::Shared;
use crate::module::{Module, ModuleRef}; use crate::module::{Module, ModuleRef};
@ -18,6 +15,9 @@ use crate::utils::{StaticVec, StraightHasherBuilder};
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
use crate::engine::FN_ANONYMOUS; use crate::engine::FN_ANONYMOUS;
#[cfg(not(feature = "no_closures"))]
use crate::engine::KEYWORD_FN_PTR_CURRY;
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
use crate::engine::{make_getter, make_setter}; use crate::engine::{make_getter, make_setter};
@ -402,29 +402,29 @@ pub enum ReturnType {
Exception, Exception,
} }
struct ParseState<'e, 's> { struct ParseState<'e> {
/// Reference to the scripting `Engine`. // Reference to the scripting `Engine`.
engine: &'e Engine, engine: &'e Engine,
/// Encapsulates a local stack with variable names to simulate an actual runtime scope. // Encapsulates a local stack with variable names to simulate an actual runtime scope.
stack: Vec<(String, ScopeEntryType)>, stack: Vec<(String, ScopeEntryType)>,
/// Tracks a list of external variables(variables that are not explicitly // Tracks a list of external variables(variables that are not explicitly
/// declared in the scope during AST evaluation). // declared in the scope during AST evaluation).
externals: &'s mut Vec<String>, #[cfg(not(feature = "no_closures"))]
/// Encapsulates a local stack with variable names to simulate an actual runtime scope. externals: Vec<String>,
// Encapsulates a local stack with variable names to simulate an actual runtime scope.
modules: Vec<String>, modules: Vec<String>,
/// Maximum levels of expression nesting. // Maximum levels of expression nesting.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
max_expr_depth: usize, max_expr_depth: usize,
/// Maximum levels of expression nesting in functions. // Maximum levels of expression nesting in functions.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
max_function_expr_depth: usize, max_function_expr_depth: usize,
} }
impl<'e, 's> ParseState<'e, 's> { impl<'e> ParseState<'e> {
/// Create a new `ParseState`. /// Create a new `ParseState`.
fn new( fn new(
engine: &'e Engine, engine: &'e Engine,
externals: &'s mut Vec<String>,
#[cfg(not(feature = "unchecked"))] max_expr_depth: usize, #[cfg(not(feature = "unchecked"))] max_expr_depth: usize,
#[cfg(not(feature = "unchecked"))] max_function_expr_depth: usize, #[cfg(not(feature = "unchecked"))] max_function_expr_depth: usize,
) -> Self { ) -> Self {
@ -432,22 +432,7 @@ impl<'e, 's> ParseState<'e, 's> {
engine, engine,
#[cfg(not(feature = "unchecked"))] max_expr_depth, #[cfg(not(feature = "unchecked"))] max_expr_depth,
#[cfg(not(feature = "unchecked"))] max_function_expr_depth, #[cfg(not(feature = "unchecked"))] max_function_expr_depth,
externals, #[cfg(not(feature = "no_closures"))] externals: Default::default(),
stack: Default::default(),
modules: Default::default(),
}
}
/// Creates a new `ParseState` with empty `stack` and `modules` lists, but
/// deriving other settings from the passed `ParseState` instance.
fn derive(&'s mut self) -> Self {
Self {
engine: self.engine,
#[cfg(not(feature = "unchecked"))]
max_expr_depth: self.max_expr_depth,
#[cfg(not(feature = "unchecked"))]
max_function_expr_depth: self.max_function_expr_depth,
externals: self.externals,
stack: Default::default(), stack: Default::default(),
modules: Default::default(), modules: Default::default(),
} }
@ -463,7 +448,7 @@ impl<'e, 's> ParseState<'e, 's> {
/// i.e. the top element of the `ParseState` is offset 1. /// i.e. the top element of the `ParseState` is offset 1.
/// Return `None` when the variable name is not found in the `stack`. /// Return `None` when the variable name is not found in the `stack`.
fn access_var(&mut self, name: &str) -> Option<NonZeroUsize> { fn access_var(&mut self, name: &str) -> Option<NonZeroUsize> {
let mut index = self.stack let index = self.stack
.iter() .iter()
.rev() .rev()
.enumerate() .enumerate()
@ -483,6 +468,7 @@ impl<'e, 's> ParseState<'e, 's> {
} }
/// Creates a curry expression from a list of external variables /// Creates a curry expression from a list of external variables
#[cfg(not(feature = "no_closures"))]
fn make_curry_from_externals(&self, fn_expr: Expr, settings: &ParseSettings) -> Expr { fn make_curry_from_externals(&self, fn_expr: Expr, settings: &ParseSettings) -> Expr {
if self.externals.is_empty() { if self.externals.is_empty() {
return fn_expr return fn_expr
@ -1846,11 +1832,10 @@ fn parse_unary(
// | ... // | ...
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Token::Pipe | Token::Or => { Token::Pipe | Token::Or => {
let mut _externals = Default::default(); let mut new_state = ParseState::new(
let mut state = ParseState::new(
state.engine, state.engine,
&mut _externals, #[cfg(not(feature = "unchecked"))]
state.max_function_expr_depth, state.max_expr_depth,
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
state.max_function_expr_depth, state.max_function_expr_depth,
); );
@ -1866,7 +1851,12 @@ fn parse_unary(
pos: *token_pos, pos: *token_pos,
}; };
let (expr, func) = parse_anon_fn(input, &mut state, lib, settings)?; let (expr, func) = parse_anon_fn(input, &mut new_state, lib, settings)?;
#[cfg(not(feature = "no_closures"))]
for closure in new_state.externals {
state.access_var(&closure);
}
// Qualifiers (none) + function name + number of arguments. // Qualifiers (none) + function name + number of arguments.
let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty()); let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty());
@ -2891,12 +2881,10 @@ fn parse_stmt(
match input.next().unwrap() { match input.next().unwrap() {
(Token::Fn, pos) => { (Token::Fn, pos) => {
let mut _externals = Default::default();
let mut state = ParseState::new( let mut state = ParseState::new(
state.engine, state.engine,
&mut _externals,
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
state.max_function_expr_depth, state.max_expr_depth,
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
state.max_function_expr_depth, state.max_function_expr_depth,
); );
@ -3195,11 +3183,15 @@ fn parse_anon_fn(
pos: settings.pos, pos: settings.pos,
}; };
let mut expr = state.make_curry_from_externals( #[cfg(not(feature = "no_closures"))]
let expr = state.make_curry_from_externals(
Expr::FnPointer(Box::new((fn_name, settings.pos))), Expr::FnPointer(Box::new((fn_name, settings.pos))),
&settings, &settings,
); );
#[cfg(feature = "no_closures")]
let expr = Expr::FnPointer(Box::new((fn_name, settings.pos)));
Ok((expr, script)) Ok((expr, script))
} }
@ -3211,10 +3203,8 @@ impl Engine {
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> { ) -> Result<AST, ParseError> {
let mut functions = Default::default(); let mut functions = Default::default();
let mut _externals = Default::default();
let mut state = ParseState::new( let mut state = ParseState::new(
self, self,
&mut _externals,
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
self.limits.max_expr_depth, self.limits.max_expr_depth,
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -3258,10 +3248,8 @@ impl Engine {
) -> Result<(Vec<Stmt>, Vec<ScriptFnDef>), ParseError> { ) -> Result<(Vec<Stmt>, Vec<ScriptFnDef>), ParseError> {
let mut statements: Vec<Stmt> = Default::default(); let mut statements: Vec<Stmt> = Default::default();
let mut functions = Default::default(); let mut functions = Default::default();
let mut _externals = Default::default();
let mut state = ParseState::new( let mut state = ParseState::new(
self, self,
&mut _externals,
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
self.limits.max_expr_depth, self.limits.max_expr_depth,
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]

View File

@ -211,22 +211,26 @@ fn test_fn_ptr_curry_call() -> Result<(), Box<EvalAltResult>> {
} }
#[test] #[test]
#[cfg(not(feature = "no_closures"))]
fn test_fn_closures() -> Result<(), Box<EvalAltResult>> { fn test_fn_closures() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new(); let engine = Engine::new();
let res = engine.eval::<INT>( assert_eq!(
engine.eval::<INT>(
r#" r#"
let x = 100; let x = 8;
let f = || x; let res = |y, z| {
let w = 12;
let x = 200; return (|| x + y + z + w).call();
}.curry(15).call(2);
f.call() res + (|| x - 3).call()
"# "#
).unwrap(); )?,
42
panic!("{:#?}", res); );
Ok(()) Ok(())
} }