1use std::sync::Mutex;
2
3use crate::{
4 env::Cx,
5 error::{Error, Result},
6 eval::Demand,
7 expr::Expr,
8 id::{CORE_THUNK_CLASS_ID, Symbol},
9 object::Object,
10 value::Value,
11};
12
13pub trait Thunk: Send + Sync {
21 fn force(&self, cx: &mut Cx, demand: Demand) -> Result<Value>;
23}
24
25enum ThunkState {
26 Pending { input: Expr, env: crate::env::Env },
27 Forcing,
28 Forced(Value),
29}
30
31struct LazyThunkState {
32 input: Expr,
33 env: crate::env::Env,
34 forcing: bool,
35}
36
37pub struct LazyThunkObject {
43 state: Mutex<LazyThunkState>,
44}
45
46impl LazyThunkObject {
47 pub fn new(input: Expr, env: crate::env::Env) -> Self {
49 Self {
50 state: Mutex::new(LazyThunkState {
51 input,
52 env,
53 forcing: false,
54 }),
55 }
56 }
57}
58
59impl Object for LazyThunkObject {
60 fn display(&self, _cx: &mut Cx) -> Result<String> {
61 Ok("#<lazy-thunk>".to_owned())
62 }
63
64 fn as_any(&self) -> &dyn std::any::Any {
65 self
66 }
67}
68
69impl crate::ObjectCompat for LazyThunkObject {
70 fn class(&self, cx: &mut Cx) -> Result<Value> {
71 if let Some(value) = cx
72 .registry()
73 .class_by_symbol(&Symbol::qualified("core", "Thunk"))
74 {
75 return Ok(value.clone());
76 }
77 cx.factory()
78 .class_stub(CORE_THUNK_CLASS_ID, Symbol::qualified("core", "Thunk"))
79 }
80 fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
81 let state = self
82 .state
83 .lock()
84 .map_err(|_| Error::PoisonedLock("lazy thunk state"))?;
85 if state.forcing {
86 return Err(Error::RecursiveThunkForce);
87 }
88 Ok(state.input.clone())
89 }
90 fn as_thunk(&self) -> Option<&dyn Thunk> {
91 Some(self)
92 }
93}
94
95impl Thunk for LazyThunkObject {
96 fn force(&self, cx: &mut Cx, _demand: Demand) -> Result<Value> {
97 let (input, env) = {
98 let mut state = self
99 .state
100 .lock()
101 .map_err(|_| Error::PoisonedLock("lazy thunk state"))?;
102 if state.forcing {
103 return Err(Error::RecursiveThunkForce);
104 }
105 state.forcing = true;
106 (state.input.clone(), state.env.clone())
107 };
108 let evaluated = eval_input_with_env(cx, input, env);
109
110 let mut state = self
111 .state
112 .lock()
113 .map_err(|_| Error::PoisonedLock("lazy thunk state"))?;
114 state.forcing = false;
115 evaluated
116 }
117}
118
119pub struct ThunkObject {
126 state: Mutex<ThunkState>,
127}
128
129impl ThunkObject {
130 pub fn new(input: Expr, env: crate::env::Env) -> Self {
132 Self {
133 state: Mutex::new(ThunkState::Pending { input, env }),
134 }
135 }
136}
137
138impl Object for ThunkObject {
139 fn display(&self, _cx: &mut Cx) -> Result<String> {
140 Ok("#<thunk>".to_owned())
141 }
142
143 fn as_any(&self) -> &dyn std::any::Any {
144 self
145 }
146}
147
148impl crate::ObjectCompat for ThunkObject {
149 fn class(&self, cx: &mut Cx) -> Result<Value> {
150 if let Some(value) = cx
151 .registry()
152 .class_by_symbol(&Symbol::qualified("core", "Thunk"))
153 {
154 return Ok(value.clone());
155 }
156 cx.factory()
157 .class_stub(CORE_THUNK_CLASS_ID, Symbol::qualified("core", "Thunk"))
158 }
159 fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
160 let state = self
161 .state
162 .lock()
163 .map_err(|_| Error::PoisonedLock("thunk state"))?;
164 match &*state {
165 ThunkState::Pending { input, .. } => Ok(input.clone()),
166 ThunkState::Forcing => Err(Error::RecursiveThunkForce),
167 ThunkState::Forced(value) => value.object().as_expr(cx),
168 }
169 }
170 fn as_thunk(&self) -> Option<&dyn Thunk> {
171 Some(self)
172 }
173}
174
175impl Thunk for ThunkObject {
176 fn force(&self, cx: &mut Cx, _demand: Demand) -> Result<Value> {
177 let (input, env) = {
178 let mut state = self
179 .state
180 .lock()
181 .map_err(|_| Error::PoisonedLock("thunk state"))?;
182 match &*state {
183 ThunkState::Forced(value) => return Ok(value.clone()),
184 ThunkState::Forcing => return Err(Error::RecursiveThunkForce),
185 ThunkState::Pending { input, env } => {
186 let pair = (input.clone(), env.clone());
187 *state = ThunkState::Forcing;
188 pair
189 }
190 }
191 };
192 let evaluated = eval_input_with_env(cx, input.clone(), env.clone());
193
194 let mut state = self
195 .state
196 .lock()
197 .map_err(|_| Error::PoisonedLock("thunk state"))?;
198 match evaluated {
199 Ok(value) => {
200 *state = ThunkState::Forced(value.clone());
201 Ok(value)
202 }
203 Err(err) => {
204 *state = ThunkState::Pending { input, env };
205 Err(err)
206 }
207 }
208 }
209}
210
211fn eval_input_with_env(cx: &mut Cx, input: Expr, env: crate::env::Env) -> Result<Value> {
212 cx.with_env(env, |cx| crate::eval::eval_expr_default(cx, input))
213}