refactor: Make DeriveShape trait

This commit is contained in:
Jeremy Wall 2023-09-04 10:53:51 -04:00 committed by Jeremy Wall
parent a301ff7dfe
commit c88b5eaf84
3 changed files with 222 additions and 221 deletions

View File

@ -546,38 +546,6 @@ impl Value {
&Value::List(_)
)
}
fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, 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::<BTreeMap<Rc<str>, 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<Rc<str>, 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<Rc<str>, 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<Rc<str>, 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::<Vec<Shape>>();
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::<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) {
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 {

View File

@ -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;

View File

@ -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<Rc<str>, Shape>) -> Shape;
}
impl DeriveShape for FuncDef {
fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
// 1. First set up our symbols.
let _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.
// 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<Rc<str>, 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<Rc<str>, 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::<Vec<Shape>>();
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::<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) {
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<Rc<str>, 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<Rc<str>, 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<Rc<str>, Shape>,