λ Support functions! #5

Open
acw wants to merge 59 commits from awick/functions into develop
4 changed files with 132 additions and 29 deletions
Showing only changes of commit 87d027bf8d - Show all commits

View File

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

View File

@@ -68,7 +68,7 @@ where
#[derive(Debug)] #[derive(Debug)]
pub enum TopLevel { pub enum TopLevel {
Statement(Statement), 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 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> { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
match self { match self {
TopLevel::Function(name, args, body) => allocator TopLevel::Function(name, args, stmts, expr) => {
.text("function") let base = allocator
.append(allocator.space()) .text("function")
.append(allocator.text(name.as_ref().to_string())) .append(allocator.space())
.append(allocator.space()) .append(allocator.text(name.as_ref().to_string()))
.append( .append(allocator.space())
allocator .append(
.intersperse( pretty_comma_separated(
args.iter().map(|x| allocator.text(x.as_ref().to_string())), allocator,
", ", &args
.iter()
.map(|x| allocator.text(x.as_ref().to_string()))
.collect(),
) )
.parens(), .parens(),
) )
.append(allocator.space()) .append(allocator.space());
.append(body.pretty(allocator)),
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), TopLevel::Statement(stmt) => stmt.pretty(allocator),
} }
} }
@@ -310,6 +324,7 @@ where
pub enum Type { pub enum Type {
Variable(Location, ArcIntern<String>), Variable(Location, ArcIntern<String>),
Primitive(PrimitiveType), Primitive(PrimitiveType),
Function(Vec<Type>, Box<Type>),
} }
impl Type { impl Type {
@@ -327,6 +342,14 @@ where
match self { match self {
Type::Variable(_, x) => allocator.text(x.to_string()), Type::Variable(_, x) => allocator.text(x.to_string()),
Type::Primitive(pt) => allocator.text(format!("{}", pt)), 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 { match self {
Type::Variable(_, x) => write!(f, "{}", x), Type::Variable(_, x) => write!(f, "{}", x),
Type::Primitive(pt) => pt.fmt(f), 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) 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>, bindings: &mut HashMap<ArcIntern<String>, Type>,
) -> Vec<ir::TopLevel> { ) -> Vec<ir::TopLevel> {
match top_level { match top_level {
syntax::TopLevel::Function(_, _arg_name, _) => { syntax::TopLevel::Function(name, args, expr) => {
unimplemented!() // 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) => { syntax::TopLevel::Statement(stmt) => {
convert_statement(stmt, constraint_db, renames, bindings) convert_statement(stmt, constraint_db, renames, bindings)
@@ -93,14 +126,7 @@ fn convert_statement(
syntax::Statement::Binding(loc, name, expr) => { syntax::Statement::Binding(loc, name, expr) => {
let (mut prereqs, expr, ty) = let (mut prereqs, expr, ty) =
convert_expression(expr, constraint_db, renames, bindings); convert_expression(expr, constraint_db, renames, bindings);
let iname = ArcIntern::new(name.to_string()); let final_name = finalize_name(bindings, renames, name);
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
};
bindings.insert(final_name.clone(), ty.clone()); bindings.insert(final_name.clone(), ty.clone());
prereqs.push(ir::Statement::Binding(loc, final_name, ty, expr)); 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@@ -16,9 +16,14 @@ pub fn finalize_program(
fn finalize_top_level(item: input::TopLevel, resolutions: &TypeResolutions) -> output::TopLevel { fn finalize_top_level(item: input::TopLevel, resolutions: &TypeResolutions) -> output::TopLevel {
match item { match item {
input::TopLevel::Function(name, args, body) => { input::TopLevel::Function(name, args, mut body, expr) => output::TopLevel::Function(
output::TopLevel::Function(name, args, finalize_expression(body, resolutions)) name,
} args,
body.drain(..)
.map(|x| finalize_statement(x, resolutions))
.collect(),
finalize_expression(expr, resolutions),
),
input::TopLevel::Statement(stmt) => { input::TopLevel::Statement(stmt) => {
output::TopLevel::Statement(finalize_statement(stmt, resolutions)) 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), None => panic!("Did not resolve type for type variable {}", tvar),
Some(pt) => output::Type::Primitive(*pt), 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 { match val {
input::Value::Unknown(base, value) => match new_type { 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( output::Type::Primitive(PrimitiveType::U8) => output::ValueOrRef::Value(
loc, loc,
new_type, new_type,