mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
feat: modules: Handle module args in let.
This commit is contained in:
parent
ce928b7bd2
commit
337b7c7203
151
src/ast/mod.rs
151
src/ast/mod.rs
@ -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
|
||||||
|
5
src/ast/typecheck/complex_module.ucg
Normal file
5
src/ast/typecheck/complex_module.ucg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module{
|
||||||
|
arg=0,
|
||||||
|
} => (foo) {
|
||||||
|
let foo = mod.arg;
|
||||||
|
};
|
5
src/ast/typecheck/infer_module_arg.ucg
Normal file
5
src/ast/typecheck/infer_module_arg.ucg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module{
|
||||||
|
arg={},
|
||||||
|
} => (foo) {
|
||||||
|
let foo = mod.arg.bar + 1;
|
||||||
|
};
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user