λ Support functions! #5
@@ -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
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
let base = 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()))
|
||||||
.append(allocator.space())
|
.append(allocator.space())
|
||||||
.append(
|
.append(
|
||||||
allocator
|
pretty_comma_separated(
|
||||||
.intersperse(
|
allocator,
|
||||||
args.iter().map(|x| allocator.text(x.as_ref().to_string())),
|
&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, ", ")
|
||||||
|
}
|
||||||
|
|||||||
@@ -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::*;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user