Last active
August 29, 2015 14:10
-
-
Save pythonesque/fc43fe529adf8ddc26cb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(rustc_private,std_misc)] | |
extern crate arena; | |
use arena::TypedArena; | |
use std::cell::Cell; | |
use std::collections::hash_map::Entry as h; | |
use std::collections::HashMap; | |
use std::default::Default; | |
use std::fmt; | |
struct GraphRoot<'a> { | |
// Arena of any names we might need to allocate | |
//names: TypedArena<&'a str>, | |
// Arena of all the nodes in the graph | |
nodes: TypedArena<Node<'a>>, | |
} | |
impl<'a> GraphRoot<'a> { | |
pub fn new() -> GraphRoot<'a> { | |
GraphRoot { | |
//names: TypedArena::new(), | |
nodes: TypedArena::new(), | |
} | |
} | |
} | |
struct Graph<'a> { | |
root: &'a GraphRoot<'a>, | |
// A lookup table to map from string to node name | |
lookup: HashMap<&'a str, &'a Node<'a>>, | |
// Lookup table for any node names that haven't been found yet. | |
unbound: HashMap<&'a str, Vec<&'a Ref<'a>>> | |
} | |
impl<'a> Graph<'a> { | |
pub fn new(root: &'a GraphRoot<'a>) -> Graph<'a> { | |
Graph { | |
root: root, | |
lookup: HashMap::new(), | |
unbound: HashMap::new(), | |
} | |
} | |
pub fn add<'b>(&'b mut self, node: Node<'a>) -> &'a Node<'a> { | |
let node = &*self.root.nodes.alloc(node); | |
// First, replace any nodes this one references with the appropriate | |
// reference, and add those that haven't been bound yet to the unbound | |
// table. | |
for noderef in node.out().iter() { | |
if let NodeReference::NameRef(name) = noderef.get() { | |
if let Some(&node) = self.lookup.get(&name) { | |
// Found the name in the lookup table, replace it | |
noderef.set(NodeReference::NodeRef(node)); | |
} else { | |
// The name hasn't been found yet, so add it to the unbound | |
// table | |
match self.unbound.entry(name) { | |
h::Occupied(mut o) => { o.get_mut().push(noderef); }, // add an entry, | |
h::Vacant(v) => { v.insert(vec![noderef]); } // initialize a new list | |
} | |
} | |
} | |
} | |
// Add this node to the lookup table. | |
if let Some(name) = node.name { | |
self.lookup.insert(&*name, node); | |
// Finally, properly rebind any nodes that haven't been bound yet and are | |
// looking for the current node. | |
if let Some(noderefs) = self.unbound.remove(&name) { | |
for noderef in noderefs.iter() { | |
noderef.set(NodeReference::NodeRef(node)); | |
} | |
} | |
} | |
node | |
} | |
} | |
#[derive(Copy,Debug)] | |
enum NodeReference<'a> { | |
NodeRef(&'a Node<'a>), | |
NameRef(&'a str), | |
} | |
fn named<'a>(name: &'a str) -> Ref<'a> { | |
Cell::new(NodeReference::NameRef(name)) | |
} | |
type Ref<'a> = Cell<NodeReference<'a>>; | |
#[derive(Debug)] | |
struct Context; // Imagine we put the context in here | |
#[derive(Debug)] | |
struct Node<'a> { | |
// Name of node | |
pub name: Option<&'a str>, | |
// The actual node operation | |
pub op: Operation<'a>, | |
} | |
impl<'a> Node<'a> { | |
fn out<'b>(&'b self) -> &'b [Ref<'a>] { | |
match self.op { | |
Operation::SourceOp(Source { ref emits, .. }) => &**emits, | |
Operation::EachOp(Each { ref emits, .. }) => &**emits, | |
} | |
} | |
} | |
#[derive(Debug)] | |
enum EndCyclePolicy { | |
NullEmit, | |
#[allow(dead_code)] Explicit, | |
} | |
impl Default for EndCyclePolicy { | |
fn default() -> EndCyclePolicy { EndCyclePolicy::NullEmit } | |
} | |
struct Source<'a> { | |
// End cycle policy, | |
pub end_cycle_policy: EndCyclePolicy, | |
pub begin_cycle: Box<FnMut(&mut Context)>, | |
// Function yielding the next tuple | |
pub next_tuple: Box<FnMut(&mut Context)>, | |
// List of nodes that are being emitted by this one | |
pub emits: Vec<Ref<'a>>, | |
} | |
impl<'a> fmt::Debug for Source<'a> { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
write!(f, "Source {{ end_cycle_policy: {:?}, emits: {:?} }}", self.end_cycle_policy, self.emits) | |
} | |
} | |
#[derive(Debug)] | |
enum OutputFormat { | |
Replace, | |
Merge, | |
} | |
impl Default for OutputFormat { | |
fn default() -> OutputFormat { OutputFormat::Replace } | |
} | |
struct Each<'a> { | |
// Output format | |
pub output_format: OutputFormat, | |
pub prepare: Box<FnMut(&mut Context)>, | |
// Function yielding the next tuple | |
pub execute: Box<FnMut(&mut Context)>, | |
// List of nodes that are being emitted by this one | |
pub emits: Vec<Ref<'a>>, | |
} | |
impl<'a> fmt::Debug for Each<'a> { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
write!(f, "Each {{ output_format: {:?}, emits: {:?} }}", self.output_format, self.emits) | |
} | |
} | |
#[derive(Debug)] | |
enum Operation<'a> { | |
SourceOp(Source<'a>), | |
EachOp(Each<'a>), | |
} | |
fn main() { | |
let storage = GraphRoot::new(); | |
let mut graph = Graph::new(&storage); | |
let source = graph.add(Node { | |
name: Some("source"), | |
op: Operation::SourceOp(Source { | |
end_cycle_policy: Default::default(), | |
begin_cycle: Box::new(|ctx| { | |
println!("Beginning cycle in {:?}", ctx) | |
}), | |
next_tuple: Box::new(|ctx| { | |
println!("Next tuple for {:?}", ctx) | |
}), | |
emits: vec![named("each1"), named("each2")], | |
}) | |
}); | |
graph.add(Node { | |
name: Some("each1"), | |
op: Operation::EachOp(Each { | |
output_format: OutputFormat::Merge, | |
prepare: Box::new(|ctx| { | |
println!("Preparing each in {:?}", ctx) | |
}), | |
execute: Box::new(|ctx| { | |
println!("Executing each in {:?}", ctx) | |
}), | |
emits: vec![], | |
}) | |
}); | |
println!("{:?}", source); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment