feat: modules: Handle module args in let.

This commit is contained in:
Jeremy Wall 2023-12-09 11:10:46 -05:00 committed by Jeremy Wall
parent ce928b7bd2
commit 337b7c7203
5 changed files with 477 additions and 173 deletions

View File

@ -259,10 +259,16 @@ pub enum ImportShape {
Unresolved(PositionedItem<Rc<str>>), Unresolved(PositionedItem<Rc<str>>),
} }
#[derive(PartialEq, Debug, Clone)]
pub enum NarrowingShape {
Narrowed(Vec<Shape>),
Any,
}
#[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Debug, Clone)]
pub struct NarrowedShape { pub struct NarrowedShape {
pub pos: Position, pub pos: Position,
pub types: Vec<Shape>, pub types: NarrowingShape,
} }
impl NarrowedShape { impl NarrowedShape {
@ -271,7 +277,10 @@ impl NarrowedShape {
} }
pub fn new_with_pos(types: Vec<Shape>, pos: Position) -> Self { pub fn new_with_pos(types: Vec<Shape>, pos: Position) -> Self {
Self { pos, types } Self {
pos,
types: NarrowingShape::Narrowed(types),
}
} }
pub fn with_pos(mut self, pos: Position) -> Self { pub fn with_pos(mut self, pos: Position) -> Self {
@ -280,12 +289,19 @@ impl NarrowedShape {
} }
pub fn merge_in_shape(&mut self, shape: Shape, symbol_table: &mut BTreeMap<Rc<str>, Shape>) { pub fn merge_in_shape(&mut self, shape: Shape, symbol_table: &mut BTreeMap<Rc<str>, Shape>) {
for s in self.types.iter() { match &mut self.types {
if s.equivalent(&shape, symbol_table) { NarrowingShape::Narrowed(ref mut types) => {
return; for s in types.iter() {
if s.equivalent(&shape, symbol_table) {
return;
}
}
types.push(shape);
}
NarrowingShape::Any => {
self.types = NarrowingShape::Narrowed(vec![shape]);
} }
} }
self.types.push(shape)
} }
} }
@ -315,11 +331,49 @@ impl Shape {
| (Shape::Int(_), Shape::Int(_)) | (Shape::Int(_), Shape::Int(_))
| (Shape::Hole(_), Shape::Hole(_)) | (Shape::Hole(_), Shape::Hole(_))
| (Shape::Float(_), Shape::Float(_)) => true, | (Shape::Float(_), Shape::Float(_)) => true,
(Shape::Narrowed(left_slist), Shape::Narrowed(right_slist)) (
| (Shape::List(left_slist), Shape::List(right_slist)) => { Shape::List(NarrowedShape {
for ls in left_slist.types.iter() { pos: _,
types: NarrowingShape::Any,
}),
Shape::List(NarrowedShape {
pos: _,
types: NarrowingShape::Any,
}),
)
| (
Shape::Narrowed(NarrowedShape {
pos: _,
types: NarrowingShape::Any,
}),
Shape::Narrowed(NarrowedShape {
pos: _,
types: NarrowingShape::Any,
}),
) => true,
(
Shape::List(NarrowedShape {
pos: _,
types: NarrowingShape::Narrowed(left_slist),
}),
Shape::List(NarrowedShape {
pos: _,
types: NarrowingShape::Narrowed(right_slist),
}),
)
| (
Shape::Narrowed(NarrowedShape {
pos: _,
types: NarrowingShape::Narrowed(left_slist),
}),
Shape::Narrowed(NarrowedShape {
pos: _,
types: NarrowingShape::Narrowed(right_slist),
}),
) => {
for ls in left_slist.iter() {
let mut found = false; let mut found = false;
for rs in right_slist.types.iter() { for rs in right_slist.iter() {
if ls.equivalent(rs, symbol_table) { if ls.equivalent(rs, symbol_table) {
found = true; found = true;
break; break;
@ -331,6 +385,46 @@ impl Shape {
} }
true true
} }
(
Shape::List(NarrowedShape {
pos: _,
types: NarrowingShape::Any,
}),
Shape::List(NarrowedShape {
pos: _,
types: NarrowingShape::Narrowed(right_slist),
}),
)
| (
Shape::Narrowed(NarrowedShape {
pos: _,
types: NarrowingShape::Any,
}),
Shape::Narrowed(NarrowedShape {
pos: _,
types: NarrowingShape::Narrowed(right_slist),
}),
) => true,
(
Shape::List(NarrowedShape {
pos,
types: NarrowingShape::Narrowed(left_slist),
}),
Shape::List(NarrowedShape {
pos: _,
types: NarrowingShape::Any,
}),
)
| (
Shape::Narrowed(NarrowedShape {
pos,
types: NarrowingShape::Narrowed(left_slist),
}),
Shape::Narrowed(NarrowedShape {
pos: _,
types: NarrowingShape::Any,
}),
) => true,
(Shape::Tuple(left_slist), Shape::Tuple(right_slist)) => { (Shape::Tuple(left_slist), Shape::Tuple(right_slist)) => {
for (lt, ls) in left_slist.val.iter() { for (lt, ls) in left_slist.val.iter() {
let mut found = false; let mut found = false;
@ -433,14 +527,24 @@ impl Shape {
right: &Shape, right: &Shape,
symbol_table: &mut BTreeMap<Rc<str>, Shape>, symbol_table: &mut BTreeMap<Rc<str>, Shape>,
) -> Shape { ) -> Shape {
let left_iter = left_slist.types.iter(); match (&left_slist.types, &right_slist.types) {
let right_iter = right_slist.types.iter(); (
if is_list_subset(left_iter, right_slist, symbol_table) { NarrowingShape::Narrowed(ref left_types),
self.clone() NarrowingShape::Narrowed(ref right_types),
} else if is_list_subset(right_iter, left_slist, symbol_table) { ) => {
right.clone() let left_iter = left_types.iter();
} else { let right_iter = right_types.iter();
Shape::TypeErr(right.pos().clone(), "Incompatible List Shapes".to_owned()) if is_list_subset(left_iter, right_slist, symbol_table) {
self.clone()
} else if is_list_subset(right_iter, left_slist, symbol_table) {
right.clone()
} else {
Shape::TypeErr(right.pos().clone(), "Incompatible List Shapes".to_owned())
}
}
(NarrowingShape::Narrowed(_), NarrowingShape::Any)
| (NarrowingShape::Any, NarrowingShape::Any) => self.clone(),
(NarrowingShape::Any, NarrowingShape::Narrowed(_)) => right.clone(),
} }
} }
@ -486,10 +590,11 @@ impl Shape {
Shape::Int(_) => Shape::Int(pos.clone()), Shape::Int(_) => Shape::Int(pos.clone()),
Shape::Float(_) => Shape::Float(pos.clone()), Shape::Float(_) => Shape::Float(pos.clone()),
Shape::Boolean(_) => Shape::Boolean(pos.clone()), Shape::Boolean(_) => Shape::Boolean(pos.clone()),
Shape::List(lst) => Shape::List(NarrowedShape::new_with_pos(lst.types, pos)), Shape::List(NarrowedShape { pos: _, types: NarrowingShape::Narrowed(lst) }) => Shape::List(NarrowedShape::new_with_pos(lst, pos)),
Shape::List(NarrowedShape { pos: _, types: NarrowingShape::Any }) => Shape::List(NarrowedShape {pos, types: NarrowingShape::Any}),
Shape::Tuple(flds) => Shape::Tuple(PositionedItem::new(flds.val, pos)), Shape::Tuple(flds) => Shape::Tuple(PositionedItem::new(flds.val, pos)),
Shape::Func(_) | Shape::Module(_) => self.clone(), Shape::Func(_) | Shape::Module(_) => self.clone(),
Shape::Narrowed(pi) => Shape::Narrowed(pi.with_pos(pos)), Shape::Narrowed(narrowed_shape) => Shape::Narrowed(narrowed_shape.with_pos(pos)),
Shape::Hole(pi) => Shape::Hole(pi.with_pos(pos)), Shape::Hole(pi) => Shape::Hole(pi.with_pos(pos)),
Shape::Import(ImportShape::Resolved(_, s)) => { Shape::Import(ImportShape::Resolved(_, s)) => {
Shape::Import(ImportShape::Resolved(pos, s)) Shape::Import(ImportShape::Resolved(pos, s))
@ -535,6 +640,10 @@ fn is_list_subset(
left_slist: &NarrowedShape, left_slist: &NarrowedShape,
symbol_table: &mut BTreeMap<Rc<str>, Shape>, symbol_table: &mut BTreeMap<Rc<str>, Shape>,
) -> bool { ) -> bool {
let left_slist = match &left_slist.types {
NarrowingShape::Any => return true,
NarrowingShape::Narrowed(ref list) => list,
};
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() {
@ -542,7 +651,7 @@ fn is_list_subset(
} else { } else {
break true; break true;
}; };
for rs in left_slist.types.iter() { for rs in left_slist.iter() {
let s = ls.narrow(rs, symbol_table); let s = ls.narrow(rs, symbol_table);
if let Shape::TypeErr(_, _) = s { if let Shape::TypeErr(_, _) = s {
// noop // noop

View File

@ -0,0 +1,5 @@
module{
arg=0,
} => (foo) {
let foo = mod.arg;
};

View File

@ -0,0 +1,5 @@
module{
arg={},
} => (foo) {
let foo = mod.arg.bar + 1;
};

View File

@ -24,7 +24,7 @@ use crate::error::{BuildError, ErrorType};
use super::{ use super::{
BinaryExprType, BinaryOpDef, CastType, CopyDef, FuncDef, ImportShape, ModuleDef, ModuleShape, BinaryExprType, BinaryOpDef, CastType, CopyDef, FuncDef, ImportShape, ModuleDef, ModuleShape,
NarrowedShape, NotDef, Position, PositionedItem, SelectDef, NarrowedShape, NarrowingShape, NotDef, Position, PositionedItem, SelectDef, TupleShape,
}; };
/// Trait for shape derivation. /// Trait for shape derivation.
@ -69,41 +69,35 @@ impl DeriveShape for FuncDef {
impl DeriveShape for ModuleDef { impl DeriveShape for ModuleDef {
fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape { fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
let sym_table: BTreeMap<Rc<str>, Shape> = self let mut sym_table: BTreeMap<Rc<str>, Shape> = BTreeMap::new();
.arg_set let mod_key: Rc<str> = "mod".into();
.iter() let mut mod_shape: TupleShape = Vec::new();
.map(|(tok, expr)| (tok.fragment.clone(), expr.derive_shape(symbol_table))) if let Some(pos) = self.arg_set.first().map(|(t, _)| t.pos.clone()) {
.collect(); mod_shape = self
let sym_positions: BTreeSet<PositionedItem<Rc<str>>> = .arg_set
self.arg_set.iter().map(|(tok, _)| tok.into()).collect(); .iter()
.map(|(tok, expr)| (tok.into(), expr.derive_shape(symbol_table)))
.collect();
sym_table.insert(
mod_key.clone(),
Shape::Tuple(PositionedItem::new(mod_shape.clone(), pos)),
);
}
// TODO(jwall): We should modify the shape_list when we can continue narrowing a type.
let mut checker = Checker::new().with_symbol_table(sym_table); let mut checker = Checker::new().with_symbol_table(sym_table);
checker.walk_statement_list(self.statements.clone().iter_mut().collect()); checker.walk_statement_list(self.statements.clone().iter_mut().collect());
if let Some(mut expr) = self.out_expr.clone() { if let Some(mut expr) = self.out_expr.clone() {
checker.walk_expression(&mut expr); checker.walk_expression(&mut expr);
} else { } else {
// TODO(jwall): We need to construct a tuple from the let statements here. // TODO(jwall): Remove this when we require out expressions.
} }
let ret = Box::new( let ret = Box::new(
checker checker
.pop_shape() .pop_shape()
.expect("There should always be a return type here"), .expect("There should always be a return type here"),
); );
let mut items = Vec::new();
let sym_table = checker
.result()
.expect("There should aways be a symbol_table here");
for pos_key in sym_positions {
let key = pos_key.val.clone();
items.push((
pos_key,
sym_table
.get(&key)
.expect("This should always have a valid shape")
.clone(),
));
}
Shape::Module(ModuleShape { Shape::Module(ModuleShape {
items, items: dbg!(mod_shape),
ret, ret,
}) })
} }
@ -117,10 +111,8 @@ impl DeriveShape for SelectDef {
tuple, tuple,
pos: _, pos: _,
} = self; } = self;
let mut narrowed_shape = NarrowedShape { let mut narrowed_shape =
pos: self.pos.clone(), NarrowedShape::new_with_pos(Vec::with_capacity(tuple.len()), self.pos.clone());
types: Vec::with_capacity(tuple.len()),
};
for (_, expr) in tuple { for (_, expr) in tuple {
let shape = expr.derive_shape(symbol_table); let shape = expr.derive_shape(symbol_table);
narrowed_shape.merge_in_shape(shape, symbol_table); narrowed_shape.merge_in_shape(shape, symbol_table);
@ -147,24 +139,40 @@ fn derive_include_shape(
fn derive_not_shape(def: &NotDef, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape { fn derive_not_shape(def: &NotDef, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
let shape = def.expr.as_ref().derive_shape(symbol_table); let shape = def.expr.as_ref().derive_shape(symbol_table);
if let Shape::Boolean(_) = &shape { match &shape {
return Shape::Boolean(def.pos.clone()); Shape::Boolean(_) => {
} else if let Shape::Hole(_) = &shape { return Shape::Boolean(def.pos.clone());
return Shape::Boolean(def.pos.clone()); }
} else if let Shape::Narrowed(shape_list) = &shape { Shape::Hole(_) => {
for s in shape_list.types.iter() { return Shape::Boolean(def.pos.clone());
if let Shape::Boolean(_) = s { }
return Shape::Boolean(def.pos.clone()); Shape::Narrowed(NarrowedShape {
pos: _,
types: NarrowingShape::Any,
}) => {
return Shape::Boolean(def.pos.clone());
}
Shape::Narrowed(NarrowedShape {
pos: _,
types: NarrowingShape::Narrowed(shape_list),
}) => {
for s in shape_list.iter() {
if let Shape::Boolean(_) = s {
return Shape::Boolean(def.pos.clone());
}
} }
} }
}; _ => {
Shape::TypeErr( // noop
}
}
return Shape::TypeErr(
def.pos.clone(), def.pos.clone(),
format!( format!(
"Expected Boolean value in Not expression but got: {:?}", "Expected Boolean value in Not expression but got: {:?}",
shape shape
), ),
) );
} }
fn derive_copy_shape(def: &CopyDef, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape { fn derive_copy_shape(def: &CopyDef, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
@ -187,19 +195,40 @@ fn derive_copy_shape(def: &CopyDef, symbol_table: &mut BTreeMap<Rc<str>, Shape>)
Shape::Tuple(PositionedItem::new(vec![], pi.pos.clone())), Shape::Tuple(PositionedItem::new(vec![], pi.pos.clone())),
Shape::Module(ModuleShape { Shape::Module(ModuleShape {
items: vec![], items: vec![],
ret: Box::new(Shape::Narrowed(NarrowedShape { ret: Box::new(Shape::Narrowed(NarrowedShape::new_with_pos(
pos: pi.pos.clone(), vec![],
types: vec![], pi.pos.clone(),
})), ))),
}), }),
Shape::Import(ImportShape::Unresolved(pi.clone())), Shape::Import(ImportShape::Unresolved(pi.clone())),
], ],
pi.pos.clone(), pi.pos.clone(),
)), )),
Shape::Narrowed(potentials) => { Shape::Narrowed(NarrowedShape {
pos,
types: NarrowingShape::Any,
}) => Shape::Narrowed(NarrowedShape::new_with_pos(
vec![
Shape::Tuple(PositionedItem {
pos: def.pos.clone(),
val: Vec::new(),
}),
Shape::Module(ModuleShape {
items: vec![],
ret: Box::new(Shape::Narrowed(NarrowedShape {
pos: def.pos.clone(),
types: NarrowingShape::Any,
})),
}),
],
def.pos.clone(),
)),
Shape::Narrowed(NarrowedShape {
pos,
types: NarrowingShape::Narrowed(potentials),
}) => {
// 1. Do the possible shapes include tuple, module, or import? // 1. Do the possible shapes include tuple, module, or import?
let filtered = potentials let filtered = potentials
.types
.iter() .iter()
.filter_map(|v| match v { .filter_map(|v| match v {
Shape::Tuple(_) | Shape::Module(_) | Shape::Import(_) | Shape::Hole(_) => { Shape::Tuple(_) | Shape::Module(_) | Shape::Import(_) | Shape::Hole(_) => {
@ -284,48 +313,20 @@ impl DeriveShape for Expression {
def.path.pos.clone(), def.path.pos.clone(),
))), ))),
Expression::Binary(def) => { Expression::Binary(def) => {
dbg!(&def);
let left_shape = def.left.derive_shape(symbol_table); let left_shape = def.left.derive_shape(symbol_table);
let right_shape = def.right.derive_shape(symbol_table); // We need to do somethig different if it's a BinaryExprType::DOT
// We need to do somethig different if it's a ShapeKind::DOT
if def.kind == BinaryExprType::DOT { if def.kind == BinaryExprType::DOT {
dbg!(&def); // TODO(jwall): We should update the symbol table with the new
// left_shape can be assumed to be of type tuple. // left shape type if it was a symbol
// If left_shape is not known it can be inferred to be a tuple with right let shape =
// shapes symbol as a field name. derive_dot_expression(&def.pos, &left_shape, &def.right, symbol_table);
if let Shape::Hole(p) = left_shape { if let Expression::Simple(Value::Symbol(pi)) = def.left.as_ref() {
dbg!(&p); symbol_table.insert(pi.val.clone(), shape.clone());
if let Shape::Hole(pi) = right_shape {
dbg!(&pi);
let derived_shape = Shape::Tuple(PositionedItem::new(
// TODO(jeremy): This needs to be a token...
vec![(
pi.into(),
Shape::Narrowed(NarrowedShape {
pos: p.pos.clone(),
types: Vec::new(),
}),
)],
p.pos.clone(),
));
symbol_table.insert(p.val.clone(), derived_shape);
return Shape::Narrowed(NarrowedShape {
pos: p.pos.clone(),
types: Vec::new(),
});
}
} else if let Shape::Tuple(fields_pi) = left_shape {
dbg!(&fields_pi);
if let Shape::Hole(pi) = right_shape {
dbg!(&pi);
for (sym, shape) in fields_pi.val {
if pi.val == sym.val {
return shape;
}
}
}
} }
Shape::TypeErr(def.pos.clone(), "Invalid Tuple field selector".to_owned()) shape
} else { } else {
let right_shape = def.right.derive_shape(symbol_table);
left_shape.narrow(&right_shape, symbol_table) left_shape.narrow(&right_shape, symbol_table)
} }
} }
@ -342,6 +343,125 @@ impl DeriveShape for Expression {
} }
} }
fn derive_dot_expression(
pos: &Position,
left_shape: &Shape,
right_expr: &Expression,
symbol_table: &mut BTreeMap<Rc<str>, Shape>,
) -> Shape {
// left shape is symbol or tuple or array.
// right shape is symbol, str, number, grouped expression
// left_shape can be assumed to be of type tuple.
// If left_shape is not known it can be inferred to be a tuple with right
// shapes symbol as a field name.
match (left_shape, right_expr) {
// These all recurse down the dot chain
(
Shape::Tuple(tshape),
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::DOT,
left,
right,
pos,
}),
) => {
let next_left_shape = left.derive_shape(symbol_table);
let mut tshape = tshape.clone();
if let Shape::Hole(pi) = next_left_shape {
tshape.val.push((
pi.clone(),
Shape::Narrowed(NarrowedShape::new_with_pos(vec![], pos.clone())),
));
}
return Shape::Tuple(tshape);
}
(
Shape::List(lshape),
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::DOT,
left,
right,
pos,
}),
) => {
let next_left_shape = left.derive_shape(symbol_table);
if let Shape::Int(_) = next_left_shape {
return Shape::List(lshape.clone());
};
if let Shape::Hole(pi) = next_left_shape {
return Shape::Tuple(PositionedItem {
pos: pi.pos.clone(),
val: vec![(
pi.clone(),
Shape::Narrowed(NarrowedShape {
pos: pi.pos.clone(),
types: NarrowingShape::Any,
}),
)],
});
} else {
todo!()
};
}
(
Shape::Narrowed(narrowed_shape),
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::DOT,
left,
right,
pos,
}),
) => {
let next_left_shape = left.derive_shape(symbol_table);
let types = match &narrowed_shape.types {
NarrowingShape::Any => todo!(),
NarrowingShape::Narrowed(ref types) => types,
};
match next_left_shape {
Shape::Hole(_) | Shape::Int(_) | Shape::Str(_) => {
if types
.iter()
.any(|s| s.type_name() == "tuple" || s.type_name() == "list")
{
todo!()
}
todo!()
}
_ => {
todo!()
}
}
}
(
Shape::Hole(_),
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::DOT,
left,
right,
pos,
}),
) => {
todo!()
}
// These do not recurse down the dot chain
(Shape::Tuple(_), Expression::Simple(Value::Str(pi)))
| (Shape::Tuple(_), Expression::Simple(Value::Symbol(pi))) => todo!(),
(Shape::List(_), Expression::Simple(Value::Symbol(pi))) => todo!(),
(Shape::List(_), Expression::Simple(Value::Int(pi))) => todo!(),
(Shape::Hole(_), Expression::Simple(Value::Str(pi))) => todo!(),
(Shape::Hole(_), Expression::Simple(Value::Symbol(pi))) => todo!(),
(Shape::Hole(_), Expression::Simple(Value::Int(pi))) => todo!(),
(Shape::Narrowed(_), Expression::Simple(Value::Symbol(pi))) => todo!(),
(Shape::Narrowed(_), Expression::Simple(Value::Str(pi))) => todo!(),
(Shape::Narrowed(_), Expression::Simple(Value::Int(pi))) => todo!(),
(_, Expression::Grouped(expr, _)) => {
return derive_dot_expression(pos, left_shape, expr.as_ref(), symbol_table)
}
(_, _) => return Shape::TypeErr(pos.clone(), "Invalid Tuple field selector".to_owned()),
}
}
impl DeriveShape for Value { impl DeriveShape for Value {
fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape { fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
match self { match self {

View File

@ -32,7 +32,11 @@ macro_rules! assert_type_success {
checker.walk_statement_list(expr.iter_mut().collect()); checker.walk_statement_list(expr.iter_mut().collect());
let maybe_shape = checker.pop_shape(); let maybe_shape = checker.pop_shape();
let result = checker.result(); let result = checker.result();
assert!(result.is_ok(), "We expect this to typecheck successfully."); assert!(
result.is_ok(),
"We expect this to typecheck successfully. {:?}",
result
);
assert!(maybe_shape.is_some(), "We got a shape out of it"); assert!(maybe_shape.is_some(), "We got a shape out of it");
assert_eq!(maybe_shape.unwrap(), $shape); assert_eq!(maybe_shape.unwrap(), $shape);
}}; }};
@ -205,10 +209,10 @@ fn infer_symbol_type_test() {
Shape::Tuple(PositionedItem::new( Shape::Tuple(PositionedItem::new(
vec![( vec![(
PositionedItem::new(foo.clone(), Position::new(1, 5, 4)), PositionedItem::new(foo.clone(), Position::new(1, 5, 4)),
Shape::Narrowed(NarrowedShape { Shape::Narrowed(NarrowedShape::new_with_pos(
pos: Position::new(0, 0, 0), Vec::new(),
types: Vec::new(), Position::new(0, 0, 0),
}), )),
)], )],
Position::new(0, 0, 0), Position::new(0, 0, 0),
)), )),
@ -289,29 +293,23 @@ fn infer_func_type_test() {
fn infer_select_shape() { fn infer_select_shape() {
assert_type_success!( assert_type_success!(
r#"select (foo) => { true = "foo", false = 1 };"#, r#"select (foo) => { true = "foo", false = 1 };"#,
Shape::Narrowed(NarrowedShape { Shape::Narrowed(NarrowedShape::new_with_pos(
pos: Position { vec![
Shape::Str(Position::new(1, 26, 25)),
Shape::Int(Position::new(1, 41, 40)),
],
Position {
file: None, file: None,
line: 1, line: 1,
column: 1, column: 1,
offset: 0 offset: 0
}, },
types: vec![ ))
Shape::Str(Position::new(1, 26, 25)),
Shape::Int(Position::new(1, 41, 40)),
]
})
); );
assert_type_success!( assert_type_success!(
r#"select (foo) => { true = "foo", false = { } };"#, r#"select (foo) => { true = "foo", false = { } };"#,
Shape::Narrowed(NarrowedShape { Shape::Narrowed(NarrowedShape::new_with_pos(
pos: Position { vec![
file: None,
line: 1,
column: 1,
offset: 0
},
types: vec![
Shape::Str(Position::new(1, 26, 25)), Shape::Str(Position::new(1, 26, 25)),
Shape::Tuple(PositionedItem { Shape::Tuple(PositionedItem {
pos: Position { pos: Position {
@ -323,18 +321,18 @@ fn infer_select_shape() {
val: vec![], val: vec![],
}), }),
], ],
}) Position {
);
assert_type_success!(
r#"select (foo) => { true = { }, false = { } };"#,
Shape::Narrowed(NarrowedShape {
pos: Position {
file: None, file: None,
line: 1, line: 1,
column: 1, column: 1,
offset: 0 offset: 0
}, },
types: vec![Shape::Tuple(PositionedItem { ))
);
assert_type_success!(
r#"select (foo) => { true = { }, false = { } };"#,
Shape::Narrowed(NarrowedShape::new_with_pos(
vec![Shape::Tuple(PositionedItem {
pos: Position { pos: Position {
file: None, file: None,
line: 1, line: 1,
@ -343,18 +341,18 @@ fn infer_select_shape() {
}, },
val: vec![], val: vec![],
}),], }),],
}) Position {
);
assert_type_success!(
r#"select (foo) => { true = { foo = 1, }, false = { bar = "foo" } };"#,
Shape::Narrowed(NarrowedShape {
pos: Position {
file: None, file: None,
line: 1, line: 1,
column: 1, column: 1,
offset: 0 offset: 0
}, },
types: vec![ ))
);
assert_type_success!(
r#"select (foo) => { true = { foo = 1, }, false = { bar = "foo" } };"#,
Shape::Narrowed(NarrowedShape::new_with_pos(
vec![
Shape::Tuple(PositionedItem { Shape::Tuple(PositionedItem {
pos: Position { pos: Position {
file: None, file: None,
@ -405,19 +403,19 @@ fn infer_select_shape() {
}) })
)] )]
}) })
] ],
}) Position {
);
assert_type_success!(
r#"select (foo) => { true = { foo = 1, bar = "quux" }, false = { bar = "foo" } };"#,
Shape::Narrowed(NarrowedShape {
pos: Position {
file: None, file: None,
line: 1, line: 1,
column: 1, column: 1,
offset: 0 offset: 0
}, },
types: vec![ ))
);
assert_type_success!(
r#"select (foo) => { true = { foo = 1, bar = "quux" }, false = { bar = "foo" } };"#,
Shape::Narrowed(NarrowedShape::new_with_pos(
vec![
Shape::Tuple(PositionedItem { Shape::Tuple(PositionedItem {
pos: Position { pos: Position {
file: None, file: None,
@ -487,49 +485,55 @@ fn infer_select_shape() {
}) })
)] )]
}) })
] ],
}) Position {
);
assert_type_success!(
r#"select (foo) => { true = [ "quux" ], false = [ 1 ] };"#,
Shape::Narrowed(NarrowedShape {
pos: Position {
file: None, file: None,
line: 1, line: 1,
column: 1, column: 1,
offset: 0 offset: 0
}, },
types: vec![ ))
Shape::List(NarrowedShape { );
pos: Position { assert_type_success!(
r#"select (foo) => { true = [ "quux" ], false = [ 1 ] };"#,
Shape::Narrowed(NarrowedShape::new_with_pos(
vec![
Shape::List(NarrowedShape::new_with_pos(
vec![Shape::Str(Position {
file: None,
line: 1,
column: 28,
offset: 27
})],
Position {
file: None, file: None,
line: 1, line: 1,
column: 26, column: 26,
offset: 25 offset: 25
}, },
types: vec![Shape::Str(Position { )),
Shape::List(NarrowedShape::new_with_pos(
vec![Shape::Int(Position {
file: None, file: None,
line: 1, line: 1,
column: 28, column: 48,
offset: 27 offset: 47
})] })],
}), Position {
Shape::List(NarrowedShape {
pos: Position {
file: None, file: None,
line: 1, line: 1,
column: 46, column: 46,
offset: 45 offset: 45
}, },
types: vec![Shape::Int(Position { ))
file: None, ],
line: 1, Position {
column: 48, file: None,
offset: 47 line: 1,
})] column: 1,
}) offset: 0
] },
}) ))
); );
} }
@ -589,5 +593,66 @@ fn test_module_inference() {
offset: 14 offset: 14
})) }))
}) })
) );
let complex_mod_stmt = include_str!("complex_module.ucg");
assert_type_success!(
complex_mod_stmt,
Shape::Module(ModuleShape {
items: vec![(
PositionedItem {
pos: Position {
file: None,
line: 2,
column: 3,
offset: 10
},
val: "arg".into()
},
Shape::Int(Position {
file: None,
line: 2,
column: 7,
offset: 14
})
)],
ret: Box::new(Shape::Int(Position {
file: None,
line: 2,
column: 7,
offset: 14
}))
})
);
let infer_mod_arg_stmt = include_str!("infer_module_arg.ucg");
assert_type_success!(
infer_mod_arg_stmt,
Shape::Module(ModuleShape {
items: vec![(
PositionedItem {
pos: Position {
file: None,
line: 2,
column: 3,
offset: 10
},
val: "arg".into()
},
Shape::Narrowed(NarrowedShape::new_with_pos(
vec![],
Position {
file: None,
line: 2,
column: 3,
offset: 10
},
))
)],
ret: Box::new(Shape::Int(Position {
file: None,
line: 2,
column: 7,
offset: 14
}))
})
);
} }