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

mattwparas / steel / 18461079395

13 Oct 2025 09:20AM UTC coverage: 42.731% (-0.9%) from 43.668%
18461079395

Pull #536

github

web-flow
Merge 6f55a8b56 into e378cba22
Pull Request #536: Initial proposal for no_std support

63 of 755 new or added lines in 38 files covered. (8.34%)

73 existing lines in 15 files now uncovered.

12324 of 28841 relevant lines covered (42.73%)

3215759.81 hits per line

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

34.29
/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,
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
#[cfg(feature = "sync")]
35
use crate::steel_vm::vm::ContinuationMark;
36
use std::{
37
    any::{Any, TypeId},
38
    cell::RefCell,
39
    cmp::Ordering,
40
    convert::TryInto,
41
    fmt,
42
    future::Future,
43
    hash::{Hash, Hasher},
44
    io::Write,
45
    ops::Deref,
46
    pin::Pin,
47
    rc::Rc,
48
    result,
49
    sync::{Arc, Mutex},
50
    task::Context,
51
};
52

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

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

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

73
use bigdecimal::BigDecimal;
74
#[cfg(feature = "sync")]
75
use parking_lot::RwLock;
76
use smallvec::SmallVec;
77
use SteelVal::*;
78

79
use crate::values::{HashMap, HashSet, Vector};
80

81
use futures_task::noop_waker_ref;
82
use futures_util::future::Shared;
83
use futures_util::FutureExt;
84

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

91
use self::cycles::{CycleDetector, IterativeDropHandler};
92

93
pub type RcRefSteelVal = Rc<RefCell<SteelVal>>;
94
pub fn new_rc_ref_cell(x: SteelVal) -> RcRefSteelVal {
×
95
    Rc::new(RefCell::new(x))
×
96
}
97

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

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

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

110
pub type AsyncSignature = fn(&[SteelVal]) -> FutureResult;
111

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

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

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

122
#[derive(Clone)]
123
pub struct FutureResult(Shared<BoxedFutureResult>);
124

125
impl FutureResult {
126
    pub fn new(fut: BoxedFutureResult) -> Self {
11✔
127
        FutureResult(fut.shared())
11✔
128
    }
129

130
    pub fn into_shared(self) -> Shared<BoxedFutureResult> {
11✔
131
        self.0
11✔
132
    }
133
}
134

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

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

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

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

158
/// Attempt to cast this custom type down to the underlying type
159
pub fn as_underlying_type<T: 'static>(value: &dyn CustomType) -> Option<&T> {
333,876✔
160
    value.as_any_ref().downcast_ref::<T>()
667,752✔
161
}
162

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

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

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

177
    fn into_serializable_steelval(&mut self) -> Option<SerializableSteelVal> {
×
178
        None
×
179
    }
180

181
    fn as_iterator(&self) -> Option<Box<dyn Iterator<Item = SteelVal>>> {
×
182
        None
×
183
    }
184

185
    fn gc_drop_mut(&mut self, _drop_handler: &mut IterativeDropHandler) {}
×
186

187
    fn gc_visit_children(&self, _context: &mut MarkAndSweepContext) {}
29,124✔
188

189
    fn visit_equality(&self, _visitor: &mut cycles::EqualityVisitor) {}
×
190

191
    fn equality_hint(&self, _other: &dyn CustomType) -> bool {
×
192
        true
×
193
    }
194

195
    fn equality_hint_general(&self, _other: &SteelVal) -> bool {
1✔
196
        false
1✔
197
    }
198

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

208
#[cfg(not(feature = "sync"))]
209
pub trait MaybeSendSyncStatic: 'static {}
210

211
#[cfg(not(feature = "sync"))]
212
impl<T: 'static> MaybeSendSyncStatic for T {}
213

214
#[cfg(feature = "sync")]
215
pub trait MaybeSendSyncStatic: Send + Sync + 'static {}
216

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

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

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

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

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

305
    fn as_serializable_steelval(&mut self) -> Option<SerializableSteelVal> {
×
306
        <T as Custom>::into_serializable_steelval(self)
×
307
    }
308

309
    fn drop_mut(&mut self, drop_handler: &mut IterativeDropHandler) {
×
310
        self.gc_drop_mut(drop_handler)
×
311
    }
312

313
    fn visit_children(&self, context: &mut MarkAndSweepContext) {
14,562✔
314
        self.gc_visit_children(context)
43,686✔
315
    }
316

317
    // TODO: Equality visitor
318
    fn visit_children_for_equality(&self, visitor: &mut cycles::EqualityVisitor) {
×
319
        self.visit_equality(visitor)
×
320
    }
321

322
    fn check_equality_hint(&self, other: &dyn CustomType) -> bool {
×
323
        self.equality_hint(other)
×
324
    }
325

326
    fn check_equality_hint_general(&self, other: &SteelVal) -> bool {
1✔
327
        self.equality_hint_general(other)
3✔
328
    }
329

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

338
impl<T: CustomType + 'static> IntoSteelVal for T {
339
    fn into_steelval(self) -> Result<SteelVal> {
15,635✔
340
        Ok(SteelVal::Custom(Gc::new_mut(Box::new(self))))
31,270✔
341
    }
342

343
    fn as_error(self) -> std::result::Result<SteelErr, Self> {
×
344
        T::into_error_(self)
×
345
    }
346
}
347

348
pub trait IntoSerializableSteelVal {
349
    fn into_serializable_steelval(val: &SteelVal) -> Result<SerializableSteelVal>;
350
}
351

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

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

373
            Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
374
        }
375
    }
376
}
377

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

402
            Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
403
        }
404
    }
405
}
406

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

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

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

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

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

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

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

451
    pub fn from_slice(args: &'a [SteelVal]) -> Result<Self> {
72,068,058✔
452
        Ok(RestArgsIter(args.iter().map(T::primitive_as_ref)))
216,204,174✔
453
    }
454
}
455

456
impl<'a, T> Iterator for RestArgsIter<'a, T> {
457
    type Item = Result<T>;
458

459
    fn next(&mut self) -> Option<Self::Item> {
148,834,931✔
460
        self.0.next()
297,669,862✔
461
    }
462

463
    fn size_hint(&self) -> (usize, Option<usize>) {
2,860✔
464
        self.0.size_hint()
5,720✔
465
    }
466
}
467

468
impl<'a, T> ExactSizeIterator for RestArgsIter<'a, T> {}
469

470
pub struct RestArgs<T: FromSteelVal>(pub Vec<T>);
471

472
impl<T: FromSteelVal> RestArgs<T> {
473
    pub fn new(args: Vec<T>) -> Self {
×
474
        RestArgs(args)
×
475
    }
476

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

485
impl<T: FromSteelVal> std::ops::Deref for RestArgs<T> {
486
    type Target = [T];
487

488
    fn deref(&self) -> &Self::Target {
×
489
        &self.0
×
490
    }
491
}
492

493
mod private {
494

495
    use std::any::Any;
496

497
    pub trait Sealed {}
498

499
    impl<T: Any> Sealed for T {}
500
}
501

502
pub enum SRef<'b, T: ?Sized + 'b> {
503
    Temporary(&'b T),
504
    Owned(MappedScopedReadContainer<'b, T>),
505
}
506

507
impl<'b, T: ?Sized + 'b> Deref for SRef<'b, T> {
508
    type Target = T;
509

510
    #[inline]
511
    fn deref(&self) -> &T {
47,635✔
512
        match self {
47,635✔
513
            SRef::Temporary(inner) => inner,
7,531✔
514
            SRef::Owned(inner) => inner,
80,208✔
515
        }
516
    }
517
}
518

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

523
    fn as_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<SRef<'b, Self>>;
524
}
525

526
pub trait AsSlice<T> {
527
    fn as_slice_repr(&self) -> &[T];
528
}
529

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

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

540
    fn as_ref_from_unsized(val: &SteelVal) -> Result<Self::Output>;
541
}
542

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

547
pub(crate) trait AsRefMutSteelValFromRef: Sized {
548
    fn as_mut_ref_from_ref(val: &SteelVal) -> crate::rvals::Result<TemporaryMutableView<Self>>;
549
}
550

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

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

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

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

570
    fn as_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<SRef<'b, Self>> {
40,104✔
571
        if let SteelVal::Custom(v) = val {
80,208✔
572
            let res = ScopedReadContainer::map(v.read(), |x| x.as_any_ref());
80,208✔
573

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

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

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

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

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

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

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

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

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

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

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

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

686
                            self.quoted = true;
687

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

694
                            self.quoted = false;
695

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

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

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

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

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

738
    pub fn proto(raw: SteelVal, syntax: SteelVal, span: Span) -> Syntax {
187✔
739
        Self {
740
            raw: Some(raw),
374✔
741
            syntax,
742
            span,
743
        }
744
    }
745

746
    pub fn syntax_e(&self) -> SteelVal {
1,897✔
747
        self.syntax.clone()
3,794✔
748
    }
749

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

758
    pub fn syntax_loc(&self) -> Span {
12,718✔
759
        self.span
12,718✔
760
    }
761

762
    pub fn syntax_datum(&self) -> SteelVal {
4,118✔
763
        self.raw.clone().unwrap()
12,354✔
764
    }
765

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

995
                    // fields.collect()
996

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

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

1003
                    recycle.extend(fields);
×
1004

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

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

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

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

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

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

1033
                            todo!()
1034

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

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

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

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

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

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

1054
                // SteelVal::HeapAllocated(allocation)
1055

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1204
impl FromIterator<SteelVal> for SteelVector {
1205
    fn from_iter<T: IntoIterator<Item = SteelVal>>(iter: T) -> Self {
1,735✔
1206
        let vec = Vector::from_iter(iter);
5,205✔
1207
        SteelVector(Gc::new(vec))
1,735✔
1208
    }
1209
}
1210

1211
impl Deref for SteelVector {
1212
    type Target = Vector<SteelVal>;
1213

UNCOV
1214
    fn deref(&self) -> &Self::Target {
×
UNCOV
1215
        &self.0
×
1216
    }
1217
}
1218

1219
impl From<Gc<Vector<SteelVal>>> for SteelVector {
1220
    fn from(value: Gc<Vector<SteelVal>>) -> Self {
1✔
1221
        SteelVector(value)
1✔
1222
    }
1223
}
1224

1225
#[derive(Clone, Debug, PartialEq)]
1226
pub struct SteelHashMap(pub(crate) Gc<HashMap<SteelVal, SteelVal>>);
1227

1228
#[cfg(feature = "imbl")]
1229
impl Hash for SteelHashMap {
1230
    fn hash<H>(&self, state: &mut H)
×
1231
    where
1232
        H: Hasher,
1233
    {
1234
        for i in self.iter() {
×
1235
            i.hash(state);
×
1236
        }
1237
    }
1238
}
1239

1240
impl Deref for SteelHashMap {
1241
    type Target = HashMap<SteelVal, SteelVal>;
1242

1243
    fn deref(&self) -> &Self::Target {
596✔
1244
        &self.0
596✔
1245
    }
1246
}
1247

1248
impl From<Gc<HashMap<SteelVal, SteelVal>>> for SteelHashMap {
1249
    fn from(value: Gc<HashMap<SteelVal, SteelVal>>) -> Self {
4,346✔
1250
        SteelHashMap(value)
4,346✔
1251
    }
1252
}
1253

1254
#[derive(Clone, Debug, PartialEq)]
1255
pub struct SteelHashSet(pub(crate) Gc<HashSet<SteelVal>>);
1256

1257
#[cfg(feature = "imbl")]
1258
impl Hash for SteelHashSet {
1259
    fn hash<H>(&self, state: &mut H)
×
1260
    where
1261
        H: Hasher,
1262
    {
1263
        for i in self.iter() {
×
1264
            i.hash(state);
×
1265
        }
1266
    }
1267
}
1268

1269
impl Deref for SteelHashSet {
1270
    type Target = HashSet<SteelVal>;
1271

UNCOV
1272
    fn deref(&self) -> &Self::Target {
×
UNCOV
1273
        &self.0
×
1274
    }
1275
}
1276

1277
impl From<Gc<HashSet<SteelVal>>> for SteelHashSet {
1278
    fn from(value: Gc<HashSet<SteelVal>>) -> Self {
1✔
1279
        SteelHashSet(value)
1✔
1280
    }
1281
}
1282

1283
pub enum TypeKind {
1284
    Any,
1285
    Bool,
1286
    Num,
1287
    Int,
1288
    Char,
1289
    Vector(Box<TypeKind>),
1290
    Void,
1291
    String,
1292
    Function,
1293
    HashMap(Box<TypeKind>, Box<TypeKind>),
1294
    HashSet(Box<TypeKind>),
1295
    List(Box<TypeKind>),
1296
}
1297

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

1383
impl Default for SteelVal {
1384
    fn default() -> Self {
2,940✔
1385
        SteelVal::Void
2,940✔
1386
    }
1387
}
1388

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

1413
#[cfg(feature = "sync")]
1414
unsafe impl Sync for SteelValPointer {}
1415
#[cfg(feature = "sync")]
1416
unsafe impl Send for SteelValPointer {}
1417

1418
#[cfg(feature = "sync")]
1419
impl SteelValPointer {
1420
    pub(crate) fn from_value(value: &SteelVal) -> Option<Self> {
36,873,376✔
1421
        match value {
36,873,376✔
1422
            Closure(gc) => Some(Self::Closure(gc.as_ptr())),
53,706✔
1423
            VectorV(steel_vector) => Some(Self::VectorV(steel_vector.0.as_ptr())),
×
1424
            SteelVal::Custom(gc) => Some(Self::Custom(gc.as_ptr())),
10,064✔
1425
            HashMapV(steel_hash_map) => Some(Self::HashMapV(steel_hash_map.0.as_ptr())),
3,808✔
1426
            HashSetV(steel_hash_set) => Some(Self::HashSetV(steel_hash_set.0.as_ptr())),
172✔
1427
            CustomStruct(gc) => Some(Self::CustomStruct(gc.as_ptr())),
378,508✔
1428
            IterV(gc) => Some(Self::IterV(gc.as_ptr())),
×
1429
            ReducerV(gc) => Some(Self::ReducerV(gc.as_ptr())),
×
1430
            StreamV(gc) => Some(Self::StreamV(gc.as_ptr())),
344✔
1431
            ListV(generic_list) => Some(Self::ListV(generic_list.as_ptr())),
127,668✔
1432
            Pair(gc) => Some(Self::Pair(gc.as_ptr())),
50✔
1433
            SteelVal::ContinuationFunction(continuation) => Some(Self::ContinuationFunction(
40✔
1434
                crate::gc::shared::StandardShared::as_ptr(&continuation.inner),
20✔
1435
            )),
1436
            // TODO: See if we can avoid these clones?
1437
            MutableVector(heap_ref) => Some(Self::MutableVector(heap_ref.clone())),
72,364,906✔
1438
            BoxedIterator(gc) => Some(Self::BoxedIterator(gc.as_ptr())),
×
1439
            SteelVal::SyntaxObject(gc) => Some(Self::SyntaxObject(gc.as_ptr())),
×
1440
            Boxed(gc) => Some(Self::Boxed(gc.as_ptr())),
×
1441
            // TODO: See if we can avoid these clones?
1442
            HeapAllocated(heap_ref) => Some(Self::HeapAllocated(heap_ref.clone())),
753,296✔
1443
            _ => None,
242✔
1444
        }
1445
    }
1446
}
1447

1448
#[cfg(feature = "sync")]
1449
#[test]
1450
fn check_send_sync() {
1451
    let value = SteelVal::IntV(10);
1452

1453
    let handle = std::thread::spawn(move || value);
1454

1455
    handle.join().unwrap();
1456
}
1457

1458
#[derive(Clone, Debug)]
1459
pub struct SteelByteVector {
1460
    pub(crate) vec: GcMut<Vec<u8>>,
1461
}
1462

1463
impl SteelByteVector {
1464
    pub fn new(vec: Vec<u8>) -> Self {
20,152✔
1465
        Self {
1466
            vec: Gc::new_mut(vec),
20,152✔
1467
        }
1468
    }
1469
}
1470

1471
impl PartialEq for SteelByteVector {
1472
    fn eq(&self, other: &Self) -> bool {
19✔
1473
        *(self.vec.read()) == *(other.vec.read())
38✔
1474
    }
1475
}
1476

1477
impl Eq for SteelByteVector {}
1478

1479
impl Hash for SteelByteVector {
1480
    fn hash<H: Hasher>(&self, state: &mut H) {
×
1481
        self.vec.read().hash(state);
×
1482
    }
1483
}
1484

1485
/// Contains a complex number.
1486
///
1487
/// TODO: Optimize the contents of complex value. Holding `SteelVal` makes it easier to use existing
1488
/// operations but a more specialized representation may be faster.
1489
#[derive(Clone, Debug, Hash, PartialEq)]
1490
pub struct SteelComplex {
1491
    /// The real part of the complex number.
1492
    pub re: SteelVal,
1493
    /// The imaginary part of the complex number.
1494
    pub im: SteelVal,
1495
}
1496

1497
impl SteelComplex {
1498
    pub fn new(real: SteelVal, imaginary: SteelVal) -> SteelComplex {
769✔
1499
        SteelComplex {
1500
            re: real,
1501
            im: imaginary,
1502
        }
1503
    }
1504

1505
    /// Returns `true` if the imaginary part is negative.
1506
    pub(crate) fn imaginary_is_negative(&self) -> bool {
1✔
1507
        match &self.im {
1✔
1508
            NumV(x) => x.is_negative(),
×
1509
            IntV(x) => x.is_negative(),
3✔
1510
            Rational(x) => x.is_negative(),
×
1511
            BigNum(x) => x.is_negative(),
×
1512
            SteelVal::BigRational(x) => x.is_negative(),
×
1513
            _ => unreachable!(),
1514
        }
1515
    }
1516

1517
    pub(crate) fn imaginary_is_finite(&self) -> bool {
1✔
1518
        match &self.im {
1✔
1519
            NumV(x) => x.is_finite(),
×
1520
            IntV(_) | Rational(_) | BigNum(_) | SteelVal::BigRational(_) => true,
1✔
1521
            _ => unreachable!(),
1522
        }
1523
    }
1524
}
1525

1526
impl IntoSteelVal for SteelComplex {
1527
    #[inline(always)]
1528
    fn into_steelval(self) -> Result<SteelVal> {
3,499,636✔
1529
        Ok(match self.im {
3,499,330✔
1530
            NumV(n) if n.is_zero() => self.re,
7,000,910✔
1531
            IntV(0) => self.re,
4✔
1532
            _ => SteelVal::Complex(Gc::new(self)),
3,498,882✔
1533
        })
1534
    }
1535
}
1536

1537
impl fmt::Display for SteelComplex {
1538
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
1539
        if self.imaginary_is_negative() || !self.imaginary_is_finite() {
×
1540
            write!(f, "{re}{im}i", re = self.re, im = self.im)
×
1541
        } else {
1542
            write!(f, "{re}+{im}i", re = self.re, im = self.im)
×
1543
        }
1544
    }
1545
}
1546

1547
impl SteelVal {
1548
    // TODO: Re-evaluate this - should this be buffered?
1549
    pub fn new_dyn_writer_port(port: impl Write + Send + Sync + 'static) -> SteelVal {
×
1550
        SteelVal::PortV(SteelPort {
×
1551
            port: Gc::new_mut(SteelPortRepr::DynWriter(Arc::new(Mutex::new(port)))),
×
1552
        })
1553
    }
1554

1555
    pub fn anonymous_boxed_function(
×
1556
        function: std::sync::Arc<
1557
            dyn Fn(&[SteelVal]) -> crate::rvals::Result<SteelVal> + Send + Sync + 'static,
1558
        >,
1559
    ) -> SteelVal {
1560
        SteelVal::BoxedFunction(Gc::new(BoxedDynFunction {
×
1561
            function,
×
1562
            name: None,
×
1563
            arity: None,
×
1564
        }))
1565
    }
1566

1567
    pub fn as_box(&self) -> Option<HeapRef<SteelVal>> {
×
1568
        if let SteelVal::HeapAllocated(heap_ref) = self {
×
1569
            Some(heap_ref.clone())
×
1570
        } else {
1571
            None
×
1572
        }
1573
    }
1574

1575
    pub fn as_box_to_inner(&self) -> Option<SteelVal> {
×
1576
        self.as_box().map(|x| x.get())
×
1577
    }
1578

1579
    pub fn as_ptr_usize(&self) -> Option<usize> {
×
1580
        match self {
×
1581
            Closure(l) => Some(l.as_ptr() as usize),
×
1582
            VectorV(v) => Some(v.0.as_ptr() as usize),
×
1583
            // Void => todo!(),
1584
            StringV(s) => Some(s.0.as_ptr() as usize),
×
1585
            FuncV(_) => todo!(),
1586
            // SymbolV(_) => todo!(),
1587
            // SteelVal::Custom(_) => todo!(),
1588
            HashMapV(h) => Some(h.0.as_ptr() as usize),
×
1589
            HashSetV(h) => Some(h.0.as_ptr() as usize),
×
1590
            CustomStruct(c) => Some(c.as_ptr() as usize),
×
1591
            // PortV(_) => todo!(),
1592
            // IterV(_) => todo!(),
1593
            // ReducerV(_) => todo!(),
1594
            // FutureFunc(_) => todo!(),
1595
            // FutureV(_) => todo!(),
1596
            // StreamV(_) => todo!(),
1597
            // BoxedFunction(_) => todo!(),
1598
            // ContinuationFunction(_) => todo!(),
1599
            ListV(l) => Some(l.as_ptr_usize()),
×
1600
            MutableVector(v) => Some(v.as_ptr_usize()),
×
1601
            Custom(c) => Some(c.as_ptr() as usize),
×
1602
            // BoxedIterator(_) => todo!(),
1603
            // SteelVal::SyntaxObject(_) => todo!(),
1604
            Boxed(b) => Some(b.as_ptr() as usize),
×
1605
            HeapAllocated(h) => Some(h.as_ptr_usize()),
×
1606
            Pair(p) => Some(p.as_ptr() as usize),
×
1607
            SyntaxObject(s) => Some(s.as_ptr() as usize),
×
1608
            // Reference(_) => todo!(),
1609
            BigNum(b) => Some(b.as_ptr() as usize),
×
1610
            _ => None,
×
1611
        }
1612
    }
1613

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

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

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

1704
//     SyntaxObject(Gc<Syntax>),
1705

1706
//     // Mutable storage, with Gc backing
1707
//     Boxed(HeapRef),
1708
// }
1709

1710
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1711
#[repr(C)]
1712
pub struct SteelString(pub(crate) Gc<String>);
1713

1714
impl Deref for SteelString {
1715
    type Target = crate::gc::Shared<String>;
1716

1717
    fn deref(&self) -> &Self::Target {
147,468✔
1718
        &self.0 .0
147,468✔
1719
    }
1720
}
1721

1722
#[cfg(not(feature = "sync"))]
1723
impl From<Arc<String>> for SteelString {
1724
    fn from(value: Arc<String>) -> Self {
1725
        SteelString(Gc(Rc::new((*value).clone())))
1726
    }
1727
}
1728

1729
#[cfg(all(feature = "sync", feature = "triomphe"))]
1730
impl From<std::sync::Arc<String>> for SteelString {
1731
    fn from(value: Arc<String>) -> Self {
143,523✔
1732
        SteelString(Gc(triomphe::Arc::new((*value).clone())))
287,046✔
1733
    }
1734
}
1735

1736
impl SteelString {
1737
    pub(crate) fn to_arc_string(&self) -> Arc<String> {
2,770✔
1738
        #[cfg(feature = "sync")]
1739
        {
1740
            // self.0 .0.clone()
1741
            Arc::new(self.0.unwrap())
8,310✔
1742
        }
1743
        #[cfg(not(feature = "sync"))]
1744
        Arc::new(self.0.unwrap())
1745
    }
1746
}
1747

1748
impl From<&str> for SteelString {
1749
    fn from(val: &str) -> Self {
5,241,024✔
1750
        SteelString(Gc::new(val.to_string()))
10,482,048✔
1751
    }
1752
}
1753

1754
impl From<&String> for SteelString {
1755
    fn from(val: &String) -> Self {
×
1756
        SteelString(Gc::new(val.to_owned()))
×
1757
    }
1758
}
1759

1760
impl From<String> for SteelString {
1761
    fn from(val: String) -> Self {
374,244✔
1762
        SteelString(Gc::new(val))
374,244✔
1763
    }
1764
}
1765

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

1772
impl From<Gc<String>> for SteelString {
1773
    fn from(val: Gc<String>) -> Self {
×
1774
        SteelString(val)
×
1775
    }
1776
}
1777

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

1784
impl From<SteelString> for Gc<String> {
1785
    fn from(value: SteelString) -> Self {
×
1786
        value.0
×
1787
    }
1788
}
1789

1790
impl std::fmt::Display for SteelString {
1791
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25,402✔
1792
        write!(f, "{}", self.0.as_str())
101,608✔
1793
    }
1794
}
1795

1796
impl std::fmt::Debug for SteelString {
1797
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95✔
1798
        write!(f, "{:?}", self.0.as_str())
380✔
1799
    }
1800
}
1801

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

1805
#[test]
1806
fn check_size_of_steelval() {
1807
    assert_eq!(std::mem::size_of::<SteelVal>(), 16);
1808
}
1809

1810
pub struct Chunks {
1811
    remaining: IntoIter<char>,
1812
}
1813

1814
impl Chunks {
1815
    fn new(s: SteelString) -> Self {
×
1816
        Chunks {
1817
            remaining: s.chars().collect::<Vec<_>>().into_iter(),
×
1818
        }
1819
    }
1820
}
1821

1822
pub struct OpaqueIterator {
1823
    pub(crate) root: SteelVal,
1824
    iterator: BuiltInDataStructureIterator,
1825
}
1826

1827
impl Custom for OpaqueIterator {
1828
    fn fmt(&self) -> Option<std::result::Result<String, std::fmt::Error>> {
×
1829
        Some(Ok("#<iterator>".to_owned()))
×
1830
    }
1831
}
1832

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

1847
impl BuiltInDataStructureIterator {
1848
    pub fn into_boxed_iterator(self, value: SteelVal) -> SteelVal {
×
1849
        SteelVal::BoxedIterator(Gc::new_mut(OpaqueIterator {
×
1850
            root: value,
×
1851
            iterator: self,
×
1852
        }))
1853
    }
1854
}
1855

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

1872
impl Iterator for BuiltInDataStructureIterator {
1873
    type Item = SteelVal;
1874

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

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

1902
thread_local! {
1903
    pub static ITERATOR_FINISHED: SteelVal = SteelVal::SymbolV("done".into());
1904
}
1905

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

1916
impl SteelVal {
1917
    pub fn boxed(value: SteelVal) -> SteelVal {
×
1918
        SteelVal::Boxed(Gc::new_mut(value))
×
1919
    }
1920

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

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

1974
    /*
1975

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

1988
    if left_next.is_some() && right_next.is_some() && left_next == right_next && l.len() == r.len()
1989
    {
1990
        let left_iter = l.current_node_iter();
1991
        let right_iter = r.current_node_iter();
1992

1993
        for (l, r) in left_iter.zip(right_iter) {
1994
            if l != r {
1995
                return false;
1996
            }
1997
        }
1998

1999
        true
2000
    } else {
2001
        false
2002
    }
2003

2004
    */
2005
}
2006

2007
impl Hash for SteelVal {
2008
    fn hash<H: Hasher>(&self, state: &mut H) {
59,940,194✔
2009
        match self {
59,940,194✔
2010
            BoolV(b) => {
259,262✔
2011
                state.write_u8(0);
518,524✔
2012
                b.hash(state)
777,786✔
2013
            }
2014
            NumV(n) => {
44,807✔
2015
                state.write_u8(1);
89,614✔
2016
                n.to_string().hash(state)
134,421✔
2017
            }
2018
            IntV(i) => {
946,289✔
2019
                state.write_u8(2);
1,892,578✔
2020
                i.hash(state)
2,838,867✔
2021
            }
2022
            Rational(f) => {
138✔
2023
                state.write_u8(3);
276✔
2024
                f.hash(state)
414✔
2025
            }
2026
            BigNum(n) => {
189✔
2027
                state.write_u8(4);
378✔
2028
                n.hash(state)
567✔
2029
            }
2030
            BigRational(f) => {
32✔
2031
                state.write_u8(5);
64✔
2032
                f.hash(state)
96✔
2033
            }
2034
            Complex(x) => {
108✔
2035
                state.write_u8(6);
216✔
2036
                x.hash(state)
324✔
2037
            }
2038
            CharV(c) => {
2,793✔
2039
                state.write_u8(7);
5,586✔
2040
                c.hash(state)
8,379✔
2041
            }
2042
            ListV(l) => {
19,511,479✔
2043
                state.write_u8(8);
39,022,958✔
2044
                l.hash(state)
58,534,437✔
2045
            }
2046
            CustomStruct(s) => {
2,501✔
2047
                state.write_u8(9);
5,002✔
2048
                s.hash(state)
7,503✔
2049
            }
2050
            VectorV(v) => {
8,188✔
2051
                state.write_u8(10);
16,376✔
2052
                v.hash(state)
16,376✔
2053
            }
2054
            v @ Void => {
×
2055
                state.write_u8(11);
×
2056
                v.hash(state)
×
2057
            }
2058
            StringV(s) => {
126,788✔
2059
                state.write_u8(12);
253,576✔
2060
                s.hash(state)
380,364✔
2061
            }
2062
            FuncV(s) => {
×
2063
                state.write_u8(13);
×
2064
                (*s as *const FunctionSignature).hash(state)
×
2065
            }
2066
            SymbolV(sym) => {
78,061,346✔
2067
                state.write_u8(14);
117,092,019✔
2068
                sym.hash(state);
78,061,346✔
2069
            }
2070
            Closure(b) => {
1,773✔
2071
                state.write_u8(15);
3,546✔
2072
                b.hash(state)
5,319✔
2073
            }
2074
            HashMapV(hm) => {
×
2075
                state.write_u8(16);
×
2076
                hm.hash(state)
×
2077
            }
2078
            IterV(s) => {
×
2079
                state.write_u8(17);
×
2080
                s.hash(state)
×
2081
            }
2082
            HashSetV(hs) => {
×
2083
                state.write_u8(18);
×
2084
                hs.hash(state)
×
2085
            }
2086
            SyntaxObject(s) => {
×
2087
                state.write_u8(19);
×
2088
                s.raw.hash(state)
×
2089
            }
2090
            Pair(p) => {
5,174✔
2091
                state.write_u8(20);
10,348✔
2092
                (**p).hash(state)
15,522✔
2093
            }
2094
            ByteVector(v) => {
×
2095
                state.write_u8(21);
×
2096
                (*v).hash(state)
×
2097
            }
2098
            _ => unimplemented!("Attempted to hash unsupported value: {self:?}"),
2099
        }
2100
    }
2101
}
2102

2103
impl SteelVal {
2104
    #[inline(always)]
2105
    pub fn is_truthy(&self) -> bool {
165,836,348✔
2106
        match &self {
165,836,348✔
2107
            SteelVal::BoolV(false) => false,
123,642,414✔
2108
            _ => true,
42,193,934✔
2109
        }
2110
    }
2111

2112
    #[inline(always)]
2113
    pub fn is_future(&self) -> bool {
×
2114
        matches!(self, SteelVal::FutureV(_))
×
2115
    }
2116

2117
    pub fn is_hashable(&self) -> bool {
133,602✔
2118
        matches!(
×
2119
            self,
133,602✔
2120
            BoolV(_)
2121
                | IntV(_)
2122
                | CharV(_)
2123
                // | Pair(_)
2124
                | VectorV(_)
2125
                | StringV(_)
2126
                | SymbolV(_)
2127
                | HashMapV(_)
2128
                | Closure(_)
2129
                | ListV(_)
2130
                | FuncV(_)
2131
                | CustomStruct(_)
2132
        )
2133
    }
2134

2135
    pub fn is_function(&self) -> bool {
272,975✔
2136
        matches!(
×
2137
            self,
272,975✔
2138
            BoxedFunction(_)
2139
                | Closure(_)
2140
                | FuncV(_)
2141
                // | ContractedFunction(_)
2142
                | BuiltIn(_)
2143
                | MutFunc(_)
2144
        )
2145
    }
2146

2147
    // pub fn is_contract(&self) -> bool {
2148
    //     matches!(self, Contract(_))
2149
    // }
2150

2151
    pub fn empty_hashmap() -> SteelVal {
×
2152
        SteelVal::HashMapV(Gc::new(HashMap::new()).into())
×
2153
    }
2154
}
2155

2156
impl SteelVal {
2157
    // pub fn res_iterator
2158

2159
    pub fn list_or_else<E, F: FnOnce() -> E>(
×
2160
        &self,
2161
        err: F,
2162
    ) -> std::result::Result<&List<SteelVal>, E> {
2163
        match self {
×
2164
            Self::ListV(v) => Ok(v),
×
2165
            _ => Err(err()),
×
2166
        }
2167
    }
2168

UNCOV
2169
    pub fn list(&self) -> Option<&List<SteelVal>> {
×
UNCOV
2170
        match self {
×
UNCOV
2171
            Self::ListV(l) => Some(l),
×
UNCOV
2172
            _ => None,
×
2173
        }
2174
    }
2175

2176
    pub fn pair(&self) -> Option<&Gc<crate::values::lists::Pair>> {
×
2177
        match self {
×
2178
            Self::Pair(p) => Some(p),
×
2179
            _ => None,
×
2180
        }
2181
    }
2182

2183
    pub fn bool_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<bool, E> {
2✔
2184
        match self {
2✔
2185
            Self::BoolV(v) => Ok(*v),
1✔
2186
            _ => Err(err()),
1✔
2187
        }
2188
    }
2189

2190
    pub fn int_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<isize, E> {
80✔
2191
        match self {
80✔
2192
            Self::IntV(v) => Ok(*v),
80✔
2193
            _ => Err(err()),
×
2194
        }
2195
    }
2196

2197
    pub fn num_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<f64, E> {
2✔
2198
        match self {
2✔
2199
            Self::NumV(v) => Ok(*v),
1✔
2200
            _ => Err(err()),
1✔
2201
        }
2202
    }
2203

2204
    pub fn char_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<char, E> {
126✔
2205
        match self {
126✔
2206
            Self::CharV(v) => Ok(*v),
125✔
2207
            _ => Err(err()),
1✔
2208
        }
2209
    }
2210

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

2222
    pub fn void_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<(), E> {
2✔
2223
        match self {
2✔
2224
            Self::Void => Ok(()),
1✔
2225
            _ => Err(err()),
1✔
2226
        }
2227
    }
2228

2229
    pub fn string_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<&str, E> {
56✔
2230
        match self {
56✔
2231
            Self::StringV(v) => Ok(v),
55✔
2232
            _ => Err(err()),
1✔
2233
        }
2234
    }
2235

2236
    pub fn func_or_else<E, F: FnOnce() -> E>(
×
2237
        &self,
2238
        err: F,
2239
    ) -> std::result::Result<&FunctionSignature, E> {
2240
        match self {
×
2241
            Self::FuncV(v) => Ok(v),
×
2242
            _ => Err(err()),
×
2243
        }
2244
    }
2245

2246
    pub fn boxed_func_or_else<E, F: FnOnce() -> E>(
×
2247
        &self,
2248
        err: F,
2249
    ) -> std::result::Result<&BoxedDynFunction, E> {
2250
        match self {
×
2251
            Self::BoxedFunction(v) => Ok(v),
×
2252
            _ => Err(err()),
×
2253
        }
2254
    }
2255

2256
    // pub fn contract_or_else<E, F: FnOnce() -> E>(
2257
    //     &self,
2258
    //     err: F,
2259
    // ) -> std::result::Result<Gc<ContractType>, E> {
2260
    //     match self {
2261
    //         Self::Contract(c) => Ok(c.clone()),
2262
    //         _ => Err(err()),
2263
    //     }
2264
    // }
2265

2266
    pub fn closure_or_else<E, F: FnOnce() -> E>(
×
2267
        &self,
2268
        err: F,
2269
    ) -> std::result::Result<Gc<ByteCodeLambda>, E> {
2270
        match self {
×
2271
            Self::Closure(c) => Ok(c.clone()),
×
2272
            _ => Err(err()),
×
2273
        }
2274
    }
2275

2276
    pub fn symbol_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<&str, E> {
2✔
2277
        match self {
2✔
2278
            Self::SymbolV(v) => Ok(v),
1✔
2279
            _ => Err(err()),
1✔
2280
        }
2281
    }
2282

2283
    pub fn clone_symbol_or_else<E, F: FnOnce() -> E>(
×
2284
        &self,
2285
        err: F,
2286
    ) -> std::result::Result<String, E> {
2287
        match self {
×
2288
            Self::SymbolV(v) => Ok(v.to_string()),
×
2289
            _ => Err(err()),
×
2290
        }
2291
    }
2292

2293
    pub fn as_isize(&self) -> Option<isize> {
×
2294
        match self {
×
2295
            Self::IntV(i) => Some(*i),
×
2296
            _ => None,
×
2297
        }
2298
    }
2299

2300
    pub fn as_usize(&self) -> Option<usize> {
×
2301
        self.as_isize()
×
2302
            .and_then(|x| if x >= 0 { Some(x as usize) } else { None })
×
2303
    }
2304

2305
    pub fn as_bool(&self) -> Option<bool> {
4✔
2306
        match self {
4✔
2307
            Self::BoolV(b) => Some(*b),
4✔
2308
            _ => None,
×
2309
        }
2310
    }
2311

2312
    pub fn as_future(&self) -> Option<Shared<BoxedFutureResult>> {
×
2313
        match self {
×
2314
            Self::FutureV(v) => Some(v.clone().unwrap().into_shared()),
×
2315
            _ => None,
×
2316
        }
2317
    }
2318

2319
    pub fn as_string(&self) -> Option<&SteelString> {
×
2320
        match self {
×
2321
            Self::StringV(s) => Some(s),
×
2322
            _ => None,
×
2323
        }
2324
    }
2325

2326
    pub fn as_symbol(&self) -> Option<&SteelString> {
78,230✔
2327
        match self {
78,230✔
2328
            Self::SymbolV(s) => Some(s),
74,822✔
2329
            _ => None,
3,408✔
2330
        }
2331
    }
2332

2333
    pub fn as_syntax_object(&self) -> Option<&Syntax> {
3,397✔
2334
        match self {
3,397✔
2335
            Self::SyntaxObject(s) => Some(s),
382✔
2336
            _ => None,
3,015✔
2337
        }
2338
    }
2339

2340
    // pub fn custom_or_else<E, F: FnOnce() -> E>(
2341
    //     &self,
2342
    //     err: F,
2343
    // ) -> std::result::Result<&Box<dyn CustomType>, E> {
2344
    //     match self {
2345
    //         Self::Custom(v) => Ok(&v),
2346
    //         _ => Err(err()),
2347
    //     }
2348
    // }
2349

2350
    // pub fn struct_or_else<E, F: FnOnce() -> E>(
2351
    //     &self,
2352
    //     err: F,
2353
    // ) -> std::result::Result<&SteelStruct, E> {
2354
    //     match self {
2355
    //         Self::StructV(v) => Ok(v),
2356
    //         _ => Err(err()),
2357
    //     }
2358
    // }
2359

2360
    pub fn closure_arity(&self) -> Option<usize> {
×
2361
        if let SteelVal::Closure(c) = self {
×
2362
            Some(c.arity())
×
2363
        } else {
2364
            None
×
2365
        }
2366
    }
2367
}
2368

2369
impl SteelVal {
2370
    pub const INT_ZERO: SteelVal = SteelVal::IntV(0);
2371
    pub const INT_ONE: SteelVal = SteelVal::IntV(1);
2372
    pub const INT_TWO: SteelVal = SteelVal::IntV(2);
2373
}
2374

2375
impl Eq for SteelVal {}
2376

2377
fn integer_float_equality(int: isize, float: f64) -> bool {
5✔
2378
    let converted = float as isize;
10✔
2379

2380
    if float == converted as f64 {
5✔
2381
        int == converted
3✔
2382
    } else {
2383
        false
2✔
2384
    }
2385
}
2386

2387
fn bignum_float_equality(bigint: &Gc<BigInt>, float: f64) -> bool {
×
2388
    if float.fract() == 0.0 {
×
2389
        if let Some(promoted) = bigint.to_f64() {
×
2390
            promoted == float
2391
        } else {
2392
            false
×
2393
        }
2394
    } else {
2395
        false
×
2396
    }
2397
}
2398

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

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

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

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

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

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

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

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

2602
pub(crate) struct SteelValDisplay<'a>(pub(crate) &'a SteelVal);
2603

2604
impl<'a> fmt::Display for SteelValDisplay<'a> {
2605
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
11,948✔
2606
        CycleDetector::detect_and_display_cycles(self.0, f, false)
35,844✔
2607
    }
2608
}
2609

2610
impl fmt::Display for SteelVal {
2611
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
514✔
2612
        CycleDetector::detect_and_display_cycles(self, f, true)
1,542✔
2613
    }
2614
}
2615

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

2626
        CycleDetector::detect_and_display_cycles(self, f, true)
157✔
2627
    }
2628
}
2629

2630
#[cfg(test)]
2631
mod or_else_tests {
2632

2633
    use super::*;
2634

2635
    #[cfg(all(feature = "sync", not(feature = "imbl")))]
2636
    use im::vector;
2637

2638
    #[cfg(all(feature = "sync", feature = "imbl"))]
2639
    use imbl::vector;
2640

2641
    #[cfg(not(feature = "sync"))]
2642
    use im_rc::vector;
2643

2644
    #[test]
2645
    fn bool_or_else_test_good() {
2646
        let input = SteelVal::BoolV(true);
2647
        assert_eq!(input.bool_or_else(throw!(Generic => "test")).unwrap(), true);
2648
    }
2649

2650
    #[test]
2651
    fn bool_or_else_test_bad() {
2652
        let input = SteelVal::CharV('f');
2653
        assert!(input.bool_or_else(throw!(Generic => "test")).is_err());
2654
    }
2655

2656
    #[test]
2657
    fn num_or_else_test_good() {
2658
        let input = SteelVal::NumV(10.0);
2659
        assert_eq!(input.num_or_else(throw!(Generic => "test")).unwrap(), 10.0);
2660
    }
2661

2662
    #[test]
2663
    fn num_or_else_test_bad() {
2664
        let input = SteelVal::CharV('f');
2665
        assert!(input.num_or_else(throw!(Generic => "test")).is_err());
2666
    }
2667

2668
    #[test]
2669
    fn char_or_else_test_good() {
2670
        let input = SteelVal::CharV('f');
2671
        assert_eq!(input.char_or_else(throw!(Generic => "test")).unwrap(), 'f');
2672
    }
2673

2674
    #[test]
2675
    fn char_or_else_test_bad() {
2676
        let input = SteelVal::NumV(10.0);
2677
        assert!(input.char_or_else(throw!(Generic => "test")).is_err());
2678
    }
2679

2680
    #[test]
2681
    fn vector_or_else_test_good() {
2682
        let input: SteelVal = vector![SteelVal::IntV(1)].into();
2683
        assert_eq!(
2684
            input.vector_or_else(throw!(Generic => "test")).unwrap(),
2685
            vector![SteelVal::IntV(1)]
2686
        );
2687
    }
2688

2689
    #[test]
2690
    fn vector_or_else_bad() {
2691
        let input = SteelVal::CharV('f');
2692
        assert!(input.vector_or_else(throw!(Generic => "test")).is_err());
2693
    }
2694

2695
    #[test]
2696
    fn void_or_else_test_good() {
2697
        let input = SteelVal::Void;
2698
        assert_eq!(input.void_or_else(throw!(Generic => "test")).unwrap(), ())
2699
    }
2700

2701
    #[test]
2702
    fn void_or_else_test_bad() {
2703
        let input = SteelVal::StringV("foo".into());
2704
        assert!(input.void_or_else(throw!(Generic => "test")).is_err());
2705
    }
2706

2707
    #[test]
2708
    fn string_or_else_test_good() {
2709
        let input = SteelVal::StringV("foo".into());
2710
        assert_eq!(
2711
            input.string_or_else(throw!(Generic => "test")).unwrap(),
2712
            "foo".to_string()
2713
        );
2714
    }
2715

2716
    #[test]
2717
    fn string_or_else_test_bad() {
2718
        let input = SteelVal::Void;
2719
        assert!(input.string_or_else(throw!(Generic => "test")).is_err())
2720
    }
2721

2722
    #[test]
2723
    fn symbol_or_else_test_good() {
2724
        let input = SteelVal::SymbolV("foo".into());
2725
        assert_eq!(
2726
            input.symbol_or_else(throw!(Generic => "test")).unwrap(),
2727
            "foo".to_string()
2728
        );
2729
    }
2730

2731
    #[test]
2732
    fn symbol_or_else_test_bad() {
2733
        let input = SteelVal::Void;
2734
        assert!(input.symbol_or_else(throw!(Generic => "test")).is_err())
2735
    }
2736

2737
    #[test]
2738
    fn num_and_char_are_not_ordered() {
2739
        assert_eq!(SteelVal::IntV(0).partial_cmp(&SteelVal::CharV('0')), None);
2740
        assert_eq!(SteelVal::NumV(0.0).partial_cmp(&SteelVal::CharV('0')), None);
2741
        assert_eq!(
2742
            SteelVal::BigNum(Gc::new(BigInt::default())).partial_cmp(&SteelVal::CharV('0')),
2743
            None
2744
        );
2745
    }
2746

2747
    #[test]
2748
    fn number_cmp() {
2749
        let less_cases = [
2750
            (SteelVal::IntV(-10), SteelVal::IntV(1)),
2751
            (
2752
                SteelVal::IntV(-10),
2753
                SteelVal::BigNum(Gc::new(BigInt::from(1))),
2754
            ),
2755
            (SteelVal::NumV(-10.0), SteelVal::IntV(1)),
2756
            (SteelVal::IntV(-10), SteelVal::NumV(1.0)),
2757
            (
2758
                SteelVal::BigNum(Gc::new(BigInt::from(-10))),
2759
                SteelVal::BigNum(Gc::new(BigInt::from(1))),
2760
            ),
2761
            (
2762
                SteelVal::NumV(-10.0),
2763
                SteelVal::BigNum(Gc::new(BigInt::from(1))),
2764
            ),
2765
        ];
2766
        for (l, r) in less_cases {
2767
            assert_eq!(l.partial_cmp(&r), Some(Ordering::Less));
2768
            assert_eq!(r.partial_cmp(&l), Some(Ordering::Greater));
2769
        }
2770
        let equal_cases = [
2771
            SteelVal::IntV(-10),
2772
            SteelVal::NumV(-10.0),
2773
            SteelVal::BigNum(Gc::new(BigInt::from(-10))),
2774
            // Added to test that the number is equal even if it points to a different object.
2775
            SteelVal::BigNum(Gc::new(BigInt::from(-10))),
2776
        ]
2777
        .into_iter();
2778
        for (l, r) in equal_cases.clone().zip(equal_cases.clone()) {
2779
            assert_eq!(l.partial_cmp(&r), Some(Ordering::Equal));
2780
        }
2781
    }
2782
}
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