mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
feat: Get rid of Empty in favor of Narrowed
Infer symbol from expression it's used in.
This commit is contained in:
parent
902199fd98
commit
a14caa77c8
@ -224,7 +224,7 @@ pub type ShapeList = Vec<Shape>;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct FuncShapeDef {
|
||||
args: Vec<Shape>,
|
||||
args: BTreeMap<Rc<str>, Shape>,
|
||||
ret: Box<Shape>,
|
||||
}
|
||||
|
||||
@ -278,7 +278,6 @@ impl NarrowedShape {
|
||||
/// Shapes represent the types that UCG values or expressions can have.
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Shape {
|
||||
Empty(Position),
|
||||
Boolean(PositionedItem<bool>),
|
||||
Int(PositionedItem<i64>),
|
||||
Float(PositionedItem<f64>),
|
||||
@ -294,20 +293,28 @@ pub enum Shape {
|
||||
}
|
||||
|
||||
impl Shape {
|
||||
pub fn narrow(&self, right: &Shape) -> Self {
|
||||
pub fn narrow(&self, right: &Shape, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Self {
|
||||
dbg!((self, right));
|
||||
match (self, right) {
|
||||
(Shape::Str(_), Shape::Str(_))
|
||||
| (Shape::Boolean(_), Shape::Boolean(_))
|
||||
| (Shape::Empty(_), Shape::Empty(_))
|
||||
| (Shape::Int(_), Shape::Int(_))
|
||||
| (Shape::Float(_), Shape::Float(_)) => self.clone(),
|
||||
(Shape::Hole(_), other) | (other, Shape::Hole(_)) => other.clone(),
|
||||
(Shape::Hole(sym), other) | (other, Shape::Hole(sym)) => {
|
||||
if symbol_table.contains_key(&sym.val) {
|
||||
symbol_table.insert(sym.val.clone(), other.clone().with_pos(sym.pos.clone()));
|
||||
} else {
|
||||
// TODO(jwall): Is this an error?
|
||||
todo!();
|
||||
}
|
||||
other.clone()
|
||||
},
|
||||
(Shape::Narrowed(left_slist), Shape::Narrowed(right_slist))
|
||||
| (Shape::List(left_slist), Shape::List(right_slist)) => {
|
||||
self.narrow_list_shapes(left_slist, right_slist, right)
|
||||
self.narrow_list_shapes(left_slist, right_slist, right, symbol_table)
|
||||
}
|
||||
(Shape::Tuple(left_slist), Shape::Tuple(right_slist)) => {
|
||||
self.narrow_tuple_shapes(left_slist, right_slist, right)
|
||||
self.narrow_tuple_shapes(left_slist, right_slist, right, symbol_table)
|
||||
}
|
||||
(Shape::Func(left_opshape), Shape::Func(right_opshape)) => {
|
||||
todo!();
|
||||
@ -331,12 +338,13 @@ impl Shape {
|
||||
left_slist: &PositionedItem<Vec<(Token, Shape)>>,
|
||||
right_slist: &PositionedItem<Vec<(Token, Shape)>>,
|
||||
right: &Shape,
|
||||
symbol_table: &mut BTreeMap<Rc<str>, Shape>,
|
||||
) -> Shape {
|
||||
let left_iter = left_slist.val.iter();
|
||||
let right_iter = right_slist.val.iter();
|
||||
if is_tuple_subset(left_iter, right_slist) {
|
||||
if is_tuple_subset(left_iter, right_slist, symbol_table) {
|
||||
self.clone()
|
||||
} else if is_tuple_subset(right_iter, left_slist) {
|
||||
} else if is_tuple_subset(right_iter, left_slist, symbol_table) {
|
||||
right.clone()
|
||||
} else {
|
||||
Shape::TypeErr(right.pos().clone(), "Incompatible Tuple Shapes".to_owned())
|
||||
@ -348,12 +356,13 @@ impl Shape {
|
||||
left_slist: &NarrowedShape,
|
||||
right_slist: &NarrowedShape,
|
||||
right: &Shape,
|
||||
symbol_table: &mut BTreeMap<Rc<str>, Shape>,
|
||||
) -> Shape {
|
||||
let left_iter = left_slist.types.iter();
|
||||
let right_iter = right_slist.types.iter();
|
||||
if is_list_subset(left_iter, right_slist) {
|
||||
if is_list_subset(left_iter, right_slist, symbol_table) {
|
||||
self.clone()
|
||||
} else if is_list_subset(right_iter, left_slist) {
|
||||
} else if is_list_subset(right_iter, left_slist, symbol_table) {
|
||||
right.clone()
|
||||
} else {
|
||||
Shape::TypeErr(right.pos().clone(), "Incompatible List Shapes".to_owned())
|
||||
@ -366,7 +375,6 @@ impl Shape {
|
||||
Shape::Int(s) => "int",
|
||||
Shape::Float(s) => "float",
|
||||
Shape::Boolean(b) => "boolean",
|
||||
Shape::Empty(p) => "nil",
|
||||
// TODO(jwall): make these type names account for what they contain.
|
||||
Shape::List(lst) => "list",
|
||||
Shape::Tuple(flds) => "tuple",
|
||||
@ -385,7 +393,6 @@ impl Shape {
|
||||
Shape::Int(s) => &s.pos,
|
||||
Shape::Float(s) => &s.pos,
|
||||
Shape::Boolean(b) => &b.pos,
|
||||
Shape::Empty(p) => p,
|
||||
Shape::List(lst) => &lst.pos,
|
||||
Shape::Tuple(flds) => &flds.pos,
|
||||
Shape::Func(def) => def.ret.pos(),
|
||||
@ -404,7 +411,6 @@ impl Shape {
|
||||
Shape::Int(s) => Shape::Int(PositionedItem::new(s.val, pos)),
|
||||
Shape::Float(s) => Shape::Float(PositionedItem::new(s.val, pos)),
|
||||
Shape::Boolean(b) => Shape::Boolean(PositionedItem::new(b.val, pos)),
|
||||
Shape::Empty(p) => Shape::Empty(pos),
|
||||
Shape::List(lst) => Shape::List(NarrowedShape::new_with_pos(lst.types, pos)),
|
||||
Shape::Tuple(flds) => Shape::Tuple(PositionedItem::new(flds.val, pos)),
|
||||
Shape::Func(_) | Shape::Module(_) => self.clone(),
|
||||
@ -424,13 +430,14 @@ impl Shape {
|
||||
fn is_tuple_subset(
|
||||
mut left_iter: std::slice::Iter<(Token, Shape)>,
|
||||
right_slist: &PositionedItem<Vec<(Token, Shape)>>,
|
||||
symbol_table: &mut BTreeMap<Rc<str>, 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) {
|
||||
if let Shape::TypeErr(_, _) = ls.narrow(rs, symbol_table) {
|
||||
// noop
|
||||
} else {
|
||||
matched = true;
|
||||
@ -448,7 +455,11 @@ fn is_tuple_subset(
|
||||
};
|
||||
}
|
||||
|
||||
fn is_list_subset(mut right_iter: std::slice::Iter<Shape>, left_slist: &NarrowedShape) -> bool {
|
||||
fn is_list_subset(
|
||||
mut right_iter: std::slice::Iter<Shape>,
|
||||
left_slist: &NarrowedShape,
|
||||
symbol_table: &mut BTreeMap<Rc<str>, Shape>,
|
||||
) -> bool {
|
||||
let right_subset = loop {
|
||||
let mut matches = false;
|
||||
let ls = if let Some(ls) = right_iter.next() {
|
||||
@ -457,7 +468,7 @@ fn is_list_subset(mut right_iter: std::slice::Iter<Shape>, left_slist: &Narrowed
|
||||
break true;
|
||||
};
|
||||
for rs in left_slist.types.iter() {
|
||||
let s = ls.narrow(rs);
|
||||
let s = ls.narrow(rs, symbol_table);
|
||||
if let Shape::TypeErr(_, _) = s {
|
||||
// noop
|
||||
} else {
|
||||
|
@ -24,10 +24,6 @@ use crate::tokenizer::tokenize;
|
||||
#[test]
|
||||
fn derive_shape_values() {
|
||||
let value_cases = vec![
|
||||
(
|
||||
Value::Empty(Position::new(0, 0, 0)),
|
||||
Shape::Empty(Position::new(0, 0, 0)),
|
||||
),
|
||||
(
|
||||
Value::Boolean(PositionedItem::new(false, Position::new(0, 1, 2))),
|
||||
Shape::Boolean(PositionedItem::new(false, Position::new(0, 1, 2))),
|
||||
|
@ -17,7 +17,9 @@ use std::collections::BTreeMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::ast::walk::Visitor;
|
||||
use crate::ast::{Expression, FailDef, ImportDef, IncludeDef, Shape, Statement, Value};
|
||||
use crate::ast::{
|
||||
Expression, FailDef, FuncShapeDef, ImportDef, IncludeDef, Shape, Statement, Value,
|
||||
};
|
||||
use crate::error::{BuildError, ErrorType};
|
||||
|
||||
use super::{
|
||||
@ -31,16 +33,20 @@ pub trait DeriveShape {
|
||||
}
|
||||
|
||||
impl DeriveShape for FuncDef {
|
||||
fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
|
||||
fn derive_shape(&self, _symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
|
||||
// 1. First set up our symbols.
|
||||
let _table = self
|
||||
let mut table = self
|
||||
.argdefs
|
||||
.iter()
|
||||
.map(|sym| (sym.val.clone(), Shape::Hole(sym.clone())))
|
||||
.collect::<BTreeMap<Rc<str>, Shape>>();
|
||||
// 2.Then determine the shapes of those symbols in our expression.
|
||||
let _shape = self.fields.derive_shape(&mut table);
|
||||
// 3. Finally determine what the return shape can be.
|
||||
todo!();
|
||||
Shape::Func(FuncShapeDef {
|
||||
args: table,
|
||||
ret: todo!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,8 +93,7 @@ fn derive_copy_shape(def: &CopyDef, symbol_table: &mut BTreeMap<Rc<str>, Shape>)
|
||||
match &base_shape {
|
||||
// TODO(jwall): Should we allow a stack of these?
|
||||
Shape::TypeErr(_, _) => base_shape,
|
||||
Shape::Empty(_)
|
||||
| Shape::Boolean(_)
|
||||
Shape::Boolean(_)
|
||||
| Shape::Int(_)
|
||||
| Shape::Float(_)
|
||||
| Shape::Str(_)
|
||||
@ -103,7 +108,10 @@ fn derive_copy_shape(def: &CopyDef, symbol_table: &mut BTreeMap<Rc<str>, Shape>)
|
||||
Shape::Tuple(PositionedItem::new(vec![], pi.pos.clone())),
|
||||
Shape::Module(ModuleShape {
|
||||
items: vec![],
|
||||
ret: Box::new(Shape::Empty(pi.pos.clone())),
|
||||
ret: Box::new(Shape::Narrowed(NarrowedShape {
|
||||
pos: pi.pos.clone(),
|
||||
types: vec![],
|
||||
})),
|
||||
}),
|
||||
Shape::Import(ImportShape::Unresolved(pi.clone())),
|
||||
],
|
||||
@ -142,7 +150,7 @@ fn derive_copy_shape(def: &CopyDef, symbol_table: &mut BTreeMap<Rc<str>, Shape>)
|
||||
// 1. Do our copyable fields have the right names and shapes based on mdef.items.
|
||||
for (tok, shape) in mdef.items.iter() {
|
||||
if let Some(s) = arg_fields.get(&tok.fragment) {
|
||||
if let Shape::TypeErr(pos, msg) = shape.narrow(s) {
|
||||
if let Shape::TypeErr(pos, msg) = shape.narrow(s, symbol_table) {
|
||||
return Shape::TypeErr(pos, msg);
|
||||
}
|
||||
}
|
||||
@ -199,7 +207,7 @@ impl DeriveShape for Expression {
|
||||
Expression::Binary(def) => {
|
||||
let left_shape = def.left.derive_shape(symbol_table);
|
||||
let right_shape = def.right.derive_shape(symbol_table);
|
||||
left_shape.narrow(&right_shape)
|
||||
left_shape.narrow(&right_shape, symbol_table)
|
||||
}
|
||||
Expression::Copy(def) => derive_copy_shape(def, symbol_table),
|
||||
Expression::Include(def) => derive_include_shape(def),
|
||||
@ -217,7 +225,7 @@ impl DeriveShape for Expression {
|
||||
impl DeriveShape for Value {
|
||||
fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
|
||||
match self {
|
||||
Value::Empty(p) => Shape::Empty(p.clone()),
|
||||
Value::Empty(p) => Shape::Narrowed(NarrowedShape::new_with_pos(vec![], p.clone())),
|
||||
Value::Boolean(p) => Shape::Boolean(p.clone()),
|
||||
Value::Int(p) => Shape::Int(p.clone()),
|
||||
Value::Float(p) => Shape::Float(p.clone()),
|
||||
@ -310,7 +318,12 @@ impl Visitor for Checker {
|
||||
|
||||
fn visit_value(&mut self, val: &mut Value) {
|
||||
match val {
|
||||
Value::Empty(p) => self.shape_stack.push(Shape::Empty(p.clone())),
|
||||
Value::Empty(p) => self
|
||||
.shape_stack
|
||||
.push(Shape::Narrowed(NarrowedShape::new_with_pos(
|
||||
vec![],
|
||||
p.clone(),
|
||||
))),
|
||||
Value::Boolean(p) => self.shape_stack.push(Shape::Boolean(p.clone())),
|
||||
Value::Int(p) => self.shape_stack.push(Shape::Int(p.clone())),
|
||||
Value::Float(p) => self.shape_stack.push(Shape::Float(p.clone())),
|
||||
|
@ -1,9 +1,12 @@
|
||||
use std::convert::Into;
|
||||
|
||||
use crate::ast::{Token, TokenType};
|
||||
use abortable_parser::SliceIter;
|
||||
|
||||
use crate::ast::walk::Walker;
|
||||
use crate::ast::{Position, PositionedItem};
|
||||
use crate::parse;
|
||||
use crate::ast::{Token, TokenType};
|
||||
use crate::parse::{expression, parse};
|
||||
use crate::tokenizer::tokenize;
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -63,10 +66,14 @@ fn simple_binary_typecheck() {
|
||||
assert_type_success!(
|
||||
"{foo = 1} + {foo = 1};",
|
||||
Shape::Tuple(PositionedItem::new(
|
||||
vec![
|
||||
(Token { typ: TokenType::BAREWORD, fragment: "foo".into(), pos: Position::new(1, 2, 1)},
|
||||
Shape::Int(PositionedItem::new_with_pos(1, Position::new(1, 8, 7)))),
|
||||
],
|
||||
vec![(
|
||||
Token {
|
||||
typ: TokenType::BAREWORD,
|
||||
fragment: "foo".into(),
|
||||
pos: Position::new(1, 2, 1)
|
||||
},
|
||||
Shape::Int(PositionedItem::new_with_pos(1, Position::new(1, 8, 7)))
|
||||
),],
|
||||
Position::new(1, 1, 0)
|
||||
))
|
||||
);
|
||||
@ -145,3 +152,25 @@ fn multiple_binary_typefail() {
|
||||
Position::new(1, 9, 8)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_symbol_type_test() {
|
||||
// foo should be determined to be int
|
||||
let expr = "1 + foo".into();
|
||||
let symbol: Rc<str> = "foo".into();
|
||||
let mut checker = Checker::new();
|
||||
checker
|
||||
.symbol_table
|
||||
.insert(symbol.clone(), Shape::Hole(PositionedItem::new(symbol.clone(), Position::new(0, 0, 0))));
|
||||
let tokens = tokenize(expr, None).unwrap();
|
||||
let token_iter = SliceIter::new(&tokens);
|
||||
let expr = expression(token_iter);
|
||||
if let abortable_parser::Result::Complete(_, mut expr) = expr {
|
||||
checker.walk_expression(&mut expr);
|
||||
dbg!(&checker.symbol_table);
|
||||
assert_eq!(
|
||||
checker.symbol_table[&symbol],
|
||||
Shape::Int(PositionedItem::new(1, Position::new(0, 0, 0)))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user