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) self.narrow_list_shapes(left_slist, right_slist, right)
} }
(Shape::Tuple(left_slist), Shape::Tuple(right_slist)) => { (Shape::Tuple(left_slist), Shape::Tuple(right_slist)) => {
// TODO self.narrow_tuple_shapes(left_slist, right_slist, right)
unimplemented!("Can't merge these yet.");
} }
(Shape::Func(left_opshape), Shape::Func(right_opshape)) => { (Shape::Func(left_opshape), Shape::Func(right_opshape)) => {
// TODO // 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( fn narrow_list_shapes(
&self, &self,
left_slist: &NarrowedShape, left_slist: &NarrowedShape,
@ -336,9 +347,9 @@ impl Shape {
) -> Shape { ) -> Shape {
let left_iter = left_slist.types.iter(); let left_iter = left_slist.types.iter();
let right_iter = right_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() self.clone()
} else if is_shape_subset(right_iter, left_slist) { } else if is_list_subset(right_iter, left_slist) {
right.clone() right.clone()
} else { } else {
Shape::TypeErr(right.pos().clone(), "Incompatible List Shapes".to_owned()) 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 right_subset = loop {
let mut matches = false; let mut matches = false;
let ls = if let Some(ls) = right_iter.next() { let ls = if let Some(ls) = right_iter.next() {

View File

@ -1,5 +1,6 @@
use std::convert::Into; use std::convert::Into;
use crate::ast::{Token, TokenType};
use crate::ast::walk::Walker; use crate::ast::walk::Walker;
use crate::ast::{Position, PositionedItem}; use crate::ast::{Position, PositionedItem};
use crate::parse; use crate::parse;
@ -54,6 +55,21 @@ fn simple_binary_typecheck() {
"[] + [];", "[] + [];",
Shape::List(crate::ast::NarrowedShape::new(vec![], 1, 1, 0)) 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!( assert_type_success!(
"[1] + [2];", "[1] + [2];",
Shape::List(crate::ast::NarrowedShape::new( Shape::List(crate::ast::NarrowedShape::new(
@ -70,14 +86,8 @@ fn simple_binary_typecheck() {
"[1, 1.0] + [1, 2.0];", "[1, 1.0] + [1, 2.0];",
Shape::List(crate::ast::NarrowedShape::new( Shape::List(crate::ast::NarrowedShape::new(
vec![ vec![
Shape::Int(PositionedItem::new_with_pos( Shape::Int(PositionedItem::new_with_pos(1, Position::new(1, 1, 0))),
1, Shape::Float(PositionedItem::new_with_pos(1.0, Position::new(1, 1, 0))),
Position::new(1, 1, 0)
)),
Shape::Float(PositionedItem::new_with_pos(
1.0,
Position::new(1, 1, 0)
)),
], ],
1, 1,
1, 1,
@ -114,6 +124,17 @@ fn simple_binary_typefail() {
"Incompatible List Shapes", "Incompatible List Shapes",
Position::new(1, 7, 6) 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] #[test]