Skip to content

Commit

Permalink
Add read_repeat_until_full primitive
Browse files Browse the repository at this point in the history
  • Loading branch information
wezm committed Aug 25, 2022
1 parent 5a55785 commit e61707a
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 1 deletion.
5 changes: 5 additions & 0 deletions fathom/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,10 @@ def_prims! {
FormatArray64 => "array64",
/// Repeat a format until the length of the given parse scope is reached.
FormatRepeatUntilEnd => "repeat_until_end",
/// Repeat a format until the array is filled.
///
/// The value read by the format is replicated according to a supplied function.
FormatRepeatUntilFull => "repeat_until_full",
/// Limit the format to an unsigned 8-bit byte length.
FormatLimit8 => "limit8",
/// Limit the format to an unsigned 16-bit byte length.
Expand Down Expand Up @@ -442,6 +446,7 @@ def_prims! {
U16And => "u16_and",
U16Or => "u16_or",
U16Xor => "u16_xor",
U16FromU8 => "u16_from_u8",

U32Eq => "u32_eq",
U32Neq => "u32_neq",
Expand Down
45 changes: 45 additions & 0 deletions fathom/src/core/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ impl<'arena, 'env, 'data> Context<'arena, 'env, 'data> {
(Prim::FormatArray32, [FunApp(len), FunApp(format)]) => self.read_array(reader, span, len, format),
(Prim::FormatArray64, [FunApp(len), FunApp(format)]) => self.read_array(reader, span, len, format),
(Prim::FormatRepeatUntilEnd, [FunApp(format)]) => self.read_repeat_until_end(reader, format),
(Prim::FormatRepeatUntilFull, [FunApp(len), FunApp(format), FunApp(replicate)]) => self.read_repeat_until_full(reader, len, replicate, 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),
Expand Down Expand Up @@ -485,6 +486,50 @@ impl<'arena, 'env, 'data> Context<'arena, 'env, 'data> {
}
}

fn read_repeat_until_full(
&mut self,
reader: &mut BufferReader<'data>,
len: &ArcValue<'arena>,
replicate: &ArcValue<'arena>,
elem_format: &ArcValue<'arena>,
) -> Result<ArcValue<'arena>, ReadError<'arena>> {
let len = match self.elim_env().force(len).as_ref() {
Value::ConstLit(Const::U8(len, _)) => Some(usize::from(*len)),
Value::ConstLit(Const::U16(len, _)) => Some(usize::from(*len)),
Value::ConstLit(Const::U32(len, _)) => usize::try_from(*len).ok(),
Value::ConstLit(Const::U64(len, _)) => usize::try_from(*len).ok(),
_ => return Err(ReadError::InvalidValue(len.span())),
}
.ok_or_else(|| ReadError::InvalidValue(len.span()))?;

// TODO: Do we need to force replicate as well?
// let replicate = self.elim_env().force(replicate);

let mut elems = Vec::with_capacity(len);
while elems.len() < len {
match self.read_format(reader, elem_format) {
Ok(elem) => {
// Call the function to determine how many items this represents
let closure_res = self.elim_env().fun_app(replicate.clone(), elem.clone());
let repeat = match closure_res.as_ref() {
Value::ConstLit(Const::U16(n, _)) => *n,
_ => return Err(ReadError::InvalidValue(replicate.span())),
};

// Push it that many times onto the array
// TODO: Error/limit if this exceeds len?
elems.extend(std::iter::repeat(elem).take(usize::from(repeat)));
}
Err(err) => return Err(err),
};
}

Ok(Spanned::new(
elem_format.span(),
Arc::new(Value::ArrayLit(elems)),
))
}

fn read_limit(
&mut self,
reader: &BufferReader<'data>,
Expand Down
8 changes: 7 additions & 1 deletion fathom/src/core/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ fn prim_step(prim: Prim) -> Option<PrimStep> {
Prim::U16And => const_step!([x, xst: U16, y, yst: U16] => Const::U16(u16::bitand(*x, *y), UIntStyle::merge(*xst, *yst))),
Prim::U16Or => const_step!([x, xst: U16, y, yst: U16] => Const::U16(u16::bitor(*x, *y), UIntStyle::merge(*xst, *yst))),
Prim::U16Xor => const_step!([x, xst: U16, y, yst: U16] => Const::U16(u16::bitxor(*x, *y), UIntStyle::merge(*xst, *yst))),
Prim::U16FromU8 => const_step!([x, xst: U8] => Const::U16(u16::from(*x), *xst)),

Prim::U32Eq => const_step!([x: U32, y: U32] => Const::Bool(x == y)),
Prim::U32Neq => const_step!([x: U32, y: U32] => Const::Bool(x != y)),
Expand Down Expand Up @@ -856,13 +857,18 @@ impl<'arena, 'env> ElimEnv<'arena, 'env> {
(Prim::FormatRepeatUntilEnd, [Elim::FunApp(elem)]) => {
Value::prim(Prim::ArrayType, [self.format_repr(elem)])
}
(Prim::FormatRepeatUntilFull, [Elim::FunApp(len), _, Elim::FunApp(elem)]) => {
Value::prim(Prim::Array16Type, [len.clone(), self.format_repr(elem)])
}
(Prim::FormatLink, [_, Elim::FunApp(elem)]) => {
Value::prim(Prim::RefType, [elem.clone()])
}
(Prim::FormatDeref, [Elim::FunApp(elem), _]) => return self.format_repr(elem),
(Prim::FormatStreamPos, []) => Value::prim(Prim::PosType, []),
(Prim::FormatSucceed, [Elim::FunApp(elem), _]) => return elem.clone(),
(Prim::FormatOrSucceed, [_, Elim::FunApp(elem), _]) => return self.format_repr(elem),
(Prim::FormatOrSucceed, [_, Elim::FunApp(elem), _]) => {
return self.format_repr(elem)
}
(Prim::FormatFail, []) => Value::prim(Prim::VoidType, []),
(Prim::FormatUnwrap, [Elim::FunApp(elem), _]) => return elem.clone(),
(Prim::ReportedError, []) => Value::prim(Prim::ReportedError, []),
Expand Down
30 changes: 30 additions & 0 deletions fathom/src/surface/elaboration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,35 @@ impl<'arena> RigidEnv<'arena> {
env.define_prim_fun(FormatArray32, [&U32_TYPE, &FORMAT_TYPE], &FORMAT_TYPE);
env.define_prim_fun(FormatArray64, [&U64_TYPE, &FORMAT_TYPE], &FORMAT_TYPE);
env.define_prim_fun(FormatRepeatUntilEnd, [&FORMAT_TYPE], &FORMAT_TYPE);
// repeat_until_full16 : U16 -> fun (A : Format) -> (Repr A -> U16) -> Format
env.define_prim(
FormatRepeatUntilFull,
scope.to_scope(core::Term::FunType(
Span::Empty,
None,
&U16_TYPE,
scope.to_scope(core::Term::FunType(
Span::Empty,
env.name("A"),
&FORMAT_TYPE,
scope.to_scope(core::Term::FunType(
Span::Empty,
None,
scope.to_scope(Term::FunType(
Span::Empty,
None,
scope.to_scope(Term::FunApp(
Span::Empty,
&Term::Prim(Span::Empty, FormatRepr),
&VAR0,
)),
&U16_TYPE,
)),
&FORMAT_TYPE,
)),
)),
)),
);
env.define_prim_fun(FormatLimit8, [&U8_TYPE, &FORMAT_TYPE], &FORMAT_TYPE);
env.define_prim_fun(FormatLimit16, [&U16_TYPE, &FORMAT_TYPE], &FORMAT_TYPE);
env.define_prim_fun(FormatLimit32, [&U32_TYPE, &FORMAT_TYPE], &FORMAT_TYPE);
Expand Down Expand Up @@ -294,6 +323,7 @@ impl<'arena> RigidEnv<'arena> {
env.define_prim_fun(U16And, [&U16_TYPE, &U16_TYPE], &U16_TYPE);
env.define_prim_fun(U16Or, [&U16_TYPE, &U16_TYPE], &U16_TYPE);
env.define_prim_fun(U16Xor, [&U16_TYPE, &U16_TYPE], &U16_TYPE);
env.define_prim_fun(U16FromU8, [&U8_TYPE], &U16_TYPE);

env.define_prim_fun(U32Eq, [&U32_TYPE, &U32_TYPE], &BOOL_TYPE);
env.define_prim_fun(U32Neq, [&U32_TYPE, &U32_TYPE], &BOOL_TYPE);
Expand Down
2 changes: 2 additions & 0 deletions tests/succeed/primitives.fathom
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ let _ = array16 : U16 -> Format -> Format;
let _ = array32 : U32 -> Format -> Format;
let _ = array64 : U64 -> Format -> Format;
let _ = repeat_until_end : Format -> Format;
let _ = repeat_until_full : U16 -> fun (A : Format) -> (Repr A -> U16) -> Format;
let _ = array8 : U8 -> Format -> Format;
let _ = array16 : U16 -> Format -> Format;
let _ = array32 : U32 -> Format -> Format;
Expand Down Expand Up @@ -108,6 +109,7 @@ let _ = u16_shr : U16 -> U8 -> U16;
let _ = u16_and : U16 -> U16 -> U16;
let _ = u16_or : U16 -> U16 -> U16;
let _ = u16_xor : U16 -> U16 -> U16;
let _ = u16_from_u8 : U8 -> U16;

let _ = u32_eq : U32 -> U32 -> Bool;
let _ = u32_neq : U32 -> U32 -> Bool;
Expand Down

0 comments on commit e61707a

Please sign in to comment.