Skip to content

Commit

Permalink
Lazy evaluation
Browse files Browse the repository at this point in the history
  • Loading branch information
Kmeakin committed Feb 10, 2023
1 parent 3960a94 commit 19c1a85
Show file tree
Hide file tree
Showing 8 changed files with 459 additions and 232 deletions.
2 changes: 1 addition & 1 deletion fathom/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ lalrpop-util = "0.19.5"
lasso = { version = "0.6.0", features = ["multi-threaded", "ahasher", "inline-more"] }
levenshtein = "1.0.5"
logos = "0.12"
once_cell = { version = "1.17.0", features = ["parking_lot"] }
once_cell = { version = "1.17.0", features = ["parking_lot"] } # TODO: remove once `std::cell::once_cell` is stabilized
pretty = "0.11.2"
rpds = "0.12.0"
scoped-arena = "0.4.1"
Expand Down
53 changes: 27 additions & 26 deletions fathom/src/core/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use std::fmt::Debug;
use std::slice::SliceIndex;
use std::sync::Arc;

use crate::core::semantics::{self, ArcValue, Elim, Head, Value};
use super::semantics::{EvalMode, LocalExprs};
use crate::core::semantics::{self, ArcValue, Elim, Head, LazyValue, Value};
use crate::core::{Const, Item, Module, Prim, Term, UIntStyle};
use crate::env::{EnvLen, SharedEnv, UniqueEnv};
use crate::source::{Span, Spanned};
Expand Down Expand Up @@ -254,8 +255,8 @@ impl fmt::Display for BufferError {
impl std::error::Error for BufferError {}

pub struct Context<'arena, 'data> {
item_exprs: UniqueEnv<ArcValue<'arena>>,
local_exprs: SharedEnv<ArcValue<'arena>>,
item_exprs: UniqueEnv<LazyValue<'arena>>,
local_exprs: LocalExprs<'arena>,
initial_buffer: Buffer<'data>,
pending_formats: Vec<(usize, ArcValue<'arena>)>,
cached_refs: HashMap<usize, Vec<ParsedRef<'arena>>>,
Expand Down Expand Up @@ -283,7 +284,7 @@ impl<'arena, 'data> Context<'arena, 'data> {

fn eval_env(&mut self) -> semantics::EvalEnv<'arena, '_> {
let elim_env = semantics::ElimEnv::new(&self.item_exprs, [][..].into());
semantics::EvalEnv::new(elim_env, &mut self.local_exprs)
semantics::EvalEnv::new(elim_env, &mut self.local_exprs).with_mode(EvalMode::Strict)
}

fn elim_env(&self) -> semantics::ElimEnv<'arena, '_> {
Expand All @@ -295,7 +296,7 @@ impl<'arena, 'data> Context<'arena, 'data> {
for item in module.items {
match item {
Item::Def { expr, .. } => {
let expr = self.eval_env().eval(expr);
let expr = self.eval_env().delay_or_eval(expr);
self.item_exprs.push(expr);
}
}
Expand Down Expand Up @@ -332,7 +333,7 @@ impl<'arena, 'data> Context<'arena, 'data> {
let mut exprs = Vec::with_capacity(formats.len());

while let Some((format, next_formats)) = self.elim_env().split_telescope(formats) {
let expr = self.read_format(reader, &format)?;
let expr = LazyValue::eager(self.read_format(reader, &format)?);
exprs.push(expr.clone());
formats = next_formats(expr);
}
Expand All @@ -343,8 +344,10 @@ impl<'arena, 'data> Context<'arena, 'data> {
))
}
Value::FormatCond(_label, format, cond) => {
let value = self.read_format(reader, format)?;
let cond_res = self.elim_env().apply_closure(cond, value.clone());
let value = self.read_format(reader, &self.elim_env().force_lazy(format))?;
let cond_res = self
.elim_env()
.apply_closure(cond, LazyValue::eager(value.clone()));

match cond_res.as_ref() {
Value::ConstLit(Const::Bool(true)) => Ok(value),
Expand All @@ -366,7 +369,7 @@ impl<'arena, 'data> Context<'arena, 'data> {
while let Some((format, next_formats)) = self.elim_env().split_telescope(formats) {
let mut reader = reader.clone();

let expr = self.read_format(&mut reader, &format)?;
let expr = LazyValue::eager(self.read_format(&mut reader, &format)?);
exprs.push(expr.clone());
formats = next_formats(expr);

Expand Down Expand Up @@ -406,6 +409,8 @@ impl<'arena, 'data> Context<'arena, 'data> {
) -> Result<ArcValue<'arena>, ReadError<'arena>> {
use crate::core::semantics::Elim::FunApp;

let force = |expr| self.elim_env().force_lazy(expr);

match (prim, slice) {
(Prim::FormatU8, []) => read_const(reader, span, read_u8, |num| Const::U8(num, UIntStyle::Decimal)),
(Prim::FormatU16Be, []) => read_const(reader, span, read_u16be, |num| Const::U16(num, UIntStyle::Decimal)),
Expand All @@ -425,22 +430,18 @@ impl<'arena, 'data> Context<'arena, 'data> {
(Prim::FormatF32Le, []) => read_const(reader, span, read_f32le, Const::F32),
(Prim::FormatF64Be, []) => read_const(reader, span, read_f64be, Const::F64),
(Prim::FormatF64Le, []) => read_const(reader, span, read_f64le, Const::F64),
(Prim::FormatRepeatLen8, [FunApp(_, len), FunApp(_, format)]) => self.read_repeat_len(reader, span, len, format),
(Prim::FormatRepeatLen16, [FunApp(_, len), FunApp(_, format)]) => self.read_repeat_len(reader, span, len, format),
(Prim::FormatRepeatLen32, [FunApp(_, len), FunApp(_, format)]) => self.read_repeat_len(reader, span, len, format),
(Prim::FormatRepeatLen64, [FunApp(_, len), FunApp(_, format)]) => self.read_repeat_len(reader, span, len, format),
(Prim::FormatRepeatUntilEnd, [FunApp(_,format)]) => self.read_repeat_until_end(reader, format),
(Prim::FormatLimit8, [FunApp(_, limit), FunApp(_, format)]) => self.read_limit(reader, limit, format),
(Prim::FormatLimit16, [FunApp(_, limit), FunApp(_, format)]) => self.read_limit(reader, limit, format),
(Prim::FormatLimit32, [FunApp(_, limit), FunApp(_, format)]) => self.read_limit(reader, limit, format),
(Prim::FormatLimit64, [FunApp(_, limit), FunApp(_, format)]) => self.read_limit(reader, limit, format),
(Prim::FormatLink, [FunApp(_, pos), FunApp(_, format)]) => self.read_link(span, pos, format),
(Prim::FormatDeref, [FunApp(_, format), FunApp(_, r#ref)]) => self.read_deref(format, r#ref),
(Prim::FormatRepeatLen8| Prim::FormatRepeatLen16 | Prim::FormatRepeatLen32 | Prim::FormatRepeatLen64,
[FunApp(_, len), FunApp(_, format)]) => self.read_repeat_len(reader, span, &force(len), &force(format)),
(Prim::FormatRepeatUntilEnd, [FunApp(_,format)]) => self.read_repeat_until_end(reader, &force(format)),
(Prim::FormatLimit8 | Prim::FormatLimit16 | Prim::FormatLimit32 | Prim::FormatLimit64,
[FunApp(_, limit), FunApp(_, format)]) => self.read_limit(reader,&force(limit) , &force(format)),
(Prim::FormatLink, [FunApp(_, pos), FunApp(_, format)]) => self.read_link(span, &force(pos), &force(format)),
(Prim::FormatDeref, [FunApp(_, format), FunApp(_, r#ref)]) => self.read_deref(&force(format), &force(r#ref)),
(Prim::FormatStreamPos, []) => read_stream_pos(reader, span),
(Prim::FormatSucceed, [_, FunApp(_, elem)]) => Ok(elem.clone()),
(Prim::FormatSucceed, [_, FunApp(_, elem)]) => Ok(force(elem)),
(Prim::FormatFail, []) => Err(ReadError::ReadFailFormat(span)),
(Prim::FormatUnwrap, [_, FunApp(_, option)]) => match option.match_prim_spine() {
Some((Prim::OptionSome, [_, FunApp(_, elem)])) => Ok(elem.clone()),
(Prim::FormatUnwrap, [_, FunApp(_, option)]) => match force(option).match_prim_spine() {
Some((Prim::OptionSome, [_, FunApp(_, elem)])) => Ok(force(elem)),
Some((Prim::OptionNone, [_])) => Err(ReadError::UnwrappedNone(span)),
_ => Err(ReadError::InvalidValue(span)),
},
Expand All @@ -464,7 +465,7 @@ impl<'arena, 'data> Context<'arena, 'data> {
};

let elem_exprs = (0..len)
.map(|_| self.read_format(reader, elem_format))
.map(|_| (self.read_format(reader, elem_format).map(LazyValue::eager)))
.collect::<Result<_, _>>()?;

Ok(Spanned::new(span, Arc::new(Value::ArrayLit(elem_exprs))))
Expand All @@ -481,7 +482,7 @@ impl<'arena, 'data> Context<'arena, 'data> {
loop {
match self.read_format(reader, elem_format) {
Ok(elem) => {
elems.push(elem);
elems.push(LazyValue::eager(elem));
current_offset = reader.relative_offset();
}
Err(ReadError::BufferError(_, BufferError::UnexpectedEndOfBuffer)) => {
Expand Down Expand Up @@ -557,7 +558,7 @@ impl<'arena, 'data> Context<'arena, 'data> {
fn lookup_ref<'context>(
&'context self,
pos: usize,
format: &ArcValue<'_>,
format: &ArcValue<'arena>,
) -> Option<&'context ParsedRef<'arena>> {
// NOTE: The number of calls to `semantics::ConversionEnv::is_equal`
// when looking up cached references is a bit of a pain. If this ever
Expand Down
90 changes: 60 additions & 30 deletions fathom/src/core/prim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::sync::Arc;
use fxhash::FxHashMap;
use scoped_arena::Scope;

use super::semantics::{LazyValue, LocalExprs};
use crate::core::semantics::{ArcValue, Elim, ElimEnv, Head, Value};
use crate::core::{self, Const, Plicity, Prim, UIntStyle};
use crate::env::{self, SharedEnv, UniqueEnv};
Expand Down Expand Up @@ -508,8 +509,8 @@ struct EnvBuilder<'arena> {
entries: FxHashMap<Symbol, (Prim, ArcValue<'arena>)>,
scope: &'arena Scope<'arena>,
meta_exprs: UniqueEnv<Option<ArcValue<'arena>>>,
item_exprs: UniqueEnv<ArcValue<'arena>>,
local_exprs: SharedEnv<ArcValue<'arena>>,
item_exprs: UniqueEnv<LazyValue<'arena>>,
local_exprs: LocalExprs<'arena>,
}

impl<'arena> EnvBuilder<'arena> {
Expand Down Expand Up @@ -577,13 +578,13 @@ macro_rules! step {
// TODO: Should we merge the spans of the param idents to produce the body span?
macro_rules! const_step {
([$($param:ident : $Input:ident),*] => $body:expr) => {
step!(_, [$($param),*] => match ($($param.as_ref(),)*) {
step!(env, [$($param),*] => match ($(env.force_lazy($param).as_ref(),)*) {
($(Value::ConstLit(Const::$Input($param, ..)),)*) => Spanned::empty(Arc::new(Value::ConstLit($body))),
_ => return None,
})
};
([$($param:ident , $style:ident : $Input:ident),*] => $body:expr) => {
step!(_, [$($param),*] => match ($($param.as_ref(),)*) {
step!(env, [$($param),*] => match ($(env.force_lazy($param).as_ref(),)*) {
($(Value::ConstLit(Const::$Input($param, $style)),)*) => Spanned::empty(Arc::new(Value::ConstLit($body))),
_ => return None,
})
Expand Down Expand Up @@ -611,21 +612,18 @@ pub fn repr(prim: Prim) -> Step {
Prim::FormatF32Le => step!(_, [] => Spanned::empty(Arc::new(Value::prim(Prim::F32Type, [])))),
Prim::FormatF64Be => step!(_, [] => Spanned::empty(Arc::new(Value::prim(Prim::F64Type, [])))),
Prim::FormatF64Le => step!(_, [] => Spanned::empty(Arc::new(Value::prim(Prim::F64Type, [])))),
Prim::FormatRepeatLen8 => step!(env, [len, elem] => Spanned::empty(Arc::new(Value::prim(Prim::Array8Type, [len.clone(), env.format_repr(elem)])))),
Prim::FormatRepeatLen16 => step!(env, [len, elem] => Spanned::empty(Arc::new(Value::prim(Prim::Array16Type, [len.clone(), env.format_repr(elem)])))),
Prim::FormatRepeatLen32 => step!(env, [len, elem] => Spanned::empty(Arc::new(Value::prim(Prim::Array32Type, [len.clone(), env.format_repr(elem)])))),
Prim::FormatRepeatLen64 => step!(env, [len, elem] => Spanned::empty(Arc::new(Value::prim(Prim::Array64Type, [len.clone(), env.format_repr(elem)])))),
Prim::FormatLimit8 => step!(env, [_, elem] => env.format_repr(elem)),
Prim::FormatLimit16 => step!(env, [_, elem] => env.format_repr(elem)),
Prim::FormatLimit32 => step!(env, [_, elem] => env.format_repr(elem)),
Prim::FormatLimit64 => step!(env, [_, elem] => env.format_repr(elem)),
Prim::FormatRepeatUntilEnd => step!(env, [elem] => Spanned::empty(Arc::new(Value::prim(Prim::ArrayType, [env.format_repr(elem)])))),
Prim::FormatLink => step!(_, [_, elem] => Spanned::empty(Arc::new(Value::prim(Prim::RefType, [elem.clone()])))),
Prim::FormatDeref => step!(env, [elem, _] => env.format_repr(elem)),
Prim::FormatRepeatLen8 => step!(env, [len, elem] => Spanned::empty(Arc::new(Value::prim(Prim::Array8Type, [env.force_lazy(len), env.format_repr(&env.force_lazy(elem))])))),
Prim::FormatRepeatLen16 => step!(env, [len, elem] => Spanned::empty(Arc::new(Value::prim(Prim::Array16Type, [env.force_lazy(len), env.format_repr(&env.force_lazy(elem))])))),
Prim::FormatRepeatLen32 => step!(env, [len, elem] => Spanned::empty(Arc::new(Value::prim(Prim::Array32Type, [env.force_lazy(len), env.format_repr(&env.force_lazy(elem))])))),
Prim::FormatRepeatLen64 => step!(env, [len, elem] => Spanned::empty(Arc::new(Value::prim(Prim::Array64Type, [env.force_lazy(len), env.format_repr(&env.force_lazy(elem))])))),
Prim::FormatLimit8 | Prim::FormatLimit16 | Prim::FormatLimit32 | Prim::FormatLimit64 => step!(env, [_, elem] => env.format_repr(&env.force_lazy(elem))),
Prim::FormatRepeatUntilEnd => step!(env, [elem] => Spanned::empty(Arc::new(Value::prim(Prim::ArrayType, [env.format_repr(&env.force_lazy(elem))])))),
Prim::FormatLink => step!(env, [_, elem] => Spanned::empty(Arc::new(Value::prim(Prim::RefType, [env.force_lazy(elem)])))),
Prim::FormatDeref => step!(env, [elem, _] => env.format_repr(&env.force_lazy(elem))),
Prim::FormatStreamPos => step!(_, [] => Spanned::empty(Arc::new(Value::prim(Prim::PosType, [])))),
Prim::FormatSucceed => step!(_, [elem, _] => elem.clone()),
Prim::FormatSucceed => step!(env, [elem, _] => env.force_lazy(elem)),
Prim::FormatFail => step!(_, [] => Spanned::empty(Arc::new(Value::prim(Prim::VoidType, [])))),
Prim::FormatUnwrap => step!(_, [elem, _] => elem.clone()),
Prim::FormatUnwrap => step!(env, [elem, _] => env.force_lazy(elem)),
Prim::ReportedError => step!(_, [] => Spanned::empty(Arc::new(Value::prim(Prim::ReportedError, [])))),
_ => |_, _| None,
}
Expand All @@ -641,15 +639,49 @@ pub fn step(prim: Prim) -> Step {
#[allow(unreachable_code)]
Prim::Absurd => step!(_, [_, _] => panic!("Constructed an element of `Void`")),

Prim::FormatRepr => step!(env, [format] => env.format_repr(format)),
Prim::FormatRepr => step!(env, [format] => env.format_repr(&env.force_lazy(format))),

Prim::BoolEq => const_step!([x: Bool, y: Bool] => Const::Bool(x == y)),
Prim::BoolNeq => const_step!([x: Bool, y: Bool] => Const::Bool(x != y)),
Prim::BoolNot => const_step!([x: Bool] => Const::Bool(bool::not(*x))),
Prim::BoolAnd => const_step!([x: Bool, y: Bool] => Const::Bool(*x && *y)),
Prim::BoolOr => const_step!([x: Bool, y: Bool] => Const::Bool(*x || *y)),
Prim::BoolXor => const_step!([x: Bool, y: Bool] => Const::Bool(*x ^ *y)),

Prim::BoolAnd => |env: &ElimEnv, spine: &[Elim]| match spine {
[Elim::FunApp(_,x), Elim::FunApp(_, y)] => {
let x = env.force_lazy(x);
match x.as_ref() {
Value::ConstLit(Const::Bool(false)) => Some(x),
Value::ConstLit(Const::Bool(true)) => {
let y = env.force_lazy(y);
match y.as_ref() {
Value::ConstLit(Const::Bool(_)) => Some(y),
_ => None,
}
}
_ => None,
}
}
_ => None,
},

Prim::BoolOr => |env: &ElimEnv, spine: &[Elim]| match spine {
[Elim::FunApp(_,x), Elim::FunApp(_, y)] => {
let x = env.force_lazy(x);
match x.as_ref() {
Value::ConstLit(Const::Bool(true)) => Some(x),
Value::ConstLit(Const::Bool(false)) => {
let y = env.force_lazy(y);
match y.as_ref() {
Value::ConstLit(Const::Bool(_)) => Some(y),
_ => None,
}
}
_ => None,
}
}
_ => None,
},

Prim::U8Eq => const_step!([x: U8, y: U8] => Const::Bool(x == y)),
Prim::U8Neq => const_step!([x: U8, y: U8] => Const::Bool(x != y)),
Prim::U8Gt => const_step!([x: U8, y: U8] => Const::Bool(x > y)),
Expand Down Expand Up @@ -775,22 +807,20 @@ pub fn step(prim: Prim) -> Step {
Prim::S64UAbs => const_step!([x: S64] => Const::U64(i64::unsigned_abs(*x), UIntStyle::Decimal)),

Prim::OptionFold => step!(env, [_, _, on_none, on_some, option] => {
match option.match_prim_spine()? {
match env.force_lazy(option).match_prim_spine()? {
(Prim::OptionSome, [_, Elim::FunApp(Plicity::Explicit, value)]) => {
env.fun_app(Plicity::Explicit, on_some.clone(), value.clone())
env.fun_app(Plicity::Explicit, env.force_lazy(on_some), value)
},
(Prim::OptionNone, [_]) => on_none.clone(),
(Prim::OptionNone, [_]) => env.force_lazy(on_none),
_ => return None,
}
}),

Prim::Array8Find | Prim::Array16Find | Prim::Array32Find | Prim::Array64Find => {
step!(env, [_, elem_type, pred, array] => match array.as_ref() {
step!(env, [_, elem_type, pred, array] => match env.force_lazy(array).as_ref() {
Value::ArrayLit(elems) => {
for elem in elems {
match env.fun_app(
Plicity::Explicit,
pred.clone(), elem.clone()).as_ref() {
match env.fun_app(Plicity::Explicit, env.force_lazy(pred), elem).as_ref() {
Value::ConstLit(Const::Bool(true)) => {
return Some(Spanned::empty(Arc::new(Value::Stuck(
Head::Prim(Prim::OptionSome),
Expand All @@ -814,16 +844,16 @@ pub fn step(prim: Prim) -> Step {
}

Prim::Array8Index | Prim::Array16Index | Prim::Array32Index | Prim::Array64Index => {
step!(_, [_, _, index, array] => match array.as_ref() {
step!(env, [_, _, index, array] => match env.force_lazy(array).as_ref() {
Value::ArrayLit(elems) => {
let index = match (index).as_ref() {
let index = match env.force_lazy(index).as_ref() {
Value::ConstLit(Const::U8(index, _)) => Some(usize::from(*index)),
Value::ConstLit(Const::U16(index, _)) => Some(usize::from(*index)),
Value::ConstLit(Const::U32(index, _)) => usize::try_from(*index).ok(),
Value::ConstLit(Const::U64(index, _)) => usize::try_from(*index).ok(),
_ => return None,
}?;
elems.get(index).cloned()?
env.force_lazy(elems.get(index)?)
}
_ => return None,
})
Expand Down
Loading

0 comments on commit 19c1a85

Please sign in to comment.