From bdfa424545b36e5b77aec22ca6d50671bafe24f5 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Thu, 31 Aug 2023 17:01:04 -0400 Subject: [PATCH] feat: Tuple shape narrowing --- src/ast/mod.rs | 48 +++++++++++++++++++++++++++++++++++---- src/ast/typecheck/test.rs | 37 +++++++++++++++++++++++------- 2 files changed, 72 insertions(+), 13 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index d5ca4d7..e0c6040 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -306,8 +306,7 @@ impl Shape { self.narrow_list_shapes(left_slist, right_slist, right) } (Shape::Tuple(left_slist), Shape::Tuple(right_slist)) => { - // TODO - unimplemented!("Can't merge these yet."); + self.narrow_tuple_shapes(left_slist, right_slist, right) } (Shape::Func(left_opshape), Shape::Func(right_opshape)) => { // TODO @@ -328,6 +327,18 @@ impl Shape { } } + fn narrow_tuple_shapes(&self, left_slist: &PositionedItem>, right_slist: &PositionedItem>, right: &Shape) -> Shape { + let left_iter = left_slist.val.iter(); + let right_iter = right_slist.val.iter(); + if is_tuple_subset(left_iter, right_slist) { + self.clone() + } else if is_tuple_subset(right_iter, left_slist) { + right.clone() + } else { + Shape::TypeErr(right.pos().clone(), "Incompatible Tuple Shapes".to_owned()) + } + } + fn narrow_list_shapes( &self, left_slist: &NarrowedShape, @@ -336,9 +347,9 @@ impl Shape { ) -> Shape { let left_iter = left_slist.types.iter(); let right_iter = right_slist.types.iter(); - if is_shape_subset(left_iter, right_slist) { + if is_list_subset(left_iter, right_slist) { self.clone() - } else if is_shape_subset(right_iter, left_slist) { + } else if is_list_subset(right_iter, left_slist) { right.clone() } else { Shape::TypeErr(right.pos().clone(), "Incompatible List Shapes".to_owned()) @@ -406,7 +417,34 @@ impl Shape { } } -fn is_shape_subset(mut right_iter: std::slice::Iter, left_slist: &NarrowedShape) -> bool { +fn is_tuple_subset( + mut left_iter: std::slice::Iter<(Token, Shape)>, + right_slist: &PositionedItem>, +) -> bool { + return loop { + if let Some((lt, ls)) = left_iter.next() { + let mut matched = false; + for (rt, rs) in right_slist.val.iter() { + if rt.fragment == lt.fragment { + if let Shape::TypeErr(_, _) = ls.narrow(rs) { + // noop + } else { + matched = true; + continue; + } + } + } + if !matched { + break false; + } else { + continue; + } + } + break true; + }; +} + +fn is_list_subset(mut right_iter: std::slice::Iter, left_slist: &NarrowedShape) -> bool { let right_subset = loop { let mut matches = false; let ls = if let Some(ls) = right_iter.next() { diff --git a/src/ast/typecheck/test.rs b/src/ast/typecheck/test.rs index 69b6fb4..f867f3a 100644 --- a/src/ast/typecheck/test.rs +++ b/src/ast/typecheck/test.rs @@ -1,5 +1,6 @@ use std::convert::Into; +use crate::ast::{Token, TokenType}; use crate::ast::walk::Walker; use crate::ast::{Position, PositionedItem}; use crate::parse; @@ -54,6 +55,21 @@ fn simple_binary_typecheck() { "[] + [];", Shape::List(crate::ast::NarrowedShape::new(vec![], 1, 1, 0)) ); + assert_type_success!( + "{} + {};", + Shape::Tuple(PositionedItem::new(vec![], Position::new(1, 1, 0))) + ); + // TODO(jwall): + isn't valid for tuples. + assert_type_success!( + "{foo = 1} + {foo = 1};", + Shape::Tuple(PositionedItem::new( + vec![ + (Token { typ: TokenType::BAREWORD, fragment: "foo".to_owned(), pos: Position::new(1, 2, 1)}, + Shape::Int(PositionedItem::new_with_pos(1, Position::new(1, 8, 7)))), + ], + Position::new(1, 1, 0) + )) + ); assert_type_success!( "[1] + [2];", Shape::List(crate::ast::NarrowedShape::new( @@ -70,14 +86,8 @@ fn simple_binary_typecheck() { "[1, 1.0] + [1, 2.0];", Shape::List(crate::ast::NarrowedShape::new( vec![ - Shape::Int(PositionedItem::new_with_pos( - 1, - Position::new(1, 1, 0) - )), - Shape::Float(PositionedItem::new_with_pos( - 1.0, - Position::new(1, 1, 0) - )), + Shape::Int(PositionedItem::new_with_pos(1, Position::new(1, 1, 0))), + Shape::Float(PositionedItem::new_with_pos(1.0, Position::new(1, 1, 0))), ], 1, 1, @@ -114,6 +124,17 @@ fn simple_binary_typefail() { "Incompatible List Shapes", Position::new(1, 7, 6) ); + // TODO(jwall): + isn't valid for tuples. + assert_type_fail!( + "{foo = 1} + {foo = 1.0};", + "Incompatible Tuple Shapes", + Position::new(1, 13, 12) + ); + assert_type_fail!( + "{foo = 1} + {bar = 1};", + "Incompatible Tuple Shapes", + Position::new(1, 13, 12) + ); } #[test]