thunk.rs
1 #![allow(dead_code)] 2 3 use std::{ 4 mem, 5 sync::Arc, 6 }; 7 8 use dup::{ 9 Dupe, 10 OptionDupedExt as _, 11 }; 12 use tokio::sync::RwLock; 13 14 use crate::{ 15 Code, 16 Operation, 17 Scopes, 18 State, 19 Value, 20 value, 21 }; 22 23 const EXPECT_SCOPE: &str = "must have at least once scope"; 24 25 #[derive(Clone, Dupe)] 26 enum ThunkInner { 27 SuspendedNative { 28 location: value::Location, 29 code: Arc<dyn Fn() -> Value + Send + Sync>, 30 is_lambda: bool, 31 argument: Option<Value>, 32 }, 33 34 Suspended { 35 location: value::Location, 36 code: Arc<Code>, 37 is_lambda: bool, 38 argument: Option<Value>, 39 scopes: Scopes, 40 }, 41 42 Evaluated { 43 scopagate: Option<Scopes>, 44 value: Value, 45 }, 46 } 47 48 impl ThunkInner { 49 thread_local! { 50 static NOT_BOOLEAN: value::Error = value::Error::new(value::string::new!("expected boolean, got something else")); 51 52 static NOT_LAMBDA: value::Error = value::Error::new(value::string::new!("expected lambda, got something else")); 53 54 static NOT_ATTRIBUTES: value::Error = value::Error::new(value::string::new!("expected attributes, got something else")); 55 } 56 57 fn black_hole(location: value::Location) -> Self { 58 ThunkInner::SuspendedNative { 59 location, 60 code: Arc::new(|| { 61 Value::from(Arc::new(value::Error::new(value::string::new!( 62 "infinite recursion encountered" 63 )))) 64 }), 65 is_lambda: false, 66 argument: None, 67 } 68 } 69 } 70 71 #[derive(Clone, Dupe)] 72 pub struct Thunk(Arc<RwLock<ThunkInner>>); 73 74 #[bon::bon] 75 impl Thunk { 76 #[must_use] 77 pub fn suspended_native( 78 code: impl Fn() -> Value + Send + Sync + 'static, 79 location: value::Location, 80 is_lambda: bool, 81 ) -> Self { 82 Self(Arc::new(RwLock::new(ThunkInner::SuspendedNative { 83 location, 84 code: Arc::new(code), 85 is_lambda, 86 argument: None, 87 }))) 88 } 89 90 #[must_use] 91 #[builder(finish_fn(name = "location"))] 92 pub fn suspended( 93 #[builder(start_fn)] code: Arc<Code>, 94 #[builder(finish_fn)] location: value::Location, 95 is_lambda: bool, 96 scopes: Scopes, 97 ) -> Self { 98 Self(Arc::new(RwLock::new(ThunkInner::Suspended { 99 location, 100 code, 101 is_lambda, 102 argument: None, 103 scopes, 104 }))) 105 } 106 107 pub async fn argument(&self, arg: Value) -> Self { 108 let mut inner = self.0.read().await.dupe(); 109 110 match inner { 111 ThunkInner::SuspendedNative { 112 ref mut argument, .. 113 } => *argument = Some(arg), 114 ThunkInner::Suspended { 115 ref mut argument, .. 116 } => *argument = Some(arg), 117 ThunkInner::Evaluated { .. } => panic!("cannot add argument to evaluated thunk"), 118 } 119 120 Thunk(Arc::new(RwLock::new(inner))) 121 } 122 123 pub async fn get(&self) -> Option<(Option<Scopes>, Value)> { 124 if let ThunkInner::Evaluated { 125 ref scopagate, 126 ref value, 127 } = *self.0.read().await 128 { 129 Some((scopagate.dupe(), value.dupe())) 130 } else { 131 None 132 } 133 } 134 135 pub async fn force(&self, state: &State) { 136 let this = mem::replace(&mut *self.0.write().await, ThunkInner::Evaluated { 137 scopagate: None, 138 // FIXME 139 value: Value::Nil(value::Nil), 140 }); 141 142 let new = match this { 143 evaluated @ ThunkInner::Evaluated { .. } => evaluated.dupe(), 144 145 ThunkInner::SuspendedNative { 146 location, 147 code, 148 is_lambda: _is_lambda, 149 argument: _argument, 150 } => { 151 *self.0.write().await = ThunkInner::black_hole(location); 152 153 ThunkInner::Evaluated { 154 scopagate: None, 155 value: code(), 156 } 157 }, 158 159 ThunkInner::Suspended { 160 location, 161 code, 162 is_lambda, 163 argument, 164 mut scopes, 165 } => { 166 if is_lambda && argument.is_none() { 167 return; // FIXME: Makes forced lambdas be nil. Also god damn, this code sucks ass. 168 } 169 170 *self.0.write().await = ThunkInner::black_hole(location.dupe()); 171 172 let mut stack = argument.into_iter().collect::<Vec<_>>(); 173 174 let items = &mut code.iter().peekable(); 175 while let Some((index, item)) = items.next() { 176 let operation = *item.as_operation().expect("next item must be an operation"); 177 178 match operation { 179 Operation::Push => { 180 let value_index = items 181 .next() 182 .expect("push must not be the last item") 183 .1 184 .as_argument() 185 .expect("push must have an argument") 186 .as_value_index() 187 .expect("push argument must be a value index"); 188 189 stack.push(match &code[value_index] { 190 &Value::Code { 191 ref code, 192 is_lambda, 193 } => { 194 Value::from( 195 Thunk::suspended(code.dupe()) 196 .scopes(scopes.dupe()) 197 .is_lambda(is_lambda) 198 // FIXME: .location(code.read_operation(index).0), 199 .location(location.dupe()), 200 ) 201 }, 202 203 other => other.dupe(), 204 }); 205 }, 206 Operation::Pop => { 207 stack 208 .pop() 209 .expect("pop operation must not be called on empty stack"); 210 }, 211 Operation::Swap => { 212 let &mut [.., ref mut x, ref mut y] = &mut *stack else { 213 unreachable!("swap must be called on stack of length 2 or higher"); 214 }; 215 216 mem::swap(x, y); 217 }, 218 operation @ (Operation::Jump | Operation::JumpIf | Operation::JumpIfError) => { 219 let target_index = items 220 .next() 221 .expect("jump must not be the last item") 222 .1 223 .as_argument() 224 .expect("jump must have an argument") 225 .as_byte_index() 226 .expect("jump argument must be a byte index"); 227 228 match operation { 229 Operation::Jump => {}, 230 Operation::JumpIf => { 231 let value = stack.last_mut().expect( 232 "jump-if and jump-if-error must be called on stack with at least \ 233 one item", 234 ); 235 236 let &mut Value::Boolean(value) = value else { 237 *value = Value::from(Arc::from( 238 ThunkInner::NOT_BOOLEAN 239 .with(Dupe::dupe) 240 .append_trace(code.read_operation(index).0), 241 )); 242 continue; 243 }; 244 245 if !value { 246 continue; 247 } 248 }, 249 Operation::JumpIfError => { 250 let value = stack.last_mut().expect( 251 "jump-if and jump-if-error must be called on stack with at least \ 252 one item", 253 ); 254 255 let &mut Value::Error(ref error) = value else { 256 continue; 257 }; 258 259 *value = Value::from(Arc::new( 260 error.append_trace(code.read_operation(index).0), 261 )); 262 }, 263 _ => unreachable!(), 264 } 265 266 while items 267 .next_if(|&(next_index, _)| next_index != target_index) 268 .is_some() 269 {} 270 }, 271 Operation::Force => { 272 let mut value = stack 273 .pop() 274 .expect("force must not be called on an empty stack"); 275 276 while let Value::Thunk(thunk) = value { 277 Box::pin(thunk.force(state)).await; 278 279 let (scope_new, value_new) = thunk 280 .get() 281 .await 282 .expect("thunk must contain value after forcing"); 283 284 value = value_new; 285 scopes = scope_new.unwrap_or(scopes); 286 } 287 288 stack.push(value); 289 }, 290 Operation::ScopeStart => { 291 scopes = scopes.push_front(value::attributes::new! {}); 292 }, 293 Operation::ScopeEnd => { 294 scopes = scopes 295 .drop_first() 296 .expect("scope-end must not be called with no scopes"); 297 }, 298 Operation::ScopePush => { 299 stack.push(Value::from(scopes.first().expect(EXPECT_SCOPE).dupe())); 300 }, 301 Operation::ScopeSwap => { 302 let value = stack 303 .last_mut() 304 .expect("scope-swap must not be called on a empty stack"); 305 306 let &mut Value::Attributes(ref mut value) = value else { 307 *value = Value::from(Arc::from( 308 ThunkInner::NOT_ATTRIBUTES 309 .with(Dupe::dupe) 310 .append_trace(code.read_operation(index).0), 311 )); 312 continue; 313 }; 314 315 let mut scope = scopes.first().expect(EXPECT_SCOPE).dupe(); 316 mem::swap(&mut scope, value); 317 318 scopes = scopes.drop_first().expect(EXPECT_SCOPE).push_front(scope); 319 }, 320 Operation::Interpolate => todo!(), 321 Operation::Resolve => { 322 let reference = stack 323 .last_mut() 324 .expect("resolve must not be called on an empty stack"); 325 326 let &mut Value::Reference(ref identifier) = reference else { 327 unreachable!("resolve must be called on an identifier"); 328 }; 329 330 let value = scopes 331 .iter() 332 .find_map(|scope| scope.get(identifier)) 333 .duped() 334 .unwrap_or_else(|| { 335 Value::from(Arc::new( 336 value::Error::new(value::SString::from(&*format!( 337 "undefined value: '{identifier}'", 338 identifier = &**identifier, 339 ))) 340 .append_trace(code.read_operation(index).0), 341 )) 342 }); 343 344 *reference = value; 345 }, 346 Operation::AssertBoolean => { 347 let value = stack 348 .last_mut() 349 .expect("assert-boolean must not be called on an empty stack"); 350 351 let &mut Value::Boolean(_) = value else { 352 *value = Value::from(Arc::from( 353 ThunkInner::NOT_BOOLEAN 354 .with(Dupe::dupe) 355 .append_trace(code.read_operation(index).0), 356 )); 357 continue; 358 }; 359 }, 360 Operation::Construct => { 361 let tail = stack 362 .pop() 363 .expect("construct must be called on a stack with 2 items or more"); 364 let head = stack 365 .pop() 366 .expect("construct must be called on a stack with 2 items or more"); 367 368 stack.push(Value::from(Arc::new(value::Cons(head, tail)))); 369 }, 370 Operation::Call => { 371 let argument = stack.pop().expect("call must not be called on empty stack"); 372 373 let Value::Thunk(thunk) = 374 stack.pop().expect("call must not be called on empty stack") 375 else { 376 stack.push(Value::from(Arc::from( 377 ThunkInner::NOT_LAMBDA 378 .with(Dupe::dupe) 379 .append_trace(code.read_operation(index).0), 380 ))); 381 continue; 382 }; 383 384 let thunk = thunk.argument(argument).await; 385 386 stack.push(Value::from(thunk)); 387 }, 388 Operation::Equal => { 389 let right = stack 390 .pop() 391 .expect("equal must be called on a stack with 2 items or more"); 392 let left = stack 393 .pop() 394 .expect("equal must be called on a stack with 2 items or more"); 395 396 // TODO: Not sure about the design here. 397 let (equal, binds) = Value::equals(&left, &right); 398 399 stack.push(Value::from(equal)); 400 scopes = scopes 401 .drop_first() 402 .expect("equal must be called with a scope") 403 .push_front( 404 scopes 405 .first() 406 .expect("equal must be called with a scope") 407 .merge(&binds), 408 ); 409 }, 410 Operation::All => todo!(), 411 Operation::Any => todo!(), 412 } 413 } 414 415 let len = stack.len(); 416 let Ok([value]) = <[_; 1]>::try_from(stack) else { 417 unreachable!("stack must have exactly one item left, has {len}"); 418 }; 419 420 ThunkInner::Evaluated { 421 scopagate: Some(scopes), 422 value, 423 } 424 }, 425 }; 426 427 *self.0.write().await = new; 428 } 429 }