From 9dc4a60ef20b54a7246888e0212cd3053f25c0ac Mon Sep 17 00:00:00 2001 From: Xavier L'Heureux <xavier.lheureux@icloud.com> Date: Wed, 10 Apr 2019 22:34:01 -0400 Subject: [PATCH] Preallocate space and reduce returns --- src/lib/shell/flow.rs | 142 +++++++------------- src/lib/shell/flow_control.rs | 246 +++++++++++++++++----------------- 2 files changed, 169 insertions(+), 219 deletions(-) diff --git a/src/lib/shell/flow.rs b/src/lib/shell/flow.rs index 968fe600..4fff77e3 100644 --- a/src/lib/shell/flow.rs +++ b/src/lib/shell/flow.rs @@ -18,19 +18,8 @@ use crate::{ }; use itertools::Itertools; use small; -use std::io::{stdout, Write}; - -macro_rules! handle_signal { - ($signal:expr) => { - match $signal { - Condition::Break => break, - Condition::SigInt => return Condition::SigInt, - _ => (), - } - }; -} -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub(crate) enum Condition { Continue, Break, @@ -120,7 +109,11 @@ impl FlowLogic for Shell { } } - handle_signal!(self.execute_statements(statements)); + match self.execute_statements(statements) { + Condition::Break => break, + Condition::SigInt => return Condition::SigInt, + Condition::Continue | Condition::NoOp => (), + } }; } @@ -149,13 +142,13 @@ impl FlowLogic for Shell { fn execute_while(&mut self, expression: &[Statement], statements: &[Statement]) -> Condition { loop { - self.execute_statements(&expression); + self.execute_statements(expression); if self.previous_status != 0 { return Condition::NoOp; } // Cloning is needed so the statement can be re-iterated again if needed. - match self.execute_statements(&statements) { + match self.execute_statements(statements) { Condition::Break => return Condition::NoOp, Condition::SigInt => return Condition::SigInt, _ => (), @@ -179,21 +172,20 @@ impl FlowLogic for Shell { self.variables.set("?", self.previous_status.to_string()); } Statement::While { expression, statements } => { - if let Condition::SigInt = self.execute_while(&expression, &statements) { + if self.execute_while(&expression, &statements) == Condition::SigInt { return Condition::SigInt; } } Statement::For { variables, values, statements } => { - if let Condition::SigInt = self.execute_for(&variables, &values, &statements) { + if self.execute_for(&variables, &values, &statements) == Condition::SigInt { return Condition::SigInt; } } Statement::If { expression, success, else_if, failure, .. } => { - match self.execute_if(&expression, &success, &else_if, &failure) { - Condition::Break => return Condition::Break, - Condition::Continue => return Condition::Continue, - Condition::NoOp => (), - Condition::SigInt => return Condition::SigInt, + let condition = self.execute_if(&expression, &success, &else_if, &failure); + + if condition != Condition::NoOp { + return condition; } } Statement::Function { name, args, statements, description } => { @@ -229,7 +221,7 @@ impl FlowLogic for Shell { } }, Statement::Time(box_statement) => { - let time = ::std::time::Instant::now(); + let time = std::time::Instant::now(); let condition = self.execute_statement(box_statement); @@ -237,24 +229,13 @@ impl FlowLogic for Shell { let seconds = duration.as_secs(); let nanoseconds = duration.subsec_nanos(); - let stdout = stdout(); - let mut stdout = stdout.lock(); - let _ = if seconds > 60 { - writeln!( - stdout, - "real {}m{:02}.{:09}s", - seconds / 60, - seconds % 60, - nanoseconds - ) + if seconds > 60 { + println!("real {}m{:02}.{:09}s", seconds / 60, seconds % 60, nanoseconds); } else { - writeln!(stdout, "real {}.{:09}s", seconds, nanoseconds) - }; - match condition { - Condition::Break => return Condition::Break, - Condition::Continue => return Condition::Continue, - Condition::NoOp => (), - Condition::SigInt => return Condition::SigInt, + println!("real {}.{:09}s", seconds, nanoseconds); + } + if condition != Condition::NoOp { + return condition; } } Statement::And(box_statement) => { @@ -263,11 +244,8 @@ impl FlowLogic for Shell { _ => Condition::NoOp, }; - match condition { - Condition::Break => return Condition::Break, - Condition::Continue => return Condition::Continue, - Condition::NoOp => (), - Condition::SigInt => return Condition::SigInt, + if condition != Condition::NoOp { + return condition; } } Statement::Or(box_statement) => { @@ -276,11 +254,8 @@ impl FlowLogic for Shell { _ => Condition::NoOp, }; - match condition { - Condition::Break => return Condition::Break, - Condition::Continue => return Condition::Continue, - Condition::NoOp => (), - Condition::SigInt => return Condition::SigInt, + if condition != Condition::NoOp { + return condition; } } Statement::Not(box_statement) => { @@ -297,11 +272,10 @@ impl FlowLogic for Shell { Statement::Break => return Condition::Break, Statement::Continue => return Condition::Continue, Statement::Match { expression, cases } => { - match self.execute_match(expression, &cases) { - Condition::Break => return Condition::Break, - Condition::Continue => return Condition::Continue, - Condition::NoOp => (), - Condition::SigInt => return Condition::SigInt, + let condition = self.execute_match(expression, &cases); + + if condition != Condition::NoOp { + return condition; } } _ => {} @@ -322,20 +296,15 @@ impl FlowLogic for Shell { fn execute_statements(&mut self, statements: &[Statement]) -> Condition { self.variables.new_scope(false); - let mut condition = None; - for statement in statements { - match self.execute_statement(statement) { - Condition::NoOp => {} - cond => { - condition = Some(cond); - break; - } - } - } + let condition = statements + .iter() + .map(|statement| self.execute_statement(statement)) + .find(|&condition| condition != Condition::NoOp) + .unwrap_or(Condition::NoOp); self.variables.pop_scope(); - condition.unwrap_or(Condition::NoOp) + condition } fn execute_match<T: AsRef<str>>(&mut self, expression: T, cases: &[Case]) -> Condition { @@ -424,13 +393,12 @@ fn expand_pipeline( shell: &Shell, pipeline: &Pipeline, ) -> Result<(Pipeline, Vec<Statement>), String> { - let mut item_iter = pipeline.items.iter(); - let mut items: Vec<PipeItem> = Vec::new(); + let mut item_iter = pipeline.items.iter().cloned(); + let mut items: Vec<PipeItem> = Vec::with_capacity(item_iter.size_hint().0); let mut statements = Vec::new(); while let Some(item) = item_iter.next() { - let possible_alias = shell.variables.get::<types::Alias>(item.job.command.as_ref()); - if let Some(alias) = possible_alias { + if let Some(alias) = shell.variables.get::<types::Alias>(item.job.command.as_ref()) { statements = StatementSplitter::new(alias.0.as_str()).map(parse_and_validate).collect(); // First item in the alias should be a pipeline item, otherwise it cannot @@ -442,14 +410,10 @@ fn expand_pipeline( first.inputs = item.inputs.clone(); // Add alias arguments to expanded args if there's any. - if item.job.args.len() > 1 { - for arg in &item.job.args[1..] { - first.job.args.push(arg.clone()); - } - } + first.job.args.extend(item.job.args.iter().skip(1).cloned()); } if len == 1 { - if let Some(last) = pline.items.last_mut() { + if let Some(mut last) = pline.items.last_mut() { last.outputs = item.outputs.clone(); last.job.kind = item.job.kind; } @@ -460,13 +424,12 @@ fn expand_pipeline( // Handle pipeline being broken half by i.e.: '&&' or '||' if !statements.is_empty() { - let err = match statements.last_mut().unwrap() { + match statements.last_mut().unwrap() { Statement::And(ref mut boxed_stm) | Statement::Or(ref mut boxed_stm) | Statement::Not(ref mut boxed_stm) | Statement::Time(ref mut boxed_stm) => { - let stm = &mut **boxed_stm; - if let Statement::Pipeline(ref mut pline) = stm { + if let Statement::Pipeline(ref mut pline) = &mut **boxed_stm { // Set output of alias to be the output of last pipeline. if let Some(last) = pline.items.last_mut() { last.outputs = item.outputs.clone(); @@ -474,24 +437,17 @@ fn expand_pipeline( } // Append rest of the pipeline to the last pipeline in the // alias. - for item in item_iter { - pline.items.push(item.clone()); - } - // No error - false + pline.items.extend(item_iter); } else { // Error in expansion - true + return Err(format!( + "unable to pipe outputs of alias: '{} = {}'", + item.job.command.as_str(), + alias.0.as_str() + )); } } - _ => false, - }; - if err { - return Err(format!( - "unable to pipe outputs of alias: '{} = {}'", - item.job.command.as_str(), - alias.0.as_str() - )); + _ => (), } break; } diff --git a/src/lib/shell/flow_control.rs b/src/lib/shell/flow_control.rs index 81e54377..1fd6372d 100644 --- a/src/lib/shell/flow_control.rs +++ b/src/lib/shell/flow_control.rs @@ -179,177 +179,173 @@ pub(crate) fn insert_statement( | Statement::While { .. } | Statement::Match { .. } | Statement::If { .. } - | Statement::Function { .. } => flow_control.block.push(statement), + | Statement::Function { .. } => { + flow_control.block.push(statement); + Ok(None) + } // Case is special as it should pop back previous Case Statement::Case(_) => { - let mut top_is_case = false; match flow_control.block.last() { - Some(Statement::Case(_)) => top_is_case = true, + Some(Statement::Case(_)) => { + let case = flow_control.block.pop().unwrap(); + let _ = insert_into_block(&mut flow_control.block, case); + } Some(Statement::Match { .. }) => (), _ => return Err("ion: error: Case { .. } found outside of Match { .. } block"), } - if top_is_case { - let case = flow_control.block.pop().unwrap(); - let _ = insert_into_block(&mut flow_control.block, case); - } flow_control.block.push(statement); + Ok(None) } Statement::End => { match flow_control.block.len() { - 0 => return Err("ion: error: keyword End found but no block to close"), + 0 => Err("ion: error: keyword End found but no block to close"), // Ready to return the complete block - 1 => return Ok(flow_control.block.pop()), + 1 => Ok(flow_control.block.pop()), // Merge back the top block into the previous one _ => { let block = flow_control.block.pop().unwrap(); - match block { - Statement::Case(_) => { - // Merge last Case back and pop off Match too - insert_into_block(&mut flow_control.block, block)?; - let match_stm = flow_control.block.pop().unwrap(); - if !flow_control.block.is_empty() { - insert_into_block(&mut flow_control.block, match_stm)?; - } else { - return Ok(Some(match_stm)); - } + let mut case = false; + if let Statement::Case(_) = block { + case = true; + } + insert_into_block(&mut flow_control.block, block)?; + if case { + // Merge last Case back and pop off Match too + let match_stm = flow_control.block.pop().unwrap(); + if !flow_control.block.is_empty() { + insert_into_block(&mut flow_control.block, match_stm)?; + + Ok(None) + } else { + Ok(Some(match_stm)) } - _ => insert_into_block(&mut flow_control.block, block)?, + } else { + Ok(None) } } } } Statement::And(_) | Statement::Or(_) if !flow_control.block.is_empty() => { let mut pushed = true; - if let Some(top) = flow_control.block.last_mut() { - match top { - Statement::If { - ref mut expression, - ref mode, - ref success, - ref mut else_if, - .. - } => match *mode { - 0 if success.is_empty() => { - // Insert into If expression if there's no previous statement. - expression.push(statement.clone()); - } - 1 => { - // Try to insert into last ElseIf expression if there's no previous - // statement. - if let Some(eif) = else_if.last_mut() { - if eif.success.is_empty() { - eif.expression.push(statement.clone()); - } else { - pushed = false; - } + match flow_control.block.last_mut().unwrap() { + Statement::If { + ref mut expression, + ref mode, + ref success, + ref mut else_if, + .. + } => match *mode { + 0 if success.is_empty() => { + // Insert into If expression if there's no previous statement. + expression.push(statement.clone()); + } + 1 => { + // Try to insert into last ElseIf expression if there's no previous + // statement. + if let Some(eif) = else_if.last_mut() { + if eif.success.is_empty() { + eif.expression.push(statement.clone()); } else { - // should not be reached... - unreachable!("Missmatch in 'If' mode!") + pushed = false; } - } - _ => pushed = false, - }, - Statement::While { ref mut expression, ref statements } => { - if statements.is_empty() { - expression.push(statement.clone()); } else { - pushed = false; + // should not be reached... + unreachable!("Missmatch in 'If' mode!") } } _ => pushed = false, + }, + Statement::While { ref mut expression, ref statements } => { + if statements.is_empty() { + expression.push(statement.clone()); + } else { + pushed = false; + } } - } else { - unreachable!() + _ => pushed = false, } if !pushed { insert_into_block(&mut flow_control.block, statement)?; } + + Ok(None) } Statement::Time(inner) => { if inner.is_block() { flow_control.block.push(Statement::Time(inner)); + Ok(None) } else { - return Ok(Some(Statement::Time(inner))); + Ok(Some(Statement::Time(inner))) } } _ => { if !flow_control.block.is_empty() { insert_into_block(&mut flow_control.block, statement)?; + Ok(None) } else { // Filter out toplevel statements that should produce an error // otherwise return the statement for immediat execution match statement { Statement::ElseIf(_) => { - return Err("ion: error: found ElseIf { .. } without If { .. } block"); - } - Statement::Else => return Err("ion: error: found Else without If { .. } block"), - Statement::Break => return Err("ion: error: found Break without loop body"), - Statement::Continue => { - return Err("ion: error: found Continue without loop body"); + Err("ion: error: found ElseIf { .. } without If { .. } block") } + Statement::Else => Err("ion: error: found Else without If { .. } block"), + Statement::Break => Err("ion: error: found Break without loop body"), + Statement::Continue => Err("ion: error: found Continue without loop body"), // Toplevel statement, return to execute immediately - _ => return Ok(Some(statement)), + _ => Ok(Some(statement)), } } } } - Ok(None) } fn insert_into_block(block: &mut Vec<Statement>, statement: Statement) -> Result<(), &'static str> { - if let Some(top_block) = block.last_mut() { - let block = match top_block { - Statement::Time(inner) => inner, - _ => top_block, - }; - - match block { - Statement::Function { ref mut statements, .. } => statements.push(statement), - Statement::For { ref mut statements, .. } => statements.push(statement), - Statement::While { ref mut statements, .. } => statements.push(statement), - Statement::Match { ref mut cases, .. } => match statement { - Statement::Case(case) => cases.push(case), - _ => { - return Err( - "ion: error: statement found outside of Case { .. } block in Match { .. }" - ); - } - }, - Statement::Case(ref mut case) => case.statements.push(statement), - Statement::If { - ref mut success, - ref mut else_if, - ref mut failure, - ref mut mode, - .. - } => match statement { - Statement::ElseIf(eif) => { - if *mode > 1 { - return Err("ion: error: ElseIf { .. } found after Else"); - } else { - *mode = 1; - else_if.push(eif); - } + let block = match block.last_mut().expect("Should not insert statement if stack is empty!") { + Statement::Time(inner) => inner, + top_block @ _ => top_block, + }; + + match block { + Statement::Function { ref mut statements, .. } => statements.push(statement), + Statement::For { ref mut statements, .. } => statements.push(statement), + Statement::While { ref mut statements, .. } => statements.push(statement), + Statement::Match { ref mut cases, .. } => match statement { + Statement::Case(case) => cases.push(case), + _ => { + return Err( + "ion: error: statement found outside of Case { .. } block in Match { .. }" + ); + } + }, + Statement::Case(ref mut case) => case.statements.push(statement), + Statement::If { + ref mut success, ref mut else_if, ref mut failure, ref mut mode, .. + } => match statement { + Statement::ElseIf(eif) => { + if *mode > 1 { + return Err("ion: error: ElseIf { .. } found after Else"); + } else { + *mode = 1; + else_if.push(eif); } - Statement::Else => { - if *mode == 2 { - return Err("ion: error: Else block already exists"); - } else { - *mode = 2; - } + } + Statement::Else => { + if *mode == 2 { + return Err("ion: error: Else block already exists"); + } else { + *mode = 2; } - _ => match *mode { - 0 => success.push(statement), - 1 => else_if.last_mut().unwrap().success.push(statement), - 2 => failure.push(statement), - _ => unreachable!(), - }, + } + _ => match *mode { + 0 => success.push(statement), + 1 => else_if.last_mut().unwrap().success.push(statement), + 2 => failure.push(statement), + _ => unreachable!(), }, - _ => unreachable!("Not block-like statement pushed to stack!"), - } - } else { - unreachable!("Should not insert statement if stack is empty!") + }, + _ => unreachable!("Not block-like statement pushed to stack!"), } Ok(()) } @@ -390,27 +386,25 @@ impl Function { return Err(FunctionError::InvalidArgumentCount); } - let name = self.name.clone(); - - let mut values: SmallVec<[_; 8]> = SmallVec::new(); - - for (type_, value) in self.args.iter().zip(args.iter().skip(1)) { - let value = match value_check(shell, value.as_ref(), &type_.kind) { - Ok(value) => value, - Err(_) => { - return Err(FunctionError::InvalidArgumentType( + let values = self + .args + .iter() + .zip(args.iter().skip(1)) + .map(|(type_, value)| { + if let Ok(value) = value_check(shell, value.as_ref(), &type_.kind) { + Ok((type_.clone(), value)) + } else { + Err(FunctionError::InvalidArgumentType( type_.kind.clone(), value.as_ref().into(), - )); + )) } - }; - - values.push((type_.clone(), value)); - } + }) + .collect::<Result<SmallVec<[_; 8]>, _>>()?; let index = shell .variables - .index_scope_for_var(&name) + .index_scope_for_var(&self.name) .expect("execute called with invalid function"); // Pop off all scopes since function temporarily -- GitLab