Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy evaluation #476

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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