add utility functions on HdlOption, inspired by Option's API
This commit is contained in:
parent
df55a514e4
commit
ff269e5def
|
@ -7,14 +7,14 @@ use crate::{
|
|||
int::Bool,
|
||||
intern::{Intern, Interned},
|
||||
module::{
|
||||
enum_match_variants_helper, EnumMatchVariantAndInactiveScopeImpl,
|
||||
EnumMatchVariantsIterImpl, Scope,
|
||||
connect, enum_match_variants_helper, incomplete_wire, wire,
|
||||
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope,
|
||||
},
|
||||
source_location::SourceLocation,
|
||||
ty::{CanonicalType, MatchVariantAndInactiveScope, StaticType, Type, TypeProperties},
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use std::{fmt, iter::FusedIterator};
|
||||
use std::{convert::Infallible, fmt, iter::FusedIterator};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct EnumVariant {
|
||||
|
@ -364,3 +364,308 @@ pub fn HdlSome<T: Type>(value: impl ToExpr<Type = T>) -> Expr<HdlOption<T>> {
|
|||
let value = value.to_expr();
|
||||
HdlOption[Expr::ty(value)].HdlSome(value)
|
||||
}
|
||||
|
||||
impl<T: Type> HdlOption<T> {
|
||||
#[track_caller]
|
||||
pub fn try_map<R: Type, E>(
|
||||
expr: Expr<Self>,
|
||||
f: impl FnOnce(Expr<T>) -> Result<Expr<R>, E>,
|
||||
) -> Result<Expr<HdlOption<R>>, E> {
|
||||
Self::try_and_then(expr, |v| Ok(HdlSome(f(v)?)))
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn map<R: Type>(
|
||||
expr: Expr<Self>,
|
||||
f: impl FnOnce(Expr<T>) -> Expr<R>,
|
||||
) -> Expr<HdlOption<R>> {
|
||||
Self::and_then(expr, |v| HdlSome(f(v)))
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn try_and_then<R: Type, E>(
|
||||
expr: Expr<Self>,
|
||||
f: impl FnOnce(Expr<T>) -> Result<Expr<HdlOption<R>>, E>,
|
||||
) -> Result<Expr<HdlOption<R>>, E> {
|
||||
// manually run match steps so we can extract the return type to construct HdlNone
|
||||
type Wrap<T> = T;
|
||||
#[hdl]
|
||||
let mut and_then_out = incomplete_wire();
|
||||
let mut iter = Self::match_variants(expr, SourceLocation::caller());
|
||||
let none = iter.next().unwrap();
|
||||
let some = iter.next().unwrap();
|
||||
assert!(iter.next().is_none());
|
||||
let (Wrap::<<Self as Type>::MatchVariant>::HdlSome(value), some_scope) =
|
||||
Self::match_activate_scope(some)
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
let value = f(value).map_err(|e| {
|
||||
and_then_out.complete(()); // avoid error
|
||||
e
|
||||
})?;
|
||||
let and_then_out = and_then_out.complete(Expr::ty(value));
|
||||
connect(and_then_out, value);
|
||||
drop(some_scope);
|
||||
let (Wrap::<<Self as Type>::MatchVariant>::HdlNone, none_scope) =
|
||||
Self::match_activate_scope(none)
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
connect(and_then_out, Expr::ty(and_then_out).HdlNone());
|
||||
drop(none_scope);
|
||||
Ok(and_then_out)
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn and_then<R: Type>(
|
||||
expr: Expr<Self>,
|
||||
f: impl FnOnce(Expr<T>) -> Expr<HdlOption<R>>,
|
||||
) -> Expr<HdlOption<R>> {
|
||||
match Self::try_and_then(expr, |v| Ok::<_, Infallible>(f(v))) {
|
||||
Ok(v) => v,
|
||||
Err(e) => match e {},
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn and<U: Type>(expr: Expr<Self>, opt_b: Expr<HdlOption<U>>) -> Expr<HdlOption<U>> {
|
||||
#[hdl]
|
||||
let and_out = wire(Expr::ty(opt_b));
|
||||
connect(and_out, Expr::ty(opt_b).HdlNone());
|
||||
#[hdl]
|
||||
if let HdlSome(_) = expr {
|
||||
connect(and_out, opt_b);
|
||||
}
|
||||
and_out
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn try_filter<E>(
|
||||
expr: Expr<Self>,
|
||||
f: impl FnOnce(Expr<T>) -> Result<Expr<Bool>, E>,
|
||||
) -> Result<Expr<Self>, E> {
|
||||
#[hdl]
|
||||
let filtered = wire(Expr::ty(expr));
|
||||
connect(filtered, Expr::ty(expr).HdlNone());
|
||||
let mut f = Some(f);
|
||||
#[hdl]
|
||||
if let HdlSome(v) = expr {
|
||||
#[hdl]
|
||||
if f.take().unwrap()(v)? {
|
||||
connect(filtered, HdlSome(v));
|
||||
}
|
||||
}
|
||||
Ok(filtered)
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn filter(expr: Expr<Self>, f: impl FnOnce(Expr<T>) -> Expr<Bool>) -> Expr<Self> {
|
||||
match Self::try_filter(expr, |v| Ok::<_, Infallible>(f(v))) {
|
||||
Ok(v) => v,
|
||||
Err(e) => match e {},
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn try_inspect<E>(
|
||||
expr: Expr<Self>,
|
||||
f: impl FnOnce(Expr<T>) -> Result<(), E>,
|
||||
) -> Result<Expr<Self>, E> {
|
||||
let mut f = Some(f);
|
||||
#[hdl]
|
||||
if let HdlSome(v) = expr {
|
||||
f.take().unwrap()(v)?;
|
||||
}
|
||||
Ok(expr)
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn inspect(expr: Expr<Self>, f: impl FnOnce(Expr<T>)) -> Expr<Self> {
|
||||
let mut f = Some(f);
|
||||
#[hdl]
|
||||
if let HdlSome(v) = expr {
|
||||
f.take().unwrap()(v);
|
||||
}
|
||||
expr
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn is_none(expr: Expr<Self>) -> Expr<Bool> {
|
||||
#[hdl]
|
||||
let is_none_out: Bool = wire();
|
||||
connect(is_none_out, false);
|
||||
#[hdl]
|
||||
if let HdlNone = expr {
|
||||
connect(is_none_out, true);
|
||||
}
|
||||
is_none_out
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn is_some(expr: Expr<Self>) -> Expr<Bool> {
|
||||
#[hdl]
|
||||
let is_some_out: Bool = wire();
|
||||
connect(is_some_out, false);
|
||||
#[hdl]
|
||||
if let HdlSome(_) = expr {
|
||||
connect(is_some_out, true);
|
||||
}
|
||||
is_some_out
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn map_or<R: Type>(
|
||||
expr: Expr<Self>,
|
||||
default: Expr<R>,
|
||||
f: impl FnOnce(Expr<T>) -> Expr<R>,
|
||||
) -> Expr<R> {
|
||||
#[hdl]
|
||||
let mapped = wire(Expr::ty(default));
|
||||
let mut f = Some(f);
|
||||
#[hdl]
|
||||
match expr {
|
||||
HdlSome(v) => connect(mapped, f.take().unwrap()(v)),
|
||||
HdlNone => connect(mapped, default),
|
||||
}
|
||||
mapped
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn map_or_else<R: Type>(
|
||||
expr: Expr<Self>,
|
||||
default: impl FnOnce() -> Expr<R>,
|
||||
f: impl FnOnce(Expr<T>) -> Expr<R>,
|
||||
) -> Expr<R> {
|
||||
#[hdl]
|
||||
let mut mapped = incomplete_wire();
|
||||
let mut default = Some(default);
|
||||
let mut f = Some(f);
|
||||
let mut retval = None;
|
||||
#[hdl]
|
||||
match expr {
|
||||
HdlSome(v) => {
|
||||
let v = f.take().unwrap()(v);
|
||||
let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
|
||||
connect(mapped, v);
|
||||
}
|
||||
HdlNone => {
|
||||
let v = default.take().unwrap()();
|
||||
let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
|
||||
connect(mapped, v);
|
||||
}
|
||||
}
|
||||
retval.unwrap()
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn or(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
|
||||
#[hdl]
|
||||
let or_out = wire(Expr::ty(expr));
|
||||
connect(or_out, opt_b);
|
||||
#[hdl]
|
||||
if let HdlSome(_) = expr {
|
||||
connect(or_out, expr);
|
||||
}
|
||||
or_out
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<Self>) -> Expr<Self> {
|
||||
#[hdl]
|
||||
let or_else_out = wire(Expr::ty(expr));
|
||||
connect(or_else_out, f());
|
||||
#[hdl]
|
||||
if let HdlSome(_) = expr {
|
||||
connect(or_else_out, expr);
|
||||
}
|
||||
or_else_out
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn unwrap_or(expr: Expr<Self>, default: Expr<T>) -> Expr<T> {
|
||||
#[hdl]
|
||||
let unwrap_or_else_out = wire(Expr::ty(default));
|
||||
connect(unwrap_or_else_out, default);
|
||||
#[hdl]
|
||||
if let HdlSome(v) = expr {
|
||||
connect(unwrap_or_else_out, v);
|
||||
}
|
||||
unwrap_or_else_out
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn unwrap_or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<T>) -> Expr<T> {
|
||||
#[hdl]
|
||||
let unwrap_or_else_out = wire(Expr::ty(expr).HdlSome);
|
||||
connect(unwrap_or_else_out, f());
|
||||
#[hdl]
|
||||
if let HdlSome(v) = expr {
|
||||
connect(unwrap_or_else_out, v);
|
||||
}
|
||||
unwrap_or_else_out
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn xor(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
|
||||
#[hdl]
|
||||
let xor_out = wire(Expr::ty(expr));
|
||||
#[hdl]
|
||||
if let HdlSome(_) = expr {
|
||||
#[hdl]
|
||||
if let HdlNone = opt_b {
|
||||
connect(xor_out, expr);
|
||||
} else {
|
||||
connect(xor_out, Expr::ty(expr).HdlNone());
|
||||
}
|
||||
} else {
|
||||
connect(xor_out, opt_b);
|
||||
}
|
||||
xor_out
|
||||
}
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn zip<U: Type>(expr: Expr<Self>, other: Expr<HdlOption<U>>) -> Expr<HdlOption<(T, U)>> {
|
||||
#[hdl]
|
||||
let zip_out = wire(HdlOption[(Expr::ty(expr).HdlSome, Expr::ty(other).HdlSome)]);
|
||||
connect(zip_out, Expr::ty(zip_out).HdlNone());
|
||||
#[hdl]
|
||||
if let HdlSome(l) = expr {
|
||||
#[hdl]
|
||||
if let HdlSome(r) = other {
|
||||
connect(zip_out, HdlSome((l, r)));
|
||||
}
|
||||
}
|
||||
zip_out
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> HdlOption<HdlOption<T>> {
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn flatten(expr: Expr<Self>) -> Expr<HdlOption<T>> {
|
||||
#[hdl]
|
||||
let flattened = wire(Expr::ty(expr).HdlSome);
|
||||
#[hdl]
|
||||
match expr {
|
||||
HdlSome(v) => connect(flattened, v),
|
||||
HdlNone => connect(flattened, Expr::ty(expr).HdlSome.HdlNone()),
|
||||
}
|
||||
flattened
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type, U: Type> HdlOption<(T, U)> {
|
||||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn unzip(expr: Expr<Self>) -> Expr<(HdlOption<T>, HdlOption<U>)> {
|
||||
let (t, u) = Expr::ty(expr).HdlSome;
|
||||
#[hdl]
|
||||
let unzipped = wire((HdlOption[t], HdlOption[u]));
|
||||
connect(unzipped, (HdlOption[t].HdlNone(), HdlOption[u].HdlNone()));
|
||||
#[hdl]
|
||||
if let HdlSome(v) = expr {
|
||||
connect(unzipped.0, HdlSome(v.0));
|
||||
connect(unzipped.1, HdlSome(v.1));
|
||||
}
|
||||
unzipped
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue