aboutsummaryrefslogtreecommitdiff
path: root/src/lazy.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lazy.rs')
-rw-r--r--src/lazy.rs74
1 files changed, 74 insertions, 0 deletions
diff --git a/src/lazy.rs b/src/lazy.rs
new file mode 100644
index 0000000..b4f801b
--- /dev/null
+++ b/src/lazy.rs
@@ -0,0 +1,74 @@
+#![macro_escape]
+
+use std::ptr;
+use std::cell::UnsafeCell;
+use std::ops::Deref;
+use std::boxed::FnBox;
+
+pub enum State<V> {
+ Evaluated(V),
+ Evaluating,
+ Unevaluated(Box<FnBox() -> V>)
+}
+
+pub struct Lazy<V> {
+ state: UnsafeCell<State<V>>,
+}
+
+impl<V> Lazy<V> {
+ pub fn new(f: Box<FnBox() -> V>) -> Lazy<V> {
+ Lazy { state: UnsafeCell::new(State::Unevaluated(f)) }
+ }
+
+ pub fn ready(v: V) -> Lazy<V> {
+ Lazy { state: UnsafeCell::new(State::Evaluated(v)) }
+ }
+
+ pub fn force(&mut self) {
+ self.value();
+ }
+
+ pub fn unwrap(self) -> V {
+ unsafe {
+ match self.state.into_inner() {
+ State::Unevaluated(f) => f(),
+ State::Evaluating => panic!("Illegal state, can't call unwrap during evaluation"),
+ State::Evaluated(v) => v
+ }
+ }
+ }
+
+ pub fn value(&self) -> &V {
+ unsafe {
+ let state = self.state.get();
+ match *state {
+ State::Evaluated(ref v) => v,
+ State::Evaluating => panic!("Illegal state, can't call value during evaluation"),
+ State::Unevaluated(_) => {
+ if let State::Unevaluated(f) = ptr::replace(state, State::Evaluating) {
+ ptr::replace(state, State::Evaluated(f()));
+ }
+ if let State::Evaluated(ref v) = *state { return v }
+ unreachable!()
+ }
+ }
+ }
+ }
+}
+
+impl<V> Deref for Lazy<V> {
+ type Target = V;
+ fn deref(&self) -> &V { self.value() }
+}
+
+#[macro_export]
+macro_rules! lazy {
+ (@as_expr $val: expr) => { $val };
+ ($($val: tt)*) => { Lazy::new(Box::new(move || lazy![@as_expr { $($val)* }])) };
+}
+
+#[macro_export]
+macro_rules! eager {
+ (@as_expr $val: expr) => { $val };
+ ($($val: tt)*) => { Lazy::<_>::ready(eager![@as_expr { $($val)* }]) };
+}