use crate::{arith::WithBoolean, Function, PrimitiveType, Type, UnknownLen};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Prelude {
False,
True,
If,
While,
Map,
Filter,
Fold,
Push,
Merge,
}
impl<Prim: WithBoolean> From<Prelude> for Type<Prim> {
fn from(value: Prelude) -> Self {
match value {
Prelude::True | Prelude::False => Type::BOOL,
Prelude::If => Function::builder()
.with_arg(Type::BOOL)
.with_arg(Type::param(0))
.with_arg(Type::param(0))
.returning(Type::param(0))
.into(),
Prelude::While => {
let condition_fn = Function::builder()
.with_arg(Type::param(0))
.returning(Type::BOOL);
let iter_fn = Function::builder()
.with_arg(Type::param(0))
.returning(Type::param(0));
Function::builder()
.with_arg(Type::param(0)) .with_arg(condition_fn)
.with_arg(iter_fn)
.returning(Type::param(0))
.into()
}
Prelude::Map => {
let map_arg = Function::builder()
.with_arg(Type::param(0))
.returning(Type::param(1));
Function::builder()
.with_arg(Type::param(0).repeat(UnknownLen::param(0)))
.with_arg(map_arg)
.returning(Type::param(1).repeat(UnknownLen::param(0)))
.into()
}
Prelude::Filter => {
let predicate_arg = Function::builder()
.with_arg(Type::param(0))
.returning(Type::BOOL);
Function::builder()
.with_arg(Type::param(0).repeat(UnknownLen::Dynamic))
.with_arg(predicate_arg)
.returning(Type::param(0).repeat(UnknownLen::Dynamic))
.into()
}
Prelude::Fold => {
let fold_arg = Function::builder()
.with_arg(Type::param(1))
.with_arg(Type::param(0))
.returning(Type::param(1));
Function::builder()
.with_arg(Type::param(0).repeat(UnknownLen::Dynamic))
.with_arg(Type::param(1))
.with_arg(fold_arg)
.returning(Type::param(1))
.into()
}
Prelude::Push => Function::builder()
.with_arg(Type::param(0).repeat(UnknownLen::param(0)))
.with_arg(Type::param(0))
.returning(Type::param(0).repeat(UnknownLen::param(0) + 1))
.into(),
Prelude::Merge => Function::builder()
.with_arg(Type::param(0).repeat(UnknownLen::Dynamic))
.with_arg(Type::param(0).repeat(UnknownLen::Dynamic))
.returning(Type::param(0).repeat(UnknownLen::Dynamic))
.into(),
}
}
}
impl Prelude {
const VALUES: &'static [Self] = &[
Self::True,
Self::False,
Self::If,
Self::While,
Self::Map,
Self::Filter,
Self::Fold,
Self::Push,
Self::Merge,
];
fn as_str(self) -> &'static str {
match self {
Self::True => "true",
Self::False => "false",
Self::If => "if",
Self::While => "while",
Self::Map => "map",
Self::Filter => "filter",
Self::Fold => "fold",
Self::Push => "push",
Self::Merge => "merge",
}
}
pub fn array<T: PrimitiveType>(index_type: T) -> Function<T> {
Function::builder()
.with_arg(Type::Prim(index_type.clone()))
.with_arg(
Function::builder()
.with_arg(Type::Prim(index_type))
.returning(Type::param(0)),
)
.returning(Type::param(0).repeat(UnknownLen::Dynamic))
}
pub fn iter<Prim: WithBoolean>() -> impl Iterator<Item = (&'static str, Type<Prim>)> {
Self::VALUES
.iter()
.map(|&value| (value.as_str(), value.into()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Assertions {
Assert,
AssertEq,
}
impl<Prim: WithBoolean> From<Assertions> for Type<Prim> {
fn from(value: Assertions) -> Self {
match value {
Assertions::Assert => Function::builder()
.with_arg(Type::BOOL)
.returning(Type::void())
.into(),
Assertions::AssertEq => Function::builder()
.with_arg(Type::param(0))
.with_arg(Type::param(0))
.returning(Type::void())
.into(),
}
}
}
impl Assertions {
const VALUES: &'static [Self] = &[Self::Assert, Self::AssertEq];
fn as_str(self) -> &'static str {
match self {
Self::Assert => "assert",
Self::AssertEq => "assert_eq",
}
}
pub fn iter<Prim: WithBoolean>() -> impl Iterator<Item = (&'static str, Type<Prim>)> {
Self::VALUES.iter().map(|&val| (val.as_str(), val.into()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::arith::Num;
use std::collections::{HashMap, HashSet};
const EXPECTED_PRELUDE_TYPES: &[(&str, &str)] = &[
("false", "Bool"),
("true", "Bool"),
("if", "(Bool, 'T, 'T) -> 'T"),
("while", "('T, ('T) -> Bool, ('T) -> 'T) -> 'T"),
("map", "(['T; N], ('T) -> 'U) -> ['U; N]"),
("filter", "(['T], ('T) -> Bool) -> ['T]"),
("fold", "(['T], 'U, ('U, 'T) -> 'U) -> 'U"),
("push", "(['T; N], 'T) -> ['T; N + 1]"),
("merge", "(['T], ['T]) -> ['T]"),
];
#[test]
fn string_presentations_of_prelude_types() {
let expected_types: HashMap<_, _> = EXPECTED_PRELUDE_TYPES.iter().copied().collect();
for (name, ty) in Prelude::iter::<Num>() {
assert_eq!(ty.to_string(), expected_types[name]);
}
assert_eq!(
Prelude::iter::<Num>()
.map(|(name, _)| name)
.collect::<HashSet<_>>(),
expected_types.keys().copied().collect::<HashSet<_>>()
);
}
#[test]
fn string_presentation_of_array_type() {
let array_fn = Prelude::array(Num::Num);
assert_eq!(array_fn.to_string(), "(Num, (Num) -> 'T) -> ['T]");
}
}