• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mattwparas / steel / 17772690385

16 Sep 2025 04:36PM UTC coverage: 43.331% (+0.04%) from 43.289%
17772690385

Pull #519

github

web-flow
Merge 98d4fd22c into 3c10433b9
Pull Request #519: fix a bunch more clippy lints

56 of 123 new or added lines in 30 files covered. (45.53%)

8 existing lines in 3 files now uncovered.

12416 of 28654 relevant lines covered (43.33%)

2985398.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

35.16
/crates/steel-core/src/rvals.rs
1
pub mod cycles;
2

3
use crate::{
4
    gc::{
5
        shared::{
6
            MappedScopedReadContainer, MappedScopedWriteContainer, ScopedReadContainer,
7
            ScopedWriteContainer, ShareableMut,
8
        },
9
        unsafe_erased_pointers::{OpaqueReference, TemporaryMutableView, TemporaryReadonlyView},
10
        Gc, GcMut,
11
    },
12
    parser::{
13
        ast::{self, Atom, ExprKind},
14
        parser::SyntaxObject,
15
        span::Span,
16
        tokens::TokenType,
17
    },
18
    primitives::numbers::realp,
19
    rerrs::{ErrorKind, SteelErr},
20
    steel_vm::vm::{
21
        threads::closure_into_serializable, BuiltInSignature, Continuation, ContinuationMark,
22
    },
23
    values::{
24
        closed::{Heap, HeapRef, MarkAndSweepContext},
25
        functions::{BoxedDynFunction, ByteCodeLambda},
26
        lazy_stream::LazyStream,
27
        port::{SendablePort, SteelPort},
28
        structs::{SerializableUserDefinedStruct, UserDefinedStruct},
29
        transducers::{Reducer, Transducer},
30
        HashMapConsumingIter, HashSetConsumingIter, SteelPortRepr, VectorConsumingIter,
31
    },
32
};
33
use std::vec::IntoIter;
34
use std::{
35
    any::{Any, TypeId},
36
    cell::RefCell,
37
    cmp::Ordering,
38
    convert::TryInto,
39
    fmt,
40
    future::Future,
41
    hash::{Hash, Hasher},
42
    io::Write,
43
    ops::Deref,
44
    pin::Pin,
45
    rc::Rc,
46
    result,
47
    sync::{Arc, Mutex},
48
    task::Context,
49
};
50

51
// TODO
52
#[macro_export]
53
macro_rules! list {
54
    () => { $crate::rvals::SteelVal::ListV(
55
        im_lists::list![]
56
    ) };
57

58
    ( $($x:expr),* ) => {{
59
        $crate::rvals::SteelVal::ListV(vec![$(
60
            $crate::rvals::IntoSteelVal::into_steelval($x).unwrap()
61
        ), *].into())
62
    }};
63

64
    ( $($x:expr ,)* ) => {{
65
        $crate::rvals::SteelVal::ListV(im_lists::list![$(
66
            $crate::rvals::IntoSteelVal::into_steelval($x).unwrap()
67
        )*])
68
    }};
69
}
70

71
use bigdecimal::BigDecimal;
72
use parking_lot::RwLock;
73
use smallvec::SmallVec;
74
use SteelVal::*;
75

76
use crate::values::{HashMap, HashSet, Vector};
77

78
use futures_task::noop_waker_ref;
79
use futures_util::future::Shared;
80
use futures_util::FutureExt;
81

82
use crate::values::lists::List;
83
use num_bigint::{BigInt, ToBigInt};
84
use num_rational::{BigRational, Rational32};
85
use num_traits::{FromPrimitive, Signed, ToPrimitive, Zero};
86
use steel_parser::tokens::{IntLiteral, RealLiteral};
87

88
use self::cycles::{CycleDetector, IterativeDropHandler};
89

90
pub type RcRefSteelVal = Rc<RefCell<SteelVal>>;
91
pub fn new_rc_ref_cell(x: SteelVal) -> RcRefSteelVal {
×
92
    Rc::new(RefCell::new(x))
×
93
}
94

95
pub type Result<T> = result::Result<T, SteelErr>;
96
pub type FunctionSignature = fn(&[SteelVal]) -> Result<SteelVal>;
97
pub type MutFunctionSignature = fn(&mut [SteelVal]) -> Result<SteelVal>;
98

99
#[cfg(not(feature = "sync"))]
100
pub type BoxedAsyncFunctionSignature =
101
    crate::gc::Shared<Box<dyn Fn(&[SteelVal]) -> Result<FutureResult>>>;
102

103
#[cfg(feature = "sync")]
104
pub type BoxedAsyncFunctionSignature =
105
    crate::gc::Shared<Box<dyn Fn(&[SteelVal]) -> Result<FutureResult> + Send + Sync + 'static>>;
106

107
pub type AsyncSignature = fn(&[SteelVal]) -> FutureResult;
108

109
#[cfg(not(feature = "sync"))]
110
pub type BoxedFutureResult = Pin<Box<dyn Future<Output = Result<SteelVal>>>>;
111

112
#[cfg(feature = "sync")]
113
pub type BoxedFutureResult = Pin<Box<dyn Future<Output = Result<SteelVal>> + Send + 'static>>;
114

115
// TODO: Why can't I put sync here?
116
// #[cfg(feature = "sync")]
117
// pub type BoxedFutureResult = Pin<Box<dyn Future<Output = Result<SteelVal>> + Send + 'static>>;
118

119
#[derive(Clone)]
120
pub struct FutureResult(Shared<BoxedFutureResult>);
121

122
impl FutureResult {
123
    pub fn new(fut: BoxedFutureResult) -> Self {
11✔
124
        FutureResult(fut.shared())
11✔
125
    }
126

127
    pub fn into_shared(self) -> Shared<BoxedFutureResult> {
11✔
128
        self.0
11✔
129
    }
130
}
131

132
// This is an attempt to one off poll a future
133
// This should enable us to use embedded async functions
134
// Will require using call/cc w/ a thread queue in steel, however it should be possible
135
pub(crate) fn poll_future(mut fut: Shared<BoxedFutureResult>) -> Option<Result<SteelVal>> {
11✔
136
    // If the future has already been awaited (by somebody) get that value instead
137
    if let Some(output) = fut.peek() {
11✔
138
        return Some(output.clone());
139
    }
140

141
    // Otherwise, go ahead and poll the value to see if its ready
142
    // The context is going to exist exclusively in Steel, hidden behind an `await`
143
    let waker = noop_waker_ref();
22✔
144
    let context = &mut Context::from_waker(waker);
22✔
145

146
    // Polling requires a pinned future - TODO make sure this is correct
147
    let mut_fut = Pin::new(&mut fut);
33✔
148

149
    match Future::poll(mut_fut, context) {
22✔
150
        std::task::Poll::Ready(r) => Some(r),
11✔
151
        std::task::Poll::Pending => None,
×
152
    }
153
}
154

155
/// Attempt to cast this custom type down to the underlying type
156
pub fn as_underlying_type<T: 'static>(value: &dyn CustomType) -> Option<&T> {
324,919✔
157
    value.as_any_ref().downcast_ref::<T>()
649,838✔
158
}
159

160
pub fn as_underlying_type_mut<T: 'static>(value: &mut dyn CustomType) -> Option<&mut T> {
×
161
    value.as_any_ref_mut().downcast_mut::<T>()
×
162
}
163

164
pub trait Custom: private::Sealed {
165
    fn fmt(&self) -> Option<std::result::Result<String, std::fmt::Error>> {
×
166
        None
×
167
    }
168

169
    #[cfg(feature = "dylibs")]
170
    fn fmt_ffi(&self) -> Option<abi_stable::std_types::RString> {
×
171
        None
×
172
    }
173

174
    fn into_serializable_steelval(&mut self) -> Option<SerializableSteelVal> {
×
175
        None
×
176
    }
177

178
    fn as_iterator(&self) -> Option<Box<dyn Iterator<Item = SteelVal>>> {
×
179
        None
×
180
    }
181

182
    fn gc_drop_mut(&mut self, _drop_handler: &mut IterativeDropHandler) {}
×
183

184
    fn gc_visit_children(&self, _context: &mut MarkAndSweepContext) {}
28,268✔
185

186
    fn visit_equality(&self, _visitor: &mut cycles::EqualityVisitor) {}
×
187

188
    fn equality_hint(&self, _other: &dyn CustomType) -> bool {
×
189
        true
×
190
    }
191

192
    fn equality_hint_general(&self, _other: &SteelVal) -> bool {
1✔
193
        false
1✔
194
    }
195

196
    #[doc(hidden)]
197
    fn into_error(self) -> std::result::Result<SteelErr, Self>
×
198
    where
199
        Self: Sized,
200
    {
201
        Err(self)
×
202
    }
203
}
204

205
#[cfg(not(feature = "sync"))]
206
pub trait MaybeSendSyncStatic: 'static {}
207

208
#[cfg(not(feature = "sync"))]
209
impl<T: 'static> MaybeSendSyncStatic for T {}
210

211
#[cfg(feature = "sync")]
212
pub trait MaybeSendSyncStatic: Send + Sync + 'static {}
213

214
#[cfg(feature = "sync")]
215
impl<T: Send + Sync + 'static> MaybeSendSyncStatic for T {}
216

217
#[cfg(feature = "sync")]
218
pub trait CustomType: MaybeSendSyncStatic {
219
    fn as_any_ref(&self) -> &dyn Any;
220
    fn as_any_ref_mut(&mut self) -> &mut dyn Any;
221
    fn name(&self) -> &str {
×
222
        std::any::type_name::<Self>()
×
223
    }
224
    fn inner_type_id(&self) -> TypeId;
225
    fn display(&self) -> std::result::Result<String, std::fmt::Error> {
×
226
        Ok(format!("#<{}>", self.name()))
×
227
    }
228
    fn as_serializable_steelval(&mut self) -> Option<SerializableSteelVal> {
×
229
        None
×
230
    }
231
    fn drop_mut(&mut self, _drop_handler: &mut IterativeDropHandler) {}
×
232
    fn visit_children(&self, _context: &mut MarkAndSweepContext) {}
×
233
    // TODO: Add this back at some point
234
    // fn visit_children_ref_queue(&self, _context: &mut MarkAndSweepContextRefQueue) {}
235
    fn visit_children_for_equality(&self, _visitor: &mut cycles::EqualityVisitor) {}
×
236
    fn check_equality_hint(&self, _other: &dyn CustomType) -> bool {
×
237
        true
×
238
    }
239
    fn check_equality_hint_general(&self, _other: &SteelVal) -> bool {
×
240
        false
×
241
    }
242

243
    #[doc(hidden)]
244
    fn into_error_(self) -> std::result::Result<SteelErr, Self>
×
245
    where
246
        Self: Sized,
247
    {
248
        Err(self)
×
249
    }
250
}
251

252
#[cfg(not(feature = "sync"))]
253
pub trait CustomType {
254
    fn as_any_ref(&self) -> &dyn Any;
255
    fn as_any_ref_mut(&mut self) -> &mut dyn Any;
256
    fn name(&self) -> &str {
×
257
        std::any::type_name::<Self>()
×
258
    }
259
    fn inner_type_id(&self) -> TypeId;
260
    fn display(&self) -> std::result::Result<String, std::fmt::Error> {
×
261
        Ok(format!("#<{}>", self.name()))
×
262
    }
263
    fn as_serializable_steelval(&mut self) -> Option<SerializableSteelVal> {
×
264
        None
×
265
    }
266
    fn drop_mut(&mut self, _drop_handler: &mut IterativeDropHandler) {}
267
    fn visit_children(&self, _context: &mut MarkAndSweepContext) {}
268
    fn visit_children_for_equality(&self, _visitor: &mut cycles::EqualityVisitor) {}
269
    fn check_equality_hint(&self, _other: &dyn CustomType) -> bool {
×
270
        true
×
271
    }
272
    fn check_equality_hint_general(&self, _other: &SteelVal) -> bool {
×
273
        false
×
274
    }
275
    #[doc(hidden)]
276
    fn into_error_(self) -> std::result::Result<SteelErr, Self>
277
    where
278
        Self: Sized,
279
    {
280
        Err(self)
×
281
    }
282
}
283

284
impl<T: Custom + MaybeSendSyncStatic> CustomType for T {
285
    fn as_any_ref(&self) -> &dyn Any {
364,560✔
286
        self as &dyn Any
364,560✔
287
    }
288
    fn as_any_ref_mut(&mut self) -> &mut dyn Any {
22,563✔
289
        self as &mut dyn Any
22,563✔
290
    }
291
    fn inner_type_id(&self) -> TypeId {
×
292
        std::any::TypeId::of::<Self>()
×
293
    }
294
    fn display(&self) -> std::result::Result<String, std::fmt::Error> {
×
295
        if let Some(formatted) = self.fmt() {
×
296
            formatted
×
297
        } else {
298
            Ok(format!("#<{}>", self.name()))
×
299
        }
300
    }
301

302
    fn as_serializable_steelval(&mut self) -> Option<SerializableSteelVal> {
×
303
        <T as Custom>::into_serializable_steelval(self)
×
304
    }
305

306
    fn drop_mut(&mut self, drop_handler: &mut IterativeDropHandler) {
×
307
        self.gc_drop_mut(drop_handler)
×
308
    }
309

310
    fn visit_children(&self, context: &mut MarkAndSweepContext) {
14,134✔
311
        self.gc_visit_children(context)
42,402✔
312
    }
313

314
    // TODO: Equality visitor
315
    fn visit_children_for_equality(&self, visitor: &mut cycles::EqualityVisitor) {
×
316
        self.visit_equality(visitor)
×
317
    }
318

319
    fn check_equality_hint(&self, other: &dyn CustomType) -> bool {
×
320
        self.equality_hint(other)
×
321
    }
322

323
    fn check_equality_hint_general(&self, other: &SteelVal) -> bool {
1✔
324
        self.equality_hint_general(other)
3✔
325
    }
326

327
    fn into_error_(self) -> std::result::Result<SteelErr, Self>
×
328
    where
329
        Self: Sized,
330
    {
331
        self.into_error()
×
332
    }
333
}
334

335
impl<T: CustomType + 'static> IntoSteelVal for T {
336
    fn into_steelval(self) -> Result<SteelVal> {
15,360✔
337
        Ok(SteelVal::Custom(Gc::new_mut(Box::new(self))))
30,720✔
338
    }
339

340
    fn as_error(self) -> std::result::Result<SteelErr, Self> {
×
341
        T::into_error_(self)
×
342
    }
343
}
344

345
pub trait IntoSerializableSteelVal {
346
    fn into_serializable_steelval(val: &SteelVal) -> Result<SerializableSteelVal>;
347
}
348

349
impl<T: CustomType + Clone + Send + Sync + 'static> IntoSerializableSteelVal for T {
350
    fn into_serializable_steelval(val: &SteelVal) -> Result<SerializableSteelVal> {
×
351
        if let SteelVal::Custom(v) = val {
×
352
            let left = v.read().as_any_ref().downcast_ref::<T>().cloned();
×
353
            let _lifted = left.ok_or_else(|| {
×
354
                let error_message = format!(
×
355
                    "Type Mismatch: Type of SteelVal: {:?}, did not match the given type: {}",
×
356
                    val,
×
357
                    std::any::type_name::<Self>()
×
358
                );
359
                SteelErr::new(ErrorKind::ConversionError, error_message)
×
360
            });
361

362
            todo!()
363
        } else {
364
            let error_message = format!(
×
365
                "Type Mismatch: Type of SteelVal: {:?} did not match the given type, expecting opaque struct: {}",
366
                val,
×
367
                std::any::type_name::<Self>()
×
368
            );
369

370
            Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
371
        }
372
    }
373
}
374

375
// TODO: Marshalling out of the type could also try to yoink from a native steel struct.
376
// If possible, we can try to line the constructor up with the fields
377
impl<T: CustomType + Clone + 'static> FromSteelVal for T {
378
    fn from_steelval(val: &SteelVal) -> Result<Self> {
5✔
379
        if let SteelVal::Custom(v) = val {
10✔
380
            // let left_type = v.borrow().as_any_ref();
381
            // TODO: @Matt - dylibs cause issues here, as the underlying type ids are different
382
            // across workspaces and builds
383
            let left = v.read().as_any_ref().downcast_ref::<T>().cloned();
×
384
            left.ok_or_else(|| {
×
385
                let error_message = format!(
×
386
                    "Type Mismatch: Type of SteelVal: {:?}, did not match the given type: {}",
×
387
                    val,
×
388
                    std::any::type_name::<Self>()
×
389
                );
390
                SteelErr::new(ErrorKind::ConversionError, error_message)
×
391
            })
392
        } else {
393
            let error_message = format!(
×
394
                "Type Mismatch: Type of SteelVal: {:?} did not match the given type, expecting opaque struct: {}",
395
                val,
×
396
                std::any::type_name::<Self>()
×
397
            );
398

399
            Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
400
        }
401
    }
402
}
403

404
/// The entry point for turning values into SteelVals
405
/// The is implemented for most primitives and collections
406
/// You can also manually implement this for any type, or can optionally
407
/// get this implementation for a custom struct by using the custom
408
/// steel derive.
409
pub trait IntoSteelVal: Sized {
410
    fn into_steelval(self) -> Result<SteelVal>;
411

412
    #[doc(hidden)]
413
    fn as_error(self) -> std::result::Result<SteelErr, Self> {
×
414
        Err(self)
×
415
    }
416
}
417

418
/// The exit point for turning SteelVals into outside world values
419
/// This is implement for most primitives and collections
420
/// You can also manually implement this for any type, or can optionally
421
/// get this implementation for a custom struct by using the custom
422
/// steel derive.
423
pub trait FromSteelVal: Sized {
424
    fn from_steelval(val: &SteelVal) -> Result<Self>;
425
}
426

427
pub trait PrimitiveAsRef<'a>: Sized {
428
    fn primitive_as_ref(val: &'a SteelVal) -> Result<Self>;
429
    fn maybe_primitive_as_ref(val: &'a SteelVal) -> Option<Self>;
430
}
431

432
pub trait PrimitiveAsRefMut<'a>: Sized {
433
    fn primitive_as_ref(val: &'a mut SteelVal) -> Result<Self>;
434
    fn maybe_primitive_as_ref(val: &'a mut SteelVal) -> Option<Self>;
435
}
436

437
pub struct RestArgsIter<'a, T>(
438
    pub std::iter::Map<std::slice::Iter<'a, SteelVal>, fn(&'a SteelVal) -> Result<T>>,
439
);
440

441
impl<'a, T: PrimitiveAsRef<'a> + 'a> RestArgsIter<'a, T> {
442
    pub fn new(
×
443
        args: std::iter::Map<std::slice::Iter<'a, SteelVal>, fn(&'a SteelVal) -> Result<T>>,
444
    ) -> Self {
445
        RestArgsIter(args)
×
446
    }
447

448
    pub fn from_slice(args: &'a [SteelVal]) -> Result<Self> {
72,067,772✔
449
        Ok(RestArgsIter(args.iter().map(T::primitive_as_ref)))
216,203,316✔
450
    }
451
}
452

453
impl<'a, T> Iterator for RestArgsIter<'a, T> {
454
    type Item = Result<T>;
455

456
    fn next(&mut self) -> Option<Self::Item> {
148,834,191✔
457
        self.0.next()
297,668,382✔
458
    }
459

460
    fn size_hint(&self) -> (usize, Option<usize>) {
2,860✔
461
        self.0.size_hint()
5,720✔
462
    }
463
}
464

465
impl<'a, T> ExactSizeIterator for RestArgsIter<'a, T> {}
466

467
pub struct RestArgs<T: FromSteelVal>(pub Vec<T>);
468

469
impl<T: FromSteelVal> RestArgs<T> {
470
    pub fn new(args: Vec<T>) -> Self {
×
471
        RestArgs(args)
×
472
    }
473

474
    pub fn from_slice(args: &[SteelVal]) -> Result<Self> {
×
475
        args.iter()
×
476
            .map(|x| T::from_steelval(x))
×
477
            .collect::<Result<Vec<_>>>()
478
            .map(RestArgs)
×
479
    }
480
}
481

482
impl<T: FromSteelVal> std::ops::Deref for RestArgs<T> {
483
    type Target = [T];
484

485
    fn deref(&self) -> &Self::Target {
×
486
        &self.0
×
487
    }
488
}
489

490
mod private {
491

492
    use std::any::Any;
493

494
    pub trait Sealed {}
495

496
    impl<T: Any> Sealed for T {}
497
}
498

499
pub enum SRef<'b, T: ?Sized + 'b> {
500
    Temporary(&'b T),
501
    Owned(MappedScopedReadContainer<'b, T>),
502
}
503

504
impl<'b, T: ?Sized + 'b> Deref for SRef<'b, T> {
505
    type Target = T;
506

507
    #[inline]
508
    fn deref(&self) -> &T {
46,895✔
509
        match self {
46,895✔
510
            SRef::Temporary(inner) => inner,
7,259✔
511
            SRef::Owned(inner) => inner,
79,272✔
512
        }
513
    }
514
}
515

516
// Can you take a steel val and execute operations on it by reference
517
pub trait AsRefSteelVal: Sized {
518
    type Nursery: Default;
519

520
    fn as_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<SRef<'b, Self>>;
521
}
522

523
pub trait AsSlice<T> {
524
    fn as_slice_repr(&self) -> &[T];
525
}
526

527
impl<T> AsSlice<T> for Vec<T> {
528
    fn as_slice_repr(&self) -> &[T] {
×
529
        self.as_slice()
×
530
    }
531
}
532

533
// TODO: Try to incorporate these all into one trait if possible
534
pub trait AsRefSteelValFromUnsized<T>: Sized {
535
    type Output: AsSlice<T>;
536

537
    fn as_ref_from_unsized(val: &SteelVal) -> Result<Self::Output>;
538
}
539

540
pub trait AsRefMutSteelVal: Sized {
541
    fn as_mut_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<MappedScopedWriteContainer<'b, Self>>;
542
}
543

544
pub(crate) trait AsRefMutSteelValFromRef: Sized {
545
    fn as_mut_ref_from_ref<'a>(
546
        val: &'a SteelVal,
547
    ) -> crate::rvals::Result<TemporaryMutableView<Self>>;
548
}
549

550
pub(crate) trait AsRefSteelValFromRef: Sized {
551
    fn as_ref_from_ref<'a>(val: &'a SteelVal) -> crate::rvals::Result<TemporaryReadonlyView<Self>>;
552
}
553

554
impl AsRefSteelVal for UserDefinedStruct {
555
    type Nursery = ();
556

557
    fn as_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<SRef<'b, Self>> {
42✔
558
        if let SteelVal::CustomStruct(l) = val {
84✔
559
            Ok(SRef::Temporary(l))
×
560
        } else {
561
            stop!(TypeMismatch => "Value cannot be referenced as a list")
×
562
        }
563
    }
564
}
565

566
impl<T: CustomType + MaybeSendSyncStatic> AsRefSteelVal for T {
567
    type Nursery = ();
568

569
    fn as_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<SRef<'b, Self>> {
39,636✔
570
        if let SteelVal::Custom(v) = val {
79,272✔
571
            let res = ScopedReadContainer::map(v.read(), |x| x.as_any_ref());
79,272✔
572

573
            if res.is::<T>() {
×
574
                Ok(SRef::Owned(MappedScopedReadContainer::map(res, |x| {
118,908✔
575
                    x.downcast_ref::<T>().unwrap()
79,272✔
576
                })))
577
            } else {
578
                let error_message = format!(
×
579
                    "Type Mismatch: Type of SteelVal: {} did not match the given type: {}",
580
                    val,
×
581
                    std::any::type_name::<Self>()
×
582
                );
583
                Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
584
            }
585
            // res
586
        } else {
587
            let error_message = format!(
×
588
                "Type Mismatch: Type of SteelVal: {} did not match the given type: {}",
589
                val,
×
590
                std::any::type_name::<Self>()
×
591
            );
592

593
            Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
594
        }
595
    }
596
}
597

598
impl<T: CustomType + MaybeSendSyncStatic> AsRefMutSteelVal for T {
599
    fn as_mut_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<MappedScopedWriteContainer<'b, Self>> {
22,563✔
600
        if let SteelVal::Custom(v) = val {
45,126✔
601
            let res = ScopedWriteContainer::map(v.write(), |x| x.as_any_ref_mut());
45,126✔
602

603
            if res.is::<T>() {
×
604
                Ok(MappedScopedWriteContainer::map(res, |x| {
67,689✔
605
                    x.downcast_mut::<T>().unwrap()
45,126✔
606
                }))
607
            } else {
608
                let error_message = format!(
×
609
                    "Type Mismatch: Type of SteelVal: {} did not match the given type: {}",
610
                    val,
×
611
                    std::any::type_name::<Self>()
×
612
                );
613
                Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
614
            }
615
            // res
616
        } else {
617
            let error_message = format!(
×
618
                "Type Mismatch: Type of SteelVal: {} did not match the given type: {}",
619
                val,
×
620
                std::any::type_name::<Self>()
×
621
            );
622

623
            Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
624
        }
625
    }
626
}
627

628
impl ast::TryFromSteelValVisitorForExprKind {
629
    pub fn visit_syntax_object(&mut self, value: &Syntax) -> Result<ExprKind> {
2,537✔
630
        let span = value.span;
5,074✔
631

632
        // dbg!(&span);
633
        // let source = self.source.clone();
634
        match &value.syntax {
596✔
635
            // Mutual recursion case
636
            SyntaxObject(s) => self.visit_syntax_object(s),
8✔
637
            BoolV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
5✔
638
                TokenType::BooleanLiteral(*x),
639
                span,
640
            )))),
641
            NumV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
642
                RealLiteral::Float(*x).into(),
×
643
                span,
×
644
            )))),
645
            IntV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
484✔
646
                RealLiteral::Int(IntLiteral::Small(*x)).into(),
242✔
647
                span,
121✔
648
            )))),
649
            VectorV(lst) => {
×
650
                let items: Result<Vec<ExprKind>> = lst.iter().map(|x| self.visit(x)).collect();
×
651
                Ok(ExprKind::List(crate::parser::ast::List::new(items?)))
×
652
            }
653
            StringV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
32✔
654
                TokenType::StringLiteral(x.to_arc_string()),
8✔
655
                span,
8✔
656
            )))),
657

658
            SymbolV(x) if x.starts_with("#:") => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
1,788✔
659
                TokenType::Keyword(x.as_str().into()),
×
660
                span,
×
661
            )))),
662

663
            SymbolV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
596✔
664
                TokenType::Identifier(x.as_str().into()),
665
                span,
666
            )))),
667

668
            ListV(l) => {
1,805✔
669
                // Rooted - things operate as normal
670
                if self.qq_depth == 0 {
1,805✔
671
                    let maybe_special_form = l.first().and_then(|x| {
9,016✔
672
                        x.as_symbol()
3,592✔
673
                            .or_else(|| x.as_syntax_object().and_then(|x| x.syntax.as_symbol()))
3,706✔
674
                    });
675

676
                    match maybe_special_form {
1,785✔
677
                        Some(x) if x.as_str() == "quote" => {
1,829✔
678
                            if self.quoted {
22✔
679
                                let items: std::result::Result<Vec<ExprKind>, _> =
×
680
                                    l.iter().map(|x| self.visit(x)).collect();
×
681

682
                                return Ok(ExprKind::List(ast::List::new(items?)));
×
683
                            }
684

685
                            self.quoted = true;
686

687
                            let return_value = l
22✔
688
                                .into_iter()
689
                                .map(|x| self.visit(x))
132✔
690
                                .collect::<std::result::Result<Vec<_>, _>>()?
691
                                .try_into()?;
692

693
                            self.quoted = false;
694

695
                            return Ok(return_value);
696
                        } // "quasiquote" => {
697
                        //     self.qq_depth += 1;
698
                        // }
699
                        // None => {
700
                        // return Ok(ExprKind::empty());
701
                        // }
702
                        _ => {}
1,783✔
703
                    }
704
                }
705

706
                Ok(l.into_iter()
1,783✔
707
                    .map(|x| self.visit(x))
39,990✔
708
                    .collect::<std::result::Result<Vec<_>, _>>()?
×
709
                    .try_into()?)
1,783✔
710
            }
711

712
            CharV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
713
                TokenType::CharacterLiteral(*x),
×
714
                span,
×
715
            )))),
716
            _ => stop!(ConversionError => "unable to convert {:?} to expression", &value.syntax),
×
717
        }
718
    }
719
}
720

721
#[derive(Debug, Clone)]
722
pub struct Syntax {
723
    pub(crate) raw: Option<SteelVal>,
724
    pub(crate) syntax: SteelVal,
725
    span: Span,
726
}
727

728
impl Syntax {
729
    pub fn new(syntax: SteelVal, span: Span) -> Syntax {
1,418✔
730
        Self {
731
            raw: None,
732
            syntax,
733
            span,
734
        }
735
    }
736

737
    pub fn proto(raw: SteelVal, syntax: SteelVal, span: Span) -> Syntax {
16,293✔
738
        Self {
739
            raw: Some(raw),
32,586✔
740
            syntax,
741
            span,
742
        }
743
    }
744

745
    pub fn syntax_e(&self) -> SteelVal {
1,841✔
746
        self.syntax.clone()
3,682✔
747
    }
748

749
    pub fn new_with_source(syntax: SteelVal, span: Span) -> Syntax {
×
750
        Self {
751
            raw: None,
752
            syntax,
753
            span,
754
        }
755
    }
756

757
    pub fn syntax_loc(&self) -> Span {
3✔
758
        self.span
3✔
759
    }
760

761
    pub fn syntax_datum(&self) -> SteelVal {
3,958✔
762
        self.raw.clone().unwrap()
11,874✔
763
    }
764

765
    pub(crate) fn steelval_to_exprkind(value: &SteelVal) -> Result<ExprKind> {
×
766
        match value {
×
767
            // Mutual recursion case
768
            SyntaxObject(s) => s.to_exprkind(),
×
769
            BoolV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::default(
×
770
                TokenType::BooleanLiteral(*x),
771
            )))),
772
            NumV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::default(
×
773
                RealLiteral::Float(*x).into(),
×
774
            )))),
775
            IntV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::default(
×
776
                RealLiteral::Int(IntLiteral::Small(*x)).into(),
×
777
            )))),
778
            VectorV(lst) => {
×
779
                let items: Result<Vec<ExprKind>> =
×
780
                    lst.iter().map(Self::steelval_to_exprkind).collect();
×
781
                Ok(ExprKind::List(crate::parser::ast::List::new(items?)))
×
782
            }
783
            StringV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::default(
×
784
                TokenType::StringLiteral(x.to_arc_string()),
×
785
            )))),
786
            // LambdaV(_) => Err("Can't convert from Lambda to expression!"),
787
            // MacroV(_) => Err("Can't convert from Macro to expression!"),
788
            SymbolV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::default(
×
789
                TokenType::Identifier(x.as_str().into()),
×
790
            )))),
791
            ListV(l) => {
×
792
                let items: Result<Vec<ExprKind>> =
×
793
                    l.iter().map(Self::steelval_to_exprkind).collect();
×
794

795
                Ok(ExprKind::List(crate::parser::ast::List::new(items?)))
×
796
            }
797
            CharV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::default(
×
798
                TokenType::CharacterLiteral(*x),
×
799
            )))),
800
            _ => stop!(ConversionError => "unable to convert {:?} to expression", value),
×
801
        }
802
    }
803

804
    // TODO: match on self.syntax. If its itself a syntax object, then just recur on that until we bottom out
805
    // Otherwise, reconstruct the ExprKind and replace the span and source information into the representation
806
    pub fn to_exprkind(&self) -> Result<ExprKind> {
×
807
        let span = self.span;
×
808
        // let source = self.source.clone();
809
        match &self.syntax {
×
810
            // Mutual recursion case
811
            SyntaxObject(s) => s.to_exprkind(),
×
812
            BoolV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
813
                TokenType::BooleanLiteral(*x),
814
                span,
815
            )))),
816
            NumV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
817
                RealLiteral::Float(*x).into(),
×
818
                span,
×
819
            )))),
820
            IntV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
821
                RealLiteral::Int(IntLiteral::Small(*x)).into(),
×
822
                span,
×
823
            )))),
824
            VectorV(lst) => {
×
825
                let items: Result<Vec<ExprKind>> =
×
826
                    lst.iter().map(Self::steelval_to_exprkind).collect();
×
827
                Ok(ExprKind::List(crate::parser::ast::List::new(items?)))
×
828
            }
829
            StringV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
830
                TokenType::StringLiteral(x.to_arc_string()),
×
831
                span,
×
832
            )))),
833
            // LambdaV(_) => Err("Can't convert from Lambda to expression!"),
834
            // MacroV(_) => Err("Can't convert from Macro to expression!"),
835
            SymbolV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
836
                TokenType::Identifier(x.as_str().into()),
×
837
                span,
×
838
            )))),
839
            ListV(l) => {
×
840
                let items: Result<Vec<ExprKind>> =
×
841
                    l.iter().map(Self::steelval_to_exprkind).collect();
×
842

843
                Ok(ExprKind::List(crate::parser::ast::List::new(items?)))
×
844
            }
845
            CharV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
846
                TokenType::CharacterLiteral(*x),
×
847
                span,
×
848
            )))),
849
            _ => stop!(ConversionError => "unable to convert {:?} to expression", &self.syntax),
×
850
        }
851
    }
852
}
853

854
impl IntoSteelVal for Syntax {
855
    fn into_steelval(self) -> Result<SteelVal> {
1,605✔
856
        Ok(SteelVal::SyntaxObject(Gc::new(self)))
1,605✔
857
    }
858
}
859

860
impl AsRefSteelVal for Syntax {
861
    type Nursery = ();
862

863
    fn as_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<SRef<'b, Self>> {
7,217✔
864
        if let SteelVal::SyntaxObject(s) = val {
14,434✔
865
            Ok(SRef::Temporary(s))
×
866
        } else {
867
            stop!(TypeMismatch => "Value cannot be referenced as a syntax object: {}", val)
×
868
        }
869
    }
870
}
871

872
impl From<Syntax> for SteelVal {
873
    fn from(val: Syntax) -> Self {
16,133✔
874
        SteelVal::SyntaxObject(Gc::new(val))
16,133✔
875
    }
876
}
877

878
// TODO:
879
// This needs to be a method on the runtime: in order to properly support
880
// threads
881
// Tracking issue here: https://github.com/mattwparas/steel/issues/98
882

883
// Values which can be sent to another thread.
884
// If it cannot be sent to another thread, then we'll error out on conversion.
885
// TODO: Add boxed dyn functions to this.
886
// #[derive(PartialEq)]
887
pub enum SerializableSteelVal {
888
    Closure(crate::values::functions::SerializedLambda),
889
    BoolV(bool),
890
    NumV(f64),
891
    IntV(isize),
892
    CharV(char),
893
    Void,
894
    StringV(String),
895
    FuncV(FunctionSignature),
896
    MutFunc(MutFunctionSignature),
897
    HashMapV(Vec<(SerializableSteelVal, SerializableSteelVal)>),
898
    ListV(Vec<SerializableSteelVal>),
899
    Pair(Box<(SerializableSteelVal, SerializableSteelVal)>),
900
    VectorV(Vec<SerializableSteelVal>),
901
    ByteVectorV(Vec<u8>),
902
    BoxedDynFunction(BoxedDynFunction),
903
    BuiltIn(BuiltInSignature),
904
    SymbolV(String),
905
    Custom(Box<dyn CustomType + Send>),
906
    CustomStruct(SerializableUserDefinedStruct),
907
    // Attempt to reuse the storage if possible
908
    HeapAllocated(usize),
909
    Port(SendablePort),
910
    Rational(Rational32),
911
}
912

913
pub enum SerializedHeapRef {
914
    Serialized(Option<SerializableSteelVal>),
915
    Closed(HeapRef<SteelVal>),
916
}
917

918
pub struct HeapSerializer<'a> {
919
    pub heap: &'a mut Heap,
920
    pub fake_heap: &'a mut std::collections::HashMap<usize, SerializedHeapRef>,
921
    // After the conversion, we go back through, and patch the values from the fake heap
922
    // in to each of the values listed here - otherwise, we'll miss cycles
923
    pub values_to_fill_in: &'a mut std::collections::HashMap<usize, HeapRef<SteelVal>>,
924

925
    // Cache the functions that get built
926
    pub built_functions: &'a mut std::collections::HashMap<u32, Gc<ByteCodeLambda>>,
927
}
928

929
// Once crossed over the line, convert BACK into a SteelVal
930
// This should be infallible.
931
pub fn from_serializable_value(ctx: &mut HeapSerializer, val: SerializableSteelVal) -> SteelVal {
×
932
    match val {
×
933
        SerializableSteelVal::Closure(c) => {
×
934
            if c.captures.is_empty() {
935
                if let Some(already_made) = ctx.built_functions.get(&c.id) {
×
936
                    SteelVal::Closure(already_made.clone())
937
                } else {
938
                    let id = c.id;
×
939
                    let value = Gc::new(ByteCodeLambda::from_serialized(ctx, c));
×
940

941
                    // Save those as well
942
                    // Probably need to just do this for all
943
                    ctx.built_functions.insert(id, value.clone());
×
944
                    SteelVal::Closure(value)
×
945
                }
946
            } else {
947
                SteelVal::Closure(Gc::new(ByteCodeLambda::from_serialized(ctx, c)))
×
948
            }
949
        }
950
        SerializableSteelVal::BoolV(b) => SteelVal::BoolV(b),
×
951
        SerializableSteelVal::NumV(n) => SteelVal::NumV(n),
×
952
        SerializableSteelVal::IntV(i) => SteelVal::IntV(i),
×
953
        SerializableSteelVal::CharV(c) => SteelVal::CharV(c),
×
954
        SerializableSteelVal::Void => SteelVal::Void,
×
955
        SerializableSteelVal::Rational(r) => SteelVal::Rational(r),
×
956
        SerializableSteelVal::StringV(s) => SteelVal::StringV(s.into()),
×
957
        SerializableSteelVal::FuncV(f) => SteelVal::FuncV(f),
×
958
        SerializableSteelVal::MutFunc(f) => SteelVal::MutFunc(f),
×
959
        SerializableSteelVal::HashMapV(h) => SteelVal::HashMapV(
960
            Gc::new(
×
961
                h.into_iter()
×
962
                    .map(|(k, v)| {
×
963
                        (
964
                            from_serializable_value(ctx, k),
×
965
                            from_serializable_value(ctx, v),
×
966
                        )
967
                    })
968
                    .collect::<HashMap<_, _>>(),
×
969
            )
970
            .into(),
×
971
        ),
972
        SerializableSteelVal::ListV(v) => SteelVal::ListV(
973
            v.into_iter()
×
974
                .map(|x| from_serializable_value(ctx, x))
×
975
                .collect(),
×
976
        ),
977
        SerializableSteelVal::VectorV(v) => SteelVal::VectorV(SteelVector(Gc::new(
×
978
            v.into_iter()
×
979
                .map(|x| from_serializable_value(ctx, x))
×
980
                .collect(),
×
981
        ))),
982
        SerializableSteelVal::BoxedDynFunction(f) => SteelVal::BoxedFunction(Gc::new(f)),
×
983
        SerializableSteelVal::BuiltIn(f) => SteelVal::BuiltIn(f),
×
984
        SerializableSteelVal::SymbolV(s) => SteelVal::SymbolV(s.into()),
×
985
        SerializableSteelVal::Custom(b) => SteelVal::Custom(Gc::new_mut(b)),
×
986
        SerializableSteelVal::CustomStruct(s) => {
×
987
            SteelVal::CustomStruct(Gc::new(UserDefinedStruct {
×
988
                fields: {
989
                    let fields = s
×
990
                        .fields
×
991
                        .into_iter()
×
992
                        .map(|x| from_serializable_value(ctx, x));
×
993

994
                    // fields.collect()
995

996
                    // let mut recycle: crate::values::recycler::Recycle<Vec<_>> =
997
                    //     crate::values::recycler::Recycle::new();
998

999
                    let mut recycle: crate::values::recycler::Recycle<SmallVec<_>> =
×
1000
                        crate::values::recycler::Recycle::new();
×
1001

1002
                    recycle.extend(fields);
×
1003

1004
                    recycle
×
1005
                },
1006
                type_descriptor: s.type_descriptor,
×
1007
            }))
1008
        }
1009
        SerializableSteelVal::Port(p) => SteelVal::PortV(SteelPort::from_sendable_port(p)),
×
1010
        SerializableSteelVal::HeapAllocated(v) => {
×
1011
            // todo!()
1012

1013
            if let Some(mut guard) = ctx.fake_heap.get_mut(&v) {
×
1014
                match &mut guard {
×
1015
                    SerializedHeapRef::Serialized(value) => {
×
1016
                        let value = std::mem::take(value);
1017

1018
                        if let Some(value) = value {
×
1019
                            let _ = from_serializable_value(ctx, value);
1020

1021
                            todo!()
1022
                            // let allocation = ctx.heap.allocate_without_collection(value);
1023

1024
                            // ctx.fake_heap
1025
                            //     .insert(v, SerializedHeapRef::Closed(allocation.clone()));
1026

1027
                            // SteelVal::HeapAllocated(allocation)
1028
                        } else {
1029
                            // println!("If we're getting here - it means the value from the heap has already
1030
                            // been converting. if so, we should do something...");
1031

1032
                            todo!()
1033

1034
                            // let fake_allocation =
1035
                            //     ctx.heap.allocate_without_collection(SteelVal::Void);
1036

1037
                            // ctx.values_to_fill_in.insert(v, fake_allocation.clone());
1038

1039
                            // SteelVal::HeapAllocated(fake_allocation)
1040
                        }
1041
                    }
1042

1043
                    SerializedHeapRef::Closed(c) => SteelVal::HeapAllocated(c.clone()),
×
1044
                }
1045
            } else {
1046
                // Shouldn't silently fail here, but we will... for now
1047

1048
                // let allocation = ctx.heap.allocate_without_collection(SteelVal::Void);
1049

1050
                // ctx.fake_heap
1051
                //     .insert(v, SerializedHeapRef::Closed(allocation.clone()));
1052

1053
                // SteelVal::HeapAllocated(allocation)
1054

1055
                todo!()
1056
            }
1057
        }
1058
        SerializableSteelVal::Pair(pair) => {
×
1059
            let (car, cdr) = *pair;
×
1060

1061
            crate::values::lists::Pair::cons(
1062
                from_serializable_value(ctx, car),
×
1063
                from_serializable_value(ctx, cdr),
×
1064
            )
1065
            .into()
1066
        }
1067
        SerializableSteelVal::ByteVectorV(bytes) => {
×
1068
            SteelVal::ByteVector(SteelByteVector::new(bytes))
×
1069
        }
1070
    }
1071
}
1072

1073
// The serializable value needs to refer to the original heap -
1074
// that way can reference the original stuff easily.
1075

1076
// TODO: Use the cycle detector instead
1077
pub fn into_serializable_value(
×
1078
    val: SteelVal,
1079
    serialized_heap: &mut std::collections::HashMap<usize, SerializableSteelVal>,
1080
    visited: &mut std::collections::HashSet<usize>,
1081
) -> Result<SerializableSteelVal> {
1082
    // dbg!(&serialized_heap);
1083

1084
    match val {
×
1085
        SteelVal::Closure(c) => closure_into_serializable(&c, serialized_heap, visited)
×
1086
            .map(SerializableSteelVal::Closure),
1087
        SteelVal::BoolV(b) => Ok(SerializableSteelVal::BoolV(b)),
×
1088
        SteelVal::NumV(n) => Ok(SerializableSteelVal::NumV(n)),
×
1089
        SteelVal::IntV(n) => Ok(SerializableSteelVal::IntV(n)),
×
1090
        SteelVal::CharV(c) => Ok(SerializableSteelVal::CharV(c)),
×
1091
        SteelVal::Void => Ok(SerializableSteelVal::Void),
×
1092
        SteelVal::StringV(s) => Ok(SerializableSteelVal::StringV(s.to_string())),
×
1093
        SteelVal::FuncV(f) => Ok(SerializableSteelVal::FuncV(f)),
×
1094
        SteelVal::ListV(l) => Ok(SerializableSteelVal::ListV(
×
1095
            l.into_iter()
×
1096
                .map(|x| into_serializable_value(x, serialized_heap, visited))
×
1097
                .collect::<Result<_>>()?,
×
1098
        )),
1099
        SteelVal::Pair(pair) => Ok(SerializableSteelVal::Pair(Box::new((
×
1100
            into_serializable_value(pair.car.clone(), serialized_heap, visited)?,
×
1101
            into_serializable_value(pair.cdr.clone(), serialized_heap, visited)?,
×
1102
        )))),
1103
        SteelVal::BoxedFunction(f) => Ok(SerializableSteelVal::BoxedDynFunction((*f).clone())),
×
1104
        SteelVal::BuiltIn(f) => Ok(SerializableSteelVal::BuiltIn(f)),
×
1105
        SteelVal::SymbolV(s) => Ok(SerializableSteelVal::SymbolV(s.to_string())),
×
1106
        SteelVal::MutFunc(f) => Ok(SerializableSteelVal::MutFunc(f)),
×
1107
        SteelVal::HashMapV(v) => Ok(SerializableSteelVal::HashMapV(
×
1108
            v.0.unwrap()
×
1109
                .into_iter()
×
1110
                .map(|(k, v)| {
×
1111
                    let kprime = into_serializable_value(k, serialized_heap, visited)?;
×
1112
                    let vprime = into_serializable_value(v, serialized_heap, visited)?;
×
1113

1114
                    Ok((kprime, vprime))
1115
                })
1116
                .collect::<Result<_>>()?,
×
1117
        )),
1118

1119
        SteelVal::Custom(c) => {
×
1120
            if let Some(output) = c.write().as_serializable_steelval() {
×
1121
                Ok(output)
1122
            } else {
1123
                stop!(Generic => "Custom type not allowed to be moved across threads!")
×
1124
            }
1125
        }
1126

1127
        SteelVal::CustomStruct(s) => Ok(SerializableSteelVal::CustomStruct(
×
1128
            SerializableUserDefinedStruct {
1129
                fields: s
×
1130
                    .fields
×
1131
                    .iter()
×
1132
                    .cloned()
×
1133
                    .map(|x| into_serializable_value(x, serialized_heap, visited))
×
1134
                    .collect::<Result<Vec<_>>>()?,
×
1135
                type_descriptor: s.type_descriptor,
×
1136
            },
1137
        )),
1138

1139
        SteelVal::PortV(p) => SendablePort::from_port(p).map(SerializableSteelVal::Port),
×
1140

1141
        // If there is a cycle, this could cause problems?
1142
        SteelVal::HeapAllocated(h) => {
×
1143
            // We should pick it up on the way back the recursion
1144
            if visited.contains(&h.as_ptr_usize())
×
1145
                && !serialized_heap.contains_key(&h.as_ptr_usize())
×
1146
            {
1147
                // println!("Already visited: {}", h.as_ptr_usize());
1148

1149
                Ok(SerializableSteelVal::HeapAllocated(h.as_ptr_usize()))
×
1150
            } else {
1151
                visited.insert(h.as_ptr_usize());
×
1152

1153
                if serialized_heap.contains_key(&h.as_ptr_usize()) {
1154
                    // println!("Already exists in map: {}", h.as_ptr_usize());
1155

1156
                    Ok(SerializableSteelVal::HeapAllocated(h.as_ptr_usize()))
×
1157
                } else {
1158
                    // println!("Trying to insert: {} @ {}", h.get(), h.as_ptr_usize());
1159

1160
                    let value = into_serializable_value(h.get(), serialized_heap, visited);
×
1161

1162
                    let value = match value {
×
1163
                        Ok(v) => v,
1164
                        Err(e) => {
×
1165
                            // println!("{}", e);
1166
                            return Err(e);
×
1167
                        }
1168
                    };
1169

1170
                    serialized_heap.insert(h.as_ptr_usize(), value);
1171

1172
                    // println!("Inserting: {}", h.as_ptr_usize());
1173

1174
                    Ok(SerializableSteelVal::HeapAllocated(h.as_ptr_usize()))
1175
                }
1176
            }
1177
        }
1178

1179
        SteelVal::VectorV(vector) => Ok(SerializableSteelVal::VectorV(
×
1180
            vector
×
1181
                .iter()
×
1182
                .cloned()
×
1183
                .map(|val| into_serializable_value(val, serialized_heap, visited))
×
1184
                .collect::<Result<_>>()?,
×
1185
        )),
1186

1187
        SteelVal::ByteVector(bytes) => {
×
1188
            Ok(SerializableSteelVal::ByteVectorV(bytes.vec.read().clone()))
×
1189
        }
1190

1191
        SteelVal::Rational(r) => Ok(SerializableSteelVal::Rational(r)),
×
1192

1193
        illegal => stop!(Generic => "Type not allowed to be moved across threads!: {}", illegal),
×
1194
    }
1195
}
1196

1197
#[derive(Clone, Debug, PartialEq, Eq)]
1198
pub struct SteelMutableVector(pub(crate) Gc<RefCell<Vec<SteelVal>>>);
1199

1200
#[derive(Clone, Debug, PartialEq, Eq)]
1201
pub struct SteelVector(pub(crate) Gc<Vector<SteelVal>>);
1202

1203
impl Deref for SteelVector {
1204
    type Target = Vector<SteelVal>;
1205

1206
    fn deref(&self) -> &Self::Target {
6,515,092✔
1207
        &self.0
6,515,092✔
1208
    }
1209
}
1210

1211
impl From<Gc<Vector<SteelVal>>> for SteelVector {
1212
    fn from(value: Gc<Vector<SteelVal>>) -> Self {
36✔
1213
        SteelVector(value)
36✔
1214
    }
1215
}
1216

1217
#[derive(Clone, Debug, PartialEq)]
1218
pub struct SteelHashMap(pub(crate) Gc<HashMap<SteelVal, SteelVal>>);
1219

1220
#[cfg(feature = "imbl")]
1221
impl Hash for SteelHashMap {
1222
    fn hash<H>(&self, state: &mut H)
×
1223
    where
1224
        H: Hasher,
1225
    {
1226
        for i in self.iter() {
×
1227
            i.hash(state);
×
1228
        }
1229
    }
1230
}
1231

1232
impl Deref for SteelHashMap {
1233
    type Target = HashMap<SteelVal, SteelVal>;
1234

1235
    fn deref(&self) -> &Self::Target {
442✔
1236
        &self.0
442✔
1237
    }
1238
}
1239

1240
impl From<Gc<HashMap<SteelVal, SteelVal>>> for SteelHashMap {
1241
    fn from(value: Gc<HashMap<SteelVal, SteelVal>>) -> Self {
14✔
1242
        SteelHashMap(value)
14✔
1243
    }
1244
}
1245

1246
#[derive(Clone, Debug, PartialEq)]
1247
pub struct SteelHashSet(pub(crate) Gc<HashSet<SteelVal>>);
1248

1249
#[cfg(feature = "imbl")]
1250
impl Hash for SteelHashSet {
1251
    fn hash<H>(&self, state: &mut H)
×
1252
    where
1253
        H: Hasher,
1254
    {
1255
        for i in self.iter() {
×
1256
            i.hash(state);
×
1257
        }
1258
    }
1259
}
1260

1261
impl Deref for SteelHashSet {
1262
    type Target = HashSet<SteelVal>;
1263

1264
    fn deref(&self) -> &Self::Target {
26✔
1265
        &self.0
26✔
1266
    }
1267
}
1268

1269
impl From<Gc<HashSet<SteelVal>>> for SteelHashSet {
1270
    fn from(value: Gc<HashSet<SteelVal>>) -> Self {
9✔
1271
        SteelHashSet(value)
9✔
1272
    }
1273
}
1274

1275
pub enum TypeKind {
1276
    Any,
1277
    Bool,
1278
    Num,
1279
    Int,
1280
    Char,
1281
    Vector(Box<TypeKind>),
1282
    Void,
1283
    String,
1284
    Function,
1285
    HashMap(Box<TypeKind>, Box<TypeKind>),
1286
    HashSet(Box<TypeKind>),
1287
    List(Box<TypeKind>),
1288
}
1289

1290
/// A value as represented in the runtime.
1291
#[derive(Clone)]
1292
pub enum SteelVal {
1293
    /// Represents a bytecode closure.
1294
    Closure(Gc<ByteCodeLambda>),
1295
    /// Represents a boolean value.
1296
    BoolV(bool),
1297
    /// Represents a number, currently only f64 numbers are supported.
1298
    NumV(f64),
1299
    /// Represents an integer.
1300
    IntV(isize),
1301
    /// Represents a rational number.
1302
    Rational(Rational32),
1303
    /// Represents a character type
1304
    CharV(char),
1305
    /// Vectors are represented as `im_rc::Vector`'s, which are immutable
1306
    /// data structures
1307
    VectorV(SteelVector),
1308
    /// Void return value
1309
    Void,
1310
    /// Represents strings
1311
    StringV(SteelString),
1312
    /// Represents built in rust functions
1313
    FuncV(FunctionSignature),
1314
    /// Represents a symbol, internally represented as `String`s
1315
    SymbolV(SteelString),
1316
    /// Container for a type that implements the `Custom Type` trait. (trait object)
1317
    Custom(GcMut<Box<dyn CustomType>>), // TODO: @Matt - consider using just a mutex here, to relax some of the bounds?
1318
    // Embedded HashMap
1319
    HashMapV(SteelHashMap),
1320
    // Embedded HashSet
1321
    HashSetV(SteelHashSet),
1322
    /// Represents a scheme-only struct
1323
    CustomStruct(Gc<UserDefinedStruct>),
1324
    /// Represents a port object
1325
    PortV(SteelPort),
1326
    /// Generic iterator wrapper
1327
    IterV(Gc<Transducer>),
1328
    /// Reducers
1329
    ReducerV(Gc<Reducer>),
1330
    /// Async Function wrapper
1331
    FutureFunc(BoxedAsyncFunctionSignature),
1332
    // Boxed Future Result
1333
    FutureV(Gc<FutureResult>),
1334
    // A stream of `SteelVal`.
1335
    StreamV(Gc<LazyStream>),
1336
    /// Custom closure
1337
    BoxedFunction(Gc<BoxedDynFunction>),
1338
    // Continuation
1339
    ContinuationFunction(Continuation),
1340
    // Function Pointer
1341
    // #[cfg(feature = "jit")]
1342
    // CompiledFunction(Box<JitFunctionPointer>),
1343
    // List
1344
    ListV(crate::values::lists::List<SteelVal>),
1345
    // Holds a pair that contains 2 `SteelVal`.
1346
    Pair(Gc<crate::values::lists::Pair>),
1347
    // Mutable functions
1348
    MutFunc(MutFunctionSignature),
1349
    // Built in functions
1350
    BuiltIn(BuiltInSignature),
1351
    // Mutable vector
1352
    MutableVector(HeapRef<Vec<SteelVal>>),
1353
    // This should delegate to the underlying iterator - can allow for faster raw iteration if possible
1354
    // Should allow for polling just a raw "next" on underlying elements
1355
    BoxedIterator(GcMut<OpaqueIterator>),
1356
    // Contains a syntax object.
1357
    SyntaxObject(Gc<Syntax>),
1358
    // Mutable storage, with Gc backing
1359
    // Boxed(HeapRef),
1360
    Boxed(GcMut<SteelVal>),
1361
    // Holds a SteelVal on the heap.
1362
    HeapAllocated(HeapRef<SteelVal>),
1363
    // TODO: This itself, needs to be boxed unfortunately.
1364
    Reference(Gc<OpaqueReference<'static>>),
1365
    // Like IntV but supports larger values.
1366
    BigNum(Gc<BigInt>),
1367
    // Like Rational but supports larger numerators and denominators.
1368
    BigRational(Gc<BigRational>),
1369
    // A complex number.
1370
    Complex(Gc<SteelComplex>),
1371
    // Byte vectors
1372
    ByteVector(SteelByteVector),
1373
}
1374

1375
impl Default for SteelVal {
1376
    fn default() -> Self {
2,828✔
1377
        SteelVal::Void
2,828✔
1378
    }
1379
}
1380

1381
// Avoid as much dropping as possible. Otherwise we thrash the drop impl
1382
// on steel values.
1383
#[cfg(feature = "sync")]
1384
pub(crate) enum SteelValPointer {
1385
    /// Represents a bytecode closure.
1386
    Closure(*const ByteCodeLambda),
1387
    VectorV(*const Vector<SteelVal>),
1388
    Custom(*const RwLock<Box<dyn CustomType>>),
1389
    HashMapV(*const HashMap<SteelVal, SteelVal>),
1390
    HashSetV(*const HashSet<SteelVal>),
1391
    CustomStruct(*const UserDefinedStruct),
1392
    IterV(*const Transducer),
1393
    ReducerV(*const Reducer),
1394
    StreamV(*const LazyStream),
1395
    ContinuationFunction(*const RwLock<ContinuationMark>),
1396
    ListV(crate::values::lists::CellPointer<SteelVal>),
1397
    Pair(*const crate::values::lists::Pair),
1398
    MutableVector(HeapRef<Vec<SteelVal>>),
1399
    SyntaxObject(*const Syntax),
1400
    BoxedIterator(*const RwLock<OpaqueIterator>),
1401
    Boxed(*const RwLock<SteelVal>),
1402
    HeapAllocated(HeapRef<SteelVal>),
1403
}
1404

1405
#[cfg(feature = "sync")]
1406
unsafe impl Sync for SteelValPointer {}
1407
#[cfg(feature = "sync")]
1408
unsafe impl Send for SteelValPointer {}
1409

1410
#[cfg(feature = "sync")]
1411
impl SteelValPointer {
1412
    pub(crate) fn from_value(value: &SteelVal) -> Option<Self> {
36,876,361✔
1413
        match value {
36,876,361✔
1414
            Closure(gc) => Some(Self::Closure(gc.as_ptr())),
44,702✔
1415
            VectorV(steel_vector) => Some(Self::VectorV(steel_vector.0.as_ptr())),
×
1416
            SteelVal::Custom(gc) => Some(Self::Custom(gc.as_ptr())),
9,864✔
1417
            HashMapV(steel_hash_map) => Some(Self::HashMapV(steel_hash_map.0.as_ptr())),
3,268✔
1418
            HashSetV(steel_hash_set) => Some(Self::HashSetV(steel_hash_set.0.as_ptr())),
172✔
1419
            CustomStruct(gc) => Some(Self::CustomStruct(gc.as_ptr())),
378,704✔
1420
            IterV(gc) => Some(Self::IterV(gc.as_ptr())),
×
1421
            ReducerV(gc) => Some(Self::ReducerV(gc.as_ptr())),
×
1422
            StreamV(gc) => Some(Self::StreamV(gc.as_ptr())),
344✔
1423
            ListV(generic_list) => Some(Self::ListV(generic_list.as_ptr())),
129,042✔
1424
            Pair(gc) => Some(Self::Pair(gc.as_ptr())),
50✔
1425
            SteelVal::ContinuationFunction(continuation) => Some(Self::ContinuationFunction(
40✔
1426
                crate::gc::shared::StandardShared::as_ptr(&continuation.inner),
20✔
1427
            )),
1428
            // TODO: See if we can avoid these clones?
1429
            MutableVector(heap_ref) => Some(Self::MutableVector(heap_ref.clone())),
72,385,930✔
1430
            BoxedIterator(gc) => Some(Self::BoxedIterator(gc.as_ptr())),
×
1431
            SteelVal::SyntaxObject(gc) => Some(Self::SyntaxObject(gc.as_ptr())),
×
1432
            Boxed(gc) => Some(Self::Boxed(gc.as_ptr())),
×
1433
            // TODO: See if we can avoid these clones?
1434
            HeapAllocated(heap_ref) => Some(Self::HeapAllocated(heap_ref.clone())),
755,420✔
1435
            _ => None,
242✔
1436
        }
1437
    }
1438
}
1439

1440
#[cfg(feature = "sync")]
1441
#[test]
1442
fn check_send_sync() {
1443
    let value = SteelVal::IntV(10);
1444

1445
    let handle = std::thread::spawn(move || value);
1446

1447
    handle.join().unwrap();
1448
}
1449

1450
#[derive(Clone, Debug)]
1451
pub struct SteelByteVector {
1452
    pub(crate) vec: GcMut<Vec<u8>>,
1453
}
1454

1455
impl SteelByteVector {
1456
    pub fn new(vec: Vec<u8>) -> Self {
20,147✔
1457
        Self {
1458
            vec: Gc::new_mut(vec),
20,147✔
1459
        }
1460
    }
1461
}
1462

1463
impl PartialEq for SteelByteVector {
1464
    fn eq(&self, other: &Self) -> bool {
19✔
1465
        *(self.vec.read()) == *(other.vec.read())
38✔
1466
    }
1467
}
1468

1469
impl Eq for SteelByteVector {}
1470

1471
impl Hash for SteelByteVector {
1472
    fn hash<H: Hasher>(&self, state: &mut H) {
×
1473
        self.vec.read().hash(state);
×
1474
    }
1475
}
1476

1477
/// Contains a complex number.
1478
///
1479
/// TODO: Optimize the contents of complex value. Holding `SteelVal` makes it easier to use existing
1480
/// operations but a more specialized representation may be faster.
1481
#[derive(Clone, Debug, Hash, PartialEq)]
1482
pub struct SteelComplex {
1483
    /// The real part of the complex number.
1484
    pub re: SteelVal,
1485
    /// The imaginary part of the complex number.
1486
    pub im: SteelVal,
1487
}
1488

1489
impl SteelComplex {
1490
    pub fn new(real: SteelVal, imaginary: SteelVal) -> SteelComplex {
769✔
1491
        SteelComplex {
1492
            re: real,
1493
            im: imaginary,
1494
        }
1495
    }
1496

1497
    /// Returns `true` if the imaginary part is negative.
1498
    pub(crate) fn imaginary_is_negative(&self) -> bool {
1✔
1499
        match &self.im {
1✔
1500
            NumV(x) => x.is_negative(),
×
1501
            IntV(x) => x.is_negative(),
3✔
1502
            Rational(x) => x.is_negative(),
×
1503
            BigNum(x) => x.is_negative(),
×
1504
            SteelVal::BigRational(x) => x.is_negative(),
×
1505
            _ => unreachable!(),
1506
        }
1507
    }
1508

1509
    pub(crate) fn imaginary_is_finite(&self) -> bool {
1✔
1510
        match &self.im {
1✔
1511
            NumV(x) => x.is_finite(),
×
1512
            IntV(_) | Rational(_) | BigNum(_) | SteelVal::BigRational(_) => true,
1✔
1513
            _ => unreachable!(),
1514
        }
1515
    }
1516
}
1517

1518
impl IntoSteelVal for SteelComplex {
1519
    #[inline(always)]
1520
    fn into_steelval(self) -> Result<SteelVal> {
381✔
1521
        Ok(match self.im {
58✔
1522
            NumV(n) if n.is_zero() => self.re,
116✔
1523
            IntV(0) => self.re,
4✔
1524
            _ => SteelVal::Complex(Gc::new(self)),
377✔
1525
        })
1526
    }
1527
}
1528

1529
impl fmt::Display for SteelComplex {
1530
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
1531
        if self.imaginary_is_negative() || !self.imaginary_is_finite() {
×
1532
            write!(f, "{re}{im}i", re = self.re, im = self.im)
×
1533
        } else {
1534
            write!(f, "{re}+{im}i", re = self.re, im = self.im)
×
1535
        }
1536
    }
1537
}
1538

1539
impl SteelVal {
1540
    // TODO: Re-evaluate this - should this be buffered?
1541
    pub fn new_dyn_writer_port(port: impl Write + Send + Sync + 'static) -> SteelVal {
×
1542
        SteelVal::PortV(SteelPort {
×
1543
            port: Gc::new_mut(SteelPortRepr::DynWriter(Arc::new(Mutex::new(port)))),
×
1544
        })
1545
    }
1546

1547
    pub fn anonymous_boxed_function(
×
1548
        function: std::sync::Arc<
1549
            dyn Fn(&[SteelVal]) -> crate::rvals::Result<SteelVal> + Send + Sync + 'static,
1550
        >,
1551
    ) -> SteelVal {
1552
        SteelVal::BoxedFunction(Gc::new(BoxedDynFunction {
×
1553
            function,
×
1554
            name: None,
×
1555
            arity: None,
×
1556
        }))
1557
    }
1558

1559
    pub fn as_box(&self) -> Option<HeapRef<SteelVal>> {
×
1560
        if let SteelVal::HeapAllocated(heap_ref) = self {
×
1561
            Some(heap_ref.clone())
×
1562
        } else {
1563
            None
×
1564
        }
1565
    }
1566

1567
    pub fn as_box_to_inner(&self) -> Option<SteelVal> {
×
1568
        self.as_box().map(|x| x.get())
×
1569
    }
1570

1571
    pub fn as_ptr_usize(&self) -> Option<usize> {
×
1572
        match self {
×
1573
            // Closure(_) => todo!(),
1574
            // BoolV(_) => todo!(),
1575
            // NumV(_) => todo!(),
1576
            // IntV(_) => todo!(),
1577
            // CharV(_) => todo!(),
1578
            // VectorV(_) => todo!(),
1579
            // Void => todo!(),
1580
            // StringV(_) => todo!(),
1581
            // FuncV(_) => todo!(),
1582
            // SymbolV(_) => todo!(),
1583
            // SteelVal::Custom(_) => todo!(),
1584
            // HashMapV(_) => todo!(),
1585
            // HashSetV(_) => todo!(),
1586
            CustomStruct(c) => Some(c.as_ptr() as usize),
×
1587
            // PortV(_) => todo!(),
1588
            // IterV(_) => todo!(),
1589
            // ReducerV(_) => todo!(),
1590
            // FutureFunc(_) => todo!(),
1591
            // FutureV(_) => todo!(),
1592
            // StreamV(_) => todo!(),
1593
            // BoxedFunction(_) => todo!(),
1594
            // ContinuationFunction(_) => todo!(),
1595
            ListV(l) => Some(l.as_ptr_usize()),
×
1596
            // MutFunc(_) => todo!(),
1597
            // BuiltIn(_) => todo!(),
1598
            // MutableVector(_) => todo!(),
1599
            // BoxedIterator(_) => todo!(),
1600
            // SteelVal::SyntaxObject(_) => todo!(),
1601
            // Boxed(_) => todo!(),
1602
            HeapAllocated(h) => Some(h.as_ptr_usize()),
×
1603
            // Reference(_) => todo!(),
1604
            // BigNum(_) => todo!(),
1605
            _ => None,
×
1606
        }
1607
    }
1608

1609
    // pub(crate) fn children_mut<'a>(&'a mut self) -> impl IntoIterator<Item = SteelVal> {
1610
    //     match self {
1611
    //         Self::CustomStruct(inner) => {
1612
    //             if let Some(inner) = inner.get_mut() {
1613
    //                 std::mem::take(&mut inner.borrow_mut().fields)
1614
    //             } else {
1615
    //                 std::iter::empty()
1616
    //             }
1617
    //         }
1618
    //         _ => todo!(),
1619
    //     }
1620
    // }
1621
}
1622

1623
// TODO: Consider unboxed value types, for optimized usages when compiling segments of code.
1624
// If we can infer the types from the concrete functions used, we don't need to have unboxed values -> We also
1625
// can use concrete forms of the underlying functions as well.
1626
// #[derive(Clone)]
1627
// pub enum UnboxedSteelVal {
1628
//     /// Represents a boolean value
1629
//     BoolV(bool),
1630
//     /// Represents a number, currently only f64 numbers are supported
1631
//     NumV(f64),
1632
//     /// Represents an integer
1633
//     IntV(isize),
1634
//     /// Represents a character type
1635
//     CharV(char),
1636
//     /// Vectors are represented as `im_rc::Vector`'s, which are immutable
1637
//     /// data structures
1638
//     VectorV(Vector<SteelVal>),
1639
//     /// Void return value
1640
//     Void,
1641
//     /// Represents strings
1642
//     StringV(SteelString),
1643
//     /// Represents built in rust functions
1644
//     FuncV(FunctionSignature),
1645
//     /// Represents a symbol, internally represented as `String`s
1646
//     SymbolV(SteelString),
1647
//     /// Container for a type that implements the `Custom Type` trait. (trait object)
1648
//     Custom(Gc<RefCell<Box<dyn CustomType>>>),
1649
//     // Embedded HashMap
1650
//     HashMapV(HashMap<SteelVal, SteelVal>),
1651
//     // Embedded HashSet
1652
//     HashSetV(HashSet<SteelVal>),
1653
//     /// Represents a scheme-only struct
1654
//     // StructV(Gc<SteelStruct>),
1655
//     /// Alternative implementation of a scheme-only struct
1656
//     CustomStruct(Gc<RefCell<UserDefinedStruct>>),
1657
//     // Represents a special rust closure
1658
//     // StructClosureV(Box<SteelStruct>, StructClosureSignature),
1659
//     // StructClosureV(Box<StructClosure>),
1660
//     /// Represents a port object
1661
//     PortV(SteelPort),
1662
//     /// Represents a bytecode closure
1663
//     Closure(Gc<ByteCodeLambda>),
1664
//     /// Generic iterator wrapper
1665
//     IterV(Gc<Transducer>),
1666
//     /// Reducers
1667
//     ReducerV(Gc<Reducer>),
1668
//     // Reducer(Reducer)
1669
//     // Generic IntoIter wrapper
1670
//     // Promise(Gc<SteelVal>),
1671
//     /// Async Function wrapper
1672
//     FutureFunc(BoxedAsyncFunctionSignature),
1673
//     // Boxed Future Result
1674
//     FutureV(Gc<FutureResult>),
1675

1676
//     StreamV(Gc<LazyStream>),
1677
//     // Break the cycle somehow
1678
//     // EvaluationEnv(Weak<RefCell<Env>>),
1679
//     /// Contract
1680
//     Contract(Gc<ContractType>),
1681
//     /// Contracted Function
1682
//     ContractedFunction(Gc<ContractedFunction>),
1683
//     /// Custom closure
1684
//     BoxedFunction(BoxedFunctionSignature),
1685
//     // Continuation
1686
//     ContinuationFunction(Gc<Continuation>),
1687
//     // List
1688
//     ListV(List<SteelVal>),
1689
//     // Mutable functions
1690
//     MutFunc(MutFunctionSignature),
1691
//     // Built in functions
1692
//     BuiltIn(BuiltInSignature),
1693
//     // Mutable vector
1694
//     MutableVector(Gc<RefCell<Vec<SteelVal>>>),
1695
//     // This should delegate to the underlying iterator - can allow for faster raw iteration if possible
1696
//     // Should allow for polling just a raw "next" on underlying elements
1697
//     BoxedIterator(Gc<RefCell<BuiltInDataStructureIterator>>),
1698

1699
//     SyntaxObject(Gc<Syntax>),
1700

1701
//     // Mutable storage, with Gc backing
1702
//     Boxed(HeapRef),
1703
// }
1704

1705
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1706
#[repr(C)]
1707
pub struct SteelString(pub(crate) Gc<String>);
1708

1709
impl Deref for SteelString {
1710
    type Target = crate::gc::Shared<String>;
1711

1712
    fn deref(&self) -> &Self::Target {
1,279✔
1713
        &self.0 .0
1,279✔
1714
    }
1715
}
1716

1717
#[cfg(not(feature = "sync"))]
1718
impl From<Arc<String>> for SteelString {
1719
    fn from(value: Arc<String>) -> Self {
1720
        SteelString(Gc(Rc::new((*value).clone())))
1721
    }
1722
}
1723

1724
#[cfg(all(feature = "sync", feature = "triomphe"))]
1725
impl From<std::sync::Arc<String>> for SteelString {
1726
    fn from(value: Arc<String>) -> Self {
135,759✔
1727
        SteelString(Gc(triomphe::Arc::new((*value).clone())))
271,518✔
1728
    }
1729
}
1730

1731
impl SteelString {
1732
    pub(crate) fn to_arc_string(&self) -> Arc<String> {
2,666✔
1733
        #[cfg(feature = "sync")]
1734
        {
1735
            // self.0 .0.clone()
1736
            Arc::new(self.0.unwrap())
7,998✔
1737
        }
1738
        #[cfg(not(feature = "sync"))]
1739
        Arc::new(self.0.unwrap())
1740
    }
1741
}
1742

1743
impl From<&str> for SteelString {
1744
    fn from(val: &str) -> Self {
5,224,743✔
1745
        SteelString(Gc::new(val.to_string()))
10,449,486✔
1746
    }
1747
}
1748

1749
impl From<&String> for SteelString {
1750
    fn from(val: &String) -> Self {
×
1751
        SteelString(Gc::new(val.to_owned()))
×
1752
    }
1753
}
1754

1755
impl From<String> for SteelString {
1756
    fn from(val: String) -> Self {
362,935✔
1757
        SteelString(Gc::new(val))
362,935✔
1758
    }
1759
}
1760

1761
impl From<crate::gc::Shared<String>> for SteelString {
1762
    fn from(val: crate::gc::Shared<String>) -> Self {
×
1763
        SteelString(Gc(val))
×
1764
    }
1765
}
1766

1767
impl From<Gc<String>> for SteelString {
1768
    fn from(val: Gc<String>) -> Self {
×
1769
        SteelString(val)
×
1770
    }
1771
}
1772

1773
impl From<SteelString> for crate::gc::Shared<String> {
1774
    fn from(value: SteelString) -> Self {
×
1775
        value.0 .0
×
1776
    }
1777
}
1778

1779
impl From<SteelString> for Gc<String> {
1780
    fn from(value: SteelString) -> Self {
×
1781
        value.0
×
1782
    }
1783
}
1784

1785
impl std::fmt::Display for SteelString {
1786
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24,803✔
1787
        write!(f, "{}", self.0.as_str())
99,212✔
1788
    }
1789
}
1790

1791
impl std::fmt::Debug for SteelString {
1792
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94✔
1793
        write!(f, "{:?}", self.0.as_str())
376✔
1794
    }
1795
}
1796

1797
// Check that steel values aren't growing without us knowing
1798
const _ASSERT_SMALL: () = assert!(std::mem::size_of::<SteelVal>() <= 16);
1799

1800
#[test]
1801
fn check_size_of_steelval() {
1802
    assert_eq!(std::mem::size_of::<SteelVal>(), 16);
1803
}
1804

1805
pub struct Chunks {
1806
    remaining: IntoIter<char>,
1807
}
1808

1809
impl Chunks {
1810
    fn new(s: SteelString) -> Self {
×
1811
        Chunks {
1812
            remaining: s.chars().collect::<Vec<_>>().into_iter(),
×
1813
        }
1814
    }
1815
}
1816

1817
pub struct OpaqueIterator {
1818
    pub(crate) root: SteelVal,
1819
    iterator: BuiltInDataStructureIterator,
1820
}
1821

1822
impl Custom for OpaqueIterator {
1823
    fn fmt(&self) -> Option<std::result::Result<String, std::fmt::Error>> {
×
1824
        Some(Ok(format!("#<iterator>")))
×
1825
    }
1826
}
1827

1828
// TODO: Convert this to just a generic custom type. This does not have to be
1829
// a special enum variant.
1830
pub enum BuiltInDataStructureIterator {
1831
    List(crate::values::lists::ConsumingIterator<SteelVal>),
1832
    Vector(VectorConsumingIter<SteelVal>),
1833
    Set(HashSetConsumingIter<SteelVal>),
1834
    Map(HashMapConsumingIter<SteelVal, SteelVal>),
1835
    String(Chunks),
1836
    #[cfg(not(feature = "sync"))]
1837
    Opaque(Box<dyn Iterator<Item = SteelVal>>),
1838
    #[cfg(feature = "sync")]
1839
    Opaque(Box<dyn Iterator<Item = SteelVal> + Send + Sync + 'static>),
1840
}
1841

1842
impl BuiltInDataStructureIterator {
1843
    pub fn into_boxed_iterator(self, value: SteelVal) -> SteelVal {
×
1844
        SteelVal::BoxedIterator(Gc::new_mut(OpaqueIterator {
×
1845
            root: value,
×
1846
            iterator: self,
×
1847
        }))
1848
    }
1849
}
1850

1851
impl BuiltInDataStructureIterator {
1852
    pub fn from_iterator<
×
1853
        T: IntoSteelVal + MaybeSendSyncStatic,
1854
        I: Iterator<Item = T> + MaybeSendSyncStatic,
1855
        S: IntoIterator<Item = T, IntoIter = I> + MaybeSendSyncStatic,
1856
    >(
1857
        value: S,
1858
    ) -> Self {
1859
        Self::Opaque(Box::new(
×
1860
            value
×
1861
                .into_iter()
×
1862
                .map(|x| x.into_steelval().expect("This shouldn't fail!")),
×
1863
        ))
1864
    }
1865
}
1866

1867
impl Iterator for BuiltInDataStructureIterator {
1868
    type Item = SteelVal;
1869

1870
    fn next(&mut self) -> Option<SteelVal> {
×
1871
        match self {
×
1872
            Self::List(l) => l.next(),
×
1873
            Self::Vector(v) => v.next(),
×
1874
            Self::String(s) => s.remaining.next().map(SteelVal::CharV),
×
1875
            Self::Set(s) => s.next(),
×
1876
            Self::Map(s) => s.next().map(|x| SteelVal::ListV(vec![x.0, x.1].into())),
×
1877
            Self::Opaque(s) => s.next(),
×
1878
        }
1879
    }
1880
}
1881

1882
pub fn value_into_iterator(val: SteelVal) -> Option<SteelVal> {
×
1883
    let root = val.clone();
×
1884
    match val {
×
1885
        SteelVal::ListV(l) => Some(BuiltInDataStructureIterator::List(l.into_iter())),
×
1886
        SteelVal::VectorV(v) => Some(BuiltInDataStructureIterator::Vector(
×
1887
            (*v).clone().into_iter(),
1888
        )),
1889
        SteelVal::StringV(s) => Some(BuiltInDataStructureIterator::String(Chunks::new(s))),
×
1890
        SteelVal::HashSetV(s) => Some(BuiltInDataStructureIterator::Set((*s).clone().into_iter())),
×
1891
        SteelVal::HashMapV(m) => Some(BuiltInDataStructureIterator::Map((*m).clone().into_iter())),
×
1892
        _ => None,
×
1893
    }
1894
    .map(|iterator| BuiltInDataStructureIterator::into_boxed_iterator(iterator, root))
×
1895
}
1896

1897
thread_local! {
1898
    pub static ITERATOR_FINISHED: SteelVal = SteelVal::SymbolV("done".into());
1899
}
1900

1901
pub fn iterator_next(args: &[SteelVal]) -> Result<SteelVal> {
×
1902
    match &args[0] {
×
1903
        SteelVal::BoxedIterator(b) => match b.write().iterator.next() {
×
1904
            Some(v) => Ok(v),
×
1905
            None => Ok(ITERATOR_FINISHED.with(|x| x.clone())),
×
1906
        },
1907
        _ => stop!(TypeMismatch => "Unexpected argument"),
×
1908
    }
1909
}
1910

1911
impl SteelVal {
1912
    pub fn boxed(value: SteelVal) -> SteelVal {
×
1913
        SteelVal::Boxed(Gc::new_mut(value))
×
1914
    }
1915

1916
    pub(crate) fn ptr_eq(&self, other: &SteelVal) -> bool {
1,936,019✔
1917
        match (self, other) {
3,872,038✔
1918
            // Integers are a special case of ptr eq -> if integers are equal? they are also eq?
1919
            (IntV(l), IntV(r)) => l == r,
30✔
1920
            (NumV(l), NumV(r)) => l == r,
6✔
1921
            (BoolV(l), BoolV(r)) => l == r,
5,075,811✔
1922
            (CharV(l), CharV(r)) => l == r,
×
1923
            (VectorV(l), VectorV(r)) => Gc::ptr_eq(&l.0, &r.0),
×
1924
            (Void, Void) => true,
×
1925
            (StringV(l), StringV(r)) => crate::gc::Shared::ptr_eq(l, r),
×
1926
            (FuncV(l), FuncV(r)) => *l as usize == *r as usize,
6✔
1927
            (SymbolV(l), SymbolV(r)) => crate::gc::Shared::ptr_eq(l, r) || l == r,
603,142✔
1928
            (SteelVal::Custom(l), SteelVal::Custom(r)) => Gc::ptr_eq(l, r),
×
1929
            (HashMapV(l), HashMapV(r)) => Gc::ptr_eq(&l.0, &r.0),
×
1930
            (HashSetV(l), HashSetV(r)) => Gc::ptr_eq(&l.0, &r.0),
×
1931
            (PortV(l), PortV(r)) => Gc::ptr_eq(&l.port, &r.port),
×
1932
            (Closure(l), Closure(r)) => Gc::ptr_eq(l, r),
6✔
1933
            (IterV(l), IterV(r)) => Gc::ptr_eq(l, r),
×
1934
            (ReducerV(l), ReducerV(r)) => Gc::ptr_eq(l, r),
×
1935
            (FutureFunc(l), FutureFunc(r)) => crate::gc::Shared::ptr_eq(l, r),
×
1936
            (FutureV(l), FutureV(r)) => Gc::ptr_eq(l, r),
×
1937
            (StreamV(l), StreamV(r)) => Gc::ptr_eq(l, r),
×
1938
            (BoxedFunction(l), BoxedFunction(r)) => Gc::ptr_eq(l, r),
×
1939
            (ContinuationFunction(l), ContinuationFunction(r)) => Continuation::ptr_eq(l, r),
×
1940
            (ListV(l), ListV(r)) => {
1,824✔
1941
                // Happy path
1942
                l.ptr_eq(r) || l.storage_ptr_eq(r) || (l.is_empty() && r.is_empty()) || {
2,812✔
1943
                    slow_path_eq_lists(l, r)
12✔
1944
                }
1945
            }
1946
            (MutFunc(l), MutFunc(r)) => *l as usize == *r as usize,
×
1947
            (BuiltIn(l), BuiltIn(r)) => *l as usize == *r as usize,
×
1948
            (MutableVector(l), MutableVector(r)) => HeapRef::ptr_eq(l, r),
688,655✔
1949
            (BigNum(l), BigNum(r)) => Gc::ptr_eq(l, r),
×
1950
            (ByteVector(l), ByteVector(r)) => Gc::ptr_eq(&l.vec, &r.vec),
10✔
1951
            (Pair(l), Pair(r)) => Gc::ptr_eq(l, r),
10✔
1952
            (_, _) => {
1953
                // dbg!(pointers);
1954
                false
2,209✔
1955
            }
1956
        }
1957
    }
1958
}
1959

1960
// How can we keep track of the provenance of where the pointers go?
1961
// for pointer equality?
1962
#[inline]
1963
fn slow_path_eq_lists(
12✔
1964
    _l: &crate::values::lists::List<SteelVal>,
1965
    _r: &crate::values::lists::List<SteelVal>,
1966
) -> bool {
1967
    false
12✔
1968

1969
    /*
1970

1971
    // If the next pointers are the same,
1972
    // then we need to check the values of the
1973
    // current node for equality. There is now the possibility
1974
    // that the previous version of this doesn't match up, and
1975
    // we need to keep the history of the previous values in order
1976
    // to do this properly.
1977
    //
1978
    // I think we can only really do this _if_ the next pointer
1979
    // exists. Otherwise this doesn't really make sense
1980
    let left_next = l.next_ptr_as_usize();
1981
    let right_next = r.next_ptr_as_usize();
1982

1983
    if left_next.is_some() && right_next.is_some() && left_next == right_next && l.len() == r.len()
1984
    {
1985
        let left_iter = l.current_node_iter();
1986
        let right_iter = r.current_node_iter();
1987

1988
        for (l, r) in left_iter.zip(right_iter) {
1989
            if l != r {
1990
                return false;
1991
            }
1992
        }
1993

1994
        true
1995
    } else {
1996
        false
1997
    }
1998

1999
    */
2000
}
2001

2002
impl Hash for SteelVal {
2003
    fn hash<H: Hasher>(&self, state: &mut H) {
59,866,758✔
2004
        match self {
59,866,758✔
2005
            BoolV(b) => {
258,042✔
2006
                state.write_u8(0);
516,084✔
2007
                b.hash(state)
774,126✔
2008
            }
2009
            NumV(n) => {
20,784✔
2010
                state.write_u8(1);
41,568✔
2011
                n.to_string().hash(state)
62,352✔
2012
            }
2013
            IntV(i) => {
941,033✔
2014
                state.write_u8(2);
1,882,066✔
2015
                i.hash(state)
2,823,099✔
2016
            }
2017
            Rational(f) => {
129✔
2018
                state.write_u8(3);
258✔
2019
                f.hash(state)
387✔
2020
            }
2021
            BigNum(n) => {
189✔
2022
                state.write_u8(4);
378✔
2023
                n.hash(state)
567✔
2024
            }
2025
            BigRational(f) => {
32✔
2026
                state.write_u8(5);
64✔
2027
                f.hash(state)
96✔
2028
            }
2029
            Complex(x) => {
110✔
2030
                state.write_u8(6);
220✔
2031
                x.hash(state)
330✔
2032
            }
2033
            CharV(c) => {
2,618✔
2034
                state.write_u8(7);
5,236✔
2035
                c.hash(state)
7,854✔
2036
            }
2037
            ListV(l) => {
19,507,892✔
2038
                state.write_u8(8);
39,015,784✔
2039
                l.hash(state)
58,523,676✔
2040
            }
2041
            CustomStruct(s) => {
2,501✔
2042
                state.write_u8(9);
5,002✔
2043
                s.hash(state)
7,503✔
2044
            }
2045
            VectorV(v) => {
5,172✔
2046
                state.write_u8(10);
10,344✔
2047
                v.hash(state)
10,344✔
2048
            }
2049
            v @ Void => {
×
2050
                state.write_u8(11);
×
2051
                v.hash(state)
×
2052
            }
2053
            StringV(s) => {
120,514✔
2054
                state.write_u8(12);
241,028✔
2055
                s.hash(state)
361,542✔
2056
            }
2057
            FuncV(s) => {
×
2058
                state.write_u8(13);
×
2059
                (*s as *const FunctionSignature).hash(state)
×
2060
            }
2061
            SymbolV(sym) => {
78,001,800✔
2062
                state.write_u8(14);
117,002,700✔
2063
                sym.hash(state);
78,001,800✔
2064
            }
2065
            Closure(b) => {
1,701✔
2066
                state.write_u8(15);
3,402✔
2067
                b.hash(state)
5,103✔
2068
            }
2069
            HashMapV(hm) => {
×
2070
                state.write_u8(16);
×
2071
                hm.hash(state)
×
2072
            }
2073
            IterV(s) => {
×
2074
                state.write_u8(17);
×
2075
                s.hash(state)
×
2076
            }
2077
            HashSetV(hs) => {
×
2078
                state.write_u8(18);
×
2079
                hs.hash(state)
×
2080
            }
2081
            SyntaxObject(s) => {
×
2082
                state.write_u8(19);
×
2083
                s.raw.hash(state)
×
2084
            }
2085
            Pair(p) => {
5,141✔
2086
                state.write_u8(20);
10,282✔
2087
                (**p).hash(state)
15,423✔
2088
            }
2089
            ByteVector(v) => {
×
2090
                state.write_u8(21);
×
NEW
2091
                (*v).hash(state)
×
2092
            }
2093
            _ => unimplemented!("Attempted to hash unsupported value: {self:?}"),
2094
        }
2095
    }
2096
}
2097

2098
impl SteelVal {
2099
    #[inline(always)]
2100
    pub fn is_truthy(&self) -> bool {
28✔
2101
        match &self {
28✔
2102
            SteelVal::BoolV(false) => false,
6✔
2103
            _ => true,
22✔
2104
        }
2105
    }
2106

2107
    #[inline(always)]
2108
    pub fn is_future(&self) -> bool {
×
2109
        matches!(self, SteelVal::FutureV(_))
×
2110
    }
2111

2112
    pub fn is_hashable(&self) -> bool {
1,871✔
2113
        matches!(
×
2114
            self,
1,871✔
2115
            BoolV(_)
2116
                | IntV(_)
2117
                | CharV(_)
2118
                // | Pair(_)
2119
                | VectorV(_)
2120
                | StringV(_)
2121
                | SymbolV(_)
2122
                | HashMapV(_)
2123
                | Closure(_)
2124
                | ListV(_)
2125
                | FuncV(_)
2126
                | CustomStruct(_)
2127
        )
2128
    }
2129

2130
    pub fn is_function(&self) -> bool {
×
2131
        matches!(
×
2132
            self,
×
2133
            BoxedFunction(_)
2134
                | Closure(_)
2135
                | FuncV(_)
2136
                // | ContractedFunction(_)
2137
                | BuiltIn(_)
2138
                | MutFunc(_)
2139
        )
2140
    }
2141

2142
    // pub fn is_contract(&self) -> bool {
2143
    //     matches!(self, Contract(_))
2144
    // }
2145

2146
    pub fn empty_hashmap() -> SteelVal {
×
2147
        SteelVal::HashMapV(Gc::new(HashMap::new()).into())
×
2148
    }
2149
}
2150

2151
impl SteelVal {
2152
    // pub fn res_iterator
2153

2154
    pub fn list_or_else<E, F: FnOnce() -> E>(
×
2155
        &self,
2156
        err: F,
2157
    ) -> std::result::Result<&List<SteelVal>, E> {
2158
        match self {
×
2159
            Self::ListV(v) => Ok(v),
×
2160
            _ => Err(err()),
×
2161
        }
2162
    }
2163

2164
    pub fn list(&self) -> Option<&List<SteelVal>> {
8,182,062✔
2165
        match self {
8,182,062✔
2166
            Self::ListV(l) => Some(l),
8,181,986✔
2167
            _ => None,
76✔
2168
        }
2169
    }
2170

2171
    pub fn pair(&self) -> Option<&Gc<crate::values::lists::Pair>> {
×
2172
        match self {
×
2173
            Self::Pair(p) => Some(p),
×
2174
            _ => None,
×
2175
        }
2176
    }
2177

2178
    pub fn bool_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<bool, E> {
2✔
2179
        match self {
2✔
2180
            Self::BoolV(v) => Ok(*v),
1✔
2181
            _ => Err(err()),
1✔
2182
        }
2183
    }
2184

2185
    pub fn int_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<isize, E> {
80✔
2186
        match self {
80✔
2187
            Self::IntV(v) => Ok(*v),
80✔
2188
            _ => Err(err()),
×
2189
        }
2190
    }
2191

2192
    pub fn num_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<f64, E> {
2✔
2193
        match self {
2✔
2194
            Self::NumV(v) => Ok(*v),
1✔
2195
            _ => Err(err()),
1✔
2196
        }
2197
    }
2198

2199
    pub fn char_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<char, E> {
126✔
2200
        match self {
126✔
2201
            Self::CharV(v) => Ok(*v),
125✔
2202
            _ => Err(err()),
1✔
2203
        }
2204
    }
2205

2206
    /// Vector does copy on the value to return
2207
    pub fn vector_or_else<E, F: FnOnce() -> E>(
2✔
2208
        &self,
2209
        err: F,
2210
    ) -> std::result::Result<Vector<SteelVal>, E> {
2211
        match self {
2✔
2212
            Self::VectorV(v) => Ok(v.0.unwrap()),
1✔
2213
            _ => Err(err()),
1✔
2214
        }
2215
    }
2216

2217
    pub fn void_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<(), E> {
2✔
2218
        match self {
2✔
2219
            Self::Void => Ok(()),
1✔
2220
            _ => Err(err()),
1✔
2221
        }
2222
    }
2223

2224
    pub fn string_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<&str, E> {
56✔
2225
        match self {
56✔
2226
            Self::StringV(v) => Ok(v),
55✔
2227
            _ => Err(err()),
1✔
2228
        }
2229
    }
2230

2231
    pub fn func_or_else<E, F: FnOnce() -> E>(
×
2232
        &self,
2233
        err: F,
2234
    ) -> std::result::Result<&FunctionSignature, E> {
2235
        match self {
×
2236
            Self::FuncV(v) => Ok(v),
×
2237
            _ => Err(err()),
×
2238
        }
2239
    }
2240

2241
    pub fn boxed_func_or_else<E, F: FnOnce() -> E>(
×
2242
        &self,
2243
        err: F,
2244
    ) -> std::result::Result<&BoxedDynFunction, E> {
2245
        match self {
×
2246
            Self::BoxedFunction(v) => Ok(v),
×
2247
            _ => Err(err()),
×
2248
        }
2249
    }
2250

2251
    // pub fn contract_or_else<E, F: FnOnce() -> E>(
2252
    //     &self,
2253
    //     err: F,
2254
    // ) -> std::result::Result<Gc<ContractType>, E> {
2255
    //     match self {
2256
    //         Self::Contract(c) => Ok(c.clone()),
2257
    //         _ => Err(err()),
2258
    //     }
2259
    // }
2260

2261
    pub fn closure_or_else<E, F: FnOnce() -> E>(
×
2262
        &self,
2263
        err: F,
2264
    ) -> std::result::Result<Gc<ByteCodeLambda>, E> {
2265
        match self {
×
2266
            Self::Closure(c) => Ok(c.clone()),
×
2267
            _ => Err(err()),
×
2268
        }
2269
    }
2270

2271
    pub fn symbol_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<&str, E> {
2✔
2272
        match self {
2✔
2273
            Self::SymbolV(v) => Ok(v),
1✔
2274
            _ => Err(err()),
1✔
2275
        }
2276
    }
2277

2278
    pub fn clone_symbol_or_else<E, F: FnOnce() -> E>(
×
2279
        &self,
2280
        err: F,
2281
    ) -> std::result::Result<String, E> {
2282
        match self {
×
2283
            Self::SymbolV(v) => Ok(v.to_string()),
×
2284
            _ => Err(err()),
×
2285
        }
2286
    }
2287

2288
    pub fn as_isize(&self) -> Option<isize> {
×
2289
        match self {
×
2290
            Self::IntV(i) => Some(*i),
×
2291
            _ => None,
×
2292
        }
2293
    }
2294

2295
    pub fn as_usize(&self) -> Option<usize> {
×
2296
        self.as_isize()
×
2297
            .and_then(|x| if x >= 0 { Some(x as usize) } else { None })
×
2298
    }
2299

2300
    pub fn as_bool(&self) -> Option<bool> {
4,254✔
2301
        match self {
4,254✔
2302
            Self::BoolV(b) => Some(*b),
4,254✔
2303
            _ => None,
×
2304
        }
2305
    }
2306

2307
    pub fn as_future(&self) -> Option<Shared<BoxedFutureResult>> {
×
2308
        match self {
×
2309
            Self::FutureV(v) => Some(v.clone().unwrap().into_shared()),
×
2310
            _ => None,
×
2311
        }
2312
    }
2313

2314
    pub fn as_string(&self) -> Option<&SteelString> {
×
2315
        match self {
×
2316
            Self::StringV(s) => Some(s),
×
2317
            _ => None,
×
2318
        }
2319
    }
2320

2321
    pub fn as_symbol(&self) -> Option<&SteelString> {
75,246✔
2322
        match self {
75,246✔
2323
            Self::SymbolV(s) => Some(s),
71,950✔
2324
            _ => None,
3,296✔
2325
        }
2326
    }
2327

2328
    pub fn as_syntax_object(&self) -> Option<&Syntax> {
3,285✔
2329
        match self {
3,285✔
2330
            Self::SyntaxObject(s) => Some(s),
382✔
2331
            _ => None,
2,903✔
2332
        }
2333
    }
2334

2335
    // pub fn custom_or_else<E, F: FnOnce() -> E>(
2336
    //     &self,
2337
    //     err: F,
2338
    // ) -> std::result::Result<&Box<dyn CustomType>, E> {
2339
    //     match self {
2340
    //         Self::Custom(v) => Ok(&v),
2341
    //         _ => Err(err()),
2342
    //     }
2343
    // }
2344

2345
    // pub fn struct_or_else<E, F: FnOnce() -> E>(
2346
    //     &self,
2347
    //     err: F,
2348
    // ) -> std::result::Result<&SteelStruct, E> {
2349
    //     match self {
2350
    //         Self::StructV(v) => Ok(v),
2351
    //         _ => Err(err()),
2352
    //     }
2353
    // }
2354

2355
    pub fn closure_arity(&self) -> Option<usize> {
×
2356
        if let SteelVal::Closure(c) = self {
×
2357
            Some(c.arity())
×
2358
        } else {
2359
            None
×
2360
        }
2361
    }
2362
}
2363

2364
impl SteelVal {
2365
    pub const INT_ZERO: SteelVal = SteelVal::IntV(0);
2366
    pub const INT_ONE: SteelVal = SteelVal::IntV(1);
2367
    pub const INT_TWO: SteelVal = SteelVal::IntV(2);
2368
}
2369

2370
impl Eq for SteelVal {}
2371

2372
fn integer_float_equality(int: isize, float: f64) -> bool {
5✔
2373
    let converted = float as isize;
10✔
2374

2375
    if float == converted as f64 {
5✔
2376
        int == converted
3✔
2377
    } else {
2378
        false
2✔
2379
    }
2380
}
2381

2382
fn bignum_float_equality(bigint: &Gc<BigInt>, float: f64) -> bool {
×
2383
    if float.fract() == 0.0 {
×
2384
        if let Some(promoted) = bigint.to_f64() {
×
2385
            promoted == float
2386
        } else {
2387
            false
×
2388
        }
2389
    } else {
2390
        false
×
2391
    }
2392
}
2393

2394
#[steel_derive::function(name = "=", constant = true)]
2395
pub fn number_equality(left: &SteelVal, right: &SteelVal) -> Result<SteelVal> {
29,456,108✔
2396
    let result = match (left, right) {
88,368,324✔
2397
        (IntV(l), IntV(r)) => l == r,
88,233,075✔
2398
        (NumV(l), NumV(r)) => l == r,
22,878✔
2399
        (IntV(l), NumV(r)) | (NumV(r), IntV(l)) => integer_float_equality(*l, *r),
15✔
2400
        (Rational(l), Rational(r)) => l == r,
×
2401
        (Rational(l), NumV(r)) | (NumV(r), Rational(l)) => l.to_f64().unwrap() == *r,
×
2402
        (BigNum(l), BigNum(r)) => l == r,
52,800✔
2403
        (BigNum(l), NumV(r)) | (NumV(r), BigNum(l)) => bignum_float_equality(l, *r),
×
2404
        (BigRational(l), BigRational(r)) => l == r,
×
2405
        (BigRational(l), NumV(r)) | (NumV(r), BigRational(l)) => l.to_f64().unwrap() == *r,
×
2406
        // The below should be impossible as integers/bignums freely convert into each
2407
        // other. Similar for int/bignum/rational/bigrational.
2408
        (Rational(_), IntV(_))
2409
        | (IntV(_), Rational(_))
2410
        | (Rational(_), BigNum(_))
2411
        | (BigNum(_), Rational(_))
2412
        | (Rational(_), BigRational(_))
2413
        | (BigRational(_), Rational(_)) => false,
×
2414
        (BigRational(_), IntV(_))
2415
        | (IntV(_), BigRational(_))
2416
        | (BigRational(_), BigNum(_))
2417
        | (BigNum(_), BigRational(_)) => false,
×
2418
        (IntV(_), BigNum(_)) | (BigNum(_), IntV(_)) => false,
4,600✔
2419
        (Complex(x), Complex(y)) => {
×
2420
            number_equality(&x.re, &y.re)? == BoolV(true)
×
2421
                && number_equality(&x.im, &y.re)? == BoolV(true)
×
2422
        }
2423
        (Complex(_), _) | (_, Complex(_)) => false,
×
2424
        _ => stop!(TypeMismatch => "= expects two numbers, found: {:?} and {:?}", left, right),
×
2425
    };
2426
    Ok(BoolV(result))
2427
}
2428

2429
impl PartialOrd for SteelVal {
2430
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
32,629,838✔
2431
        // TODO: Attempt to avoid converting to f64 for cases below as it may lead to precision loss
2432
        // at tiny and large values.
2433
        match (self, other) {
65,259,676✔
2434
            // Comparison of matching `SteelVal` variants:
2435
            (IntV(x), IntV(y)) => x.partial_cmp(y),
99,682,810✔
2436
            (BigNum(x), BigNum(y)) => x.partial_cmp(y),
25✔
2437
            (Rational(x), Rational(y)) => x.partial_cmp(y),
×
2438
            (BigRational(x), BigRational(y)) => x.partial_cmp(y),
×
2439
            (NumV(x), NumV(y)) => x.partial_cmp(y),
12,675,231✔
2440
            (StringV(s), StringV(o)) => s.partial_cmp(o),
×
2441
            (CharV(l), CharV(r)) => l.partial_cmp(r),
×
2442

2443
            // Comparison of `IntV`, means promoting to the rhs type
2444
            (IntV(x), BigNum(y)) => x
8✔
2445
                .to_bigint()
2446
                .expect("integers are representable by bigint")
2447
                .partial_cmp(y),
4✔
2448
            (IntV(x), Rational(y)) => {
×
2449
                // Since we have platform-dependent type for rational conditional compilation is required to find
2450
                // the common ground
2451
                #[cfg(target_pointer_width = "32")]
2452
                {
2453
                    let x_rational = num_rational::Rational32::new_raw(*x as i32, 1);
2454
                    x_rational.partial_cmp(y)
2455
                }
2456
                #[cfg(target_pointer_width = "64")]
2457
                {
2458
                    let x_rational = num_rational::Rational64::new_raw(*x as i64, 1);
×
2459
                    x_rational.partial_cmp(&num_rational::Rational64::new_raw(
×
2460
                        *y.numer() as i64,
×
2461
                        *y.denom() as i64,
×
2462
                    ))
2463
                }
2464
            }
2465
            (IntV(x), BigRational(y)) => {
×
2466
                let x_rational = BigRational::from_integer(
2467
                    x.to_bigint().expect("integers are representable by bigint"),
×
2468
                );
2469
                x_rational.partial_cmp(y)
×
2470
            }
2471
            (IntV(x), NumV(y)) => (*x as f64).partial_cmp(y),
30✔
2472

2473
            // BigNum comparisons means promoting to BigInt for integers, BigRational for ratios,
2474
            // or Decimal otherwise
2475
            (BigNum(x), IntV(y)) => x
3✔
2476
                .as_ref()
2477
                .partial_cmp(&y.to_bigint().expect("integers are representable by bigint")),
4✔
2478
            (BigNum(x), Rational(y)) => {
×
2479
                let x_big_rational = BigRational::from_integer(x.unwrap());
×
2480
                let y_big_rational = BigRational::new_raw(
2481
                    y.numer()
×
2482
                        .to_bigint()
×
2483
                        .expect("integers are representable by bigint"),
×
2484
                    y.denom()
×
2485
                        .to_bigint()
×
2486
                        .expect("integers are representable by bigint"),
×
2487
                );
2488
                x_big_rational.partial_cmp(&y_big_rational)
×
2489
            }
2490
            (BigNum(x), BigRational(y)) => {
×
2491
                let x_big_rational = BigRational::from_integer(x.unwrap());
×
NEW
2492
                x_big_rational.partial_cmp(y)
×
2493
            }
2494
            (BigNum(x), NumV(y)) => {
2✔
2495
                let x_decimal = BigDecimal::new(x.unwrap(), 0);
4✔
2496
                let y_decimal_opt = BigDecimal::from_f64(*y);
3✔
2497
                y_decimal_opt.and_then(|y_decimal| x_decimal.partial_cmp(&y_decimal))
5✔
2498
            }
2499

2500
            // Rationals require rationals, regular or bigger versions; for float it will be divided to float as well
2501
            (Rational(x), IntV(y)) => {
4✔
2502
                // Same as before, but opposite direction
2503
                #[cfg(target_pointer_width = "32")]
2504
                {
2505
                    let y_rational = num_rational::Rational32::new_raw(*y as i32, 1);
2506
                    x.partial_cmp(&y_rational)
2507
                }
2508
                #[cfg(target_pointer_width = "64")]
2509
                {
2510
                    let y_rational = num_rational::Rational64::new_raw(*y as i64, 1);
6✔
2511
                    num_rational::Rational64::new_raw(*x.numer() as i64, *x.denom() as i64)
6✔
2512
                        .partial_cmp(&y_rational)
4✔
2513
                }
2514
            }
2515
            (Rational(x), BigNum(y)) => {
×
2516
                let x_big_rational = BigRational::new_raw(
2517
                    x.numer()
×
2518
                        .to_bigint()
×
2519
                        .expect("integers are representable by bigint"),
×
2520
                    x.denom()
×
2521
                        .to_bigint()
×
2522
                        .expect("integers are representable by bigint"),
×
2523
                );
2524
                let y_big_rational = BigRational::from_integer(y.unwrap());
×
2525
                x_big_rational.partial_cmp(&y_big_rational)
×
2526
            }
2527
            (Rational(x), BigRational(y)) => {
×
2528
                let x_big_rational = BigRational::new_raw(
2529
                    x.numer()
×
2530
                        .to_bigint()
×
2531
                        .expect("integers are representable by bigint"),
×
2532
                    x.denom()
×
2533
                        .to_bigint()
×
2534
                        .expect("integers are representable by bigint"),
×
2535
                );
NEW
2536
                x_big_rational.partial_cmp(y)
×
2537
            }
2538
            (Rational(x), NumV(y)) => (*x.numer() as f64 / *x.denom() as f64).partial_cmp(y),
×
2539

2540
            // The most capacious set, but need to cover float case with BigDecimal anyways
2541
            (BigRational(x), IntV(y)) => {
×
2542
                let y_rational = BigRational::from_integer(
2543
                    y.to_bigint().expect("integers are representable by bigint"),
×
2544
                );
2545
                x.as_ref().partial_cmp(&y_rational)
×
2546
            }
2547
            (BigRational(x), BigNum(y)) => {
×
2548
                let y_big_rational = BigRational::from_integer(y.unwrap());
×
2549
                x.as_ref().partial_cmp(&y_big_rational)
×
2550
            }
2551
            (BigRational(x), Rational(y)) => {
×
2552
                let y_big_rational = BigRational::new_raw(
2553
                    y.numer()
×
2554
                        .to_bigint()
×
2555
                        .expect("integers are representable by bigint"),
×
2556
                    y.denom()
×
2557
                        .to_bigint()
×
2558
                        .expect("integers are representable by bigint"),
×
2559
                );
2560
                x.as_ref().partial_cmp(&y_big_rational)
×
2561
            }
2562
            (BigRational(x), NumV(y)) => {
×
2563
                let x_decimal =
×
2564
                    BigDecimal::new(x.numer().clone(), 0) / BigDecimal::new(x.denom().clone(), 0);
×
2565
                let y_decimal_opt = BigDecimal::from_f64(*y);
×
2566
                y_decimal_opt.and_then(|y_decimal| x_decimal.partial_cmp(&y_decimal))
×
2567
            }
2568

2569
            // The opposite of all float cases above
2570
            (NumV(x), IntV(y)) => x.partial_cmp(&(*y as f64)),
90,115✔
2571
            (NumV(x), BigNum(y)) => {
4✔
2572
                let x_decimal_opt = BigDecimal::from_f64(*x);
6✔
2573
                let y_decimal = BigDecimal::new(y.unwrap(), 0);
8✔
2574
                x_decimal_opt.and_then(|x_decimal| x_decimal.partial_cmp(&y_decimal))
10✔
2575
            }
2576
            (NumV(x), Rational(y)) => x.partial_cmp(&(*y.numer() as f64 / *y.denom() as f64)),
×
2577
            (NumV(x), BigRational(y)) => {
×
2578
                let x_decimal_opt = BigDecimal::from_f64(*x);
×
2579
                let y_decimal =
×
2580
                    BigDecimal::new(y.numer().clone(), 0) / BigDecimal::new(y.denom().clone(), 0);
×
2581
                x_decimal_opt.and_then(|x_decimal| x_decimal.partial_cmp(&y_decimal))
×
2582
            }
2583

2584
            (l, r) => {
6✔
2585
                // All real numbers (not complex) should have order defined.
2586
                debug_assert!(
3✔
2587
                    !(realp(l) && realp(r)),
12✔
2588
                    "Numbers {l:?} and {r:?} should implement partial_cmp"
×
2589
                );
2590
                // Unimplemented for other types
2591
                None
3✔
2592
            }
2593
        }
2594
    }
2595
}
2596

2597
pub(crate) struct SteelValDisplay<'a>(pub(crate) &'a SteelVal);
2598

2599
impl<'a> fmt::Display for SteelValDisplay<'a> {
2600
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
11,799✔
2601
        CycleDetector::detect_and_display_cycles(self.0, f, false)
35,397✔
2602
    }
2603
}
2604

2605
impl fmt::Display for SteelVal {
2606
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
505✔
2607
        CycleDetector::detect_and_display_cycles(self, f, true)
1,515✔
2608
    }
2609
}
2610

2611
impl fmt::Debug for SteelVal {
2612
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157✔
2613
        // at the top level, print a ' if we are
2614
        // trying to print a symbol or list
2615
        match self {
157✔
2616
            SymbolV(_) | ListV(_) | VectorV(_) => write!(f, "'")?,
18✔
2617
            _ => (),
139✔
2618
        };
2619
        // display_helper(self, f)
2620

2621
        CycleDetector::detect_and_display_cycles(self, f, true)
157✔
2622
    }
2623
}
2624

2625
#[cfg(test)]
2626
mod or_else_tests {
2627

2628
    use super::*;
2629

2630
    #[cfg(all(feature = "sync", not(feature = "imbl")))]
2631
    use im::vector;
2632

2633
    #[cfg(all(feature = "sync", feature = "imbl"))]
2634
    use imbl::vector;
2635

2636
    #[cfg(not(feature = "sync"))]
2637
    use im_rc::vector;
2638

2639
    #[test]
2640
    fn bool_or_else_test_good() {
2641
        let input = SteelVal::BoolV(true);
2642
        assert_eq!(input.bool_or_else(throw!(Generic => "test")).unwrap(), true);
2643
    }
2644

2645
    #[test]
2646
    fn bool_or_else_test_bad() {
2647
        let input = SteelVal::CharV('f');
2648
        assert!(input.bool_or_else(throw!(Generic => "test")).is_err());
2649
    }
2650

2651
    #[test]
2652
    fn num_or_else_test_good() {
2653
        let input = SteelVal::NumV(10.0);
2654
        assert_eq!(input.num_or_else(throw!(Generic => "test")).unwrap(), 10.0);
2655
    }
2656

2657
    #[test]
2658
    fn num_or_else_test_bad() {
2659
        let input = SteelVal::CharV('f');
2660
        assert!(input.num_or_else(throw!(Generic => "test")).is_err());
2661
    }
2662

2663
    #[test]
2664
    fn char_or_else_test_good() {
2665
        let input = SteelVal::CharV('f');
2666
        assert_eq!(input.char_or_else(throw!(Generic => "test")).unwrap(), 'f');
2667
    }
2668

2669
    #[test]
2670
    fn char_or_else_test_bad() {
2671
        let input = SteelVal::NumV(10.0);
2672
        assert!(input.char_or_else(throw!(Generic => "test")).is_err());
2673
    }
2674

2675
    #[test]
2676
    fn vector_or_else_test_good() {
2677
        let input: SteelVal = vector![SteelVal::IntV(1)].into();
2678
        assert_eq!(
2679
            input.vector_or_else(throw!(Generic => "test")).unwrap(),
2680
            vector![SteelVal::IntV(1)]
2681
        );
2682
    }
2683

2684
    #[test]
2685
    fn vector_or_else_bad() {
2686
        let input = SteelVal::CharV('f');
2687
        assert!(input.vector_or_else(throw!(Generic => "test")).is_err());
2688
    }
2689

2690
    #[test]
2691
    fn void_or_else_test_good() {
2692
        let input = SteelVal::Void;
2693
        assert_eq!(input.void_or_else(throw!(Generic => "test")).unwrap(), ())
2694
    }
2695

2696
    #[test]
2697
    fn void_or_else_test_bad() {
2698
        let input = SteelVal::StringV("foo".into());
2699
        assert!(input.void_or_else(throw!(Generic => "test")).is_err());
2700
    }
2701

2702
    #[test]
2703
    fn string_or_else_test_good() {
2704
        let input = SteelVal::StringV("foo".into());
2705
        assert_eq!(
2706
            input.string_or_else(throw!(Generic => "test")).unwrap(),
2707
            "foo".to_string()
2708
        );
2709
    }
2710

2711
    #[test]
2712
    fn string_or_else_test_bad() {
2713
        let input = SteelVal::Void;
2714
        assert!(input.string_or_else(throw!(Generic => "test")).is_err())
2715
    }
2716

2717
    #[test]
2718
    fn symbol_or_else_test_good() {
2719
        let input = SteelVal::SymbolV("foo".into());
2720
        assert_eq!(
2721
            input.symbol_or_else(throw!(Generic => "test")).unwrap(),
2722
            "foo".to_string()
2723
        );
2724
    }
2725

2726
    #[test]
2727
    fn symbol_or_else_test_bad() {
2728
        let input = SteelVal::Void;
2729
        assert!(input.symbol_or_else(throw!(Generic => "test")).is_err())
2730
    }
2731

2732
    #[test]
2733
    fn num_and_char_are_not_ordered() {
2734
        assert_eq!(SteelVal::IntV(0).partial_cmp(&SteelVal::CharV('0')), None);
2735
        assert_eq!(SteelVal::NumV(0.0).partial_cmp(&SteelVal::CharV('0')), None);
2736
        assert_eq!(
2737
            SteelVal::BigNum(Gc::new(BigInt::default())).partial_cmp(&SteelVal::CharV('0')),
2738
            None
2739
        );
2740
    }
2741

2742
    #[test]
2743
    fn number_cmp() {
2744
        let less_cases = [
2745
            (SteelVal::IntV(-10), SteelVal::IntV(1)),
2746
            (
2747
                SteelVal::IntV(-10),
2748
                SteelVal::BigNum(Gc::new(BigInt::from(1))),
2749
            ),
2750
            (SteelVal::NumV(-10.0), SteelVal::IntV(1)),
2751
            (SteelVal::IntV(-10), SteelVal::NumV(1.0)),
2752
            (
2753
                SteelVal::BigNum(Gc::new(BigInt::from(-10))),
2754
                SteelVal::BigNum(Gc::new(BigInt::from(1))),
2755
            ),
2756
            (
2757
                SteelVal::NumV(-10.0),
2758
                SteelVal::BigNum(Gc::new(BigInt::from(1))),
2759
            ),
2760
        ];
2761
        for (l, r) in less_cases {
2762
            assert_eq!(l.partial_cmp(&r), Some(Ordering::Less));
2763
            assert_eq!(r.partial_cmp(&l), Some(Ordering::Greater));
2764
        }
2765
        let equal_cases = [
2766
            SteelVal::IntV(-10),
2767
            SteelVal::NumV(-10.0),
2768
            SteelVal::BigNum(Gc::new(BigInt::from(-10))),
2769
            // Added to test that the number is equal even if it points to a different object.
2770
            SteelVal::BigNum(Gc::new(BigInt::from(-10))),
2771
        ]
2772
        .into_iter();
2773
        for (l, r) in equal_cases.clone().zip(equal_cases.clone()) {
2774
            assert_eq!(l.partial_cmp(&r), Some(Ordering::Equal));
2775
        }
2776
    }
2777
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc