From c88b5eaf84fac94ed06668cf705165d0acb645f6 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 4 Sep 2023 10:53:51 -0400 Subject: [PATCH] refactor: Make DeriveShape trait --- src/ast/mod.rs | 212 ------------------------------------ src/ast/test.rs | 2 +- src/ast/typecheck/mod.rs | 229 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 222 insertions(+), 221 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 9f229d7..5c4cd46 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -546,38 +546,6 @@ impl Value { &Value::List(_) ) } - - fn derive_shape(&self, symbol_table: &mut BTreeMap, Shape>) -> Shape { - let shape = match self { - Value::Empty(p) => Shape::Empty(p.clone()), - Value::Boolean(p) => Shape::Boolean(p.clone()), - Value::Int(p) => Shape::Int(p.clone()), - Value::Float(p) => Shape::Float(p.clone()), - Value::Str(p) => Shape::Str(p.clone()), - Value::Symbol(p) => { - if let Some(s) = symbol_table.get(&p.val) { - s.clone() - } else { - Shape::Hole(p.clone()) - } - } - Value::Tuple(flds) => { - let mut field_shapes = Vec::new(); - for &(ref tok, ref expr) in &flds.val { - field_shapes.push((tok.clone(), expr.derive_shape(symbol_table))); - } - Shape::Tuple(PositionedItem::new(field_shapes, flds.pos.clone())) - } - Value::List(flds) => { - let mut field_shapes = Vec::new(); - for f in &flds.elems { - field_shapes.push(f.derive_shape(symbol_table)); - } - Shape::List(NarrowedShape::new_with_pos(field_shapes, flds.pos.clone())) - } - }; - shape - } } /// Represents an expansion of a Macro that is expected to already have been @@ -715,20 +683,6 @@ pub struct FuncDef { pub pos: Position, } -impl FuncDef { - fn derive_shape(&self) -> Shape { - // 1. First set up our symbols. - let _table = self - .argdefs - .iter() - .map(|sym| (sym.val.clone(), Shape::Hole(sym.clone()))) - .collect::, Shape>>(); - // 2.Then determine the shapes of those symbols in our expression. - // 3. Finally determine what the return shape can be. - todo!(); - } -} - /// Specifies the types of binary operations supported in /// UCG expression. #[derive(Debug, PartialEq, Clone)] @@ -991,172 +945,6 @@ impl Expression { &Expression::Debug(ref def) => &def.pos, } } - - fn derive_shape(&self, symbol_table: &mut BTreeMap, Shape>) -> Shape { - let shape = match self { - Expression::Simple(v) => v.derive_shape(symbol_table), - Expression::Format(def) => { - Shape::Str(PositionedItem::new("".into(), def.pos.clone())) - } - Expression::Not(def) => derive_not_shape(def, symbol_table), - Expression::Grouped(v, _pos) => v.as_ref().derive_shape(symbol_table), - Expression::Range(def) => Shape::List(NarrowedShape::new_with_pos( - vec![Shape::Int(PositionedItem::new(0, def.start.pos().clone()))], - def.pos.clone(), - )), - Expression::Cast(def) => match def.cast_type { - CastType::Int => Shape::Int(PositionedItem::new(0, def.pos.clone())), - CastType::Str => Shape::Str(PositionedItem::new("".into(), def.pos.clone())), - CastType::Float => Shape::Float(PositionedItem::new(0.0, def.pos.clone())), - CastType::Bool => Shape::Boolean(PositionedItem::new(true, def.pos.clone())), - }, - Expression::Import(def) => Shape::Import(ImportShape::Unresolved(PositionedItem::new( - def.path.fragment.clone(), - def.path.pos.clone(), - ))), - 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) - } - Expression::Copy(def) => derive_copy_shape(def, symbol_table), - Expression::Include(def) => derive_include_shape(def), - Expression::Call(_) => todo!(), - Expression::Func(def) => def.derive_shape(), - Expression::Select(_) => todo!(), - Expression::FuncOp(_) => todo!(), - Expression::Module(_) => todo!(), - Expression::Fail(_) => todo!(), - Expression::Debug(_) => todo!(), - }; - shape - } -} - -fn derive_include_shape( - IncludeDef { - pos, - path: _path, - typ: _typ, - }: &IncludeDef, -) -> Shape { - Shape::Narrowed(NarrowedShape::new_with_pos( - vec![ - Shape::Tuple(PositionedItem::new(vec![], pos.clone())), - Shape::List(NarrowedShape::new_with_pos(vec![], pos.clone())), - ], - pos.clone(), - )) -} - -fn derive_not_shape(def: &NotDef, symbol_table: &mut BTreeMap, Shape>) -> Shape { - let shape = def.expr.as_ref().derive_shape(symbol_table); - if let Shape::Boolean(b) = shape { - Shape::Boolean(PositionedItem::new(!b.val, def.pos.clone())) - } else { - // TODO(jwall): Display implementations for shapes. - Shape::TypeErr( - def.pos.clone(), - format!( - "Expected Boolean value in Not expression but got: {:?}", - shape - ), - ) - } -} - -fn derive_copy_shape(def: &CopyDef, symbol_table: &mut BTreeMap, Shape>) -> Shape { - let base_shape = def.selector.derive_shape(symbol_table); - match &base_shape { - // TODO(jwall): Should we allow a stack of these? - Shape::TypeErr(_, _) => base_shape, - Shape::Empty(_) - | Shape::Boolean(_) - | Shape::Int(_) - | Shape::Float(_) - | Shape::Str(_) - | Shape::List(_) - | Shape::Func(_) => Shape::TypeErr( - def.pos.clone(), - format!("Not a Copyable type {}", base_shape.type_name()), - ), - // This is an interesting one. Do we assume tuple or module here? - // TODO(jwall): Maybe we want a Shape::Narrowed? - Shape::Hole(pi) => Shape::Narrowed(NarrowedShape::new_with_pos( - vec![ - Shape::Tuple(PositionedItem::new(vec![], pi.pos.clone())), - Shape::Module(ModuleShape { - items: vec![], - ret: Box::new(Shape::Empty(pi.pos.clone())), - }), - Shape::Import(ImportShape::Unresolved(pi.clone())), - ], - pi.pos.clone(), - )), - Shape::Narrowed(potentials) => { - // 1. Do the possible shapes include tuple, module, or import? - let filtered = potentials - .types - .iter() - .filter_map(|v| match v { - Shape::Tuple(_) | Shape::Module(_) | Shape::Import(_) | Shape::Hole(_) => { - Some(v.clone()) - } - _ => None, - }) - .collect::>(); - if !filtered.is_empty() { - // 1.1 Then return those and strip the others. - Shape::Narrowed(NarrowedShape::new_with_pos(filtered, def.pos.clone())) - } else { - // 2. Else return a type error - Shape::TypeErr( - def.pos.clone(), - format!("Not a Copyable type {}", base_shape.type_name()), - ) - } - } - // These have understandable ways to resolve the type. - Shape::Module(mdef) => { - let arg_fields = def - .fields - .iter() - .map(|(tok, expr)| (tok.fragment.clone(), expr.derive_shape(symbol_table))) - .collect::, 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) { - return Shape::TypeErr(pos, msg); - } - } - } - // 1.1 If so then return the ret as our shape. - mdef.ret.as_ref().clone() - } - Shape::Tuple(t_def) => { - let mut base_fields = t_def.clone(); - base_fields.val.extend( - def.fields - .iter() - .map(|(tok, expr)| (tok.clone(), expr.derive_shape(symbol_table))), - ); - Shape::Tuple(base_fields).with_pos(def.pos.clone()) - } - Shape::Import(ImportShape::Unresolved(_)) => Shape::Narrowed(NarrowedShape::new_with_pos( - vec![Shape::Tuple(PositionedItem::new(vec![], def.pos.clone()))], - def.pos.clone(), - )), - Shape::Import(ImportShape::Resolved(_, tuple_shape)) => { - let mut base_fields = tuple_shape.clone(); - base_fields.extend( - def.fields - .iter() - .map(|(tok, expr)| (tok.clone(), expr.derive_shape(symbol_table))), - ); - Shape::Tuple(PositionedItem::new(base_fields, def.pos.clone())) - } - } } impl fmt::Display for Expression { diff --git a/src/ast/test.rs b/src/ast/test.rs index 2017376..9bb97a1 100644 --- a/src/ast/test.rs +++ b/src/ast/test.rs @@ -16,7 +16,7 @@ use std::collections::BTreeMap; use abortable_parser::iter::SliceIter; use abortable_parser::Result as ParseResult; -use crate::ast::{Expression, ListDef, Position, PositionedItem, Shape, Token, TokenType, Value ,NarrowedShape}; +use crate::ast::{Expression, ListDef, Position, PositionedItem, Shape, Token, TokenType, Value ,NarrowedShape, typecheck::DeriveShape}; use crate::iter::OffsetStrIter; use crate::parse::expression; use crate::tokenizer::tokenize; diff --git a/src/ast/typecheck/mod.rs b/src/ast/typecheck/mod.rs index d3efa5f..e3808f5 100644 --- a/src/ast/typecheck/mod.rs +++ b/src/ast/typecheck/mod.rs @@ -13,20 +13,233 @@ // limitations under the License. //! Implements typechecking for the parsed ucg AST. -// FIXME(jwall): This probably just needs to disappear now. use std::collections::BTreeMap; use std::rc::Rc; use crate::ast::walk::Visitor; -use crate::ast::{Expression, FailDef, ImportDef, IncludeDef, Position, Shape, Statement, Value}; +use crate::ast::{Expression, FailDef, ImportDef, IncludeDef, Shape, Statement, Value}; use crate::error::{BuildError, ErrorType}; -use Expression::{ - Binary, Call, Cast, Copy, Debug, Fail, Format, Func, FuncOp, Grouped, Import, Include, Module, - Not, Range, Select, Simple, -}; -use Statement::Let; -use Value::{Boolean, Empty, Float, Int, List, Str, Symbol, Tuple}; +use super::{PositionedItem, NarrowedShape, ImportShape, CastType, NotDef, CopyDef, ModuleShape, FuncDef}; + +/// Trait for shape derivation. +pub trait DeriveShape { + /// Derive a shape using a provided symbol table. + fn derive_shape(&self, symbol_table: &mut BTreeMap, Shape>) -> Shape; +} + +impl DeriveShape for FuncDef { + fn derive_shape(&self, symbol_table: &mut BTreeMap, Shape>) -> Shape { + // 1. First set up our symbols. + let _table = self + .argdefs + .iter() + .map(|sym| (sym.val.clone(), Shape::Hole(sym.clone()))) + .collect::, Shape>>(); + // 2.Then determine the shapes of those symbols in our expression. + // 3. Finally determine what the return shape can be. + todo!(); + } +} + +fn derive_include_shape( + IncludeDef { + pos, + path: _path, + typ: _typ, + }: &IncludeDef, +) -> Shape { + Shape::Narrowed(NarrowedShape::new_with_pos( + vec![ + Shape::Tuple(PositionedItem::new(vec![], pos.clone())), + Shape::List(NarrowedShape::new_with_pos(vec![], pos.clone())), + ], + pos.clone(), + )) +} + +fn derive_not_shape(def: &NotDef, symbol_table: &mut BTreeMap, Shape>) -> Shape { + let shape = def.expr.as_ref().derive_shape(symbol_table); + if let Shape::Boolean(b) = shape { + Shape::Boolean(PositionedItem::new(!b.val, def.pos.clone())) + } else { + // TODO(jwall): Display implementations for shapes. + Shape::TypeErr( + def.pos.clone(), + format!( + "Expected Boolean value in Not expression but got: {:?}", + shape + ), + ) + } +} + +fn derive_copy_shape(def: &CopyDef, symbol_table: &mut BTreeMap, Shape>) -> Shape { + let base_shape = def.selector.derive_shape(symbol_table); + match &base_shape { + // TODO(jwall): Should we allow a stack of these? + Shape::TypeErr(_, _) => base_shape, + Shape::Empty(_) + | Shape::Boolean(_) + | Shape::Int(_) + | Shape::Float(_) + | Shape::Str(_) + | Shape::List(_) + | Shape::Func(_) => Shape::TypeErr( + def.pos.clone(), + format!("Not a Copyable type {}", base_shape.type_name()), + ), + // This is an interesting one. Do we assume tuple or module here? + Shape::Hole(pi) => Shape::Narrowed(NarrowedShape::new_with_pos( + vec![ + Shape::Tuple(PositionedItem::new(vec![], pi.pos.clone())), + Shape::Module(ModuleShape { + items: vec![], + ret: Box::new(Shape::Empty(pi.pos.clone())), + }), + Shape::Import(ImportShape::Unresolved(pi.clone())), + ], + pi.pos.clone(), + )), + Shape::Narrowed(potentials) => { + // 1. Do the possible shapes include tuple, module, or import? + let filtered = potentials + .types + .iter() + .filter_map(|v| match v { + Shape::Tuple(_) | Shape::Module(_) | Shape::Import(_) | Shape::Hole(_) => { + Some(v.clone()) + } + _ => None, + }) + .collect::>(); + if !filtered.is_empty() { + // 1.1 Then return those and strip the others. + Shape::Narrowed(NarrowedShape::new_with_pos(filtered, def.pos.clone())) + } else { + // 2. Else return a type error + Shape::TypeErr( + def.pos.clone(), + format!("Not a Copyable type {}", base_shape.type_name()), + ) + } + } + // These have understandable ways to resolve the type. + Shape::Module(mdef) => { + let arg_fields = def + .fields + .iter() + .map(|(tok, expr)| (tok.fragment.clone(), expr.derive_shape(symbol_table))) + .collect::, 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) { + return Shape::TypeErr(pos, msg); + } + } + } + // 1.1 If so then return the ret as our shape. + mdef.ret.as_ref().clone() + } + Shape::Tuple(t_def) => { + let mut base_fields = t_def.clone(); + base_fields.val.extend( + def.fields + .iter() + .map(|(tok, expr)| (tok.clone(), expr.derive_shape(symbol_table))), + ); + Shape::Tuple(base_fields).with_pos(def.pos.clone()) + } + Shape::Import(ImportShape::Unresolved(_)) => Shape::Narrowed(NarrowedShape::new_with_pos( + vec![Shape::Tuple(PositionedItem::new(vec![], def.pos.clone()))], + def.pos.clone(), + )), + Shape::Import(ImportShape::Resolved(_, tuple_shape)) => { + let mut base_fields = tuple_shape.clone(); + base_fields.extend( + def.fields + .iter() + .map(|(tok, expr)| (tok.clone(), expr.derive_shape(symbol_table))), + ); + Shape::Tuple(PositionedItem::new(base_fields, def.pos.clone())) + } + } +} + +impl DeriveShape for Expression { + fn derive_shape(&self, symbol_table: &mut BTreeMap, Shape>) -> Shape { + match self { + Expression::Simple(v) => v.derive_shape(symbol_table), + Expression::Format(def) => { + Shape::Str(PositionedItem::new("".into(), def.pos.clone())) + } + Expression::Not(def) => derive_not_shape(def, symbol_table), + Expression::Grouped(v, _pos) => v.as_ref().derive_shape(symbol_table), + Expression::Range(def) => Shape::List(NarrowedShape::new_with_pos( + vec![Shape::Int(PositionedItem::new(0, def.start.pos().clone()))], + def.pos.clone(), + )), + Expression::Cast(def) => match def.cast_type { + CastType::Int => Shape::Int(PositionedItem::new(0, def.pos.clone())), + CastType::Str => Shape::Str(PositionedItem::new("".into(), def.pos.clone())), + CastType::Float => Shape::Float(PositionedItem::new(0.0, def.pos.clone())), + CastType::Bool => Shape::Boolean(PositionedItem::new(true, def.pos.clone())), + }, + Expression::Import(def) => Shape::Import(ImportShape::Unresolved(PositionedItem::new( + def.path.fragment.clone(), + def.path.pos.clone(), + ))), + 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) + } + Expression::Copy(def) => derive_copy_shape(def, symbol_table), + Expression::Include(def) => derive_include_shape(def), + Expression::Call(_) => todo!(), + Expression::Func(def) => def.derive_shape(symbol_table), + Expression::Select(_) => todo!(), + Expression::FuncOp(_) => todo!(), + Expression::Module(_) => todo!(), + Expression::Fail(_) => todo!(), + Expression::Debug(_) => todo!(), + } + } +} + +impl DeriveShape for Value { + fn derive_shape(&self, symbol_table: &mut BTreeMap, Shape>) -> Shape { + match self { + Value::Empty(p) => Shape::Empty(p.clone()), + Value::Boolean(p) => Shape::Boolean(p.clone()), + Value::Int(p) => Shape::Int(p.clone()), + Value::Float(p) => Shape::Float(p.clone()), + Value::Str(p) => Shape::Str(p.clone()), + Value::Symbol(p) => { + if let Some(s) = symbol_table.get(&p.val) { + s.clone() + } else { + Shape::Hole(p.clone()) + } + } + Value::Tuple(flds) => { + let mut field_shapes = Vec::new(); + for &(ref tok, ref expr) in &flds.val { + field_shapes.push((tok.clone(), expr.derive_shape(symbol_table))); + } + Shape::Tuple(PositionedItem::new(field_shapes, flds.pos.clone())) + } + Value::List(flds) => { + let mut field_shapes = Vec::new(); + for f in &flds.elems { + field_shapes.push(f.derive_shape(symbol_table)); + } + Shape::List(NarrowedShape::new_with_pos(field_shapes, flds.pos.clone())) + } + } + } +} pub struct Checker { symbol_table: BTreeMap, Shape>,