From 4c6a93302d260ffa26f2efd69777336b2040cc42 Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Mon, 1 Nov 2021 13:46:38 -0700 Subject: [PATCH] Support Record patterns --- schala-lang/language/src/parsing/mod.rs | 6 +++- schala-lang/language/src/parsing/test.rs | 5 +--- schala-lang/language/src/reduced_ir/mod.rs | 30 ++++++++++++++----- schala-lang/language/src/reduced_ir/types.rs | 1 + schala-lang/language/src/symbol_table/mod.rs | 5 ++-- .../language/src/tree_walk_eval/evaluator.rs | 14 +++++++++ .../language/src/tree_walk_eval/test.rs | 23 ++++++++++++++ 7 files changed, 68 insertions(+), 16 deletions(-) diff --git a/schala-lang/language/src/parsing/mod.rs b/schala-lang/language/src/parsing/mod.rs index 4222189..1723dec 100644 --- a/schala-lang/language/src/parsing/mod.rs +++ b/schala-lang/language/src/parsing/mod.rs @@ -1123,7 +1123,11 @@ impl Parser { let pat = self.pattern()?; (name, pat) } - _ => (name.clone(), Pattern::Literal(PatternLiteral::StringPattern(name))), + _ => { + let qualified_identifier = + QualifiedName { id: self.id_store.fresh(), components: vec![name.clone()] }; + (name, Pattern::VarOrName(qualified_identifier)) + } }) } diff --git a/schala-lang/language/src/parsing/test.rs b/schala-lang/language/src/parsing/test.rs index 8b4e74c..5599734 100644 --- a/schala-lang/language/src/parsing/test.rs +++ b/schala-lang/language/src/parsing/test.rs @@ -1099,10 +1099,7 @@ fn pattern_matching() { body: bx(IfExpressionBody::SimplePatternMatch { pattern: Pattern::Record( qn!(Something), - vec![ - (rc("a"), Pattern::Literal(PatternLiteral::StringPattern(rc("a")))), - (rc("b"), Pattern::VarOrName(qn!(x))) - ] + vec![(rc("a"), Pattern::VarOrName(qn!(a))), (rc("b"), Pattern::VarOrName(qn!(x)))] ), then_case: vec![exst(NatLiteral(4))].into(), else_case: Some(vec![exst(NatLiteral(9))].into()), diff --git a/schala-lang/language/src/reduced_ir/mod.rs b/schala-lang/language/src/reduced_ir/mod.rs index b22424f..630f2c8 100644 --- a/schala-lang/language/src/reduced_ir/mod.rs +++ b/schala-lang/language/src/reduced_ir/mod.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, str::FromStr}; +use std::{collections::HashMap, rc::Rc, str::FromStr}; use crate::{ ast, @@ -149,10 +149,9 @@ impl<'a, 'b> Reducer<'a, 'b> { }), NamedStruct { name, fields } => { self.symbol_table.debug(); - println!("Namedstruct name {} id: {}", name, name.id); let symbol = self.symbol_table.lookup_symbol(&name.id).unwrap(); let (tag, type_id) = match symbol.spec() { - SymbolSpec::RecordConstructor { tag, members: _, type_id } => (tag, type_id), + SymbolSpec::RecordConstructor { tag, type_id } => (tag, type_id), e => return Expression::ReductionError(format!("Bad symbol for NamedStruct: {:?}", e)), }; @@ -279,7 +278,6 @@ impl<'a, 'b> Reducer<'a, 'b> { } fn prefix(&mut self, prefix: &ast::PrefixOp, arg: &ast::Expression) -> Expression { - println!("PREFIX: {:?} and ARG: {:?}", prefix, arg); let builtin: Option = TryFrom::try_from(prefix).ok(); match builtin { Some(op) => Expression::Call { @@ -395,11 +393,27 @@ impl ast::Pattern { spec => return Err(format!("Unexpected VarOrName symbol: {:?}", spec).into()), } } - ast::Pattern::Record(name, _specified_members /*Vec<(Rc, Pattern)>*/) => { + ast::Pattern::Record(name, specified_members) => { let symbol = symbol_table.lookup_symbol(&name.id).unwrap(); - match symbol.spec() { - SymbolSpec::RecordConstructor { tag: _, members: _, type_id: _ } => unimplemented!(), - spec => return Err(format!("Unexpected Record pattern symbol: {:?}", spec).into()), + if let SymbolSpec::RecordConstructor { tag, type_id: _ } = symbol.spec() { + //TODO do this computation from the type_id + /* + if specified_members.iter().any(|(member, _)| !members.contains_key(member)) { + return Err(format!("Unknown key in record pattern").into()); + } + */ + + let subpatterns: Result, Pattern)>, PatternError> = specified_members + .iter() + .map(|(name, pat)| { + pat.reduce(symbol_table).map(|reduced_pat| (name.clone(), reduced_pat)) + }) + .into_iter() + .collect(); + let subpatterns = subpatterns?; + Pattern::Record { tag, subpatterns } + } else { + return Err(format!("Unexpected Record pattern symbol: {:?}", symbol.spec()).into()); } } }) diff --git a/schala-lang/language/src/reduced_ir/types.rs b/schala-lang/language/src/reduced_ir/types.rs index ca6a1f2..5927c88 100644 --- a/schala-lang/language/src/reduced_ir/types.rs +++ b/schala-lang/language/src/reduced_ir/types.rs @@ -108,6 +108,7 @@ pub struct Alternative { #[derive(Debug, Clone)] pub enum Pattern { Tuple { subpatterns: Vec, tag: Option }, + Record { tag: u32, subpatterns: Vec<(Rc, Pattern)> }, Literal(Literal), Ignored, Binding(DefId), diff --git a/schala-lang/language/src/symbol_table/mod.rs b/schala-lang/language/src/symbol_table/mod.rs index eb406ce..3605d78 100644 --- a/schala-lang/language/src/symbol_table/mod.rs +++ b/schala-lang/language/src/symbol_table/mod.rs @@ -282,7 +282,7 @@ pub enum SymbolSpec { Builtin(Builtin), Func, DataConstructor { tag: u32, type_id: TypeId }, - RecordConstructor { tag: u32, members: HashMap, TypeId>, type_id: TypeId }, + RecordConstructor { tag: u32, type_id: TypeId }, GlobalBinding, //Only for global variables, not for function-local ones or ones within a `let` scope context LocalVariable, FunctionParam(u8), @@ -515,8 +515,7 @@ impl<'a> SymbolTableRunner<'a> { let spec = match &variant.members { type_inference::VariantMembers::Unit => SymbolSpec::DataConstructor { tag, type_id }, type_inference::VariantMembers::Tuple(..) => SymbolSpec::DataConstructor { tag, type_id }, - type_inference::VariantMembers::Record(..) => - SymbolSpec::RecordConstructor { tag, members: HashMap::new(), type_id }, + type_inference::VariantMembers::Record(..) => SymbolSpec::RecordConstructor { tag, type_id }, }; self.table.add_symbol(id, fqsn, spec); } diff --git a/schala-lang/language/src/tree_walk_eval/evaluator.rs b/schala-lang/language/src/tree_walk_eval/evaluator.rs index d86bb97..172ade2 100644 --- a/schala-lang/language/src/tree_walk_eval/evaluator.rs +++ b/schala-lang/language/src/tree_walk_eval/evaluator.rs @@ -261,6 +261,20 @@ impl<'a, 'b> Evaluator<'a, 'b> { _ => false, }, }, + Pattern::Record { tag: pattern_tag, subpatterns } => match scrut { + //TODO several types of possible error here + Primitive::Object { tag, items, ordered_fields: Some(ordered_fields), .. } + if tag == pattern_tag => + subpatterns.iter().all(|(field_name, subpat)| { + let idx = ordered_fields + .iter() + .position(|field| field.as_str() == field_name.as_ref()) + .unwrap(); + let item = &items[idx]; + matches(item, subpat, scope) + }), + _ => false, + }, } } let cond = self.expression(cond)?; diff --git a/schala-lang/language/src/tree_walk_eval/test.rs b/schala-lang/language/src/tree_walk_eval/test.rs index 4fdddcb..b710668 100644 --- a/schala-lang/language/src/tree_walk_eval/test.rs +++ b/schala-lang/language/src/tree_walk_eval/test.rs @@ -172,6 +172,29 @@ if x { eval_assert(&source, expected); } +#[test] +fn record_patterns() { + let source = r#" +type Ara = Kueh { a: Int, b: String } | Morbuk + +let alpha = Ara::Kueh { a: 10, b: "sanchez" } +if alpha { + is Ara::Kueh { a, b } then (b, a) + is _ then ("nooo", 8888) +}"#; + eval_assert(source, r#"("sanchez", 10)"#); + + let source = r#" +type Ara = Kueh { a: Int, b: String } | Morbuk + +let alpha = Ara::Kueh { a: 10, b: "sanchez" } +if alpha { + is Ara::Kueh { a, b: le_value } then (le_value, (a*2)) + is _ then ("nooo", 8888) +}"#; + eval_assert(source, r#"("sanchez", 20)"#); +} + #[test] fn if_is_patterns() { let source = r#"