From ec29077247379468efd3c2a47c8b1782ffe69b05 Mon Sep 17 00:00:00 2001 From: greg Date: Mon, 5 Nov 2018 03:41:03 -0800 Subject: [PATCH] More tuple-matching Also discovered parser bug --- schala-lang/language/src/eval.rs | 117 ++++++++++++++++-------- schala-lang/language/src/reduced_ast.rs | 6 +- 2 files changed, 83 insertions(+), 40 deletions(-) diff --git a/schala-lang/language/src/eval.rs b/schala-lang/language/src/eval.rs index ecbb32a..44e266a 100644 --- a/schala-lang/language/src/eval.rs +++ b/schala-lang/language/src/eval.rs @@ -7,7 +7,7 @@ use std::io; use itertools::Itertools; use util::ScopeStack; -use reduced_ast::{ReducedAST, Stmt, Expr, Lit, Func, Alternative}; +use reduced_ast::{BoundVars, ReducedAST, Stmt, Expr, Lit, Func, Alternative, Subpattern}; use symbol_table::{SymbolSpec, Symbol, SymbolTable}; pub struct State<'a> { @@ -21,7 +21,6 @@ macro_rules! builtin_binding { } } -//TODO add a more concise way of getting a new frame impl<'a> State<'a> { pub fn new(symbol_table_handle: Rc>) -> State<'a> { let mut values = ScopeStack::new(Some(format!("global"))); @@ -34,6 +33,19 @@ impl<'a> State<'a> { pub fn debug_print(&self) -> String { format!("Values: {:?}", self.values) } + + fn new_frame(&'a self, items: &'a Vec, bound_vars: &BoundVars) -> State<'a> { + let mut inner_state = State { + values: self.values.new_scope(None), + symbol_table_handle: self.symbol_table_handle.clone(), + }; + for (bound_var, val) in bound_vars.iter().zip(items.iter()) { + if let Some(bv) = bound_var.as_ref() { + inner_state.values.insert(bv.clone(), ValueEntry::Binding { constant: true, val: val.clone() }); + } + } + inner_state + } } #[derive(Debug, Clone)] @@ -391,19 +403,27 @@ impl<'a> State<'a> { } } + fn all_subpatterns_pass(state: &mut State, subpatterns: &Vec>, items: &Vec) -> EvalResult { + for (maybe_subp, cond) in subpatterns.iter().zip(items.iter()) { + if let Some(subp) = maybe_subp { + if let Some(ref guard) = subp.guard { + let guard_expr = match &cond { + Node::Expr(ref e) => guard.clone().replace_conditional_target_sigil(e), + _ => guard.clone() + }; + if !state.expression(guard_expr.to_node())?.is_true() { + return Ok(false); + } + } + } + } + Ok(true) + } + match cond { Node::PrimObject { ref tag, ref items, .. } => { if alt.tag.map(|t| t == *tag).unwrap_or(true) { - let mut inner_state = State { - values: self.values.new_scope(None), - symbol_table_handle: self.symbol_table_handle.clone(), - }; - for (bound_var, val) in alt.bound_vars.iter().zip(items.iter()) { - if let Some(bv) = bound_var.as_ref() { - inner_state.values.insert(bv.clone(), ValueEntry::Binding { constant: true, val: val.clone() }); - } - } - + let mut inner_state = self.new_frame(items, &alt.bound_vars); return inner_state.block(alt.item) } }, @@ -412,32 +432,8 @@ impl<'a> State<'a> { return Err(format!("Subpattern length isn't correct")); } - let mut inner_state = State { - values: self.values.new_scope(None), - symbol_table_handle: self.symbol_table_handle.clone(), - }; - for (bound_var, val) in alt.bound_vars.iter().zip(items.iter()) { - if let Some(bv) = bound_var.as_ref() { - inner_state.values.insert(bv.clone(), ValueEntry::Binding { constant: true, val: val.clone() }); - } - } - - let mut passes = true; - for (maybe_subp, cond) in alt.subpatterns.iter().zip(items.iter()) { - if let Some(subp) = maybe_subp { - if let Some(ref guard) = subp.guard { - let guard_expr = match &cond { - Node::Expr(ref e) => guard.clone().replace_conditional_target_sigil(e), - _ => guard.clone() - }; - if !inner_state.expression(guard_expr.to_node())?.is_true() { - passes = false; - break; - } - } - } - } - if !passes { + let mut inner_state = self.new_frame(items, &alt.bound_vars); + if !all_subpatterns_pass(&mut inner_state, &alt.subpatterns, items)? { continue; } return inner_state.block(alt.item) @@ -644,4 +640,49 @@ if Some(10) { "#; test_in_fresh_env!(source, "\"hella\""); } + + #[test] + fn tuple_pattern() { + let source = r#" +if (1, 2) { + is (1, x) -> x, + is _ -> 99 +} +"#; + test_in_fresh_env!(source, 2); + } + + + #[test] + fn tuple_pattern_2() { + let source = r#" +if (1, 2) { + is (10, x) -> x, + is (y, x) -> x + y +} +"#; + test_in_fresh_env!(source, 3); + } + + #[test] + fn tuple_pattern_3() { + let source = r#" +if (1, 5) { + is (10, x) -> x, + is (1, x) -> x +} +"#; + test_in_fresh_env!(source, 5); + } + + #[test] + fn need_to_fix_parser_to_make_work() { + let source = r#" +if (1, 5) { + is (10, x) -> x, + is (1, x) -> x, +} +"#; + test_in_fresh_env!(source, 5); + } } diff --git a/schala-lang/language/src/reduced_ast.rs b/schala-lang/language/src/reduced_ast.rs index fcf8c86..05fab9d 100644 --- a/schala-lang/language/src/reduced_ast.rs +++ b/schala-lang/language/src/reduced_ast.rs @@ -56,12 +56,14 @@ pub enum Expr { UnimplementedSigilValue } +pub type BoundVars = Vec>>; //remember that order matters here + #[derive(Debug, Clone)] pub struct Alternative { pub tag: Option, pub subpatterns: Vec>, pub guard: Option, - pub bound_vars: Vec>>, //remember that order matters here + pub bound_vars: BoundVars, pub item: Vec, } @@ -69,7 +71,7 @@ pub struct Alternative { pub struct Subpattern { pub tag: Option, pub subpatterns: Vec>, - pub bound_vars: Vec>>, + pub bound_vars: BoundVars, pub guard: Option, }