Checkpoint: pushed through type checking, at least.

This commit is contained in:
2023-10-07 14:36:17 +02:00
parent ca26d0ea40
commit 87d027bf8d
4 changed files with 132 additions and 29 deletions

View File

@@ -77,7 +77,7 @@ impl Arbitrary for Program {
#[derive(Debug)]
pub enum TopLevel {
Statement(Statement),
Function(Variable, Vec<Variable>, Expression),
Function(Variable, Vec<Variable>, Vec<Statement>, Expression),
}
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TopLevel
@@ -87,7 +87,7 @@ where
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
match self {
TopLevel::Function(name, args, body) => allocator
TopLevel::Function(name, args, stmts, body) => allocator
.text("function")
.append(allocator.space())
.append(allocator.text(name.as_ref().to_string()))
@@ -378,6 +378,7 @@ where
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Type {
Primitive(PrimitiveType),
Function(Vec<Type>, Box<Type>),
}
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Type

View File

@@ -68,7 +68,7 @@ where
#[derive(Debug)]
pub enum TopLevel {
Statement(Statement),
Function(Variable, Vec<Variable>, Expression),
Function(Variable, Vec<Variable>, Vec<Statement>, Expression),
}
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TopLevel
@@ -78,22 +78,36 @@ where
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
match self {
TopLevel::Function(name, args, body) => allocator
.text("function")
.append(allocator.space())
.append(allocator.text(name.as_ref().to_string()))
.append(allocator.space())
.append(
allocator
.intersperse(
args.iter().map(|x| allocator.text(x.as_ref().to_string())),
", ",
TopLevel::Function(name, args, stmts, expr) => {
let base = allocator
.text("function")
.append(allocator.space())
.append(allocator.text(name.as_ref().to_string()))
.append(allocator.space())
.append(
pretty_comma_separated(
allocator,
&args
.iter()
.map(|x| allocator.text(x.as_ref().to_string()))
.collect(),
)
.parens(),
)
.append(allocator.space())
.append(body.pretty(allocator)),
)
.append(allocator.space());
let mut body = allocator.nil();
for stmt in stmts {
body = body
.append(stmt.pretty(allocator))
.append(allocator.text(";"))
.append(allocator.hardline());
}
body = body.append(expr.pretty(allocator));
body = body.append(allocator.hardline());
body = body.braces();
base.append(body)
}
TopLevel::Statement(stmt) => stmt.pretty(allocator),
}
}
@@ -310,6 +324,7 @@ where
pub enum Type {
Variable(Location, ArcIntern<String>),
Primitive(PrimitiveType),
Function(Vec<Type>, Box<Type>),
}
impl Type {
@@ -327,6 +342,14 @@ where
match self {
Type::Variable(_, x) => allocator.text(x.to_string()),
Type::Primitive(pt) => allocator.text(format!("{}", pt)),
Type::Function(args, rettype) => {
pretty_comma_separated(allocator, &args.iter().collect())
.parens()
.append(allocator.space())
.append(allocator.text("->"))
.append(allocator.space())
.append(rettype.pretty(allocator))
}
}
}
}
@@ -336,6 +359,18 @@ impl fmt::Display for Type {
match self {
Type::Variable(_, x) => write!(f, "{}", x),
Type::Primitive(pt) => pt.fmt(f),
Type::Function(args, ret) => {
write!(f, "(")?;
let mut argiter = args.iter().peekable();
while let Some(arg) = argiter.next() {
arg.fmt(f)?;
if argiter.peek().is_some() {
write!(f, ",")?;
}
}
write!(f, "->")?;
ret.fmt(f)
}
}
}
}
@@ -373,3 +408,16 @@ pub fn gentype() -> Type {
Type::Variable(Location::manufactured(), name)
}
fn pretty_comma_separated<'a, D, A, P>(
allocator: &'a D,
args: &Vec<P>,
) -> pretty::DocBuilder<'a, D, A>
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
P: Pretty<'a, D, A>,
{
let individuals = args.iter().map(|x| x.pretty(allocator));
allocator.intersperse(individuals, ", ")
}

View File

@@ -44,8 +44,41 @@ pub fn convert_top_level(
bindings: &mut HashMap<ArcIntern<String>, Type>,
) -> Vec<ir::TopLevel> {
match top_level {
syntax::TopLevel::Function(_, _arg_name, _) => {
unimplemented!()
syntax::TopLevel::Function(name, args, expr) => {
// First, let us figure out what we're going to name this function. If the user
// didn't provide one, we'll just call it "function:<something>" for them. (We'll
// want a name for this function, eventually, so we might as well do it now.)
//
// If they did provide a name, see if we're shadowed. IF we are, then we'll have
// to specialize the name a bit. Otherwise we'll stick with their name.
let funname = match name {
None => ir::gensym("function"),
Some(unbound) => finalize_name(bindings, renames, unbound),
};
// Now we manufacture types for the inputs and outputs, and then a type for the
// function itself. We're not going to make any claims on these types, yet; they're
// all just unknown type variables we need to work out.
let argtypes: Vec<Type> = args.iter().map(|_| ir::gentype()).collect();
let rettype = ir::gentype();
let funtype = Type::Function(argtypes.clone(), Box::new(rettype.clone()));
// Now let's bind these types into the environment. First, we bind our function
// namae to the function type we just generated.
bindings.insert(funname, funtype);
// And then we attach the argument names to the argument types. (We have to go
// convert all the names, first.)
let iargs: Vec<ArcIntern<String>> =
args.iter().map(|x| ArcIntern::new(x.to_string())).collect();
assert_eq!(argtypes.len(), iargs.len());
for (arg_name, arg_type) in iargs.iter().zip(argtypes) {
bindings.insert(arg_name.clone(), arg_type.clone());
}
let (stmts, expr, ty) = convert_expression(expr, constraint_db, renames, bindings);
constraint_db.push(Constraint::Equivalent(expr.location().clone(), rettype, ty));
vec![ir::TopLevel::Function(funname, iargs, stmts, expr)]
}
syntax::TopLevel::Statement(stmt) => {
convert_statement(stmt, constraint_db, renames, bindings)
@@ -93,14 +126,7 @@ fn convert_statement(
syntax::Statement::Binding(loc, name, expr) => {
let (mut prereqs, expr, ty) =
convert_expression(expr, constraint_db, renames, bindings);
let iname = ArcIntern::new(name.to_string());
let final_name = if bindings.contains_key(&iname) {
let new_name = ir::gensym(iname.as_str());
renames.insert(iname, new_name.clone());
new_name
} else {
iname
};
let final_name = finalize_name(bindings, renames, name);
bindings.insert(final_name.clone(), ty.clone());
prereqs.push(ir::Statement::Binding(loc, final_name, ty, expr));
@@ -260,6 +286,20 @@ fn simplify_expr(expr: ir::Expression, stmts: &mut Vec<ir::Statement>) -> ir::Va
}
}
fn finalize_name(
bindings: &HashMap<ArcIntern<String>, Type>,
renames: &mut HashMap<ArcIntern<String>, ArcIntern<String>>,
name: syntax::Name,
) -> ArcIntern<String> {
if bindings.contains_key(&ArcIntern::new(name.name)) {
let new_name = ir::gensym(&name.name);
renames.insert(ArcIntern::new(name.name.to_string()), new_name.clone());
new_name
} else {
ArcIntern::new(name.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -16,9 +16,14 @@ pub fn finalize_program(
fn finalize_top_level(item: input::TopLevel, resolutions: &TypeResolutions) -> output::TopLevel {
match item {
input::TopLevel::Function(name, args, body) => {
output::TopLevel::Function(name, args, finalize_expression(body, resolutions))
}
input::TopLevel::Function(name, args, mut body, expr) => output::TopLevel::Function(
name,
args,
body.drain(..)
.map(|x| finalize_statement(x, resolutions))
.collect(),
finalize_expression(expr, resolutions),
),
input::TopLevel::Statement(stmt) => {
output::TopLevel::Statement(finalize_statement(stmt, resolutions))
}
@@ -73,6 +78,12 @@ fn finalize_type(ty: input::Type, resolutions: &TypeResolutions) -> output::Type
None => panic!("Did not resolve type for type variable {}", tvar),
Some(pt) => output::Type::Primitive(*pt),
},
input::Type::Function(mut args, ret) => output::Type::Function(
args.drain(..)
.map(|x| finalize_type(x, resolutions))
.collect(),
Box::new(finalize_type(*ret, resolutions)),
),
}
}
@@ -89,6 +100,9 @@ fn finalize_val_or_ref(
match val {
input::Value::Unknown(base, value) => match new_type {
output::Type::Function(_, _) => {
panic!("Somehow inferred that a constant was a function")
}
output::Type::Primitive(PrimitiveType::U8) => output::ValueOrRef::Value(
loc,
new_type,