From e3acc416c3da54c7b03b7079cc57e4d9063d0f3c Mon Sep 17 00:00:00 2001
From: Michael Aaron Murphy <mmstickman@gmail.com>
Date: Sat, 8 Apr 2017 13:40:05 -0400
Subject: [PATCH] Implement Continue Keyword

---
 src/flow_control.rs        |  1 +
 src/parser/grammar.rustpeg |  9 +++--
 src/shell/flow.rs          | 83 +++++++++++++++++++++++++-------------
 3 files changed, 61 insertions(+), 32 deletions(-)

diff --git a/src/flow_control.rs b/src/flow_control.rs
index b9a2bbc2..6d1d28bc 100644
--- a/src/flow_control.rs
+++ b/src/flow_control.rs
@@ -45,6 +45,7 @@ pub enum Statement {
     Else,
     End,
     Break,
+    Continue,
     Pipeline(Pipeline),
     Default
 }
diff --git a/src/parser/grammar.rustpeg b/src/parser/grammar.rustpeg
index 1962b6d8..93f2f35e 100644
--- a/src/parser/grammar.rustpeg
+++ b/src/parser/grammar.rustpeg
@@ -12,6 +12,7 @@ parse_ -> Statement
       / fn_
       / end_
       / break_
+      / continue_
       / pipelines
 
 #[pub]
@@ -22,9 +23,11 @@ let_ -> Statement
 
 #[pub]
 break_ -> Statement
-    = whitespace* "break" {
-        Statement::Break
-    }
+    = whitespace* "break" { Statement::Break }
+
+#[pub]
+continue_ -> Statement
+    = whitespace* "continue" { Statement::Continue }
 
 #[pub]
 if_ -> Statement
diff --git a/src/shell/flow.rs b/src/shell/flow.rs
index f0e92dcc..08176786 100644
--- a/src/shell/flow.rs
+++ b/src/shell/flow.rs
@@ -10,6 +10,12 @@ use super::assignments::let_assignment;
 
 use glob::glob;
 
+pub enum Condition {
+    Continue,
+    Break,
+    NoOp
+}
+
 pub trait FlowLogic {
     /// Receives a command and attempts to execute the contents.
     fn on_command(&mut self, command_string: &str);
@@ -26,10 +32,10 @@ pub trait FlowLogic {
 
     /// Conditionally executes branches of statements according to evaluated expressions
     fn execute_if(&mut self, expression: Pipeline, success: Vec<Statement>,
-        else_if: Vec<ElseIf>, failure: Vec<Statement>) -> bool;
+        else_if: Vec<ElseIf>, failure: Vec<Statement>) -> Condition;
 
     /// Simply executes all supplied statemnts.
-    fn execute_statements(&mut self, statements: Vec<Statement>) -> bool;
+    fn execute_statements(&mut self, statements: Vec<Statement>) -> Condition;
 }
 
 impl<'a> FlowLogic for Shell<'a> {
@@ -94,7 +100,7 @@ impl<'a> FlowLogic for Shell<'a> {
                 mem::swap(&mut self.flow_control.current_statement, &mut replacement);
 
                 match replacement {
-                    Statement::Let{ expression } => {
+                    Statement::Let { expression } => {
                         self.previous_status = let_assignment(&expression, &mut self.variables, &self.directory_stack);
                     },
                     Statement::While { expression, statements } => {
@@ -131,11 +137,11 @@ impl<'a> FlowLogic for Shell<'a> {
         }
     }
 
-    fn execute_statements(&mut self, mut statements: Vec<Statement>) -> bool {
+    fn execute_statements(&mut self, mut statements: Vec<Statement>) -> Condition {
         let mut iterator = statements.drain(..);
         while let Some(statement) = iterator.next() {
             match statement {
-                Statement::Let{ expression } => {
+                Statement::Let { expression } => {
                     self.previous_status = let_assignment(&expression, &mut self.variables, &self.directory_stack);
                 },
                 Statement::While { expression, mut statements } => {
@@ -151,16 +157,20 @@ impl<'a> FlowLogic for Shell<'a> {
                 Statement::If { expression, mut success, mut else_if, mut failure } => {
                     self.flow_control.level += 1;
                     if let Err(why) = collect_if(&mut iterator, &mut success, &mut else_if,
-                        &mut failure, &mut self.flow_control.level, 0) {
-                            let stderr = io::stderr();
-                            let mut stderr = stderr.lock();
-                            let _ = writeln!(stderr, "{}", why);
-                            self.flow_control.level = 0;
-                            self.flow_control.current_if_mode = 0;
-                            return true
-                        }
-                    if self.execute_if(expression, success, else_if, failure) {
-                        return true
+                        &mut failure, &mut self.flow_control.level, 0)
+                    {
+                        let stderr = io::stderr();
+                        let mut stderr = stderr.lock();
+                        let _ = writeln!(stderr, "{}", why);
+                        self.flow_control.level = 0;
+                        self.flow_control.current_if_mode = 0;
+                        return Condition::Break
+                    }
+
+                    match self.execute_if(expression, success, else_if, failure) {
+                        Condition::Break    => return Condition::Break,
+                        Condition::Continue => return Condition::Continue,
+                        Condition::NoOp     => ()
                     }
                 },
                 Statement::Function { name, args, mut statements } => {
@@ -173,19 +183,18 @@ impl<'a> FlowLogic for Shell<'a> {
                     });
                 },
                 Statement::Pipeline(mut pipeline) => { self.run_pipeline(&mut pipeline, false); },
-                Statement::Break => {
-                    return true
-                }
+                Statement::Break => { return Condition::Break }
+                Statement::Continue => { return Condition::Continue }
                 _ => {}
             }
         }
-        false
+        Condition::NoOp
     }
 
     fn execute_while(&mut self, expression: Pipeline, statements: Vec<Statement>) {
         while self.run_pipeline(&mut expression.clone(), false) == Some(SUCCESS) {
             // Cloning is needed so the statement can be re-iterated again if needed.
-            if self.execute_statements(statements.clone()) {
+            if let Condition::Break = self.execute_statements(statements.clone()) {
                 break
             }
         }
@@ -206,35 +215,51 @@ impl<'a> FlowLogic for Shell<'a> {
             }
         }
 
+        let ignore_variable = variable == "_";
         match ForExpression::new(values, &self.directory_stack, &self.variables) {
+            ForExpression::Multiple(ref values) if ignore_variable => {
+                for _ in values.iter().flat_map(|x| glob_expand(x.as_str())) {
+                    if let Condition::Break = self.execute_statements(statements.clone()) { break }
+                }
+            },
             ForExpression::Multiple(values) => {
                 for value in values.iter().flat_map(|x| glob_expand(x.as_str())) {
-                    if value != "_" { self.variables.set_var(variable, &value); }
-                    if self.execute_statements(statements.clone()) { break }
+                    self.variables.set_var(variable, &value);
+                    if let Condition::Break = self.execute_statements(statements.clone()) { break }
+                }
+            },
+            ForExpression::Normal(ref values) if ignore_variable => {
+                for _ in values.lines().flat_map(glob_expand) {
+                    if let Condition::Break = self.execute_statements(statements.clone()) { break }
                 }
             },
             ForExpression::Normal(values) => {
                 for value in values.lines().flat_map(glob_expand) {
-                    if value != "_" { self.variables.set_var(variable, &value); }
-                    if self.execute_statements(statements.clone()) { break }
+                    self.variables.set_var(variable, &value);
+                    if let Condition::Break = self.execute_statements(statements.clone()) { break }
                 }
             },
+            ForExpression::Range(start, end) if ignore_variable => {
+                for _ in start..end {
+                    if let Condition::Break = self.execute_statements(statements.clone()) { break }
+                }
+            }
             ForExpression::Range(start, end) => {
                 for value in (start..end).map(|x| x.to_string()) {
-                    if value != "_" { self.variables.set_var(variable, &value); }
-                    if self.execute_statements(statements.clone()) { break }
+                    self.variables.set_var(variable, &value);
+                    if let Condition::Break = self.execute_statements(statements.clone()) { break }
                 }
             }
         }
     }
 
     fn execute_if(&mut self, mut expression: Pipeline, success: Vec<Statement>,
-        mut else_if: Vec<ElseIf>, failure: Vec<Statement>) -> bool
+        else_if: Vec<ElseIf>, failure: Vec<Statement>) -> Condition
     {
         match self.run_pipeline(&mut expression, false) {
             Some(SUCCESS) => self.execute_statements(success),
             _             => {
-                for mut elseif in else_if.drain(..) {
+                for mut elseif in else_if.into_iter() {
                     if self.run_pipeline(&mut elseif.expression, false) == Some(SUCCESS) {
                         return self.execute_statements(elseif.success);
                     }
@@ -249,7 +274,7 @@ impl<'a> FlowLogic for Shell<'a> {
     {
         match statement {
             // Execute a Let Statement
-            Statement::Let{ expression } => {
+            Statement::Let { expression } => {
                 self.previous_status = let_assignment(&expression, &mut self.variables, &self.directory_stack);
             },
             // Collect the statements for the while loop, and if the loop is complete,
-- 
GitLab