Mercurial > lbo > hg > arithmetal
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))))))); } }