From b11b8d6d397688591f8eb8cdb221751ac0706e83 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 9 Mar 2021 23:30:48 +0800 Subject: [PATCH] Reduce redirections in Stmt. --- src/ast.rs | 68 ++++++++++++++---------------- src/engine.rs | 64 ++++++++++++++--------------- src/fn_call.rs | 9 ++-- src/optimize.rs | 100 ++++++++++++++------------------------------- src/parser.rs | 59 +++++++++++++++----------- tests/optimizer.rs | 3 +- 6 files changed, 132 insertions(+), 171 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 6ad9d148..a8ed9a4e 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -757,6 +757,8 @@ pub struct Ident { pub name: ImmutableString, /// Declaration position. pub pos: Position, + /// Is this identifier public? + pub public: bool, } impl fmt::Debug for Ident { @@ -815,7 +817,7 @@ pub enum Stmt { /// No-op. Noop(Position), /// `if` expr `{` stmt `}` `else` `{` stmt `}` - If(Expr, Box<(Stmt, Option)>, Position), + If(Expr, Box<(Stmt, Stmt)>, Position), /// `switch` expr `{` literal or _ `=>` stmt `,` ... `}` Switch( Expr, @@ -826,15 +828,15 @@ pub enum Stmt { Position, ), /// `while` expr `{` stmt `}` - While(Option, Box, Position), + While(Expr, Box, Position), /// `do` `{` stmt `}` `while`|`until` expr Do(Box, Expr, bool, Position), /// `for` id `in` expr `{` stmt `}` - For(Expr, Box<(String, Stmt)>, Position), + For(Expr, String, Box, Position), /// \[`export`\] `let` id `=` expr - Let(Box, Option, bool, Position), + Let(Expr, Ident, Position), /// \[`export`\] `const` id `=` expr - Const(Box, Option, bool, Position), + Const(Expr, Ident, Position), /// expr op`=` expr Assignment(Box<(Expr, Expr, Option)>, Position), /// `{` stmt`;` ... `}` @@ -848,10 +850,10 @@ pub enum Stmt { /// `break` Break(Position), /// `return`/`throw` - Return((ReturnType, Position), Option, Position), + Return(ReturnType, Option, Position), /// `import` expr `as` var #[cfg(not(feature = "no_module"))] - Import(Expr, Option>, Position), + Import(Expr, Option, Position), /// `export` var `as` var `,` ... #[cfg(not(feature = "no_module"))] Export(Vec<(Ident, Option)>, Position), @@ -888,10 +890,10 @@ impl Stmt { | Self::Switch(_, _, pos) | Self::While(_, _, pos) | Self::Do(_, _, _, pos) - | Self::For(_, _, pos) - | Self::Return((_, pos), _, _) - | Self::Let(_, _, _, pos) - | Self::Const(_, _, _, pos) + | Self::For(_, _, _, pos) + | Self::Return(_, _, pos) + | Self::Let(_, _, pos) + | Self::Const(_, _, pos) | Self::TryCatch(_, pos, _) => *pos, Self::Expr(x) => x.position(), @@ -917,10 +919,10 @@ impl Stmt { | Self::Switch(_, _, pos) | Self::While(_, _, pos) | Self::Do(_, _, _, pos) - | Self::For(_, _, pos) - | Self::Return((_, pos), _, _) - | Self::Let(_, _, _, pos) - | Self::Const(_, _, _, pos) + | Self::For(_, _, _, pos) + | Self::Return(_, _, pos) + | Self::Let(_, _, pos) + | Self::Const(_, _, pos) | Self::TryCatch(_, pos, _) => *pos = new_pos, Self::Expr(x) => { @@ -944,15 +946,15 @@ impl Stmt { Self::If(_, _, _) | Self::Switch(_, _, _) | Self::While(_, _, _) - | Self::For(_, _, _) + | Self::For(_, _, _, _) | Self::Block(_, _) | Self::TryCatch(_, _, _) => true, // A No-op requires a semicolon in order to know it is an empty statement! Self::Noop(_) => false, - Self::Let(_, _, _, _) - | Self::Const(_, _, _, _) + Self::Let(_, _, _) + | Self::Const(_, _, _) | Self::Assignment(_, _) | Self::Expr(_) | Self::Do(_, _, _, _) @@ -974,22 +976,17 @@ impl Stmt { match self { Self::Noop(_) => true, Self::Expr(expr) => expr.is_pure(), - Self::If(condition, x, _) => { - condition.is_pure() - && x.0.is_pure() - && x.1.as_ref().map(Stmt::is_pure).unwrap_or(true) - } + Self::If(condition, x, _) => condition.is_pure() && x.0.is_pure() && x.1.is_pure(), Self::Switch(expr, x, _) => { expr.is_pure() && x.0.values().all(Stmt::is_pure) && x.1.as_ref().map(Stmt::is_pure).unwrap_or(true) } - Self::While(Some(condition), block, _) | Self::Do(block, condition, _, _) => { + Self::While(condition, block, _) | Self::Do(block, condition, _, _) => { condition.is_pure() && block.is_pure() } - Self::While(None, block, _) => block.is_pure(), - Self::For(iterable, x, _) => iterable.is_pure() && x.1.is_pure(), - Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false, + Self::For(iterable, _, block, _) => iterable.is_pure() && block.is_pure(), + Self::Let(_, _, _) | Self::Const(_, _, _) | Self::Assignment(_, _) => false, Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false, Self::TryCatch(x, _, _) => x.0.is_pure() && x.2.is_pure(), @@ -1010,13 +1007,11 @@ impl Stmt { on_node(path); match self { - Self::Let(_, Some(e), _, _) | Self::Const(_, Some(e), _, _) => e.walk(path, on_node), + Self::Let(e, _, _) | Self::Const(e, _, _) => e.walk(path, on_node), Self::If(e, x, _) => { e.walk(path, on_node); x.0.walk(path, on_node); - if let Some(ref s) = x.1 { - s.walk(path, on_node); - } + x.1.walk(path, on_node); } Self::Switch(e, x, _) => { e.walk(path, on_node); @@ -1025,14 +1020,13 @@ impl Stmt { s.walk(path, on_node); } } - Self::While(Some(e), s, _) | Self::Do(s, e, _, _) => { + Self::While(e, s, _) | Self::Do(s, e, _, _) => { e.walk(path, on_node); s.walk(path, on_node); } - Self::While(None, s, _) => s.walk(path, on_node), - Self::For(e, x, _) => { + Self::For(e, _, s, _) => { e.walk(path, on_node); - x.1.walk(path, on_node); + s.walk(path, on_node); } Self::Assignment(x, _) => { x.0.walk(path, on_node); @@ -1583,8 +1577,8 @@ mod tests { assert_eq!(size_of::(), 4); assert_eq!(size_of::(), 16); assert_eq!(size_of::>(), 16); - assert_eq!(size_of::(), 32); - assert_eq!(size_of::>(), 32); + assert_eq!(size_of::(), 40); + assert_eq!(size_of::>(), 40); assert_eq!(size_of::(), 32); assert_eq!(size_of::(), 48); assert_eq!(size_of::(), 56); diff --git a/src/engine.rs b/src/engine.rs index 274710bd..62cf0ac7 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -946,7 +946,7 @@ impl Engine { match expr { Expr::Variable(v) => match v.as_ref() { // Qualified variable - (_, Some((hash_var, modules)), Ident { name, pos }) => { + (_, Some((hash_var, modules)), Ident { name, pos, .. }) => { let module = self.search_imports(mods, state, modules).ok_or_else(|| { EvalAltResult::ErrorModuleNotFound( modules[0].name.to_string(), @@ -985,7 +985,7 @@ impl Engine { this_ptr: &'s mut Option<&mut Dynamic>, expr: &Expr, ) -> Result<(Target<'s>, Position), Box> { - let (index, _, Ident { name, pos }) = match expr { + let (index, _, Ident { name, pos, .. }) = match expr { Expr::Variable(v) => v.as_ref(), _ => unreachable!("Expr::Variable expected, but gets {:?}", expr), }; @@ -1181,7 +1181,7 @@ impl Engine { } // {xxx:map}.id op= ??? Expr::Property(x) if target_val.is::() && new_val.is_some() => { - let Ident { name, pos } = &x.4; + let Ident { name, pos, .. } = &x.4; let index = name.clone().into(); let val = self.get_indexed_mut( mods, state, lib, target_val, index, *pos, true, is_ref, false, level, @@ -1194,7 +1194,7 @@ impl Engine { } // {xxx:map}.id Expr::Property(x) if target_val.is::() => { - let Ident { name, pos } = &x.4; + let Ident { name, pos, .. } = &x.4; let index = name.clone().into(); let val = self.get_indexed_mut( mods, state, lib, target_val, index, *pos, false, is_ref, false, level, @@ -1229,7 +1229,7 @@ impl Engine { Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target_val.is::() => { let mut val = match &x.lhs { Expr::Property(p) => { - let Ident { name, pos } = &p.4; + let Ident { name, pos, .. } = &p.4; let index = name.clone().into(); self.get_indexed_mut( mods, state, lib, target_val, index, *pos, false, is_ref, true, @@ -1378,6 +1378,7 @@ impl Engine { let Ident { name: var_name, pos: var_pos, + .. } = &x.2; self.inc_operations(state, *var_pos)?; @@ -2037,8 +2038,8 @@ impl Engine { .and_then(|guard_val| { if guard_val { self.eval_stmt(scope, mods, state, lib, this_ptr, if_block, level) - } else if let Some(stmt) = else_block { - self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) + } else if !else_block.is_noop() { + self.eval_stmt(scope, mods, state, lib, this_ptr, else_block, level) } else { Ok(Dynamic::UNIT) } @@ -2076,7 +2077,7 @@ impl Engine { // While loop Stmt::While(expr, body, _) => loop { - let condition = if let Some(expr) = expr { + let condition = if !expr.is_unit() { self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .as_bool() .map_err(|err| self.make_type_mismatch_err::(err, expr.position()))? @@ -2125,8 +2126,8 @@ impl Engine { }, // For loop - Stmt::For(expr, x, _) => { - let (name, stmt) = x.as_ref(); + Stmt::For(expr, name, x, _) => { + let stmt = x.as_ref(); let iter_obj = self .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .flatten(); @@ -2287,7 +2288,7 @@ impl Engine { } // Return value - Stmt::Return((ReturnType::Return, pos), Some(expr), _) => { + Stmt::Return(ReturnType::Return, Some(expr), pos) => { let value = self .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .flatten(); @@ -2295,12 +2296,12 @@ impl Engine { } // Empty return - Stmt::Return((ReturnType::Return, pos), None, _) => { + Stmt::Return(ReturnType::Return, None, pos) => { EvalAltResult::Return(Default::default(), *pos).into() } // Throw value - Stmt::Return((ReturnType::Exception, pos), Some(expr), _) => { + Stmt::Return(ReturnType::Exception, Some(expr), pos) => { let value = self .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .flatten(); @@ -2308,38 +2309,34 @@ impl Engine { } // Empty throw - Stmt::Return((ReturnType::Exception, pos), None, _) => { + Stmt::Return(ReturnType::Exception, None, pos) => { EvalAltResult::ErrorRuntime(Dynamic::UNIT, *pos).into() } // Let/const statement - Stmt::Let(var_def, expr, export, _) | Stmt::Const(var_def, expr, export, _) => { + Stmt::Let(expr, Ident { name, public, .. }, _) + | Stmt::Const(expr, Ident { name, public, .. }, _) => { let entry_type = match stmt { - Stmt::Let(_, _, _, _) => AccessMode::ReadWrite, - Stmt::Const(_, _, _, _) => AccessMode::ReadOnly, + Stmt::Let(_, _, _) => AccessMode::ReadWrite, + Stmt::Const(_, _, _) => AccessMode::ReadOnly, _ => unreachable!("should be Stmt::Let or Stmt::Const, but gets {:?}", stmt), }; - let value = if let Some(expr) = expr { - self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? - .flatten() - } else { - Dynamic::UNIT - }; + let value = self + .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .flatten(); + let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() { ( - var_def.name.to_string().into(), - if *export { - Some(var_def.name.clone()) - } else { - None - }, + name.to_string().into(), + if *public { Some(name.clone()) } else { None }, ) - } else if *export { + } else if *public { unreachable!("exported variable not on global level"); } else { - (unsafe_cast_var_name_to_lifetime(&var_def.name).into(), None) + (unsafe_cast_var_name_to_lifetime(name).into(), None) }; + scope.push_dynamic_value(var_name, entry_type, value); #[cfg(not(feature = "no_module"))] @@ -2400,14 +2397,13 @@ impl Engine { // Export statement #[cfg(not(feature = "no_module"))] Stmt::Export(list, _) => { - for (Ident { name, pos: id_pos }, rename) in list.iter() { + for (Ident { name, pos, .. }, rename) in list.iter() { // Mark scope variables as public if let Some(index) = scope.get_index(name).map(|(i, _)| i) { let alias = rename.as_ref().map(|x| &x.name).unwrap_or_else(|| name); scope.add_entry_alias(index, alias.clone()); } else { - return EvalAltResult::ErrorVariableNotFound(name.to_string(), *id_pos) - .into(); + return EvalAltResult::ErrorVariableNotFound(name.to_string(), *pos).into(); } } Ok(Dynamic::UNIT) diff --git a/src/fn_call.rs b/src/fn_call.rs index f9ce5626..8a959115 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -903,7 +903,7 @@ impl Engine { let fn_name = fn_ptr.fn_name(); let args_len = call_args.len() + fn_ptr.curry().len(); // Recalculate hashes - let hash = FnHash::from_script(calc_fn_hash(empty(), fn_name, args_len)); + let new_hash = FnHash::from_script(calc_fn_hash(empty(), fn_name, args_len)); // Arguments are passed as-is, adding the curried arguments let mut curry = fn_ptr.curry().iter().cloned().collect::>(); let mut arg_values = curry @@ -914,7 +914,7 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - mods, state, lib, fn_name, hash, args, false, false, pos, None, level, + mods, state, lib, fn_name, new_hash, args, false, false, pos, None, level, ) } KEYWORD_FN_PTR_CALL => { @@ -939,7 +939,7 @@ impl Engine { let fn_name = fn_ptr.fn_name(); let args_len = call_args.len() + fn_ptr.curry().len(); // Recalculate hash - let hash = FnHash::from_script_and_native( + let new_hash = FnHash::from_script_and_native( calc_fn_hash(empty(), fn_name, args_len), calc_fn_hash(empty(), fn_name, args_len + 1), ); @@ -953,7 +953,7 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - mods, state, lib, fn_name, hash, args, is_ref, true, pos, None, level, + mods, state, lib, fn_name, new_hash, args, is_ref, true, pos, None, level, ) } KEYWORD_FN_PTR_CURRY => { @@ -1096,7 +1096,6 @@ impl Engine { FnHash::from_native(calc_fn_hash(empty(), name, args_len)) }; } - // Handle Fn() KEYWORD_FN_PTR if args_expr.len() == 1 => { // Fn - only in function call style diff --git a/src/optimize.rs b/src/optimize.rs index 25f423ee..571ebd01 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,6 +1,6 @@ //! Module implementing the [`AST`] optimizer. -use crate::ast::{Expr, Stmt}; +use crate::ast::{Expr, Ident, Stmt}; use crate::dynamic::AccessMode; use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF}; use crate::fn_builtin::get_builtin_binary_op_fn; @@ -181,27 +181,17 @@ fn optimize_stmt_block( statements.iter_mut().for_each(|stmt| { match stmt { // Add constant literals into the state - Stmt::Const(var_def, Some(value_expr), _, _) => { + Stmt::Const(value_expr, Ident { name, .. }, _) => { optimize_expr(value_expr, state); if value_expr.is_constant() { - state.push_var(&var_def.name, AccessMode::ReadOnly, value_expr.clone()); + state.push_var(name, AccessMode::ReadOnly, value_expr.clone()); } } - Stmt::Const(var_def, None, _, _) => { - state.push_var(&var_def.name, AccessMode::ReadOnly, Expr::Unit(var_def.pos)); - } // Add variables into the state - Stmt::Let(var_def, expr, _, _) => { - if let Some(value_expr) = expr { - optimize_expr(value_expr, state); - } - - state.push_var( - &var_def.name, - AccessMode::ReadWrite, - Expr::Unit(var_def.pos), - ); + Stmt::Let(value_expr, Ident { name, pos, .. }, _) => { + optimize_expr(value_expr, state); + state.push_var(name, AccessMode::ReadWrite, Expr::Unit(*pos)); } // Optimize the statement _ => optimize_stmt(stmt, state, preserve_result), @@ -228,9 +218,7 @@ fn optimize_stmt_block( while let Some(expr) = statements.pop() { match expr { - Stmt::Let(_, expr, _, _) | Stmt::Const(_, expr, _, _) => { - removed = expr.as_ref().map(Expr::is_pure).unwrap_or(true) - } + Stmt::Let(expr, _, _) | Stmt::Const(expr, _, _) => removed = expr.is_pure(), #[cfg(not(feature = "no_module"))] Stmt::Import(expr, _, _) => removed = expr.is_pure(), _ => { @@ -286,9 +274,9 @@ fn optimize_stmt_block( Stmt::Noop(pos) } // Only one let statement - leave it alone - [x] if matches!(x, Stmt::Let(_, _, _, _)) => Stmt::Block(statements, pos), + [x] if matches!(x, Stmt::Let(_, _, _)) => Stmt::Block(statements, pos), // Only one const statement - leave it alone - [x] if matches!(x, Stmt::Const(_, _, _, _)) => Stmt::Block(statements, pos), + [x] if matches!(x, Stmt::Const(_, _, _)) => Stmt::Block(statements, pos), // Only one import statement - leave it alone #[cfg(not(feature = "no_module"))] [x] if matches!(x, Stmt::Import(_, _, _)) => Stmt::Block(statements, pos), @@ -316,17 +304,17 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { }, // if false { if_block } -> Noop - Stmt::If(Expr::BoolConstant(false, pos), x, _) if x.1.is_none() => { + Stmt::If(Expr::BoolConstant(false, pos), x, _) if x.1.is_noop() => { state.set_dirty(); *stmt = Stmt::Noop(*pos); } // if true { if_block } -> if_block - Stmt::If(Expr::BoolConstant(true, _), x, _) if x.1.is_none() => { + Stmt::If(Expr::BoolConstant(true, _), x, _) if x.1.is_noop() => { *stmt = mem::take(&mut x.0); optimize_stmt(stmt, state, true); } // if expr { Noop } - Stmt::If(condition, x, _) if x.1.is_none() && matches!(x.0, Stmt::Noop(_)) => { + Stmt::If(condition, x, _) if x.1.is_noop() && x.0.is_noop() => { state.set_dirty(); let pos = condition.position(); @@ -342,13 +330,13 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { }; } // if expr { if_block } - Stmt::If(condition, x, _) if x.1.is_none() => { + Stmt::If(condition, x, _) if x.1.is_noop() => { optimize_expr(condition, state); optimize_stmt(&mut x.0, state, true); } // if false { if_block } else { else_block } -> else_block - Stmt::If(Expr::BoolConstant(false, _), x, _) if x.1.is_some() => { - *stmt = mem::take(x.1.as_mut().unwrap()); + Stmt::If(Expr::BoolConstant(false, _), x, _) if !x.1.is_noop() => { + *stmt = mem::take(&mut x.1); optimize_stmt(stmt, state, true); } // if true { if_block } else { else_block } -> if_block @@ -360,13 +348,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { Stmt::If(condition, x, _) => { optimize_expr(condition, state); optimize_stmt(&mut x.0, state, true); - if let Some(else_block) = x.1.as_mut() { - optimize_stmt(else_block, state, true); - match else_block { - Stmt::Noop(_) => x.1 = None, // Noop -> no else block - _ => (), - } - } + optimize_stmt(&mut x.1, state, true); } // switch const { ... } @@ -406,24 +388,21 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { } // while false { block } -> Noop - Stmt::While(Some(Expr::BoolConstant(false, pos)), _, _) => { + Stmt::While(Expr::BoolConstant(false, pos), _, _) => { state.set_dirty(); *stmt = Stmt::Noop(*pos) } // while expr { block } Stmt::While(condition, block, _) => { optimize_stmt(block, state, false); - - if let Some(condition) = condition { - optimize_expr(condition, state); - } + optimize_expr(condition, state); match **block { // while expr { break; } -> { expr; } Stmt::Break(pos) => { // Only a single break statement - turn into running the guard expression once state.set_dirty(); - if let Some(condition) = condition { + if !condition.is_unit() { let mut statements = vec![Stmt::Expr(mem::take(condition))]; if preserve_result { statements.push(Stmt::Noop(pos)) @@ -449,14 +428,12 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { optimize_expr(condition, state); } // for id in expr { block } - Stmt::For(iterable, x, _) => { + Stmt::For(iterable, _, block, _) => { optimize_expr(iterable, state); - optimize_stmt(&mut x.1, state, false); + optimize_stmt(block, state, false); } // let id = expr; - Stmt::Let(_, Some(expr), _, _) => optimize_expr(expr, state), - // let id; - Stmt::Let(_, None, _, _) => (), + Stmt::Let(expr, _, _) => optimize_expr(expr, state), // import expr as var; #[cfg(not(feature = "no_module"))] Stmt::Import(expr, _, _) => optimize_expr(expr, state), @@ -789,40 +766,23 @@ fn optimize_top_level( statements.iter_mut().enumerate().for_each(|(i, stmt)| { match stmt { - Stmt::Const(var_def, expr, _, _) if expr.is_some() => { + Stmt::Const(value_expr, Ident { name, .. }, _) => { // Load constants - let value_expr = expr.as_mut().unwrap(); optimize_expr(value_expr, &mut state); if value_expr.is_constant() { - state.push_var(&var_def.name, AccessMode::ReadOnly, value_expr.clone()); - } - - // Keep it in the global scope - if value_expr.is_unit() { - state.set_dirty(); - *expr = None; + state.push_var(name, AccessMode::ReadOnly, value_expr.clone()); } } - Stmt::Const(var_def, None, _, _) => { - state.push_var(&var_def.name, AccessMode::ReadOnly, Expr::Unit(var_def.pos)); - } - Stmt::Let(var_def, expr, _, _) => { - if let Some(value_expr) = expr { - optimize_expr(value_expr, &mut state); - } - - state.push_var( - &var_def.name, - AccessMode::ReadWrite, - Expr::Unit(var_def.pos), - ); + Stmt::Let(value_expr, Ident { name, pos, .. }, _) => { + optimize_expr(value_expr, &mut state); + state.push_var(name, AccessMode::ReadWrite, Expr::Unit(*pos)); } _ => { // Keep all variable declarations at this level // and always keep the last return value let keep = match stmt { - Stmt::Let(_, _, _, _) | Stmt::Const(_, _, _, _) => true, + Stmt::Let(_, _, _) | Stmt::Const(_, _, _) => true, #[cfg(not(feature = "no_module"))] Stmt::Import(_, _, _) => true, _ => i == num_statements - 1, @@ -911,11 +871,11 @@ pub fn optimize_into_ast( // {} -> Noop fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) { // { return val; } -> val - Stmt::Return((crate::ast::ReturnType::Return, _), Some(expr), _) => { + Stmt::Return(crate::ast::ReturnType::Return, Some(expr), _) => { Stmt::Expr(expr) } // { return; } -> () - Stmt::Return((crate::ast::ReturnType::Return, pos), None, _) => { + Stmt::Return(crate::ast::ReturnType::Return, None, pos) => { Stmt::Expr(Expr::Unit(pos)) } // All others diff --git a/src/parser.rs b/src/parser.rs index 3b1d05f7..e0c58dc9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -755,7 +755,8 @@ fn parse_map_literal( let expr = parse_expr(input, state, lib, settings.level_up())?; let name = state.get_interned_string(name); - map.push((Ident { name, pos }, expr)); + let public = false; + map.push((Ident { name, pos, public }, expr)); match input.peek().unwrap() { (Token::Comma, _) => { @@ -1033,6 +1034,7 @@ fn parse_primary( let var_name_def = Ident { name: state.get_interned_string(s), pos: settings.pos, + public: false, }; Expr::Variable(Box::new((None, None, var_name_def))) } @@ -1047,6 +1049,7 @@ fn parse_primary( let var_name_def = Ident { name: state.get_interned_string(s), pos: settings.pos, + public: false, }; Expr::Variable(Box::new((None, None, var_name_def))) } @@ -1056,6 +1059,7 @@ fn parse_primary( let var_name_def = Ident { name: state.get_interned_string(s), pos: settings.pos, + public: false, }; Expr::Variable(Box::new((index, None, var_name_def))) } @@ -1075,6 +1079,7 @@ fn parse_primary( let var_name_def = Ident { name: state.get_interned_string(s), pos: settings.pos, + public: false, }; Expr::Variable(Box::new((None, None, var_name_def))) } @@ -1083,6 +1088,7 @@ fn parse_primary( let var_name_def = Ident { name: state.get_interned_string(s), pos: settings.pos, + public: false, }; Expr::Variable(Box::new((None, None, var_name_def))) } @@ -1147,14 +1153,14 @@ fn parse_primary( .into_err(pos)); } - let (_, namespace, Ident { name, pos }) = *x; + let (_, namespace, Ident { name, pos, .. }) = *x; settings.pos = pos; let ns = namespace.map(|(_, ns)| ns); parse_fn_call(input, state, lib, name, true, ns, settings.level_up())? } // Function call (Expr::Variable(x), Token::LeftParen) => { - let (_, namespace, Ident { name, pos }) = *x; + let (_, namespace, Ident { name, pos, .. }) = *x; settings.pos = pos; let ns = namespace.map(|(_, ns)| ns); parse_fn_call(input, state, lib, name, false, ns, settings.level_up())? @@ -1176,6 +1182,7 @@ fn parse_primary( let var_name_def = Ident { name: state.get_interned_string(id2), pos: pos2, + public: false, }; Expr::Variable(Box::new((index, namespace, var_name_def))) } @@ -1395,7 +1402,7 @@ fn make_assignment_stmt<'a>( } // var (indexed) = rhs Expr::Variable(x) => { - let (index, _, Ident { name, pos }) = x.as_ref(); + let (index, _, Ident { name, pos, .. }) = x.as_ref(); match state.stack[(state.stack.len() - index.unwrap().get())].1 { AccessMode::ReadWrite => { Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos)) @@ -1416,7 +1423,7 @@ fn make_assignment_stmt<'a>( } // var[???] (indexed) = rhs, var.??? (indexed) = rhs Expr::Variable(x) => { - let (index, _, Ident { name, pos }) = x.as_ref(); + let (index, _, Ident { name, pos, .. }) = x.as_ref(); match state.stack[(state.stack.len() - index.unwrap().get())].1 { AccessMode::ReadWrite => { Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos)) @@ -1846,7 +1853,8 @@ fn parse_custom_syntax( let name = state.get_interned_string(s); segments.push(name.clone()); tokens.push(state.get_interned_string(MARKER_IDENT)); - let var_name_def = Ident { name, pos }; + let public = false; + let var_name_def = Ident { name, pos, public }; keywords.push(Expr::Variable(Box::new((None, None, var_name_def)))); } (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { @@ -1995,15 +2003,15 @@ fn parse_if( // if guard { if_body } else ... let else_body = if match_token(input, Token::Else).0 { - Some(if let (Token::If, _) = input.peek().unwrap() { + if let (Token::If, _) = input.peek().unwrap() { // if guard { if_body } else if ... parse_if(input, state, lib, settings.level_up())? } else { // if guard { if_body } else { else-body } parse_block(input, state, lib, settings.level_up())? - }) + } } else { - None + Stmt::Noop(Position::NONE) }; Ok(Stmt::If( @@ -2028,9 +2036,9 @@ fn parse_while_loop( (Token::While, pos) => { ensure_not_statement_expr(input, "a boolean")?; let expr = parse_expr(input, state, lib, settings.level_up())?; - (Some(expr), pos) + (expr, pos) } - (Token::Loop, pos) => (None, pos), + (Token::Loop, pos) => (Expr::Unit(Position::NONE), pos), _ => unreachable!(), }; settings.pos = token_pos; @@ -2130,7 +2138,7 @@ fn parse_for( state.stack.truncate(prev_stack_len); - Ok(Stmt::For(expr, Box::new((name, body)), settings.pos)) + Ok(Stmt::For(expr, name, Box::new(body), settings.pos)) } /// Parse a variable definition statement. @@ -2162,23 +2170,24 @@ fn parse_let( let var_def = Ident { name: name.clone(), pos, + public: export, }; // let name = ... let expr = if match_token(input, Token::Equals).0 { // let name = expr - Some(parse_expr(input, state, lib, settings.level_up())?) + parse_expr(input, state, lib, settings.level_up())? } else { - None + Expr::Unit(Position::NONE) }; state.stack.push((name, var_type)); match var_type { // let name = expr - AccessMode::ReadWrite => Ok(Stmt::Let(Box::new(var_def), expr, export, settings.pos)), + AccessMode::ReadWrite => Ok(Stmt::Let(expr, var_def, settings.pos)), // const name = { expr:constant } - AccessMode::ReadOnly => Ok(Stmt::Const(Box::new(var_def), expr, export, settings.pos)), + AccessMode::ReadOnly => Ok(Stmt::Const(expr, var_def, settings.pos)), } } @@ -2219,10 +2228,11 @@ fn parse_import( Ok(Stmt::Import( expr, - Some(Box::new(Ident { + Some(Ident { name, pos: name_pos, - })), + public: false, + }), settings.pos, )) } @@ -2273,6 +2283,7 @@ fn parse_export( (Token::Identifier(s), pos) => Some(Ident { name: state.get_interned_string(s), pos, + public: false, }), (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { return Err(PERR::Reserved(s).into_err(pos)); @@ -2288,6 +2299,7 @@ fn parse_export( Ident { name: state.get_interned_string(id), pos: id_pos, + public: false, }, rename, )); @@ -2565,16 +2577,13 @@ fn parse_stmt( match input.peek().unwrap() { // `return`/`throw` at - (Token::EOF, pos) => Ok(Stmt::Return((return_type, token_pos), None, *pos)), + (Token::EOF, _) => Ok(Stmt::Return(return_type, None, token_pos)), // `return;` or `throw;` - (Token::SemiColon, _) => { - Ok(Stmt::Return((return_type, token_pos), None, settings.pos)) - } + (Token::SemiColon, _) => Ok(Stmt::Return(return_type, None, token_pos)), // `return` or `throw` with expression (_, _) => { let expr = parse_expr(input, state, lib, settings.level_up())?; - let pos = expr.position(); - Ok(Stmt::Return((return_type, token_pos), Some(expr), pos)) + Ok(Stmt::Return(return_type, Some(expr), token_pos)) } } } @@ -2629,6 +2638,7 @@ fn parse_try_catch( (Token::Identifier(s), pos) => Ident { name: state.get_interned_string(s), pos, + public: false, }, (_, pos) => return Err(PERR::VariableExpected.into_err(pos)), }; @@ -2861,6 +2871,7 @@ fn parse_anon_fn( .map(|(name, &pos)| Ident { name: name.clone(), pos, + public: false, }) .collect() } diff --git a/tests/optimizer.rs b/tests/optimizer.rs index d062ec5f..c5b91b42 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -54,8 +54,9 @@ fn test_optimizer_parse() -> Result<(), Box> { engine.set_optimization_level(OptimizationLevel::Simple); let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?; + println!("{:?}", ast); - assert!(format!("{:?}", ast).starts_with(r#"AST { source: None, statements: [Block([Const(Ident("DECISION" @ 1:9), Some(BoolConstant(false, 1:20)), false, 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)]"#)); + assert!(format!("{:?}", ast).starts_with(r#"AST { source: None, statements: [Block([Const(BoolConstant(false, 1:20), Ident("DECISION" @ 1:9), 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)], functions: Module("#)); let ast = engine.compile("if 1 == 2 { 42 }")?;