From 81b7b70a4763ab7b4ce800ab738056ee90b14daa Mon Sep 17 00:00:00 2001
From: Michael Aaron Murphy <mmstickman@gmail.com>
Date: Sun, 30 Jul 2017 02:55:44 -0400
Subject: [PATCH] Reduce Dependency On PEG

---
 examples/conditionals.ion  |   9 ++++
 examples/conditionals.out  |   9 ++++
 src/parser/grammar.rustpeg |  68 +++--------------------
 src/parser/peg.rs          | 108 +++++++++++++++++++++++++++----------
 4 files changed, 105 insertions(+), 89 deletions(-)

diff --git a/examples/conditionals.ion b/examples/conditionals.ion
index 46c1b24b..e8674142 100644
--- a/examples/conditionals.ion
+++ b/examples/conditionals.ion
@@ -7,3 +7,12 @@ false && echo cant get here
 false || false || echo double or
 true && true && echo double and
 false || true && echo or and
+contains "one two three" two && echo true || echo false
+contains "one two three" abc two && echo true || echo false
+contains "one two three" foo bar && echo true || echo false
+starts-with "one two three" one && echo true || echo false
+starts-with "one two three" abc one && echo true || echo false
+starts-with "one two three" three && echo true || echo false
+ends-with "one two three" three && echo true || echo false
+ends-with "one two three" abc three && echo true || echo false
+ends-with "one two three" one two && echo true || echo false
diff --git a/examples/conditionals.out b/examples/conditionals.out
index 1bb8764d..41c844c6 100644
--- a/examples/conditionals.out
+++ b/examples/conditionals.out
@@ -5,3 +5,12 @@ true
 double or
 double and
 or and
+true
+true
+false
+true
+true
+false
+true
+true
+false
diff --git a/src/parser/grammar.rustpeg b/src/parser/grammar.rustpeg
index ac86c196..2ceded8c 100644
--- a/src/parser/grammar.rustpeg
+++ b/src/parser/grammar.rustpeg
@@ -1,75 +1,19 @@
-use parser::assignments::parse_assignment;
 use parser::{pipelines, ArgumentSplitter};
 use parser::peg::get_function_args;
 use shell::flow_control::*;
 
 #[pub]
 parse_ -> Statement
-      = let_
-      / export_
-      / if_
-      / else_if_
-      / else_
-      / for_
+      = for_
       / while_
       / fn_
-      / break_
-      / continue_
       / match_
       / case_
       / pipelines
 
-#[pub]
-let_ -> Statement
-    = whitespace* "let" whitespace+ value:$(.*) {
-        Statement::Let { expression: parse_assignment(value) }
-    }
-
-#[pub]
-export_ -> Statement
-    = whitespace* "export" whitespace+ value:$(.*) {
-        Statement::Export(parse_assignment(value))
-    }
-
-#[pub]
-break_ -> Statement
-    = whitespace* "break" { Statement::Break }
-
-#[pub]
-continue_ -> Statement
-    = whitespace* "continue" { Statement::Continue }
-
-#[pub]
-if_ -> Statement
-    = whitespace* "if" whitespace+ command:$(.*) {?
-      pipelines::Collector::run(command).map(|p| {
-        Statement::If {
-          expression: p,
-          success: Vec::new(),
-          else_if: Vec::new(),
-          failure: Vec::new()
-        }
-      })
-    }
-
-#[pub]
-else_if_ -> Statement
-    = whitespace* "else" whitespace+ "if" whitespace+ command:$(.*) {?
-        pipelines::Collector::run(command).map(|p| {
-            Statement::ElseIf(ElseIf {
-                expression: p,
-                success:    Vec::new(),
-            })
-        })
-    }
-
-#[pub]
-else_ -> Statement
-    = whitespace* "else" whitespace*  { Statement::Else }
-
 #[pub]
 fn_ -> Statement
-    = whitespace* "fn " n:_name whitespace* args:_fn_args whitespace* description:_description? {?
+    = "fn " n:_name whitespace* args:_fn_args whitespace* description:_description? {?
         get_function_args(args).map(|args| Statement::Function {
             description: description.unwrap_or("".into()),
             name: n.into(),
@@ -98,7 +42,7 @@ _fn_arg -> String
 
 #[pub]
 for_ -> Statement
-    = whitespace* "for" whitespace+ n:_name whitespace+ "in" whitespace+ expr:$(.*) {
+    = "for" whitespace+ n:_name whitespace+ "in" whitespace+ expr:$(.*) {
         Statement::For {
             variable: n.into(),
             values: ArgumentSplitter::new(expr).map(String::from).collect(),
@@ -112,12 +56,12 @@ value_ -> Option<String> = contents:$(.*) { Some(contents.into())}
 pattern_ -> Option<String> = wildcard_ / value_
 
 case_ -> Statement
-    = whitespace* "case" whitespace+ p:pattern_ {
+    = "case" whitespace+ p:pattern_ {
     Statement::Case(Case { value: p, statements: Vec::new() })
   }
 
 #[pub]
-match_ -> Statement = whitespace* "match" whitespace+ expression:$(.*) {
+match_ -> Statement = "match" whitespace+ expression:$(.*) {
   Statement::Match {
     expression: expression.into(),
     cases: Vec::new()
@@ -126,7 +70,7 @@ match_ -> Statement = whitespace* "match" whitespace+ expression:$(.*) {
 
 #[pub]
 while_ -> Statement
-    = whitespace* "while" whitespace+ command:$(.*) {?
+    = "while" whitespace+ command:$(.*) {?
       pipelines::Collector::run(command).map(|p| {
         Statement::While {
           expression: p,
diff --git a/src/parser/peg.rs b/src/parser/peg.rs
index 014720c5..52fe7a91 100644
--- a/src/parser/peg.rs
+++ b/src/parser/peg.rs
@@ -1,12 +1,14 @@
 use std::io::{stderr, Write};
 use std::fmt;
 
-use shell::flow_control::{Statement, FunctionArgument, Type};
+use shell::flow_control::{ElseIf, Statement, FunctionArgument, Type};
 use self::grammar::parse_;
 use shell::directory_stack::DirectoryStack;
 use shell::{JobKind, Job};
 use shell::variables::Variables;
-use parser::{expand_string, ExpanderFunctions, Select};
+use super::{expand_string, ExpanderFunctions, Select};
+use super::assignments::parse_assignment;
+use super::pipelines;
 
 #[derive(Debug, PartialEq, Clone, Copy)]
 pub enum RedirectFrom { Stdout, Stderr, Both}
@@ -112,8 +114,67 @@ impl fmt::Display for Pipeline {
 }
 
 pub fn parse(code: &str) -> Statement {
-    let trimmed = code.trim();
-    if trimmed == "end" { return Statement::End; }
+    let mut trimmed = code.trim();
+    match trimmed {
+        "end"      => return Statement::End,
+        "break"    => return Statement::Break,
+        "continue" => return Statement::Continue,
+        _ if trimmed.starts_with("let ") => {
+            trimmed = trimmed[4..].trim_left();
+            return Statement::Let { expression: parse_assignment(trimmed) }
+        },
+        _ if trimmed.starts_with("export ") => {
+            trimmed = trimmed[7..].trim_left();
+            return Statement::Export(parse_assignment(trimmed))
+        },
+        _ if trimmed.starts_with("if ") => {
+            trimmed = trimmed[3..].trim_left();
+
+            let result = pipelines::Collector::run(trimmed).map(|p| {
+                Statement::If {
+                    expression: p,
+                    success: Vec::new(),
+                    else_if: Vec::new(),
+                    failure: Vec::new()
+                    }
+                });
+
+            match result {
+                Ok(statement) => return statement,
+                Err(err) => {
+                    let stderr = stderr();
+                    let _ = writeln!(stderr.lock(), "ion: Syntax {}", err);
+                    return Statement::Default
+                }
+            }
+        }
+        "else" => return Statement::Else,
+        _ if trimmed.starts_with("else") => {
+            let mut trimmed = trimmed[4..].trim_left();
+            if trimmed.len() == 0 {
+                return Statement::Else
+            } else if trimmed.starts_with("if ") {
+                trimmed = trimmed[3..].trim_left();
+                let result = pipelines::Collector::run(&trimmed).map(|p| {
+                    Statement::ElseIf(ElseIf {
+                        expression: p,
+                        success:    Vec::new(),
+                    })
+                });
+
+                match result {
+                    Ok(statement) => return statement,
+                    Err(err) => {
+                        let stderr = stderr();
+                        let _ = writeln!(stderr.lock(), "ion: Syntax {}", err);
+                        return Statement::Default
+                    }
+                }
+            }
+        }
+        _ => ()
+    }
+
     match parse_(trimmed) {
         Ok(code_ok) => code_ok,
         Err(err) => {
@@ -228,7 +289,7 @@ else
     #[test]
     fn parsing_ifs() {
         // Default case where spaced normally
-        let parsed_if = if_("if test 1 -eq 2").unwrap();
+        let parsed_if = parse("if test 1 -eq 2");
         let correct_parse = Statement::If {
             expression: Pipeline::new(
                 vec!(Job::new(
@@ -246,42 +307,40 @@ else
         assert_eq!(correct_parse, parsed_if);
 
         // Trailing spaces after final value
-        let parsed_if = if_("if test 1 -eq 2         ").unwrap();
+        let parsed_if = parse("if test 1 -eq 2         ");
         assert_eq!(correct_parse, parsed_if);
     }
 
     #[test]
     fn parsing_elses() {
         // Default case where spaced normally
-        let parsed_if = else_("else").unwrap();
+        let mut parsed_if = parse("else");
         let correct_parse = Statement::Else;
         assert_eq!(correct_parse, parsed_if);
 
         // Trailing spaces after final value
-        let parsed_if = else_("else         ").unwrap();
-        let correct_parse = Statement::Else;
+        parsed_if = parse("else         ");
         assert_eq!(correct_parse, parsed_if);
 
         // Leading spaces after final value
-        let parsed_if = else_("         else").unwrap();
-        let correct_parse = Statement::Else;
+        parsed_if = parse("         else");
         assert_eq!(correct_parse, parsed_if);
     }
 
     #[test]
     fn parsing_ends() {
         // Default case where spaced normally
-        let parsed_if = end_("end").unwrap();
+        let parsed_if = parse("end");
         let correct_parse = Statement::End;
         assert_eq!(correct_parse, parsed_if);
 
         // Trailing spaces after final value
-        let parsed_if = end_("end         ").unwrap();
+        let parsed_if = parse("end         ");
         let correct_parse = Statement::End;
         assert_eq!(correct_parse, parsed_if);
 
         // Leading spaces after final value
-        let parsed_if = end_("         end").unwrap();
+        let parsed_if = parse("         end");
         let correct_parse = Statement::End;
         assert_eq!(correct_parse, parsed_if);
     }
@@ -289,7 +348,7 @@ else
     #[test]
     fn parsing_functions() {
         // Default case where spaced normally
-        let parsed_if = fn_("fn bob").unwrap();
+        let parsed_if = parse("fn bob");
         let correct_parse = Statement::Function{
             description: "".into(),
             name:        "bob".into(),
@@ -299,15 +358,14 @@ else
         assert_eq!(correct_parse, parsed_if);
 
         // Trailing spaces after final value
-        let parsed_if = fn_("fn bob        ").unwrap();
+        let parsed_if = parse("fn bob        ");
         assert_eq!(correct_parse, parsed_if);
 
         // Leading spaces after final value
-        let parsed_if = fn_("         fn bob").unwrap();
-        assert_eq!(correct_parse, parsed_if);
+        let parsed_if = parse("         fn bob");
 
         // Default case where spaced normally
-        let parsed_if = fn_("fn bob a b").unwrap();
+        let parsed_if = parse("fn bob a b");
         let correct_parse = Statement::Function{
             description: "".into(),
             name:        "bob".into(),
@@ -317,14 +375,10 @@ else
         assert_eq!(correct_parse, parsed_if);
 
         // Trailing spaces after final value
-        let parsed_if = fn_("fn bob a b       ").unwrap();
-        assert_eq!(correct_parse, parsed_if);
-
-        // Leading spaces after final value
-        let parsed_if = fn_("         fn bob a b").unwrap();
+        let parsed_if = parse("fn bob a b       ");
         assert_eq!(correct_parse, parsed_if);
 
-        let parsed_if = fn_("fn bob a b --bob is a nice function").unwrap();
+        let parsed_if = parse("fn bob a b --bob is a nice function");
         let correct_parse = Statement::Function{
             description: "bob is a nice function".to_string(),
             name:        "bob".into(),
@@ -332,9 +386,9 @@ else
             statements:  vec!()
         };
         assert_eq!(correct_parse, parsed_if);
-        let parsed_if = fn_("fn bob a b --          bob is a nice function").unwrap();
+        let parsed_if = parse("fn bob a b --          bob is a nice function");
         assert_eq!(correct_parse, parsed_if);
-        let parsed_if = fn_("fn bob a b      --bob is a nice function").unwrap();
+        let parsed_if = parse("fn bob a b      --bob is a nice function");
         assert_eq!(correct_parse, parsed_if);
     }
 }
-- 
GitLab