use crate::dag::{InternalSharing, MaxSharing, PostOrderIterItem};
use crate::human_encoding::{Error, ErrorSet, Position, WitnessOrHole};
use crate::jet::Jet;
use crate::node::{
self, Commit, CommitData, CommitNode, Converter, Inner, NoDisconnect, NoWitness, Node, Witness,
WitnessData,
};
use crate::node::{Construct, ConstructData, Constructible};
use crate::types;
use crate::types::arrow::{Arrow, FinalArrow};
use crate::{encode, Value, WitnessNode};
use crate::{BitWriter, Cmr, Imr};
use std::collections::HashMap;
use std::io;
use std::marker::PhantomData;
use std::sync::Arc;
pub type NamedCommitNode<J> = Node<Named<Commit<J>>>;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Named<N> {
never: std::convert::Infallible,
phantom: std::marker::PhantomData<N>,
}
impl<J: Jet> node::Marker for Named<Commit<J>> {
type CachedData = NamedCommitData<J>;
type Witness = <Commit<J> as node::Marker>::Witness;
type Disconnect = <Commit<J> as node::Marker>::Disconnect;
type SharingId = Arc<str>;
type Jet = J;
fn compute_sharing_id(_: Cmr, cached_data: &Self::CachedData) -> Option<Arc<str>> {
Some(Arc::clone(&cached_data.name))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct NamedCommitData<J> {
internal: Arc<CommitData<J>>,
name: Arc<str>,
}
impl<J: Jet> NamedCommitNode<J> {
pub fn from_node(root: &CommitNode<J>) -> Arc<Self> {
let mut namer = Namer::new_rooted(root.cmr());
root.convert::<MaxSharing<Commit<J>>, _, _>(&mut namer)
.unwrap()
}
pub fn name(&self) -> &Arc<str> {
&self.cached_data().name
}
pub fn imr(&self) -> Option<Imr> {
self.cached_data().internal.imr()
}
pub fn arrow(&self) -> &FinalArrow {
self.cached_data().internal.arrow()
}
pub fn to_commit_node(&self) -> Arc<CommitNode<J>> {
struct Forgetter<J>(PhantomData<J>);
impl<J: Jet> Converter<Named<Commit<J>>, Commit<J>> for Forgetter<J> {
type Error = ();
fn convert_witness(
&mut self,
_: &PostOrderIterItem<&NamedCommitNode<J>>,
_: &NoWitness,
) -> Result<NoWitness, Self::Error> {
Ok(NoWitness)
}
fn convert_disconnect(
&mut self,
_: &PostOrderIterItem<&NamedCommitNode<J>>,
_: Option<&Arc<CommitNode<J>>>,
_: &NoDisconnect,
) -> Result<NoDisconnect, Self::Error> {
Ok(NoDisconnect)
}
fn convert_data(
&mut self,
data: &PostOrderIterItem<&NamedCommitNode<J>>,
_: node::Inner<&Arc<CommitNode<J>>, J, &NoDisconnect, &NoWitness>,
) -> Result<Arc<CommitData<J>>, Self::Error> {
Ok(Arc::clone(&data.node.cached_data().internal))
}
}
self.convert::<InternalSharing, _, _>(&mut Forgetter(PhantomData))
.unwrap()
}
pub fn to_witness_node(
&self,
witness: &HashMap<Arc<str>, Arc<Value>>,
disconnect: &HashMap<Arc<str>, Arc<NamedCommitNode<J>>>,
) -> Arc<WitnessNode<J>> {
struct Populator<'a, J: Jet>(
&'a HashMap<Arc<str>, Arc<Value>>,
&'a HashMap<Arc<str>, Arc<NamedCommitNode<J>>>,
PhantomData<J>,
);
impl<'a, J: Jet> Converter<Named<Commit<J>>, Witness<J>> for Populator<'a, J> {
type Error = ();
fn convert_witness(
&mut self,
data: &PostOrderIterItem<&Node<Named<Commit<J>>>>,
_: &NoWitness,
) -> Result<Option<Arc<Value>>, Self::Error> {
let name = &data.node.cached_data().name;
Ok(self.0.get(name).cloned())
}
fn convert_disconnect(
&mut self,
data: &PostOrderIterItem<&Node<Named<Commit<J>>>>,
maybe_converted: Option<&Arc<Node<Witness<J>>>>,
_: &NoDisconnect,
) -> Result<Option<Arc<Node<Witness<J>>>>, Self::Error> {
if let Some(converted) = maybe_converted {
Ok(Some(converted.clone()))
} else {
let name = &data.node.cached_data().name;
let maybe_commit = self.1.get(name);
let witness = maybe_commit.map(|commit| commit.to_witness_node(self.0, self.1));
Ok(witness)
}
}
fn convert_data(
&mut self,
_: &PostOrderIterItem<&Node<Named<Commit<J>>>>,
inner: Inner<
&Arc<Node<Witness<J>>>,
J,
&Option<Arc<WitnessNode<J>>>,
&Option<Arc<Value>>,
>,
) -> Result<WitnessData<J>, Self::Error> {
let inner = inner
.map(|node| node.cached_data())
.map_witness(|maybe_value| maybe_value.clone());
Ok(WitnessData::from_inner(inner).expect("types are already finalized"))
}
}
self.convert::<InternalSharing, _, _>(&mut Populator(witness, disconnect, PhantomData))
.unwrap()
}
pub fn encode<W: io::Write>(&self, w: &mut BitWriter<W>) -> io::Result<usize> {
let program_bits = encode::encode_program(self.to_commit_node().as_ref(), w)?;
w.flush_all()?;
Ok(program_bits)
}
pub fn encode_to_vec(&self) -> Vec<u8> {
let mut program_and_witness_bytes = Vec::<u8>::new();
let mut writer = BitWriter::new(&mut program_and_witness_bytes);
self.encode(&mut writer)
.expect("write to vector never fails");
debug_assert!(!program_and_witness_bytes.is_empty());
program_and_witness_bytes
}
}
pub type NamedConstructNode<J> = Node<Named<Construct<J>>>;
impl<J: Jet> node::Marker for Named<Construct<J>> {
type CachedData = NamedConstructData<J>;
type Witness = WitnessOrHole;
type Disconnect = Arc<NamedConstructNode<J>>;
type SharingId = Arc<str>;
type Jet = J;
fn compute_sharing_id(_: Cmr, cached_data: &Self::CachedData) -> Option<Arc<str>> {
Some(Arc::clone(&cached_data.name))
}
}
#[derive(Clone, Debug)]
pub struct NamedConstructData<J> {
internal: ConstructData<J>,
name: Arc<str>,
position: Position,
user_source_types: Arc<[types::Type]>,
user_target_types: Arc<[types::Type]>,
}
impl<J: Jet> NamedConstructNode<J> {
pub fn new(
name: Arc<str>,
position: Position,
user_source_types: Arc<[types::Type]>,
user_target_types: Arc<[types::Type]>,
inner: node::Inner<Arc<Self>, J, Arc<Self>, WitnessOrHole>,
) -> Result<Self, types::Error> {
let construct_data = ConstructData::from_inner(
inner
.as_ref()
.map(|data| &data.cached_data().internal)
.map_disconnect(|_| &None)
.map_witness(|_| NoWitness),
)?;
let named_data = NamedConstructData {
internal: construct_data,
name,
position,
user_source_types,
user_target_types,
};
Ok(Node::from_parts(inner, named_data))
}
pub fn renamed(&self, new_name: Arc<str>) -> Self {
let data = NamedConstructData {
internal: self.cached_data().internal.clone(),
user_source_types: Arc::clone(&self.cached_data().user_source_types),
user_target_types: Arc::clone(&self.cached_data().user_target_types),
name: new_name,
position: self.position(),
};
Self::from_parts(self.inner().clone(), data)
}
pub fn name(&self) -> &Arc<str> {
&self.cached_data().name
}
pub fn position(&self) -> Position {
self.cached_data().position
}
pub fn arrow(&self) -> &Arrow {
self.cached_data().internal.arrow()
}
pub fn finalize_types_main(&self) -> Result<Arc<NamedCommitNode<J>>, ErrorSet> {
self.finalize_types_inner(true)
}
pub fn finalize_types_non_main(&self) -> Result<Arc<NamedCommitNode<J>>, ErrorSet> {
self.finalize_types_inner(false)
}
pub fn finalize_types_inner(
&self,
for_main: bool,
) -> Result<Arc<NamedCommitNode<J>>, ErrorSet> {
struct FinalizeTypes<J: Jet> {
for_main: bool,
errors: ErrorSet,
pending_hole_error: Option<(Position, Error)>,
phantom: PhantomData<J>,
}
impl<J: Jet> Converter<Named<Construct<J>>, Named<Commit<J>>> for FinalizeTypes<J> {
type Error = ErrorSet;
fn visit_node(&mut self, data: &PostOrderIterItem<&NamedConstructNode<J>>) {
let inner = data.node.inner();
if let node::Inner::Disconnect(..) = inner {
self.pending_hole_error = None;
} else {
if let Some((position, error)) = self.pending_hole_error.take() {
self.errors.add(position, error);
}
if let node::Inner::Witness(WitnessOrHole::TypedHole(name)) = inner {
self.pending_hole_error = Some((
data.node.position(),
Error::HoleAtCommitTime {
name: Arc::clone(name),
arrow: data.node.arrow().shallow_clone(),
},
));
}
}
}
fn convert_witness(
&mut self,
_: &PostOrderIterItem<&NamedConstructNode<J>>,
_: &WitnessOrHole,
) -> Result<NoWitness, Self::Error> {
Ok(NoWitness)
}
fn convert_disconnect(
&mut self,
data: &PostOrderIterItem<&NamedConstructNode<J>>,
_: Option<&Arc<NamedCommitNode<J>>>,
disc: &Arc<NamedConstructNode<J>>,
) -> Result<NoDisconnect, Self::Error> {
if !matches!(
disc.inner(),
node::Inner::Witness(WitnessOrHole::TypedHole(..))
) {
self.errors
.add(data.node.position(), Error::HoleFilledAtCommitTime);
}
Ok(NoDisconnect)
}
fn convert_data(
&mut self,
data: &PostOrderIterItem<&NamedConstructNode<J>>,
inner: node::Inner<&Arc<NamedCommitNode<J>>, J, &NoDisconnect, &NoWitness>,
) -> Result<NamedCommitData<J>, Self::Error> {
let converted_data = inner
.as_ref()
.map(|node| &node.cached_data().internal)
.map_disconnect(|disc| *disc)
.copy_witness();
if !self.for_main {
let arrow = data.node.arrow();
for ty in data.node.cached_data().user_source_types.as_ref() {
if let Err(e) = arrow.source.unify(ty, "binding source type annotation") {
self.errors.add(data.node.position(), e);
}
}
for ty in data.node.cached_data().user_target_types.as_ref() {
if let Err(e) = arrow.target.unify(ty, "binding target type annotation") {
self.errors.add(data.node.position(), e);
}
}
}
let commit_data = match CommitData::new(data.node.arrow(), converted_data) {
Ok(commit_data) => Arc::new(commit_data),
Err(e) => {
self.errors.add(data.node.position(), e);
return Err(self.errors.clone());
}
};
if self.for_main {
let source_bound =
types::Bound::Complete(Arc::clone(&commit_data.arrow().source));
let source_ty = types::Type::from(source_bound);
for ty in data.node.cached_data().user_source_types.as_ref() {
if let Err(e) = source_ty.unify(ty, "binding source type annotation") {
self.errors.add(data.node.position(), e);
}
}
let target_bound =
types::Bound::Complete(Arc::clone(&commit_data.arrow().target));
let target_ty = types::Type::from(target_bound);
for ty in data.node.cached_data().user_target_types.as_ref() {
if let Err(e) = target_ty.unify(ty, "binding target type annotation") {
self.errors.add(data.node.position(), e);
}
}
}
Ok(NamedCommitData {
name: Arc::clone(&data.node.cached_data().name),
internal: commit_data,
})
}
}
let mut finalizer = FinalizeTypes {
for_main,
errors: ErrorSet::default(),
pending_hole_error: None,
phantom: PhantomData,
};
if for_main {
let unit_ty = types::Type::unit();
if self.cached_data().user_source_types.is_empty() {
if let Err(e) = self
.arrow()
.source
.unify(&unit_ty, "setting root source to unit")
{
finalizer.errors.add(self.position(), e);
}
}
if self.cached_data().user_target_types.is_empty() {
if let Err(e) = self
.arrow()
.target
.unify(&unit_ty, "setting root source to unit")
{
finalizer.errors.add(self.position(), e);
}
}
}
let root = self.convert::<InternalSharing, _, _>(&mut finalizer)?;
finalizer.errors.into_result(root)
}
}
pub struct Namer {
const_idx: usize,
wit_idx: usize,
other_idx: usize,
root_cmr: Option<Cmr>,
}
impl Namer {
pub fn new_rooted(root_cmr: Cmr) -> Self {
Namer {
const_idx: 0,
wit_idx: 0,
other_idx: 0,
root_cmr: Some(root_cmr),
}
}
pub fn new() -> Self {
Namer {
const_idx: 0,
wit_idx: 0,
other_idx: 0,
root_cmr: None,
}
}
pub fn assign_name<C, J, X, W>(&mut self, inner: node::Inner<C, J, X, W>) -> String {
let prefix = match inner {
node::Inner::Iden => "id",
node::Inner::Unit => "ut",
node::Inner::InjL(..) => "jl",
node::Inner::InjR(..) => "jr",
node::Inner::Drop(..) => "dp",
node::Inner::Take(..) => "tk",
node::Inner::Comp(..) => "cp",
node::Inner::Case(..) => "cs",
node::Inner::AssertL(..) => "asstl",
node::Inner::AssertR(..) => "asstr",
node::Inner::Pair(..) => "pr",
node::Inner::Disconnect(..) => "disc",
node::Inner::Witness(..) => "wit",
node::Inner::Fail(..) => "FAIL",
node::Inner::Jet(..) => "jt",
node::Inner::Word(..) => "const",
};
let index = match inner {
node::Inner::Word(..) => &mut self.const_idx,
node::Inner::Witness(..) => &mut self.wit_idx,
_ => &mut self.other_idx,
};
*index += 1;
format!("{}{}", prefix, index)
}
}
impl<J: Jet> Converter<Commit<J>, Named<Commit<J>>> for Namer {
type Error = ();
fn convert_witness(
&mut self,
_: &PostOrderIterItem<&CommitNode<J>>,
_: &NoWitness,
) -> Result<NoWitness, Self::Error> {
Ok(NoWitness)
}
fn convert_disconnect(
&mut self,
_: &PostOrderIterItem<&CommitNode<J>>,
_: Option<&Arc<NamedCommitNode<J>>>,
_: &NoDisconnect,
) -> Result<NoDisconnect, Self::Error> {
Ok(NoDisconnect)
}
fn convert_data(
&mut self,
data: &PostOrderIterItem<&CommitNode<J>>,
inner: node::Inner<&Arc<NamedCommitNode<J>>, J, &NoDisconnect, &NoWitness>,
) -> Result<NamedCommitData<J>, Self::Error> {
if Some(data.node.cmr()) == self.root_cmr {
return Ok(NamedCommitData {
internal: Arc::clone(data.node.cached_data()),
name: Arc::from("main"),
});
}
Ok(NamedCommitData {
internal: Arc::clone(data.node.cached_data()),
name: Arc::from(self.assign_name(inner).as_str()),
})
}
}