diff --git a/compiler/cl-ast/src/ast.rs b/compiler/cl-ast/src/ast.rs index 249f3ba..97c96f7 100644 --- a/compiler/cl-ast/src/ast.rs +++ b/compiler/cl-ast/src/ast.rs @@ -317,6 +317,8 @@ pub enum ExprKind { /// An empty expression: `(` `)` #[default] Empty, + /// A [Closure] expression: `|` [`Expr`] `|` ( -> [`Ty`])? [`Expr`] + Closure(Closure), /// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)` Tuple(Tuple), /// A [Struct creation](Structor) expression: [Path] `{` ([Fielder] `,`)* [Fielder]? `}` @@ -371,6 +373,13 @@ pub enum ExprKind { Continue, } +/// A Closure [expression](Expr): `|` [`Expr`] `|` ( -> [`Ty`])? [`Expr`] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Closure { + pub arg: Box, + pub body: Box, +} + /// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)` #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Tuple { diff --git a/compiler/cl-ast/src/ast_impl/convert.rs b/compiler/cl-ast/src/ast_impl/convert.rs index 5d72213..a48e51a 100644 --- a/compiler/cl-ast/src/ast_impl/convert.rs +++ b/compiler/cl-ast/src/ast_impl/convert.rs @@ -51,6 +51,7 @@ impl_from! { } impl From for ExprKind { Let => ExprKind::Let, + Closure => ExprKind::Closure, Quote => ExprKind::Quote, Match => ExprKind::Match, Assign => ExprKind::Assign, diff --git a/compiler/cl-ast/src/ast_impl/display.rs b/compiler/cl-ast/src/ast_impl/display.rs index 323c852..1f24d3c 100644 --- a/compiler/cl-ast/src/ast_impl/display.rs +++ b/compiler/cl-ast/src/ast_impl/display.rs @@ -402,6 +402,7 @@ impl Display for ExprKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ExprKind::Empty => "()".fmt(f), + ExprKind::Closure(v) => v.fmt(f), ExprKind::Quote(v) => v.fmt(f), ExprKind::Let(v) => v.fmt(f), ExprKind::Match(v) => v.fmt(f), @@ -431,6 +432,17 @@ impl Display for ExprKind { } } +impl Display for Closure { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { arg, body } = self; + match arg.as_ref() { + Pattern::Tuple(args) => separate(args, ", ")(f.delimit_with("|", "|")), + _ => arg.fmt(f), + }?; + write!(f, " {body}") + } +} + impl Display for Quote { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { quote } = self; diff --git a/compiler/cl-ast/src/ast_impl/weight_of.rs b/compiler/cl-ast/src/ast_impl/weight_of.rs index f1630ba..46ec5d1 100644 --- a/compiler/cl-ast/src/ast_impl/weight_of.rs +++ b/compiler/cl-ast/src/ast_impl/weight_of.rs @@ -283,6 +283,7 @@ impl WeightOf for ExprKind { fn weight_of(&self) -> usize { match self { ExprKind::Empty => size_of_val(self), + ExprKind::Closure(v) => v.weight_of(), ExprKind::Quote(v) => v.weight_of(), ExprKind::Let(v) => v.weight_of(), ExprKind::Match(v) => v.weight_of(), @@ -312,6 +313,13 @@ impl WeightOf for ExprKind { } } +impl WeightOf for Closure { + fn weight_of(&self) -> usize { + let Self { arg, body } = self; + arg.weight_of() + body.weight_of() + } +} + impl WeightOf for Quote { fn weight_of(&self) -> usize { let Self { quote } = self; diff --git a/compiler/cl-ast/src/ast_visitor/fold.rs b/compiler/cl-ast/src/ast_visitor/fold.rs index 2327365..5dc77ea 100644 --- a/compiler/cl-ast/src/ast_visitor/fold.rs +++ b/compiler/cl-ast/src/ast_visitor/fold.rs @@ -236,6 +236,10 @@ pub trait Fold { fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind { or_fold_expr_kind(self, kind) } + fn fold_closure(&mut self, value: Closure) -> Closure { + let Closure { arg, body } = value; + Closure { arg: Box::new(self.fold_pattern(*arg)), body: Box::new(self.fold_expr(*body)) } + } fn fold_let(&mut self, l: Let) -> Let { let Let { mutable, name, ty, init } = l; Let { @@ -547,6 +551,7 @@ pub fn or_fold_stmt_kind(folder: &mut F, kind: StmtKind) -> St pub fn or_fold_expr_kind(folder: &mut F, kind: ExprKind) -> ExprKind { match kind { ExprKind::Empty => ExprKind::Empty, + ExprKind::Closure(c) => ExprKind::Closure(folder.fold_closure(c)), ExprKind::Quote(q) => ExprKind::Quote(q), // quoted expressions are left unmodified ExprKind::Let(l) => ExprKind::Let(folder.fold_let(l)), ExprKind::Match(m) => ExprKind::Match(folder.fold_match(m)), diff --git a/compiler/cl-ast/src/ast_visitor/visit.rs b/compiler/cl-ast/src/ast_visitor/visit.rs index 60fd453..4ff40d9 100644 --- a/compiler/cl-ast/src/ast_visitor/visit.rs +++ b/compiler/cl-ast/src/ast_visitor/visit.rs @@ -17,8 +17,9 @@ use super::walk::Walk; pub trait Visit<'a>: Sized { /// Visits a [Walker](Walk) #[inline] - fn visit(&mut self, walker: &'a W) { - walker.visit_in(self) + fn visit(&mut self, walker: &'a W) -> &mut Self { + walker.visit_in(self); + self } /// Visits the children of a [Walker](Walk) fn visit_children(&mut self, walker: &'a W) { @@ -160,6 +161,9 @@ pub trait Visit<'a>: Sized { fn visit_expr_kind(&mut self, value: &'a ExprKind) { value.children(self) } + fn visit_closure(&mut self, value: &'a Closure) { + value.children(self) + } fn visit_quote(&mut self, value: &'a Quote) { value.children(self) } diff --git a/compiler/cl-ast/src/ast_visitor/walk.rs b/compiler/cl-ast/src/ast_visitor/walk.rs index eae8480..bd3a487 100644 --- a/compiler/cl-ast/src/ast_visitor/walk.rs +++ b/compiler/cl-ast/src/ast_visitor/walk.rs @@ -504,25 +504,26 @@ impl Walk for ExprKind { fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) { match self { ExprKind::Empty => {} + ExprKind::Closure(value) => value.visit_in(v), + ExprKind::Tuple(value) => value.visit_in(v), + ExprKind::Structor(value) => value.visit_in(v), + ExprKind::Array(value) => value.visit_in(v), + ExprKind::ArrayRep(value) => value.visit_in(v), + ExprKind::AddrOf(value) => value.visit_in(v), ExprKind::Quote(value) => value.visit_in(v), - ExprKind::Let(value) => value.visit_in(v), - ExprKind::Match(value) => value.visit_in(v), + ExprKind::Literal(value) => value.visit_in(v), + ExprKind::Group(value) => value.visit_in(v), + ExprKind::Block(value) => value.visit_in(v), ExprKind::Assign(value) => value.visit_in(v), ExprKind::Modify(value) => value.visit_in(v), ExprKind::Binary(value) => value.visit_in(v), ExprKind::Unary(value) => value.visit_in(v), - ExprKind::Cast(value) => value.visit_in(v), ExprKind::Member(value) => value.visit_in(v), ExprKind::Index(value) => value.visit_in(v), - ExprKind::Structor(value) => value.visit_in(v), + ExprKind::Cast(value) => value.visit_in(v), ExprKind::Path(value) => value.visit_in(v), - ExprKind::Literal(value) => value.visit_in(v), - ExprKind::Array(value) => value.visit_in(v), - ExprKind::ArrayRep(value) => value.visit_in(v), - ExprKind::AddrOf(value) => value.visit_in(v), - ExprKind::Block(value) => value.visit_in(v), - ExprKind::Group(value) => value.visit_in(v), - ExprKind::Tuple(value) => value.visit_in(v), + ExprKind::Let(value) => value.visit_in(v), + ExprKind::Match(value) => value.visit_in(v), ExprKind::While(value) => value.visit_in(v), ExprKind::If(value) => value.visit_in(v), ExprKind::For(value) => value.visit_in(v), @@ -533,6 +534,18 @@ impl Walk for ExprKind { } } +impl Walk for Closure { + fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) { + v.visit_closure(self); + } + + fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) { + let Self { arg, body } = self; + v.visit_pattern(arg); + v.visit_expr(body); + } +} + impl Walk for Tuple { #[inline] fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) { diff --git a/compiler/cl-interpret/src/closure.rs b/compiler/cl-interpret/src/closure.rs new file mode 100644 index 0000000..7f4be74 --- /dev/null +++ b/compiler/cl-interpret/src/closure.rs @@ -0,0 +1,68 @@ +use crate::{ + Callable, + convalue::ConValue, + env::{Environment, Place}, + error::{Error, ErrorKind, IResult}, + function::collect_upvars::CollectUpvars, + interpret::Interpret, + pattern, +}; +use cl_ast::{Sym, ast_visitor::Visit}; +use std::{collections::HashMap, fmt::Display}; + +/// Represents an ad-hoc anonymous function +/// which captures surrounding state by reference. +#[derive(Clone, Debug)] +pub struct Closure { + decl: cl_ast::Closure, + lift: HashMap, +} + +impl Closure { + const NAME: &'static str = "{closure}"; +} + +impl Closure { + pub fn new(env: &mut Environment, decl: &cl_ast::Closure) -> Self { + let lift = CollectUpvars::new(env).visit(decl).finish(); + Self { decl: decl.clone(), lift } + } +} + +impl Display for Closure { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { decl, lift: _ } = self; + write!(f, "{decl}") + } +} + +impl Callable for Closure { + fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult { + let Self { decl, lift } = self; + let mut env = env.frame(Self::NAME); + + // place lifts in scope + for (name, place) in lift { + env.insert(*name, Some(ConValue::Ref(*place))); + } + + let mut env = env.frame("args"); + + for (name, value) in pattern::substitution(&decl.arg, ConValue::Tuple(args.into()))? { + env.insert(*name, Some(value)); + } + + let res = decl.body.interpret(&mut env); + drop(env); + + match res { + Err(Error { kind: ErrorKind::Return(value), .. }) => Ok(value), + Err(Error { kind: ErrorKind::Break(value), .. }) => Err(Error::BadBreak(value)), + other => other, + } + } + + fn name(&self) -> cl_ast::Sym { + "{closure}".into() + } +} diff --git a/compiler/cl-interpret/src/convalue.rs b/compiler/cl-interpret/src/convalue.rs index 2770941..74a39b3 100644 --- a/compiler/cl-interpret/src/convalue.rs +++ b/compiler/cl-interpret/src/convalue.rs @@ -3,7 +3,7 @@ //! The most permanent fix is a temporary one. use cl_ast::{Expr, Sym, format::FmtAdapter}; -use crate::env::Place; +use crate::{closure::Closure, env::Place}; use super::{ Callable, Environment, @@ -71,6 +71,8 @@ pub enum ConValue { Quote(Box), /// A callable thing Function(Rc), + /// A closure, capturing by reference + Closure(Rc), /// A built-in function Builtin(&'static Builtin), } @@ -140,6 +142,7 @@ impl Callable for ConValue { fn name(&self) -> Sym { match self { ConValue::Function(func) => func.name(), + ConValue::Closure(func) => func.name(), ConValue::Builtin(func) => func.name(), _ => "".into(), } @@ -147,6 +150,7 @@ impl Callable for ConValue { fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult { match self { Self::Function(func) => func.call(interpreter, args), + Self::Closure(func) => func.call(interpreter, args), Self::Builtin(func) => func.call(interpreter, args), _ => Err(Error::NotCallable(self.clone())), } @@ -368,6 +372,9 @@ impl std::fmt::Display for ConValue { ConValue::Function(func) => { write!(f, "{}", func.decl()) } + ConValue::Closure(func) => { + write!(f, "{}", func.as_ref()) + } ConValue::Builtin(func) => { write!(f, "{}", func.description()) } diff --git a/compiler/cl-interpret/src/env.rs b/compiler/cl-interpret/src/env.rs index c901991..5010d56 100644 --- a/compiler/cl-interpret/src/env.rs +++ b/compiler/cl-interpret/src/env.rs @@ -159,30 +159,14 @@ impl Environment { } } - pub(crate) fn get_local(&self, name: Sym) -> IResult { - for EnvFrame { binds, .. } in self.frames.iter().skip(2).rev() { - if let Some(var) = binds.get(&name) { - if let Some(var) = self.values.get(*var) { - return match var { - Some(value) => Ok(value.clone()), - None => Err(Error::NotInitialized(name)), - }; - } - } - } - Err(Error::NotDefined(name)) - } - + /// Resolves the [Place] associated with a [Sym] pub fn id_of(&self, name: Sym) -> IResult { for EnvFrame { binds, .. } in self.frames.iter().rev() { if let Some(id) = binds.get(&name).copied() { return Ok(Place::Local(id)); } } - self.global - .contains_key(&name) - .then_some(Place::Global(name)) - .ok_or(Error::NotDefined(name)) + Ok(Place::Global(name)) } pub fn get_id(&self, at: Place) -> Option<&ConValue> { diff --git a/compiler/cl-interpret/src/function/collect_upvars.rs b/compiler/cl-interpret/src/function/collect_upvars.rs index c5cbae3..76a4778 100644 --- a/compiler/cl-interpret/src/function/collect_upvars.rs +++ b/compiler/cl-interpret/src/function/collect_upvars.rs @@ -1,5 +1,5 @@ //! Collects the "Upvars" of a function at the point of its creation, allowing variable capture -use crate::{convalue::ConValue, env::Environment}; +use crate::env::{Environment, Place}; use cl_ast::{ Function, Let, Path, PathPart, Pattern, Sym, ast_visitor::{visit::*, walk::Walk}, @@ -7,13 +7,18 @@ use cl_ast::{ use std::collections::{HashMap, HashSet}; pub fn collect_upvars(f: &Function, env: &Environment) -> super::Upvars { - CollectUpvars::new(env).get_upvars(f) + CollectUpvars::new(env) + .visit(f) + .finish() + .into_iter() + .map(|(k, v)| (k, env.get_id(v).cloned())) + .collect() } #[derive(Clone, Debug)] pub struct CollectUpvars<'env> { env: &'env Environment, - upvars: HashMap>, + upvars: HashMap, blacklist: HashSet, } @@ -21,9 +26,9 @@ impl<'env> CollectUpvars<'env> { pub fn new(env: &'env Environment) -> Self { Self { upvars: HashMap::new(), blacklist: HashSet::new(), env } } - pub fn get_upvars(mut self, f: &cl_ast::Function) -> HashMap> { - self.visit_function(f); - self.upvars + + pub fn finish(&mut self) -> HashMap { + std::mem::take(&mut self.upvars) } pub fn add_upvar(&mut self, name: &Sym) { @@ -31,8 +36,8 @@ impl<'env> CollectUpvars<'env> { if blacklist.contains(name) || upvars.contains_key(name) { return; } - if let Ok(upvar) = env.get_local(*name) { - upvars.insert(*name, Some(upvar)); + if let Ok(place) = env.id_of(*name) { + upvars.insert(*name, place); } } diff --git a/compiler/cl-interpret/src/interpret.rs b/compiler/cl-interpret/src/interpret.rs index b2e9d3e..67df8c8 100644 --- a/compiler/cl-interpret/src/interpret.rs +++ b/compiler/cl-interpret/src/interpret.rs @@ -295,6 +295,7 @@ impl Interpret for ExprKind { fn interpret(&self, env: &mut Environment) -> IResult { match self { ExprKind::Empty => Ok(ConValue::Empty), + ExprKind::Closure(v) => v.interpret(env), ExprKind::Quote(q) => q.interpret(env), ExprKind::Let(v) => v.interpret(env), ExprKind::Match(v) => v.interpret(env), @@ -324,6 +325,14 @@ impl Interpret for ExprKind { } } +impl Interpret for Closure { + fn interpret(&self, env: &mut Environment) -> IResult { + Ok(ConValue::Closure( + crate::closure::Closure::new(env, self).into(), + )) + } +} + impl Interpret for Quote { fn interpret(&self, _env: &mut Environment) -> IResult { // TODO: squoosh down into a ConValue? diff --git a/compiler/cl-interpret/src/lib.rs b/compiler/cl-interpret/src/lib.rs index 17598c4..5fdd71a 100644 --- a/compiler/cl-interpret/src/lib.rs +++ b/compiler/cl-interpret/src/lib.rs @@ -23,6 +23,8 @@ pub mod interpret; pub mod function; +pub mod closure; + pub mod builtin; pub mod pattern; diff --git a/compiler/cl-parser/src/error.rs b/compiler/cl-parser/src/error.rs index 11e04b8..9bd72ed 100644 --- a/compiler/cl-parser/src/error.rs +++ b/compiler/cl-parser/src/error.rs @@ -97,6 +97,7 @@ pub enum Parsing { Expr, ExprKind, + Closure, Assign, AssignKind, Binary, @@ -214,6 +215,7 @@ impl Display for Parsing { Parsing::Expr => "an expression", Parsing::ExprKind => "an expression", + Parsing::Closure => "an anonymous function", Parsing::Assign => "an assignment", Parsing::AssignKind => "an assignment operator", Parsing::Binary => "a binary expression", diff --git a/compiler/cl-parser/src/parser.rs b/compiler/cl-parser/src/parser.rs index 8332211..097ef18 100644 --- a/compiler/cl-parser/src/parser.rs +++ b/compiler/cl-parser/src/parser.rs @@ -884,6 +884,31 @@ impl Parse<'_> for Expr { } } +impl Parse<'_> for Closure { + fn parse(p: &mut Parser<'_>) -> PResult { + let args = sep( + Pattern::parse, + TokenKind::Comma, + TokenKind::Bar, + Parsing::Closure, + ); + + let arg = match p.peek_kind(Parsing::Closure)? { + TokenKind::BarBar => { + p.consume_peeked(); + Box::new(Pattern::Tuple(vec![])) + } + _ => Box::new(delim( + |p| args(p).map(Pattern::Tuple), + (TokenKind::Bar, TokenKind::Bar), + Parsing::Closure, + )(p)?), + }; + let body = p.parse()?; + Ok(Closure { arg, body }) + } +} + impl Parse<'_> for Quote { fn parse(p: &mut Parser<'_>) -> PResult { let quote = delim( @@ -1104,6 +1129,12 @@ impl Parse<'_> for MatchArm { fn ret_body(p: &mut Parser, while_parsing: Parsing) -> PResult>> { Ok(match p.peek_kind(while_parsing)? { TokenKind::Semi => None, - _ => Some(Expr::parse(p)?.into()), + _ => Some(p.parse()?), }) } + +impl<'t, P: Parse<'t>> Parse<'t> for Box

{ + fn parse(p: &mut Parser<'t>) -> PResult { + p.parse().map(Box::new) + } +} diff --git a/compiler/cl-parser/src/parser/prec.rs b/compiler/cl-parser/src/parser/prec.rs index 6efc267..fed343e 100644 --- a/compiler/cl-parser/src/parser/prec.rs +++ b/compiler/cl-parser/src/parser/prec.rs @@ -17,6 +17,7 @@ pub fn expr(p: &mut Parser, power: u8) -> PResult { literal_like!() => Literal::parse(p)?.into(), path_like!() => exprkind_pathlike(p)?, TokenKind::Amp | TokenKind::AmpAmp => AddrOf::parse(p)?.into(), + TokenKind::Bar | TokenKind::BarBar => Closure::parse(p)?.into(), TokenKind::Grave => Quote::parse(p)?.into(), TokenKind::LCurly => Block::parse(p)?.into(), TokenKind::LBrack => exprkind_arraylike(p)?, diff --git a/compiler/cl-repl/examples/to_c.rs b/compiler/cl-repl/examples/to_c.rs index 242c09c..26ab218 100644 --- a/compiler/cl-repl/examples/to_c.rs +++ b/compiler/cl-repl/examples/to_c.rs @@ -428,6 +428,7 @@ pub mod clangify { impl CLangify for ExprKind { fn print(&self, y: &mut CLangifier) { match self { + ExprKind::Closure(k) => todo!("Downgrade {k}"), ExprKind::Quote(k) => k.print(y), ExprKind::Let(k) => k.print(y), ExprKind::Match(k) => k.print(y), diff --git a/compiler/cl-repl/examples/yaml.rs b/compiler/cl-repl/examples/yaml.rs index a66bb08..12e31b7 100644 --- a/compiler/cl-repl/examples/yaml.rs +++ b/compiler/cl-repl/examples/yaml.rs @@ -370,6 +370,7 @@ pub mod yamlify { impl Yamlify for ExprKind { fn yaml(&self, y: &mut Yamler) { match self { + ExprKind::Closure(k) => k.yaml(y), ExprKind::Quote(k) => k.yaml(y), ExprKind::Let(k) => k.yaml(y), ExprKind::Match(k) => k.yaml(y), @@ -401,6 +402,12 @@ pub mod yamlify { } } } + impl Yamlify for Closure { + fn yaml(&self, y: &mut Yamler) { + let Self { arg, body } = self; + y.key("Closure").pair("arg", arg).pair("body", body); + } + } impl Yamlify for Quote { fn yaml(&self, y: &mut Yamler) { y.key("Quote").value(self); diff --git a/compiler/cl-typeck/src/stage/infer/inference.rs b/compiler/cl-typeck/src/stage/infer/inference.rs index 0b32174..ab76adc 100644 --- a/compiler/cl-typeck/src/stage/infer/inference.rs +++ b/compiler/cl-typeck/src/stage/infer/inference.rs @@ -30,6 +30,7 @@ impl<'a> Inference<'a> for cl_ast::ExprKind { fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result { match self { ExprKind::Empty => Ok(e.empty()), + ExprKind::Closure(_) => todo!("Infer the type of a closure"), ExprKind::Tuple(tuple) => tuple.infer(e), ExprKind::Structor(structor) => structor.infer(e), ExprKind::Array(array) => array.infer(e),