changeset 2:7ed53d7e278e draft

Finish initial implementation
author Lewin Bormann <lbo@spheniscida.de>
date Wed, 23 Nov 2016 20:33:39 +0100
parents 77d76a662f3e
children 1d5a1a347148
files src/lib.rs
diffstat 1 files changed, 123 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib.rs	Tue Nov 22 21:01:41 2016 +0100
+++ b/src/lib.rs	Wed Nov 23 20:33:39 2016 +0100
@@ -111,7 +111,11 @@
 }
 
 fn err<'a>(s: &str, st: State<'a>) -> ParseResult {
-    ParseResult::Err(s.to_string(), format!("at {}-{}", st.from, st.to))
+    ParseResult::Err(s.to_string(),
+                     format!("at {}-{}: [{}]",
+                             st.from,
+                             st.to,
+                             String::from_iter(st.iter())))
 }
 
 // Parse helpers
@@ -144,9 +148,33 @@
 fn pexpr<'a>(st: State<'a>) -> ParseResult {
     if st.len() == 0 {
         err("Empty", st)
-    } else if st.at(0) == '(' && st.at(st.len() - 1) == ')' {
-        // parentheses around expression; parse subexpression
-        pexpr(st.restrict(1, st.len() - 1))
+    } else if st.at(0) == '(' {
+        if st.at(st.len() - 1) == ')' {
+            // parentheses around expression; parse subexpression
+            pexpr(st.restrict(1, st.len() - 1))
+        } else {
+            // Make sure that paren has matching close
+            let mut paren_lvl = 0;
+            let mut good = false;
+            for c in st.iter() {
+                if c == '(' {
+                    paren_lvl += 1;
+                } else if c == ')' && paren_lvl > 1 {
+                    paren_lvl -= 1;
+                } else if c == ')' {
+                    good = true;
+                    break;
+                } else {
+                }
+            }
+
+            // Paren is closed somewhere
+            if good {
+                expr(st)
+            } else {
+                err("Missing closing paren", st)
+            }
+        }
     } else {
         expr(st)
     }
@@ -174,7 +202,6 @@
             ix += 1;
         }
 
-        println!("{}", op_pos);
         if op_pos > -1 {
             let left = pexpr(st.restrict(0, op_pos as usize));
             let right = pexpr(st.restrict(op_pos as usize + 1, st.len()));
@@ -196,14 +223,58 @@
                 err("Right or both parts of expr is|are bad", st)
             }
         } else {
-            // term(st)
-            sym(st)
+            term(st)
         }
     }
 }
 
 fn term<'a>(st: State<'a>) -> ParseResult {
-    err("term", st)
+    if st.len() == 0 {
+        err("Empty", st)
+    } else {
+        // Test if addition or subtraction
+        let mut paren_lvl = 0;
+        let mut ix = 0;
+        let mut op_pos: i32 = -1;
+
+        // Look for operator at top level
+        for c in st.iter() {
+            if c == '(' {
+                paren_lvl += 1;
+            } else if c == ')' {
+                paren_lvl -= 1;
+            } else if paren_lvl == 0 && (c == '*' || c == '/') {
+                // yep!
+                op_pos = ix;
+                break;
+            }
+            ix += 1;
+        }
+
+        if op_pos > -1 {
+            let left = pexpr(st.restrict(0, op_pos as usize));
+            let right = pexpr(st.restrict(op_pos as usize + 1, st.len()));
+
+            if left.ok() && right.ok() {
+                match (left, right) {
+                    (ParseResult::Ok(a1), ParseResult::Ok(a2)) => {
+                        if st.at(op_pos as usize) == '*' {
+                            ParseResult::Ok(Ast::Multiplication(Box::new(a1), Box::new(a2)))
+                        } else {
+                            ParseResult::Ok(Ast::Division(Box::new(a1), Box::new(a2)))
+                        }
+                    }
+                    _ => panic!("Expected OK results"),
+                }
+            } else if !left.ok() {
+                err("Left part of term is bad", st)
+            } else {
+                err("Right or both parts of term is|are bad", st)
+            }
+        } else {
+            sym(st)
+        }
+    }
 }
 
 fn sym<'a>(st: State<'a>) -> ParseResult {
@@ -259,7 +330,8 @@
 
         let v = super::str_to_vec("-a");
         let st = super::state(&v);
-        assert_eq!(super::sym(st).err(), "Not a number <=> at 1-2".to_string());
+        assert_eq!(super::sym(st).err(),
+                   "Not a number <=> at 1-2: [a]".to_string());
     }
 
     #[test]
@@ -283,5 +355,47 @@
                    ParseResult::Ok(Ast::Addition(Box::new(Ast::Num(2)),
                                                  Box::new(Ast::Addition(Box::new(Ast::Num(4)),
                                                                         Box::new(Ast::Num(5)))))));
+
+        let v = super::str_to_vec("2+");
+        let st = super::state(&v);
+
+        assert_eq!(super::pexpr(st),
+                   ParseResult::Err("Right or both parts of expr is|are bad".to_string(),
+                                    "at 0-2: [2+]".to_string()));
+    }
+
+    #[test]
+    fn test_term() {
+        let v = super::str_to_vec("3*6");
+        let st = super::state(&v);
+
+        assert_eq!(super::pexpr(st),
+                   ParseResult::Ok(Ast::Multiplication(Box::new(Ast::Num(3)),
+                                                       Box::new(Ast::Num(6)))));
+    }
+
+    #[test]
+    fn test_pexpr() {
+        let v = super::str_to_vec("((3*6))");
+        let st = super::state(&v);
+
+        assert_eq!(super::pexpr(st),
+                   ParseResult::Ok(Ast::Multiplication(Box::new(Ast::Num(3)),
+                                                       Box::new(Ast::Num(6)))));
+
+        let v = super::str_to_vec("((3+6)*2)");
+        let st = super::state(&v);
+
+        assert_eq!(super::pexpr(st),
+                   ParseResult::Ok(
+                       Ast::Multiplication(Box::new(Ast::Addition(Box::new(Ast::Num(3)),
+                       Box::new(Ast::Num(6)))), Box::new(Ast::Num(2)))));
+
+        let v = super::str_to_vec("(3+6*2)");
+        let st = super::state(&v);
+
+        assert_eq!(super::pexpr(st),
+                   ParseResult::Ok(Ast::Addition(Box::new(Ast::Num(3)),
+                   Box::new(Ast::Multiplication(Box::new(Ast::Num(6)), Box::new(Ast::Num(2)))))));
     }
 }