feat: Tuple shape narrowing

This commit is contained in:
Jeremy Wall 2023-08-31 17:01:04 -04:00 committed by Jeremy Wall
parent 5835adbf7a
commit bdfa424545
2 changed files with 72 additions and 13 deletions

View File

@ -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<Vec<(Token, Shape)>>, right_slist: &PositionedItem<Vec<(Token, Shape)>>, 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<Shape>, left_slist: &NarrowedShape) -> bool {
fn is_tuple_subset(
mut left_iter: std::slice::Iter<(Token, Shape)>,
right_slist: &PositionedItem<Vec<(Token, Shape)>>,
) -> 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<Shape>, left_slist: &NarrowedShape) -> bool {
let right_subset = loop {
let mut matches = false;
let ls = if let Some(ls) = right_iter.next() {

View File

@ -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]