Support Record patterns

This commit is contained in:
Greg Shuflin 2021-11-01 13:46:38 -07:00
parent a3f2539993
commit 4c6a93302d
7 changed files with 68 additions and 16 deletions

View File

@ -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))
}
})
}

View File

@ -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()),

View File

@ -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<Builtin> = 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<String>, 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<Vec<(Rc<String>, 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());
}
}
})

View File

@ -108,6 +108,7 @@ pub struct Alternative {
#[derive(Debug, Clone)]
pub enum Pattern {
Tuple { subpatterns: Vec<Pattern>, tag: Option<u32> },
Record { tag: u32, subpatterns: Vec<(Rc<String>, Pattern)> },
Literal(Literal),
Ignored,
Binding(DefId),

View File

@ -282,7 +282,7 @@ pub enum SymbolSpec {
Builtin(Builtin),
Func,
DataConstructor { tag: u32, type_id: TypeId },
RecordConstructor { tag: u32, members: HashMap<Rc<String>, 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);
}

View File

@ -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)?;

View File

@ -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#"