Skip to content

Commit

Permalink
Reintroduce Into coercion to builder API
Browse files Browse the repository at this point in the history
  • Loading branch information
CAD97 committed Jun 17, 2020
1 parent aa24fa4 commit c667b8c
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 14 deletions.
22 changes: 12 additions & 10 deletions src/green/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,12 @@ impl Builder {
/// This checks children for identity equivalence, not structural,
/// so it is `O(children.len())` and only caches higher-level nodes
/// if the lower-level nodes have also been cached.
pub fn node<I>(&mut self, kind: Kind, children: I) -> Arc<Node>
pub fn node<I, R>(&mut self, kind: Kind, children: I) -> Arc<Node>
where
I: IntoIterator<Item = NodeOrToken<Arc<Node>, Arc<Token>>>,
I::IntoIter: ExactSizeIterator + AsRef<[NodeOrToken<Arc<Node>, Arc<Token>>]>,
I: IntoIterator,
I::Item: Into<NodeOrToken<Arc<Node>, Arc<Token>>>,
I::IntoIter: ExactSizeIterator + AsRef<[R]>,
for<'a> &'a R: Into<NodeOrToken<&'a Node, &'a Token>>,
{
let hasher = &self.hasher;
let children = children.into_iter();
Expand All @@ -105,12 +107,12 @@ impl Builder {
let state = &mut hasher.build_hasher();
kind.hash(state);
for child in children.as_ref() {
match child {
match child.into() {
NodeOrToken::Node(node) => {
ptr::hash(&**node as *const Node as *const (), state)
ptr::hash(node as *const Node as *const (), state)
}
NodeOrToken::Token(token) => {
ptr::hash(&**token as *const Token as *const (), state)
ptr::hash(token as *const Token as *const (), state)
}
}
}
Expand All @@ -119,16 +121,16 @@ impl Builder {

let entry = self.nodes.raw_entry_mut().from_hash(hash, |ThinEqNode(node)| {
node.kind() == kind
&& node.children().zip(children.as_ref().iter()).all(|pair| match pair {
(NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => ptr::eq(&*lhs, &**rhs),
(NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => ptr::eq(&*lhs, &**rhs),
&& node.children().zip(children.as_ref().iter().map(Into::into)).all(|pair| match pair {
(NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => ptr::eq(&*lhs, rhs),
(NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => ptr::eq(&*lhs, rhs),
_ => false,
})
});
let (ThinEqNode(node), ()) = match entry {
RawEntryMut::Occupied(entry) => entry.into_key_value(),
RawEntryMut::Vacant(entry) => {
let node = Node::new(kind, children.map(pack_node_or_token));
let node = Node::new(kind, children.map(Into::into).map(pack_node_or_token));
entry.insert_with_hasher(hash, ThinEqNode(node), (), |x| do_hash(hasher, x))
}
};
Expand Down
54 changes: 53 additions & 1 deletion src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use {
crate::prelude::{GreenNode, GreenToken},
crate::{
prelude::{GreenNode, GreenToken},
ArcBorrow,
},
std::{
fmt::{self, Debug},
ops::Deref,
Expand Down Expand Up @@ -119,3 +122,52 @@ impl From<Arc<GreenToken>> for NodeOrToken<Arc<GreenNode>, Arc<GreenToken>> {
NodeOrToken::Token(this)
}
}

impl<'a> From<&'a GreenNode> for NodeOrToken<&'a GreenNode, &'a GreenToken> {
fn from(this: &'a GreenNode) -> Self {
NodeOrToken::Node(this)
}
}

impl<'a> From<&'a GreenToken> for NodeOrToken<&'a GreenNode, &'a GreenToken> {
fn from(this: &'a GreenToken) -> Self {
NodeOrToken::Token(this)
}
}

impl<'a> From<&'a NodeOrToken<Arc<GreenNode>, Arc<GreenToken>>> for NodeOrToken<&'a GreenNode, &'a GreenToken> {
fn from(this: &'a NodeOrToken<Arc<GreenNode>, Arc<GreenToken>>) -> Self {
this.as_deref()
}
}

impl<'a> From<&'a Arc<GreenNode>> for NodeOrToken<&'a GreenNode, &'a GreenToken> {
fn from(this: &'a Arc<GreenNode>) -> Self {
NodeOrToken::Node(&*this)
}
}

impl<'a> From<&'a Arc<GreenToken>> for NodeOrToken<&'a GreenNode, &'a GreenToken> {
fn from(this: &'a Arc<GreenToken>) -> Self {
NodeOrToken::Token(&*this)
}
}


impl<'a> From<ArcBorrow<'a, GreenNode>> for NodeOrToken<&'a GreenNode, &'a GreenToken> {
fn from(this: ArcBorrow<'a, GreenNode>) -> Self {
NodeOrToken::Node(ArcBorrow::downgrade(this))
}
}

impl<'a> From<ArcBorrow<'a, GreenToken>> for NodeOrToken<&'a GreenNode, &'a GreenToken> {
fn from(this: ArcBorrow<'a, GreenToken>) -> Self {
NodeOrToken::Token(ArcBorrow::downgrade(this))
}
}

impl<'a> From<NodeOrToken<ArcBorrow<'a, GreenNode>, ArcBorrow<'a, GreenToken>>> for NodeOrToken<&'a GreenNode, &'a GreenToken> {
fn from(this: NodeOrToken<ArcBorrow<'a, GreenNode>, ArcBorrow<'a, GreenToken>>) -> Self {
this.map(ArcBorrow::downgrade, ArcBorrow::downgrade)
}
}
6 changes: 3 additions & 3 deletions tests/smoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,15 @@ fn make_math_tree() {
// Invocations of the builder with the same (id) arguments produces the same (id) results
assert!(Arc::ptr_eq(&ws, &builder.token(WS, " ")));

// builder.node accepts iterator of NodeOrToken<Arc<Node>, Arc<Token>> so you need to wrap.
// You'll know if you need the bottom-up builder (LR or such). Use TreeBuilder otherwise.
// builder.node accepts iterator of Arc<Node>, Arc<Token>, or NodeOrToken<Arc<Node>, Arc<Token>>
// so if you're mixing nodes and tokens, you need to include the type changing boilerplate.
let n = |node: &Arc<green::Node>| NodeOrToken::from(node.clone());
let t = |token: &Arc<green::Token>| NodeOrToken::from(token.clone());

// Currently, only vec::IntoIter and vec::Drain are able to be used for builder.node, as
// building a node requires being able to pre-iterate the children to check the dedup cache.
// (Please, const-generic angels, give us `for<const N: usize> [_; N]: IntoIterator` soon(ish)!)
let inner_mul = builder.node(EXPR, vec![t(&n2), t(&ws), t(&mul), t(&ws), t(&n3)]);
let inner_mul = builder.node(EXPR, vec![n2, ws.clone(), mul, ws.clone(), n3]);
let left_add = builder.node(EXPR, vec![t(&n1), t(&ws), t(&add), t(&ws), n(&inner_mul)]);
let right_add = builder.node(EXPR, vec![n(&left_add), t(&ws), t(&add), t(&ws), t(&n4)]);

Expand Down

0 comments on commit c667b8c

Please sign in to comment.