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

mattwparas / steel / 15223567548

24 May 2025 04:45AM UTC coverage: 45.922% (+0.003%) from 45.919%
15223567548

Pull #399

github

web-flow
Merge a84526a96 into 64cb606af
Pull Request #399: Use xdg home as default for searching for steel home

0 of 23 new or added lines in 2 files covered. (0.0%)

20 existing lines in 5 files now uncovered.

12505 of 27231 relevant lines covered (45.92%)

418383.87 hits per line

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

32.73
/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,
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::{threads::closure_into_serializable, BuiltInSignature, Continuation},
21
    values::{
22
        closed::{Heap, HeapRef, MarkAndSweepContext},
23
        functions::{BoxedDynFunction, ByteCodeLambda},
24
        lazy_stream::LazyStream,
25
        port::{SendablePort, SteelPort},
26
        structs::{SerializableUserDefinedStruct, UserDefinedStruct},
27
        transducers::{Reducer, Transducer},
28
        HashMapConsumingIter, HashSetConsumingIter, SteelPortRepr, VectorConsumingIter,
29
    },
30
};
31
use std::vec::IntoIter;
32
use std::{
33
    any::{Any, TypeId},
34
    cell::RefCell,
35
    cmp::Ordering,
36
    convert::TryInto,
37
    fmt,
38
    future::Future,
39
    hash::{Hash, Hasher},
40
    io::Write,
41
    ops::Deref,
42
    pin::Pin,
43
    rc::Rc,
44
    result,
45
    sync::{Arc, Mutex},
46
    task::Context,
47
};
48

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

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

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

69
use bigdecimal::BigDecimal;
70
use smallvec::SmallVec;
71
use SteelVal::*;
72

73
use crate::values::{HashMap, HashSet, Vector};
74

75
use futures_task::noop_waker_ref;
76
use futures_util::future::Shared;
77
use futures_util::FutureExt;
78

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

85
use self::cycles::{CycleDetector, IterativeDropHandler};
86

87
pub type RcRefSteelVal = Rc<RefCell<SteelVal>>;
88
pub fn new_rc_ref_cell(x: SteelVal) -> RcRefSteelVal {
×
89
    Rc::new(RefCell::new(x))
×
90
}
91

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

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

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

104
pub type AsyncSignature = fn(&[SteelVal]) -> FutureResult;
105

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

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

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

116
#[derive(Clone)]
117
pub struct FutureResult(Shared<BoxedFutureResult>);
118

119
impl FutureResult {
120
    pub fn new(fut: BoxedFutureResult) -> Self {
11✔
121
        FutureResult(fut.shared())
11✔
122
    }
123

124
    pub fn into_shared(self) -> Shared<BoxedFutureResult> {
11✔
125
        self.0
11✔
126
    }
127
}
128

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

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

143
    // Polling requires a pinned future - TODO make sure this is correct
144
    let mut_fut = Pin::new(&mut fut);
11✔
145

146
    match Future::poll(mut_fut, context) {
11✔
147
        std::task::Poll::Ready(r) => Some(r),
11✔
148
        std::task::Poll::Pending => None,
×
149
    }
150
}
151

152
/// Attempt to cast this custom type down to the underlying type
153
pub fn as_underlying_type<T: 'static>(value: &dyn CustomType) -> Option<&T> {
25,888✔
154
    value.as_any_ref().downcast_ref::<T>()
25,888✔
155
}
156

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

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

166
    fn into_serializable_steelval(&mut self) -> Option<SerializableSteelVal> {
×
167
        None
×
168
    }
169

170
    fn as_iterator(&self) -> Option<Box<dyn Iterator<Item = SteelVal>>> {
×
171
        None
×
172
    }
173

174
    fn gc_drop_mut(&mut self, _drop_handler: &mut IterativeDropHandler) {}
×
175

176
    fn gc_visit_children(&self, _context: &mut MarkAndSweepContext) {}
970✔
177

178
    fn visit_equality(&self, _visitor: &mut cycles::EqualityVisitor) {}
×
179

180
    fn equality_hint(&self, _other: &dyn CustomType) -> bool {
×
181
        true
×
182
    }
183

184
    fn equality_hint_general(&self, _other: &SteelVal) -> bool {
1✔
185
        false
1✔
186
    }
187
}
188

189
#[cfg(not(feature = "sync"))]
190
pub trait MaybeSendSyncStatic: 'static {}
191

192
#[cfg(not(feature = "sync"))]
193
impl<T: 'static> MaybeSendSyncStatic for T {}
194

195
#[cfg(feature = "sync")]
196
pub trait MaybeSendSyncStatic: Send + Sync + 'static {}
197

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

201
#[cfg(feature = "sync")]
202
pub trait CustomType: MaybeSendSyncStatic {
203
    fn as_any_ref(&self) -> &dyn Any;
204
    fn as_any_ref_mut(&mut self) -> &mut dyn Any;
205
    fn name(&self) -> &str {
×
206
        std::any::type_name::<Self>()
×
207
    }
208
    fn inner_type_id(&self) -> TypeId;
209
    fn display(&self) -> std::result::Result<String, std::fmt::Error> {
×
210
        Ok(format!("#<{}>", self.name().to_string()))
×
211
    }
212
    fn as_serializable_steelval(&mut self) -> Option<SerializableSteelVal> {
×
213
        None
×
214
    }
215
    fn drop_mut(&mut self, _drop_handler: &mut IterativeDropHandler) {}
×
216
    fn visit_children(&self, _context: &mut MarkAndSweepContext) {}
×
217
    fn visit_children_for_equality(&self, _visitor: &mut cycles::EqualityVisitor) {}
×
218
    fn check_equality_hint(&self, _other: &dyn CustomType) -> bool {
×
219
        true
×
220
    }
221
    fn check_equality_hint_general(&self, _other: &SteelVal) -> bool {
×
222
        false
×
223
    }
224
}
225

226
#[cfg(not(feature = "sync"))]
227
pub trait CustomType {
228
    fn as_any_ref(&self) -> &dyn Any;
229
    fn as_any_ref_mut(&mut self) -> &mut dyn Any;
230
    fn name(&self) -> &str {
×
231
        std::any::type_name::<Self>()
×
232
    }
233
    fn inner_type_id(&self) -> TypeId;
234
    fn display(&self) -> std::result::Result<String, std::fmt::Error> {
×
235
        Ok(format!("#<{}>", self.name().to_string()))
×
236
    }
237
    fn as_serializable_steelval(&mut self) -> Option<SerializableSteelVal> {
×
238
        None
×
239
    }
240
    fn drop_mut(&mut self, _drop_handler: &mut IterativeDropHandler) {}
241
    fn visit_children(&self, _context: &mut MarkAndSweepContext) {}
242
    fn visit_children_for_equality(&self, _visitor: &mut cycles::EqualityVisitor) {}
243
    fn check_equality_hint(&self, _other: &dyn CustomType) -> bool {
×
244
        true
×
245
    }
246
    fn check_equality_hint_general(&self, _other: &SteelVal) -> bool {
×
247
        false
×
248
    }
249
}
250

251
impl<T: Custom + MaybeSendSyncStatic> CustomType for T {
252
    fn as_any_ref(&self) -> &dyn Any {
49,082✔
253
        self as &dyn Any
49,082✔
254
    }
255
    fn as_any_ref_mut(&mut self) -> &mut dyn Any {
1,896✔
256
        self as &mut dyn Any
1,896✔
257
    }
258
    fn inner_type_id(&self) -> TypeId {
×
259
        std::any::TypeId::of::<Self>()
×
260
    }
261
    fn display(&self) -> std::result::Result<String, std::fmt::Error> {
×
262
        if let Some(formatted) = self.fmt() {
×
263
            formatted
×
264
        } else {
265
            Ok(format!("#<{}>", self.name().to_string()))
×
266
        }
267
    }
268

269
    fn as_serializable_steelval(&mut self) -> Option<SerializableSteelVal> {
×
270
        <T as Custom>::into_serializable_steelval(self)
×
271
    }
272

273
    fn drop_mut(&mut self, drop_handler: &mut IterativeDropHandler) {
×
274
        self.gc_drop_mut(drop_handler)
×
275
    }
276

277
    fn visit_children(&self, context: &mut MarkAndSweepContext) {
970✔
278
        self.gc_visit_children(context)
970✔
279
    }
280

281
    // TODO: Equality visitor
282
    fn visit_children_for_equality(&self, visitor: &mut cycles::EqualityVisitor) {
×
283
        self.visit_equality(visitor)
×
284
    }
285

286
    fn check_equality_hint(&self, other: &dyn CustomType) -> bool {
×
287
        self.equality_hint(other)
×
288
    }
289

290
    fn check_equality_hint_general(&self, other: &SteelVal) -> bool {
1✔
291
        self.equality_hint_general(other)
1✔
292
    }
293
}
294

295
impl<T: CustomType + 'static> IntoSteelVal for T {
296
    fn into_steelval(self) -> Result<SteelVal> {
13,577✔
297
        Ok(SteelVal::Custom(Gc::new_mut(Box::new(self))))
13,577✔
298
    }
299
}
300

301
pub trait IntoSerializableSteelVal {
302
    fn into_serializable_steelval(val: &SteelVal) -> Result<SerializableSteelVal>;
303
}
304

305
impl<T: CustomType + Clone + Send + Sync + 'static> IntoSerializableSteelVal for T {
306
    fn into_serializable_steelval(val: &SteelVal) -> Result<SerializableSteelVal> {
×
307
        if let SteelVal::Custom(v) = val {
×
308
            // let left_type = v.borrow().as_any_ref();
309
            // TODO: @Matt - dylibs cause issues here, as the underlying type ids are different
310
            // across workspaces and builds
311
            let left = v.read().as_any_ref().downcast_ref::<T>().cloned();
×
312
            let _lifted = left.ok_or_else(|| {
×
313
                let error_message = format!(
×
314
                    "Type Mismatch: Type of SteelVal: {:?}, did not match the given type: {}",
×
315
                    val,
×
316
                    std::any::type_name::<Self>()
×
317
                );
318
                SteelErr::new(ErrorKind::ConversionError, error_message)
×
319
            });
320

321
            todo!()
322
        } else {
323
            let error_message = format!(
×
324
                "Type Mismatch: Type of SteelVal: {:?} did not match the given type, expecting opaque struct: {}",
325
                val,
×
326
                std::any::type_name::<Self>()
×
327
            );
328

329
            Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
330
        }
331
    }
332
}
333

334
// TODO: Marshalling out of the type could also try to yoink from a native steel struct.
335
// If possible, we can try to line the constructor up with the fields
336
impl<T: CustomType + Clone + 'static> FromSteelVal for T {
337
    fn from_steelval(val: &SteelVal) -> Result<Self> {
5✔
338
        if let SteelVal::Custom(v) = val {
10✔
339
            // let left_type = v.borrow().as_any_ref();
340
            // TODO: @Matt - dylibs cause issues here, as the underlying type ids are different
341
            // across workspaces and builds
342
            let left = v.read().as_any_ref().downcast_ref::<T>().cloned();
×
343
            left.ok_or_else(|| {
×
344
                let error_message = format!(
×
345
                    "Type Mismatch: Type of SteelVal: {:?}, did not match the given type: {}",
×
346
                    val,
×
347
                    std::any::type_name::<Self>()
×
348
                );
349
                SteelErr::new(ErrorKind::ConversionError, error_message)
×
350
            })
351
        } else {
352
            let error_message = format!(
×
353
                "Type Mismatch: Type of SteelVal: {:?} did not match the given type, expecting opaque struct: {}",
354
                val,
×
355
                std::any::type_name::<Self>()
×
356
            );
357

358
            Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
359
        }
360
    }
361
}
362

363
/// The entry point for turning values into SteelVals
364
/// The is implemented for most primitives and collections
365
/// You can also manually implement this for any type, or can optionally
366
/// get this implementation for a custom struct by using the custom
367
/// steel derive.
368
pub trait IntoSteelVal: Sized {
369
    fn into_steelval(self) -> Result<SteelVal>;
370
}
371

372
/// The exit point for turning SteelVals into outside world values
373
/// This is implement for most primitives and collections
374
/// You can also manually implement this for any type, or can optionally
375
/// get this implementation for a custom struct by using the custom
376
/// steel derive.
377
pub trait FromSteelVal: Sized {
378
    fn from_steelval(val: &SteelVal) -> Result<Self>;
379
}
380

381
pub trait PrimitiveAsRef<'a>: Sized {
382
    fn primitive_as_ref(val: &'a SteelVal) -> Result<Self>;
383
    fn maybe_primitive_as_ref(val: &'a SteelVal) -> Option<Self>;
384
}
385

386
pub trait PrimitiveAsRefMut<'a>: Sized {
387
    fn primitive_as_ref(val: &'a mut SteelVal) -> Result<Self>;
388
    fn maybe_primitive_as_ref(val: &'a mut SteelVal) -> Option<Self>;
389
}
390

391
pub struct RestArgsIter<'a, T>(
392
    pub std::iter::Map<std::slice::Iter<'a, SteelVal>, fn(&'a SteelVal) -> Result<T>>,
393
);
394

395
impl<'a, T: PrimitiveAsRef<'a> + 'a> RestArgsIter<'a, T> {
396
    pub fn new(
×
397
        args: std::iter::Map<std::slice::Iter<'a, SteelVal>, fn(&'a SteelVal) -> Result<T>>,
398
    ) -> Self {
399
        RestArgsIter(args)
×
400
    }
401

402
    pub fn from_slice(args: &'a [SteelVal]) -> Result<Self> {
74,386✔
403
        Ok(RestArgsIter(args.iter().map(T::primitive_as_ref)))
74,386✔
404
    }
405
}
406

407
impl<'a, T> Iterator for RestArgsIter<'a, T> {
408
    type Item = Result<T>;
409

410
    fn next(&mut self) -> Option<Self::Item> {
211,409✔
411
        self.0.next()
211,409✔
412
    }
413

414
    fn size_hint(&self) -> (usize, Option<usize>) {
833✔
415
        self.0.size_hint()
833✔
416
    }
417
}
418

419
impl<'a, T> ExactSizeIterator for RestArgsIter<'a, T> {}
420

421
pub struct RestArgs<T: FromSteelVal>(pub Vec<T>);
422

423
impl<T: FromSteelVal> RestArgs<T> {
424
    pub fn new(args: Vec<T>) -> Self {
×
425
        RestArgs(args)
×
426
    }
427

428
    pub fn from_slice(args: &[SteelVal]) -> Result<Self> {
×
429
        args.iter()
×
430
            .map(|x| T::from_steelval(x))
×
431
            .collect::<Result<Vec<_>>>()
432
            .map(RestArgs)
×
433
    }
434
}
435

436
impl<T: FromSteelVal> std::ops::Deref for RestArgs<T> {
437
    type Target = [T];
438

439
    fn deref(&self) -> &Self::Target {
×
440
        &self.0
×
441
    }
442
}
443

444
mod private {
445

446
    use std::any::Any;
447

448
    pub trait Sealed {}
449

450
    impl<T: Any> Sealed for T {}
451
}
452

453
pub enum SRef<'b, T: ?Sized + 'b> {
454
    Temporary(&'b T),
455
    Owned(MappedScopedReadContainer<'b, T>),
456
}
457

458
impl<'b, T: ?Sized + 'b> Deref for SRef<'b, T> {
459
    type Target = T;
460

461
    #[inline]
462
    fn deref(&self) -> &T {
29,593✔
463
        match self {
29,593✔
464
            SRef::Temporary(inner) => inner,
6,404✔
465
            SRef::Owned(inner) => inner,
23,189✔
466
        }
467
    }
468
}
469

470
// Can you take a steel val and execute operations on it by reference
471
pub trait AsRefSteelVal: Sized {
472
    type Nursery: Default;
473

474
    fn as_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<SRef<'b, Self>>;
475
}
476

477
pub trait AsSlice<T> {
478
    fn as_slice_repr(&self) -> &[T];
479
}
480

481
impl<T> AsSlice<T> for Vec<T> {
482
    fn as_slice_repr(&self) -> &[T] {
×
483
        self.as_slice()
×
484
    }
485
}
486

487
// TODO: Try to incorporate these all into one trait if possible
488
pub trait AsRefSteelValFromUnsized<T>: Sized {
489
    type Output: AsSlice<T>;
490

491
    fn as_ref_from_unsized(val: &SteelVal) -> Result<Self::Output>;
492
}
493

494
pub trait AsRefMutSteelVal: Sized {
495
    fn as_mut_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<MappedScopedWriteContainer<'b, Self>>;
496
}
497

498
pub trait AsRefMutSteelValFromRef: Sized {
499
    fn as_mut_ref_from_ref<'a>(val: &'a SteelVal) -> crate::rvals::Result<&'a mut Self>;
500
}
501

502
pub trait AsRefSteelValFromRef: Sized {
503
    fn as_ref_from_ref<'a>(val: &'a SteelVal) -> crate::rvals::Result<&'a Self>;
504
}
505

506
impl AsRefSteelVal for UserDefinedStruct {
507
    type Nursery = ();
508

509
    fn as_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<SRef<'b, Self>> {
38✔
510
        if let SteelVal::CustomStruct(l) = val {
76✔
511
            Ok(SRef::Temporary(l))
×
512
        } else {
513
            stop!(TypeMismatch => "Value cannot be referenced as a list")
×
514
        }
515
    }
516
}
517

518
impl<T: CustomType + MaybeSendSyncStatic> AsRefSteelVal for T {
519
    type Nursery = ();
520

521
    fn as_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<SRef<'b, Self>> {
23,189✔
522
        if let SteelVal::Custom(v) = val {
46,378✔
523
            let res = ScopedReadContainer::map(v.read(), |x| x.as_any_ref());
23,189✔
524

525
            if res.is::<T>() {
×
526
                Ok(SRef::Owned(MappedScopedReadContainer::map(res, |x| {
46,378✔
527
                    x.downcast_ref::<T>().unwrap()
23,189✔
528
                })))
529
            } else {
530
                let error_message = format!(
×
531
                    "Type Mismatch: Type of SteelVal: {} did not match the given type: {}",
532
                    val,
×
533
                    std::any::type_name::<Self>()
×
534
                );
535
                Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
536
            }
537
            // res
538
        } else {
539
            let error_message = format!(
×
540
                "Type Mismatch: Type of SteelVal: {} did not match the given type: {}",
541
                val,
×
542
                std::any::type_name::<Self>()
×
543
            );
544

545
            Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
546
        }
547
    }
548
}
549

550
impl<T: CustomType + MaybeSendSyncStatic> AsRefMutSteelVal for T {
551
    fn as_mut_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<MappedScopedWriteContainer<'b, Self>> {
1,896✔
552
        if let SteelVal::Custom(v) = val {
3,792✔
553
            let res = ScopedWriteContainer::map(v.write(), |x| x.as_any_ref_mut());
1,896✔
554

555
            if res.is::<T>() {
×
556
                Ok(MappedScopedWriteContainer::map(res, |x| {
3,792✔
557
                    x.downcast_mut::<T>().unwrap()
1,896✔
558
                }))
559
            } else {
560
                let error_message = format!(
×
561
                    "Type Mismatch: Type of SteelVal: {} did not match the given type: {}",
562
                    val,
×
563
                    std::any::type_name::<Self>()
×
564
                );
565
                Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
566
            }
567
            // res
568
        } else {
569
            let error_message = format!(
×
570
                "Type Mismatch: Type of SteelVal: {} did not match the given type: {}",
571
                val,
×
572
                std::any::type_name::<Self>()
×
573
            );
574

575
            Err(SteelErr::new(ErrorKind::ConversionError, error_message))
×
576
        }
577
    }
578
}
579

580
impl ast::TryFromSteelValVisitorForExprKind {
581
    pub fn visit_syntax_object(&mut self, value: &Syntax) -> Result<ExprKind> {
2,442✔
582
        let span = value.span;
2,442✔
583

584
        // dbg!(&span);
585
        // let source = self.source.clone();
586
        match &value.syntax {
643✔
587
            // Mutual recursion case
588
            SyntaxObject(s) => self.visit_syntax_object(s),
2✔
589
            BoolV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
5✔
590
                TokenType::BooleanLiteral(*x),
5✔
591
                span,
5✔
592
            )))),
593
            NumV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
594
                RealLiteral::Float(*x).into(),
×
595
                span,
×
596
            )))),
597
            IntV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
121✔
598
                RealLiteral::Int(IntLiteral::Small(*x)).into(),
121✔
599
                span,
121✔
600
            )))),
601
            VectorV(lst) => {
×
602
                let items: Result<Vec<ExprKind>> = lst.iter().map(|x| self.visit(x)).collect();
×
603
                Ok(ExprKind::List(crate::parser::ast::List::new(items?)))
×
604
            }
605
            StringV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
12✔
606
                TokenType::StringLiteral(x.to_arc_string()),
12✔
607
                span,
12✔
608
            )))),
609

610
            SymbolV(x) if x.starts_with("#:") => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
645✔
611
                TokenType::Keyword(x.as_str().into()),
2✔
612
                span,
2✔
613
            )))),
614

615
            SymbolV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
641✔
616
                TokenType::Identifier(x.as_str().into()),
641✔
617
                span,
641✔
618
            )))),
619

620
            ListV(l) => {
1,659✔
621
                // Rooted - things operate as normal
622
                if self.qq_depth == 0 {
1,659✔
623
                    let maybe_special_form = l.first().and_then(|x| {
3,309✔
624
                        x.as_symbol()
1,650✔
625
                            .or_else(|| x.as_syntax_object().and_then(|x| x.syntax.as_symbol()))
4,527✔
626
                    });
627

628
                    match maybe_special_form {
1,641✔
629
                        Some(x) if x.as_str() == "quote" => {
1,687✔
630
                            if self.quoted {
23✔
631
                                let items: std::result::Result<Vec<ExprKind>, _> =
×
632
                                    l.iter().map(|x| self.visit(x)).collect();
×
633

634
                                return Ok(ExprKind::List(ast::List::new(items?)));
×
635
                            }
636

637
                            self.quoted = true;
23✔
638

639
                            let return_value = l
23✔
640
                                .into_iter()
641
                                .map(|x| self.visit(x))
46✔
642
                                .collect::<std::result::Result<Vec<_>, _>>()?
643
                                .try_into()?;
644

645
                            self.quoted = false;
646

647
                            return Ok(return_value);
648
                        } // "quasiquote" => {
649
                        //     self.qq_depth += 1;
650
                        // }
651
                        // None => {
652
                        // return Ok(ExprKind::empty());
653
                        // }
654
                        _ => {}
1,636✔
655
                    }
656
                }
657

658
                Ok(l.into_iter()
1,636✔
659
                    .map(|x| self.visit(x))
13,468✔
660
                    .collect::<std::result::Result<Vec<_>, _>>()?
×
661
                    .try_into()?)
1,636✔
662
            }
663

664
            CharV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
665
                TokenType::CharacterLiteral(*x),
×
666
                span,
×
667
            )))),
668
            _ => stop!(ConversionError => "unable to convert {:?} to expression", &value.syntax),
×
669
        }
670
    }
671
}
672

673
#[derive(Debug, Clone)]
674
pub struct Syntax {
675
    pub(crate) raw: Option<SteelVal>,
676
    pub(crate) syntax: SteelVal,
677
    span: Span,
678
}
679

680
impl Syntax {
681
    pub fn new(syntax: SteelVal, span: Span) -> Syntax {
776✔
682
        Self {
683
            raw: None,
684
            syntax,
685
            span,
686
        }
687
    }
688

689
    pub fn proto(raw: SteelVal, syntax: SteelVal, span: Span) -> Syntax {
5,338✔
690
        Self {
691
            raw: Some(raw),
5,338✔
692
            syntax,
693
            span,
694
        }
695
    }
696

697
    pub fn syntax_e(&self) -> SteelVal {
1,667✔
698
        self.syntax.clone()
1,667✔
699
    }
700

701
    pub fn new_with_source(syntax: SteelVal, span: Span) -> Syntax {
×
702
        Self {
703
            raw: None,
704
            syntax,
705
            span,
706
        }
707
    }
708

709
    pub fn syntax_loc(&self) -> Span {
776✔
710
        self.span
776✔
711
    }
712

713
    pub fn syntax_datum(&self) -> SteelVal {
3,455✔
714
        self.raw.clone().unwrap()
3,455✔
715
    }
716

717
    pub(crate) fn steelval_to_exprkind(value: &SteelVal) -> Result<ExprKind> {
×
718
        match value {
×
719
            // Mutual recursion case
720
            SyntaxObject(s) => s.to_exprkind(),
×
721
            BoolV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::default(
×
722
                TokenType::BooleanLiteral(*x),
×
723
            )))),
724
            NumV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::default(
×
725
                RealLiteral::Float(*x).into(),
×
726
            )))),
727
            IntV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::default(
×
728
                RealLiteral::Int(IntLiteral::Small(*x)).into(),
×
729
            )))),
730
            VectorV(lst) => {
×
731
                let items: Result<Vec<ExprKind>> =
×
732
                    lst.iter().map(Self::steelval_to_exprkind).collect();
×
733
                Ok(ExprKind::List(crate::parser::ast::List::new(items?)))
×
734
            }
735
            StringV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::default(
×
736
                TokenType::StringLiteral(x.to_arc_string()),
×
737
            )))),
738
            // LambdaV(_) => Err("Can't convert from Lambda to expression!"),
739
            // MacroV(_) => Err("Can't convert from Macro to expression!"),
740
            SymbolV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::default(
×
741
                TokenType::Identifier(x.as_str().into()),
×
742
            )))),
743
            ListV(l) => {
×
744
                let items: Result<Vec<ExprKind>> =
×
745
                    l.iter().map(Self::steelval_to_exprkind).collect();
×
746

747
                Ok(ExprKind::List(crate::parser::ast::List::new(items?)))
×
748
            }
749
            CharV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::default(
×
750
                TokenType::CharacterLiteral(*x),
×
751
            )))),
752
            _ => stop!(ConversionError => "unable to convert {:?} to expression", value),
×
753
        }
754
    }
755

756
    // TODO: match on self.syntax. If its itself a syntax object, then just recur on that until we bottom out
757
    // Otherwise, reconstruct the ExprKind and replace the span and source information into the representation
758
    pub fn to_exprkind(&self) -> Result<ExprKind> {
×
759
        let span = self.span;
×
760
        // let source = self.source.clone();
761
        match &self.syntax {
×
762
            // Mutual recursion case
763
            SyntaxObject(s) => s.to_exprkind(),
×
764
            BoolV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
765
                TokenType::BooleanLiteral(*x),
×
766
                span,
×
767
            )))),
768
            NumV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
769
                RealLiteral::Float(*x).into(),
×
770
                span,
×
771
            )))),
772
            IntV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
773
                RealLiteral::Int(IntLiteral::Small(*x)).into(),
×
774
                span,
×
775
            )))),
776
            VectorV(lst) => {
×
777
                let items: Result<Vec<ExprKind>> =
×
778
                    lst.iter().map(Self::steelval_to_exprkind).collect();
×
779
                Ok(ExprKind::List(crate::parser::ast::List::new(items?)))
×
780
            }
781
            StringV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
782
                TokenType::StringLiteral(x.to_arc_string()),
×
783
                span,
×
784
            )))),
785
            // LambdaV(_) => Err("Can't convert from Lambda to expression!"),
786
            // MacroV(_) => Err("Can't convert from Macro to expression!"),
787
            SymbolV(x) => Ok(ExprKind::Atom(Atom::new(SyntaxObject::new(
×
788
                TokenType::Identifier(x.as_str().into()),
×
789
                span,
×
790
            )))),
791
            ListV(l) => {
×
792
                let items: Result<Vec<ExprKind>> =
×
793
                    l.iter().map(Self::steelval_to_exprkind).collect();
×
794

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

806
impl IntoSteelVal for Syntax {
807
    fn into_steelval(self) -> Result<SteelVal> {
1,431✔
808
        Ok(SteelVal::SyntaxObject(Gc::new(self)))
1,431✔
809
    }
810
}
811

812
impl AsRefSteelVal for Syntax {
813
    type Nursery = ();
814

815
    fn as_ref<'b, 'a: 'b>(val: &'a SteelVal) -> Result<SRef<'b, Self>> {
6,366✔
816
        if let SteelVal::SyntaxObject(s) = val {
12,732✔
817
            Ok(SRef::Temporary(s))
×
818
        } else {
819
            stop!(TypeMismatch => "Value cannot be referenced as a syntax object: {}", val)
×
820
        }
821
    }
822
}
823

824
impl From<Syntax> for SteelVal {
825
    fn from(val: Syntax) -> Self {
14,343✔
826
        SteelVal::SyntaxObject(Gc::new(val))
14,343✔
827
    }
828
}
829

830
// TODO:
831
// This needs to be a method on the runtime: in order to properly support
832
// threads
833
// Tracking issue here: https://github.com/mattwparas/steel/issues/98
834

835
// Values which can be sent to another thread.
836
// If it cannot be sent to another thread, then we'll error out on conversion.
837
// TODO: Add boxed dyn functions to this.
838
// #[derive(PartialEq)]
839
pub enum SerializableSteelVal {
840
    Closure(crate::values::functions::SerializedLambda),
841
    BoolV(bool),
842
    NumV(f64),
843
    IntV(isize),
844
    CharV(char),
845
    Void,
846
    StringV(String),
847
    FuncV(FunctionSignature),
848
    MutFunc(MutFunctionSignature),
849
    HashMapV(Vec<(SerializableSteelVal, SerializableSteelVal)>),
850
    ListV(Vec<SerializableSteelVal>),
851
    Pair(Box<(SerializableSteelVal, SerializableSteelVal)>),
852
    VectorV(Vec<SerializableSteelVal>),
853
    ByteVectorV(Vec<u8>),
854
    BoxedDynFunction(BoxedDynFunction),
855
    BuiltIn(BuiltInSignature),
856
    SymbolV(String),
857
    Custom(Box<dyn CustomType + Send>),
858
    CustomStruct(SerializableUserDefinedStruct),
859
    // Attempt to reuse the storage if possible
860
    HeapAllocated(usize),
861
    Port(SendablePort),
862
    Rational(Rational32),
863
}
864

865
pub enum SerializedHeapRef {
866
    Serialized(Option<SerializableSteelVal>),
867
    Closed(HeapRef<SteelVal>),
868
}
869

870
pub struct HeapSerializer<'a> {
871
    pub heap: &'a mut Heap,
872
    pub fake_heap: &'a mut std::collections::HashMap<usize, SerializedHeapRef>,
873
    // After the conversion, we go back through, and patch the values from the fake heap
874
    // in to each of the values listed here - otherwise, we'll miss cycles
875
    pub values_to_fill_in: &'a mut std::collections::HashMap<usize, HeapRef<SteelVal>>,
876

877
    // Cache the functions that get built
878
    pub built_functions: &'a mut std::collections::HashMap<u32, Gc<ByteCodeLambda>>,
879
}
880

881
// Once crossed over the line, convert BACK into a SteelVal
882
// This should be infallible.
883
pub fn from_serializable_value(ctx: &mut HeapSerializer, val: SerializableSteelVal) -> SteelVal {
×
884
    match val {
×
885
        SerializableSteelVal::Closure(c) => {
×
886
            if c.captures.is_empty() {
×
887
                if let Some(already_made) = ctx.built_functions.get(&c.id) {
×
888
                    SteelVal::Closure(already_made.clone())
889
                } else {
890
                    let id = c.id;
×
891
                    let value = Gc::new(ByteCodeLambda::from_serialized(ctx, c));
×
892

893
                    // Save those as well
894
                    // Probably need to just do this for all
895
                    ctx.built_functions.insert(id, value.clone());
×
896
                    SteelVal::Closure(value)
×
897
                }
898
            } else {
899
                SteelVal::Closure(Gc::new(ByteCodeLambda::from_serialized(ctx, c)))
×
900
            }
901
        }
902
        SerializableSteelVal::BoolV(b) => SteelVal::BoolV(b),
×
903
        SerializableSteelVal::NumV(n) => SteelVal::NumV(n),
×
904
        SerializableSteelVal::IntV(i) => SteelVal::IntV(i),
×
905
        SerializableSteelVal::CharV(c) => SteelVal::CharV(c),
×
906
        SerializableSteelVal::Void => SteelVal::Void,
×
907
        SerializableSteelVal::Rational(r) => SteelVal::Rational(r),
×
908
        SerializableSteelVal::StringV(s) => SteelVal::StringV(s.into()),
×
909
        SerializableSteelVal::FuncV(f) => SteelVal::FuncV(f),
×
910
        SerializableSteelVal::MutFunc(f) => SteelVal::MutFunc(f),
×
911
        SerializableSteelVal::HashMapV(h) => SteelVal::HashMapV(
912
            Gc::new(
×
913
                h.into_iter()
×
914
                    .map(|(k, v)| {
×
915
                        (
916
                            from_serializable_value(ctx, k),
×
917
                            from_serializable_value(ctx, v),
×
918
                        )
919
                    })
920
                    .collect::<HashMap<_, _>>(),
×
921
            )
922
            .into(),
×
923
        ),
924
        SerializableSteelVal::ListV(v) => SteelVal::ListV(
925
            v.into_iter()
×
926
                .map(|x| from_serializable_value(ctx, x))
×
927
                .collect(),
×
928
        ),
929
        SerializableSteelVal::VectorV(v) => SteelVal::VectorV(SteelVector(Gc::new(
×
930
            v.into_iter()
×
931
                .map(|x| from_serializable_value(ctx, x))
×
932
                .collect(),
×
933
        ))),
934
        SerializableSteelVal::BoxedDynFunction(f) => SteelVal::BoxedFunction(Gc::new(f)),
×
935
        SerializableSteelVal::BuiltIn(f) => SteelVal::BuiltIn(f),
×
936
        SerializableSteelVal::SymbolV(s) => SteelVal::SymbolV(s.into()),
×
937
        SerializableSteelVal::Custom(b) => SteelVal::Custom(Gc::new_mut(b)),
×
938
        SerializableSteelVal::CustomStruct(s) => {
×
939
            SteelVal::CustomStruct(Gc::new(UserDefinedStruct {
×
940
                fields: {
×
941
                    let fields = s
×
942
                        .fields
×
943
                        .into_iter()
×
944
                        .map(|x| from_serializable_value(ctx, x));
×
945

946
                    // fields.collect()
947

948
                    // let mut recycle: crate::values::recycler::Recycle<Vec<_>> =
949
                    //     crate::values::recycler::Recycle::new();
950

951
                    let mut recycle: crate::values::recycler::Recycle<SmallVec<_>> =
×
952
                        crate::values::recycler::Recycle::new();
×
953

954
                    recycle.extend(fields);
×
955

956
                    recycle
×
957
                },
958
                type_descriptor: s.type_descriptor,
×
959
            }))
960
        }
961
        SerializableSteelVal::Port(p) => SteelVal::PortV(SteelPort::from_sendable_port(p)),
×
962
        SerializableSteelVal::HeapAllocated(v) => {
×
963
            // todo!()
964

965
            if let Some(mut guard) = ctx.fake_heap.get_mut(&v) {
×
966
                match &mut guard {
967
                    SerializedHeapRef::Serialized(value) => {
×
968
                        let value = std::mem::take(value);
×
969

970
                        if let Some(value) = value {
×
971
                            let value = from_serializable_value(ctx, value);
972
                            let allocation = ctx.heap.allocate_without_collection(value);
973

974
                            ctx.fake_heap
975
                                .insert(v, SerializedHeapRef::Closed(allocation.clone()));
976

977
                            SteelVal::HeapAllocated(allocation)
978
                        } else {
979
                            // println!("If we're getting here - it means the value from the heap has already
980
                            // been converting. if so, we should do something...");
981

982
                            let fake_allocation =
×
983
                                ctx.heap.allocate_without_collection(SteelVal::Void);
×
984

985
                            ctx.values_to_fill_in.insert(v, fake_allocation.clone());
×
986

987
                            SteelVal::HeapAllocated(fake_allocation)
×
988
                        }
989
                    }
990

991
                    SerializedHeapRef::Closed(c) => SteelVal::HeapAllocated(c.clone()),
×
992
                }
993
            } else {
994
                // Shouldn't silently fail here, but we will... for now
995

996
                let allocation = ctx.heap.allocate_without_collection(SteelVal::Void);
×
997

998
                ctx.fake_heap
×
999
                    .insert(v, SerializedHeapRef::Closed(allocation.clone()));
×
1000

1001
                SteelVal::HeapAllocated(allocation)
×
1002
            }
1003
        }
1004
        SerializableSteelVal::Pair(pair) => {
×
1005
            let (car, cdr) = *pair;
×
1006

1007
            crate::values::lists::Pair::cons(
1008
                from_serializable_value(ctx, car),
×
1009
                from_serializable_value(ctx, cdr),
×
1010
            )
1011
            .into()
1012
        }
1013
        SerializableSteelVal::ByteVectorV(bytes) => {
×
1014
            SteelVal::ByteVector(SteelByteVector::new(bytes))
×
1015
        }
1016
    }
1017
}
1018

1019
// The serializable value needs to refer to the original heap -
1020
// that way can reference the original stuff easily.
1021

1022
// TODO: Use the cycle detector instead
1023
pub fn into_serializable_value(
×
1024
    val: SteelVal,
1025
    serialized_heap: &mut std::collections::HashMap<usize, SerializableSteelVal>,
1026
    visited: &mut std::collections::HashSet<usize>,
1027
) -> Result<SerializableSteelVal> {
1028
    // dbg!(&serialized_heap);
1029

1030
    match val {
×
1031
        SteelVal::Closure(c) => closure_into_serializable(&c, serialized_heap, visited)
×
1032
            .map(SerializableSteelVal::Closure),
×
1033
        SteelVal::BoolV(b) => Ok(SerializableSteelVal::BoolV(b)),
×
1034
        SteelVal::NumV(n) => Ok(SerializableSteelVal::NumV(n)),
×
1035
        SteelVal::IntV(n) => Ok(SerializableSteelVal::IntV(n)),
×
1036
        SteelVal::CharV(c) => Ok(SerializableSteelVal::CharV(c)),
×
1037
        SteelVal::Void => Ok(SerializableSteelVal::Void),
×
1038
        SteelVal::StringV(s) => Ok(SerializableSteelVal::StringV(s.to_string())),
×
1039
        SteelVal::FuncV(f) => Ok(SerializableSteelVal::FuncV(f)),
×
1040
        SteelVal::ListV(l) => Ok(SerializableSteelVal::ListV(
×
1041
            l.into_iter()
×
1042
                .map(|x| into_serializable_value(x, serialized_heap, visited))
×
1043
                .collect::<Result<_>>()?,
×
1044
        )),
1045
        SteelVal::Pair(pair) => Ok(SerializableSteelVal::Pair(Box::new((
×
1046
            into_serializable_value(pair.car.clone(), serialized_heap, visited)?,
×
1047
            into_serializable_value(pair.cdr.clone(), serialized_heap, visited)?,
×
1048
        )))),
1049
        SteelVal::BoxedFunction(f) => Ok(SerializableSteelVal::BoxedDynFunction((*f).clone())),
×
1050
        SteelVal::BuiltIn(f) => Ok(SerializableSteelVal::BuiltIn(f)),
×
1051
        SteelVal::SymbolV(s) => Ok(SerializableSteelVal::SymbolV(s.to_string())),
×
1052
        SteelVal::MutFunc(f) => Ok(SerializableSteelVal::MutFunc(f)),
×
1053
        SteelVal::HashMapV(v) => Ok(SerializableSteelVal::HashMapV(
×
1054
            v.0.unwrap()
×
1055
                .into_iter()
×
1056
                .map(|(k, v)| {
×
1057
                    let kprime = into_serializable_value(k, serialized_heap, visited)?;
×
1058
                    let vprime = into_serializable_value(v, serialized_heap, visited)?;
×
1059

1060
                    Ok((kprime, vprime))
1061
                })
1062
                .collect::<Result<_>>()?,
×
1063
        )),
1064

1065
        SteelVal::Custom(c) => {
×
1066
            if let Some(output) = c.write().as_serializable_steelval() {
×
1067
                Ok(output)
1068
            } else {
1069
                stop!(Generic => "Custom type not allowed to be moved across threads!")
×
1070
            }
1071
        }
1072

1073
        SteelVal::CustomStruct(s) => Ok(SerializableSteelVal::CustomStruct(
×
1074
            SerializableUserDefinedStruct {
×
1075
                fields: s
×
1076
                    .fields
×
1077
                    .iter()
×
1078
                    .cloned()
×
1079
                    .map(|x| into_serializable_value(x, serialized_heap, visited))
×
1080
                    .collect::<Result<Vec<_>>>()?,
×
1081
                type_descriptor: s.type_descriptor,
×
1082
            },
1083
        )),
1084

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

1087
        // If there is a cycle, this could cause problems?
1088
        SteelVal::HeapAllocated(h) => {
×
1089
            // We should pick it up on the way back the recursion
1090
            if visited.contains(&h.as_ptr_usize())
×
1091
                && !serialized_heap.contains_key(&h.as_ptr_usize())
×
1092
            {
1093
                // println!("Already visited: {}", h.as_ptr_usize());
1094

1095
                Ok(SerializableSteelVal::HeapAllocated(h.as_ptr_usize()))
×
1096
            } else {
1097
                visited.insert(h.as_ptr_usize());
×
1098

1099
                if serialized_heap.contains_key(&h.as_ptr_usize()) {
×
1100
                    // println!("Already exists in map: {}", h.as_ptr_usize());
1101

1102
                    Ok(SerializableSteelVal::HeapAllocated(h.as_ptr_usize()))
×
1103
                } else {
1104
                    // println!("Trying to insert: {} @ {}", h.get(), h.as_ptr_usize());
1105

1106
                    let value = into_serializable_value(h.get(), serialized_heap, visited);
×
1107

1108
                    let value = match value {
×
1109
                        Ok(v) => v,
1110
                        Err(e) => {
×
1111
                            // println!("{}", e);
1112
                            return Err(e);
×
1113
                        }
1114
                    };
1115

1116
                    serialized_heap.insert(h.as_ptr_usize(), value);
1117

1118
                    // println!("Inserting: {}", h.as_ptr_usize());
1119

1120
                    Ok(SerializableSteelVal::HeapAllocated(h.as_ptr_usize()))
1121
                }
1122
            }
1123
        }
1124

1125
        SteelVal::VectorV(vector) => Ok(SerializableSteelVal::VectorV(
×
1126
            vector
×
1127
                .iter()
×
1128
                .cloned()
×
1129
                .map(|val| into_serializable_value(val, serialized_heap, visited))
×
1130
                .collect::<Result<_>>()?,
×
1131
        )),
1132

1133
        SteelVal::ByteVector(bytes) => {
×
1134
            Ok(SerializableSteelVal::ByteVectorV(bytes.vec.read().clone()))
×
1135
        }
1136

1137
        SteelVal::Rational(r) => Ok(SerializableSteelVal::Rational(r)),
×
1138

1139
        illegal => stop!(Generic => "Type not allowed to be moved across threads!: {}", illegal),
×
1140
    }
1141
}
1142

1143
#[derive(Clone, PartialEq, Eq)]
1144
pub struct SteelMutableVector(pub(crate) Gc<RefCell<Vec<SteelVal>>>);
1145

1146
#[derive(Clone, PartialEq, Eq)]
1147
pub struct SteelVector(pub(crate) Gc<Vector<SteelVal>>);
1148

1149
impl Deref for SteelVector {
1150
    type Target = Vector<SteelVal>;
1151

1152
    fn deref(&self) -> &Self::Target {
36✔
1153
        &self.0
36✔
1154
    }
1155
}
1156

1157
impl From<Gc<Vector<SteelVal>>> for SteelVector {
1158
    fn from(value: Gc<Vector<SteelVal>>) -> Self {
12✔
1159
        SteelVector(value)
12✔
1160
    }
1161
}
1162

1163
#[derive(Clone, PartialEq)]
1164
pub struct SteelHashMap(pub(crate) Gc<HashMap<SteelVal, SteelVal>>);
1165

1166
impl Deref for SteelHashMap {
1167
    type Target = HashMap<SteelVal, SteelVal>;
1168

1169
    fn deref(&self) -> &Self::Target {
502✔
1170
        &self.0
502✔
1171
    }
1172
}
1173

1174
impl From<Gc<HashMap<SteelVal, SteelVal>>> for SteelHashMap {
1175
    fn from(value: Gc<HashMap<SteelVal, SteelVal>>) -> Self {
1✔
1176
        SteelHashMap(value)
1✔
1177
    }
1178
}
1179

1180
#[derive(Clone, PartialEq)]
1181
pub struct SteelHashSet(pub(crate) Gc<HashSet<SteelVal>>);
1182

1183
impl Deref for SteelHashSet {
1184
    type Target = HashSet<SteelVal>;
1185

1186
    fn deref(&self) -> &Self::Target {
9✔
1187
        &self.0
9✔
1188
    }
1189
}
1190

1191
impl From<Gc<HashSet<SteelVal>>> for SteelHashSet {
1192
    fn from(value: Gc<HashSet<SteelVal>>) -> Self {
111✔
1193
        SteelHashSet(value)
111✔
1194
    }
1195
}
1196

1197
pub enum TypeKind {
1198
    Any,
1199
    Bool,
1200
    Num,
1201
    Int,
1202
    Char,
1203
    Vector(Box<TypeKind>),
1204
    Void,
1205
    String,
1206
    Function,
1207
    HashMap(Box<TypeKind>, Box<TypeKind>),
1208
    HashSet(Box<TypeKind>),
1209
    List(Box<TypeKind>),
1210
}
1211

1212
/// A value as represented in the runtime.
1213
#[derive(Clone)]
1214
pub enum SteelVal {
1215
    /// Represents a bytecode closure.
1216
    Closure(Gc<ByteCodeLambda>),
1217
    /// Represents a boolean value.
1218
    BoolV(bool),
1219
    /// Represents a number, currently only f64 numbers are supported.
1220
    NumV(f64),
1221
    /// Represents an integer.
1222
    IntV(isize),
1223
    /// Represents a rational number.
1224
    Rational(Rational32),
1225
    /// Represents a character type
1226
    CharV(char),
1227
    /// Vectors are represented as `im_rc::Vector`'s, which are immutable
1228
    /// data structures
1229
    VectorV(SteelVector),
1230
    /// Void return value
1231
    Void,
1232
    /// Represents strings
1233
    StringV(SteelString),
1234
    /// Represents built in rust functions
1235
    FuncV(FunctionSignature),
1236
    /// Represents a symbol, internally represented as `String`s
1237
    SymbolV(SteelString),
1238
    /// Container for a type that implements the `Custom Type` trait. (trait object)
1239
    Custom(GcMut<Box<dyn CustomType>>), // TODO: @Matt - consider using just a mutex here, to relax some of the bounds?
1240
    // Embedded HashMap
1241
    HashMapV(SteelHashMap),
1242
    // Embedded HashSet
1243
    HashSetV(SteelHashSet),
1244
    /// Represents a scheme-only struct
1245
    CustomStruct(Gc<UserDefinedStruct>),
1246
    /// Represents a port object
1247
    PortV(SteelPort),
1248
    /// Generic iterator wrapper
1249
    IterV(Gc<Transducer>),
1250
    /// Reducers
1251
    ReducerV(Gc<Reducer>),
1252
    /// Async Function wrapper
1253
    FutureFunc(BoxedAsyncFunctionSignature),
1254
    // Boxed Future Result
1255
    FutureV(Gc<FutureResult>),
1256
    // A stream of `SteelVal`.
1257
    StreamV(Gc<LazyStream>),
1258
    /// Custom closure
1259
    BoxedFunction(Gc<BoxedDynFunction>),
1260
    // Continuation
1261
    ContinuationFunction(Continuation),
1262
    // Function Pointer
1263
    // #[cfg(feature = "jit")]
1264
    // CompiledFunction(Box<JitFunctionPointer>),
1265
    // List
1266
    ListV(crate::values::lists::List<SteelVal>),
1267
    // Holds a pair that contains 2 `SteelVal`.
1268
    Pair(Gc<crate::values::lists::Pair>),
1269
    // Mutable functions
1270
    MutFunc(MutFunctionSignature),
1271
    // Built in functions
1272
    BuiltIn(BuiltInSignature),
1273
    // Mutable vector
1274
    MutableVector(HeapRef<Vec<SteelVal>>),
1275
    // This should delegate to the underlying iterator - can allow for faster raw iteration if possible
1276
    // Should allow for polling just a raw "next" on underlying elements
1277
    BoxedIterator(GcMut<OpaqueIterator>),
1278
    // Contains a syntax object.
1279
    SyntaxObject(Gc<Syntax>),
1280
    // Mutable storage, with Gc backing
1281
    // Boxed(HeapRef),
1282
    Boxed(GcMut<SteelVal>),
1283
    // Holds a SteelVal on the heap.
1284
    HeapAllocated(HeapRef<SteelVal>),
1285
    // TODO: This itself, needs to be boxed unfortunately.
1286
    Reference(Gc<OpaqueReference<'static>>),
1287
    // Like IntV but supports larger values.
1288
    BigNum(Gc<BigInt>),
1289
    // Like Rational but supports larger numerators and denominators.
1290
    BigRational(Gc<BigRational>),
1291
    // A complex number.
1292
    Complex(Gc<SteelComplex>),
1293
    // Byte vectors
1294
    ByteVector(SteelByteVector),
1295
}
1296

1297
#[cfg(feature = "sync")]
1298
#[test]
1299
fn check_send_sync() {
1300
    let value = SteelVal::IntV(10);
1301

1302
    let handle = std::thread::spawn(move || value);
1303

1304
    handle.join().unwrap();
1305
}
1306

1307
#[derive(Clone)]
1308
pub struct SteelByteVector {
1309
    pub(crate) vec: GcMut<Vec<u8>>,
1310
}
1311

1312
impl SteelByteVector {
1313
    pub fn new(vec: Vec<u8>) -> Self {
137✔
1314
        Self {
1315
            vec: Gc::new_mut(vec),
137✔
1316
        }
1317
    }
1318
}
1319

1320
impl PartialEq for SteelByteVector {
1321
    fn eq(&self, other: &Self) -> bool {
19✔
1322
        *(self.vec.read()) == *(other.vec.read())
19✔
1323
    }
1324
}
1325

1326
impl Eq for SteelByteVector {}
1327

1328
impl Hash for SteelByteVector {
1329
    fn hash<H: Hasher>(&self, state: &mut H) {
18✔
1330
        self.vec.read().hash(state);
18✔
1331
    }
1332
}
1333

1334
/// Contains a complex number.
1335
///
1336
/// TODO: Optimize the contents of complex value. Holding `SteelVal` makes it easier to use existing
1337
/// operations but a more specialized representation may be faster.
1338
#[derive(Clone, Debug, Hash, PartialEq)]
1339
pub struct SteelComplex {
1340
    /// The real part of the complex number.
1341
    pub re: SteelVal,
1342
    /// The imaginary part of the complex number.
1343
    pub im: SteelVal,
1344
}
1345

1346
impl SteelComplex {
1347
    pub fn new(real: SteelVal, imaginary: SteelVal) -> SteelComplex {
5✔
1348
        SteelComplex {
1349
            re: real,
1350
            im: imaginary,
1351
        }
1352
    }
1353

1354
    /// Returns `true` if the imaginary part is negative.
1355
    pub(crate) fn imaginary_is_negative(&self) -> bool {
1✔
1356
        match &self.im {
1✔
1357
            NumV(x) => x.is_negative(),
×
1358
            IntV(x) => x.is_negative(),
1✔
1359
            Rational(x) => x.is_negative(),
×
1360
            BigNum(x) => x.is_negative(),
×
1361
            SteelVal::BigRational(x) => x.is_negative(),
×
1362
            _ => unreachable!(),
1363
        }
1364
    }
1365

1366
    pub(crate) fn imaginary_is_finite(&self) -> bool {
1✔
1367
        match &self.im {
1✔
1368
            NumV(x) => x.is_finite(),
×
1369
            IntV(_) | Rational(_) | BigNum(_) | SteelVal::BigRational(_) => true,
1✔
1370
            _ => unreachable!(),
1371
        }
1372
    }
1373
}
1374

1375
impl IntoSteelVal for SteelComplex {
1376
    #[inline(always)]
1377
    fn into_steelval(self) -> Result<SteelVal> {
5✔
1378
        Ok(match self.im {
×
1379
            NumV(n) if n.is_zero() => self.re,
×
1380
            IntV(0) => self.re,
×
1381
            _ => SteelVal::Complex(Gc::new(self)),
5✔
1382
        })
1383
    }
1384
}
1385

1386
impl fmt::Display for SteelComplex {
1387
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
1388
        if self.imaginary_is_negative() || !self.imaginary_is_finite() {
×
1389
            write!(f, "{re}{im}i", re = self.re, im = self.im)
×
1390
        } else {
1391
            write!(f, "{re}+{im}i", re = self.re, im = self.im)
×
1392
        }
1393
    }
1394
}
1395

1396
impl SteelVal {
1397
    // TODO: Re-evaluate this - should this be buffered?
1398
    pub fn new_dyn_writer_port(port: impl Write + Send + Sync + 'static) -> SteelVal {
×
1399
        SteelVal::PortV(SteelPort {
×
1400
            port: Gc::new_mut(SteelPortRepr::DynWriter(Arc::new(Mutex::new(port)))),
×
1401
        })
1402
    }
1403

1404
    pub fn anonymous_boxed_function(
×
1405
        function: std::sync::Arc<
1406
            dyn Fn(&[SteelVal]) -> crate::rvals::Result<SteelVal> + Send + Sync + 'static,
1407
        >,
1408
    ) -> SteelVal {
1409
        SteelVal::BoxedFunction(Gc::new(BoxedDynFunction {
×
1410
            function,
×
1411
            name: None,
×
1412
            arity: None,
×
1413
        }))
1414
    }
1415

1416
    pub fn as_box(&self) -> Option<HeapRef<SteelVal>> {
×
1417
        if let SteelVal::HeapAllocated(heap_ref) = self {
×
1418
            Some(heap_ref.clone())
×
1419
        } else {
1420
            None
×
1421
        }
1422
    }
1423

1424
    pub fn as_box_to_inner(&self) -> Option<SteelVal> {
×
1425
        self.as_box().map(|x| x.get())
×
1426
    }
1427

1428
    pub fn as_ptr_usize(&self) -> Option<usize> {
×
1429
        match self {
×
1430
            // Closure(_) => todo!(),
1431
            // BoolV(_) => todo!(),
1432
            // NumV(_) => todo!(),
1433
            // IntV(_) => todo!(),
1434
            // CharV(_) => todo!(),
1435
            // VectorV(_) => todo!(),
1436
            // Void => todo!(),
1437
            // StringV(_) => todo!(),
1438
            // FuncV(_) => todo!(),
1439
            // SymbolV(_) => todo!(),
1440
            // SteelVal::Custom(_) => todo!(),
1441
            // HashMapV(_) => todo!(),
1442
            // HashSetV(_) => todo!(),
1443
            CustomStruct(c) => Some(c.as_ptr() as usize),
×
1444
            // PortV(_) => todo!(),
1445
            // IterV(_) => todo!(),
1446
            // ReducerV(_) => todo!(),
1447
            // FutureFunc(_) => todo!(),
1448
            // FutureV(_) => todo!(),
1449
            // StreamV(_) => todo!(),
1450
            // BoxedFunction(_) => todo!(),
1451
            // ContinuationFunction(_) => todo!(),
1452
            ListV(l) => Some(l.as_ptr_usize()),
×
1453
            // MutFunc(_) => todo!(),
1454
            // BuiltIn(_) => todo!(),
1455
            // MutableVector(_) => todo!(),
1456
            // BoxedIterator(_) => todo!(),
1457
            // SteelVal::SyntaxObject(_) => todo!(),
1458
            // Boxed(_) => todo!(),
1459
            HeapAllocated(h) => Some(h.as_ptr_usize()),
×
1460
            // Reference(_) => todo!(),
1461
            // BigNum(_) => todo!(),
1462
            _ => None,
×
1463
        }
1464
    }
1465

1466
    // pub(crate) fn children_mut<'a>(&'a mut self) -> impl IntoIterator<Item = SteelVal> {
1467
    //     match self {
1468
    //         Self::CustomStruct(inner) => {
1469
    //             if let Some(inner) = inner.get_mut() {
1470
    //                 std::mem::take(&mut inner.borrow_mut().fields)
1471
    //             } else {
1472
    //                 std::iter::empty()
1473
    //             }
1474
    //         }
1475
    //         _ => todo!(),
1476
    //     }
1477
    // }
1478
}
1479

1480
// TODO: Consider unboxed value types, for optimized usages when compiling segments of code.
1481
// If we can infer the types from the concrete functions used, we don't need to have unboxed values -> We also
1482
// can use concrete forms of the underlying functions as well.
1483
// #[derive(Clone)]
1484
// pub enum UnboxedSteelVal {
1485
//     /// Represents a boolean value
1486
//     BoolV(bool),
1487
//     /// Represents a number, currently only f64 numbers are supported
1488
//     NumV(f64),
1489
//     /// Represents an integer
1490
//     IntV(isize),
1491
//     /// Represents a character type
1492
//     CharV(char),
1493
//     /// Vectors are represented as `im_rc::Vector`'s, which are immutable
1494
//     /// data structures
1495
//     VectorV(Vector<SteelVal>),
1496
//     /// Void return value
1497
//     Void,
1498
//     /// Represents strings
1499
//     StringV(SteelString),
1500
//     /// Represents built in rust functions
1501
//     FuncV(FunctionSignature),
1502
//     /// Represents a symbol, internally represented as `String`s
1503
//     SymbolV(SteelString),
1504
//     /// Container for a type that implements the `Custom Type` trait. (trait object)
1505
//     Custom(Gc<RefCell<Box<dyn CustomType>>>),
1506
//     // Embedded HashMap
1507
//     HashMapV(HashMap<SteelVal, SteelVal>),
1508
//     // Embedded HashSet
1509
//     HashSetV(HashSet<SteelVal>),
1510
//     /// Represents a scheme-only struct
1511
//     // StructV(Gc<SteelStruct>),
1512
//     /// Alternative implementation of a scheme-only struct
1513
//     CustomStruct(Gc<RefCell<UserDefinedStruct>>),
1514
//     // Represents a special rust closure
1515
//     // StructClosureV(Box<SteelStruct>, StructClosureSignature),
1516
//     // StructClosureV(Box<StructClosure>),
1517
//     /// Represents a port object
1518
//     PortV(SteelPort),
1519
//     /// Represents a bytecode closure
1520
//     Closure(Gc<ByteCodeLambda>),
1521
//     /// Generic iterator wrapper
1522
//     IterV(Gc<Transducer>),
1523
//     /// Reducers
1524
//     ReducerV(Gc<Reducer>),
1525
//     // Reducer(Reducer)
1526
//     // Generic IntoIter wrapper
1527
//     // Promise(Gc<SteelVal>),
1528
//     /// Async Function wrapper
1529
//     FutureFunc(BoxedAsyncFunctionSignature),
1530
//     // Boxed Future Result
1531
//     FutureV(Gc<FutureResult>),
1532

1533
//     StreamV(Gc<LazyStream>),
1534
//     // Break the cycle somehow
1535
//     // EvaluationEnv(Weak<RefCell<Env>>),
1536
//     /// Contract
1537
//     Contract(Gc<ContractType>),
1538
//     /// Contracted Function
1539
//     ContractedFunction(Gc<ContractedFunction>),
1540
//     /// Custom closure
1541
//     BoxedFunction(BoxedFunctionSignature),
1542
//     // Continuation
1543
//     ContinuationFunction(Gc<Continuation>),
1544
//     // List
1545
//     ListV(List<SteelVal>),
1546
//     // Mutable functions
1547
//     MutFunc(MutFunctionSignature),
1548
//     // Built in functions
1549
//     BuiltIn(BuiltInSignature),
1550
//     // Mutable vector
1551
//     MutableVector(Gc<RefCell<Vec<SteelVal>>>),
1552
//     // This should delegate to the underlying iterator - can allow for faster raw iteration if possible
1553
//     // Should allow for polling just a raw "next" on underlying elements
1554
//     BoxedIterator(Gc<RefCell<BuiltInDataStructureIterator>>),
1555

1556
//     SyntaxObject(Gc<Syntax>),
1557

1558
//     // Mutable storage, with Gc backing
1559
//     Boxed(HeapRef),
1560
// }
1561

1562
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1563
#[repr(C)]
1564
pub struct SteelString(Gc<String>);
1565

1566
impl Deref for SteelString {
1567
    type Target = crate::gc::Shared<String>;
1568

1569
    fn deref(&self) -> &Self::Target {
202,721✔
1570
        &self.0 .0
202,721✔
1571
    }
1572
}
1573

1574
#[cfg(not(feature = "sync"))]
1575
impl From<Arc<String>> for SteelString {
1576
    fn from(value: Arc<String>) -> Self {
1577
        SteelString(Gc(Rc::new((*value).clone())))
1578
    }
1579
}
1580

1581
impl SteelString {
1582
    pub(crate) fn to_arc_string(&self) -> Arc<String> {
1,996✔
1583
        #[cfg(feature = "sync")]
1584
        {
1585
            self.0 .0.clone()
1,996✔
1586
        }
1587
        #[cfg(not(feature = "sync"))]
1588
        Arc::new(self.0.unwrap())
1,996✔
1589
    }
1590
}
1591

1592
impl From<&str> for SteelString {
1593
    fn from(val: &str) -> Self {
306,947✔
1594
        SteelString(Gc::new(val.to_string()))
306,947✔
1595
    }
1596
}
1597

1598
impl From<&String> for SteelString {
1599
    fn from(val: &String) -> Self {
×
1600
        SteelString(Gc::new(val.to_owned()))
×
1601
    }
1602
}
1603

1604
impl From<String> for SteelString {
1605
    fn from(val: String) -> Self {
118,115✔
1606
        SteelString(Gc::new(val))
118,115✔
1607
    }
1608
}
1609

1610
impl From<crate::gc::Shared<String>> for SteelString {
1611
    fn from(val: crate::gc::Shared<String>) -> Self {
13,285✔
1612
        SteelString(Gc(val))
13,285✔
1613
    }
1614
}
1615

1616
impl From<Gc<String>> for SteelString {
1617
    fn from(val: Gc<String>) -> Self {
×
1618
        SteelString(val)
×
1619
    }
1620
}
1621

1622
impl From<SteelString> for crate::gc::Shared<String> {
1623
    fn from(value: SteelString) -> Self {
×
1624
        value.0 .0
×
1625
    }
1626
}
1627

1628
impl From<SteelString> for Gc<String> {
1629
    fn from(value: SteelString) -> Self {
×
1630
        value.0
×
1631
    }
1632
}
1633

1634
impl std::fmt::Display for SteelString {
1635
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13,442✔
1636
        write!(f, "{}", self.0.as_str())
13,442✔
1637
    }
1638
}
1639

1640
impl std::fmt::Debug for SteelString {
1641
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93✔
1642
        write!(f, "{:?}", self.0.as_str())
93✔
1643
    }
1644
}
1645

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

1649
#[test]
1650
fn check_size_of_steelval() {
1651
    assert_eq!(std::mem::size_of::<SteelVal>(), 16);
1652
}
1653

1654
pub struct Chunks {
1655
    remaining: IntoIter<char>,
1656
}
1657

1658
impl Chunks {
1659
    fn new(s: SteelString) -> Self {
×
1660
        Chunks {
1661
            remaining: s.chars().collect::<Vec<_>>().into_iter(),
×
1662
        }
1663
    }
1664
}
1665

1666
pub struct OpaqueIterator {
1667
    pub(crate) root: SteelVal,
1668
    iterator: BuiltInDataStructureIterator,
1669
}
1670

1671
impl Custom for OpaqueIterator {
1672
    fn fmt(&self) -> Option<std::result::Result<String, std::fmt::Error>> {
×
1673
        Some(Ok(format!("#<iterator>")))
×
1674
    }
1675
}
1676

1677
// TODO: Convert this to just a generic custom type. This does not have to be
1678
// a special enum variant.
1679
pub enum BuiltInDataStructureIterator {
1680
    List(crate::values::lists::ConsumingIterator<SteelVal>),
1681
    Vector(VectorConsumingIter<SteelVal>),
1682
    Set(HashSetConsumingIter<SteelVal>),
1683
    Map(HashMapConsumingIter<SteelVal, SteelVal>),
1684
    String(Chunks),
1685
    #[cfg(not(feature = "sync"))]
1686
    Opaque(Box<dyn Iterator<Item = SteelVal>>),
1687
    #[cfg(feature = "sync")]
1688
    Opaque(Box<dyn Iterator<Item = SteelVal> + Send + Sync + 'static>),
1689
}
1690

1691
impl BuiltInDataStructureIterator {
1692
    pub fn into_boxed_iterator(self, value: SteelVal) -> SteelVal {
×
1693
        SteelVal::BoxedIterator(Gc::new_mut(OpaqueIterator {
×
1694
            root: value,
×
1695
            iterator: self,
×
1696
        }))
1697
    }
1698
}
1699

1700
impl BuiltInDataStructureIterator {
1701
    pub fn from_iterator<
×
1702
        T: IntoSteelVal + MaybeSendSyncStatic,
1703
        I: Iterator<Item = T> + MaybeSendSyncStatic,
1704
        S: IntoIterator<Item = T, IntoIter = I> + MaybeSendSyncStatic,
1705
    >(
1706
        value: S,
1707
    ) -> Self {
1708
        Self::Opaque(Box::new(
×
1709
            value
×
1710
                .into_iter()
×
1711
                .map(|x| x.into_steelval().expect("This shouldn't fail!")),
×
1712
        ))
1713
    }
1714
}
1715

1716
impl Iterator for BuiltInDataStructureIterator {
1717
    type Item = SteelVal;
1718

1719
    fn next(&mut self) -> Option<SteelVal> {
×
1720
        match self {
×
1721
            Self::List(l) => l.next(),
×
1722
            Self::Vector(v) => v.next(),
×
1723
            Self::String(s) => s.remaining.next().map(SteelVal::CharV),
×
1724
            Self::Set(s) => s.next(),
×
1725
            Self::Map(s) => s.next().map(|x| SteelVal::ListV(vec![x.0, x.1].into())),
×
1726
            Self::Opaque(s) => s.next(),
×
1727
        }
1728
    }
1729
}
1730

1731
pub fn value_into_iterator(val: SteelVal) -> Option<SteelVal> {
×
1732
    let root = val.clone();
×
1733
    match val {
×
1734
        SteelVal::ListV(l) => Some(BuiltInDataStructureIterator::List(l.into_iter())),
×
1735
        SteelVal::VectorV(v) => Some(BuiltInDataStructureIterator::Vector(
×
1736
            (*v).clone().into_iter(),
×
1737
        )),
1738
        SteelVal::StringV(s) => Some(BuiltInDataStructureIterator::String(Chunks::new(s))),
×
1739
        SteelVal::HashSetV(s) => Some(BuiltInDataStructureIterator::Set((*s).clone().into_iter())),
×
1740
        SteelVal::HashMapV(m) => Some(BuiltInDataStructureIterator::Map((*m).clone().into_iter())),
×
1741
        _ => None,
×
1742
    }
1743
    .map(|iterator| BuiltInDataStructureIterator::into_boxed_iterator(iterator, root))
×
1744
}
1745

1746
thread_local! {
1747
    pub static ITERATOR_FINISHED: SteelVal = SteelVal::SymbolV("done".into());
1748
}
1749

1750
pub fn iterator_next(args: &[SteelVal]) -> Result<SteelVal> {
×
1751
    match &args[0] {
×
1752
        SteelVal::BoxedIterator(b) => match b.write().iterator.next() {
×
1753
            Some(v) => Ok(v),
×
1754
            None => Ok(ITERATOR_FINISHED.with(|x| x.clone())),
×
1755
        },
1756
        _ => stop!(TypeMismatch => "Unexpected argument"),
×
1757
    }
1758
}
1759

1760
impl SteelVal {
1761
    pub fn boxed(value: SteelVal) -> SteelVal {
×
1762
        SteelVal::Boxed(Gc::new_mut(value))
×
1763
    }
1764

1765
    pub(crate) fn ptr_eq(&self, other: &SteelVal) -> bool {
2,361✔
1766
        match (self, other) {
2,361✔
1767
            // Integers are a special case of ptr eq -> if integers are equal? they are also eq?
1768
            (IntV(l), IntV(r)) => l == r,
10✔
1769
            (NumV(l), NumV(r)) => l == r,
2✔
1770
            (BoolV(l), BoolV(r)) => l == r,
×
1771
            (CharV(l), CharV(r)) => l == r,
×
1772
            (VectorV(l), VectorV(r)) => Gc::ptr_eq(&l.0, &r.0),
×
1773
            (Void, Void) => true,
×
1774
            (StringV(l), StringV(r)) => crate::gc::Shared::ptr_eq(l, r),
×
1775
            (FuncV(l), FuncV(r)) => *l as usize == *r as usize,
2✔
1776
            (SymbolV(l), SymbolV(r)) => crate::gc::Shared::ptr_eq(l, r),
2,129✔
1777
            (SteelVal::Custom(l), SteelVal::Custom(r)) => Gc::ptr_eq(l, r),
×
1778
            (HashMapV(l), HashMapV(r)) => Gc::ptr_eq(&l.0, &r.0),
×
1779
            (HashSetV(l), HashSetV(r)) => Gc::ptr_eq(&l.0, &r.0),
×
1780
            (PortV(l), PortV(r)) => Gc::ptr_eq(&l.port, &r.port),
×
1781
            (Closure(l), Closure(r)) => Gc::ptr_eq(l, r),
6✔
1782
            (IterV(l), IterV(r)) => Gc::ptr_eq(l, r),
×
1783
            (ReducerV(l), ReducerV(r)) => Gc::ptr_eq(l, r),
×
1784
            #[allow(clippy::vtable_address_comparisons)]
1785
            (FutureFunc(l), FutureFunc(r)) => crate::gc::Shared::ptr_eq(l, r),
×
1786
            (FutureV(l), FutureV(r)) => Gc::ptr_eq(l, r),
×
1787
            (StreamV(l), StreamV(r)) => Gc::ptr_eq(l, r),
×
1788
            (BoxedFunction(l), BoxedFunction(r)) => Gc::ptr_eq(l, r),
×
1789
            (ContinuationFunction(l), ContinuationFunction(r)) => Continuation::ptr_eq(l, r),
×
1790
            (ListV(l), ListV(r)) => {
183✔
1791
                l.ptr_eq(r) || l.storage_ptr_eq(r) || l.is_empty() && r.is_empty()
207✔
1792
            }
1793
            (MutFunc(l), MutFunc(r)) => *l as usize == *r as usize,
×
1794
            (BuiltIn(l), BuiltIn(r)) => *l as usize == *r as usize,
×
1795
            (MutableVector(l), MutableVector(r)) => HeapRef::ptr_eq(l, r),
×
1796
            (BigNum(l), BigNum(r)) => Gc::ptr_eq(l, r),
×
1797
            (ByteVector(l), ByteVector(r)) => Gc::ptr_eq(&l.vec, &r.vec),
2✔
1798
            (_, _) => false,
27✔
1799
        }
1800
    }
1801
}
1802

1803
impl Hash for SteelVal {
1804
    fn hash<H: Hasher>(&self, state: &mut H) {
711,368✔
1805
        match self {
711,368✔
1806
            BoolV(b) => b.hash(state),
18,274✔
1807
            NumV(n) => n.to_string().hash(state),
356✔
1808
            IntV(i) => i.hash(state),
80,793✔
1809
            Rational(f) => f.hash(state),
103✔
1810
            BigNum(n) => n.hash(state),
74✔
1811
            BigRational(f) => f.hash(state),
12✔
1812
            Complex(x) => x.hash(state),
89✔
1813
            CharV(c) => c.hash(state),
1,476✔
1814
            ListV(l) => l.hash(state),
57,765✔
1815
            CustomStruct(s) => s.hash(state),
×
1816
            VectorV(v) => v.hash(state),
49✔
1817
            v @ Void => v.hash(state),
×
1818
            StringV(s) => s.hash(state),
44,278✔
1819
            FuncV(s) => (*s as *const FunctionSignature).hash(state),
×
1820
            SymbolV(sym) => {
506,463✔
1821
                "symbol".hash(state);
506,463✔
1822
                sym.hash(state);
506,463✔
1823
            }
1824
            Closure(b) => b.hash(state),
1,476✔
1825
            HashMapV(hm) => hm.hash(state),
×
1826
            IterV(s) => s.hash(state),
×
1827
            HashSetV(hs) => hs.hash(state),
×
1828
            SyntaxObject(s) => s.raw.hash(state),
×
1829
            Pair(p) => (&**p).hash(state),
142✔
1830
            ByteVector(v) => (&*v).hash(state),
18✔
1831
            _ => unimplemented!("Attempted to hash unsupported value: {self:?}"),
1832
        }
1833
    }
1834
}
1835

1836
impl SteelVal {
1837
    #[inline(always)]
1838
    pub fn is_truthy(&self) -> bool {
2,647✔
1839
        match &self {
2,647✔
1840
            SteelVal::BoolV(false) => false,
666✔
1841
            _ => true,
1,981✔
1842
        }
1843
    }
1844

1845
    #[inline(always)]
1846
    pub fn is_future(&self) -> bool {
×
1847
        matches!(self, SteelVal::FutureV(_))
×
1848
    }
1849

1850
    pub fn is_hashable(&self) -> bool {
30,372✔
1851
        matches!(
×
1852
            self,
30,372✔
1853
            BoolV(_)
1854
                | IntV(_)
1855
                | CharV(_)
1856
                // | Pair(_)
1857
                | VectorV(_)
1858
                | StringV(_)
1859
                | SymbolV(_)
1860
                | HashMapV(_)
1861
                | Closure(_)
1862
                | ListV(_)
1863
                | FuncV(_)
1864
                | CustomStruct(_)
1865
        )
1866
    }
1867

UNCOV
1868
    pub fn is_function(&self) -> bool {
×
1869
        matches!(
×
UNCOV
1870
            self,
×
1871
            BoxedFunction(_)
1872
                | Closure(_)
1873
                | FuncV(_)
1874
                // | ContractedFunction(_)
1875
                | BuiltIn(_)
1876
                | MutFunc(_)
1877
        )
1878
    }
1879

1880
    // pub fn is_contract(&self) -> bool {
1881
    //     matches!(self, Contract(_))
1882
    // }
1883

1884
    pub fn empty_hashmap() -> SteelVal {
×
1885
        SteelVal::HashMapV(Gc::new(HashMap::new()).into())
×
1886
    }
1887
}
1888

1889
impl SteelVal {
1890
    // pub fn res_iterator
1891

1892
    pub fn list_or_else<E, F: FnOnce() -> E>(
×
1893
        &self,
1894
        err: F,
1895
    ) -> std::result::Result<&List<SteelVal>, E> {
1896
        match self {
×
1897
            Self::ListV(v) => Ok(v),
×
1898
            _ => Err(err()),
×
1899
        }
1900
    }
1901

1902
    pub fn list(&self) -> Option<&List<SteelVal>> {
47,108✔
1903
        match self {
47,108✔
1904
            Self::ListV(l) => Some(l),
47,020✔
1905
            _ => None,
88✔
1906
        }
1907
    }
1908

1909
    pub fn pair(&self) -> Option<&Gc<crate::values::lists::Pair>> {
×
1910
        match self {
×
1911
            Self::Pair(p) => Some(p),
×
1912
            _ => None,
×
1913
        }
1914
    }
1915

1916
    pub fn bool_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<bool, E> {
2✔
1917
        match self {
2✔
1918
            Self::BoolV(v) => Ok(*v),
1✔
1919
            _ => Err(err()),
1✔
1920
        }
1921
    }
1922

1923
    pub fn int_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<isize, E> {
80✔
1924
        match self {
80✔
1925
            Self::IntV(v) => Ok(*v),
80✔
1926
            _ => Err(err()),
×
1927
        }
1928
    }
1929

1930
    pub fn num_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<f64, E> {
2✔
1931
        match self {
2✔
1932
            Self::NumV(v) => Ok(*v),
1✔
1933
            _ => Err(err()),
1✔
1934
        }
1935
    }
1936

1937
    pub fn char_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<char, E> {
126✔
1938
        match self {
126✔
1939
            Self::CharV(v) => Ok(*v),
125✔
1940
            _ => Err(err()),
1✔
1941
        }
1942
    }
1943

1944
    /// Vector does copy on the value to return
1945
    pub fn vector_or_else<E, F: FnOnce() -> E>(
2✔
1946
        &self,
1947
        err: F,
1948
    ) -> std::result::Result<Vector<SteelVal>, E> {
1949
        match self {
2✔
1950
            Self::VectorV(v) => Ok(v.0.unwrap()),
1✔
1951
            _ => Err(err()),
1✔
1952
        }
1953
    }
1954

1955
    pub fn void_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<(), E> {
2✔
1956
        match self {
2✔
1957
            Self::Void => Ok(()),
1✔
1958
            _ => Err(err()),
1✔
1959
        }
1960
    }
1961

1962
    pub fn string_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<&str, E> {
63✔
1963
        match self {
63✔
1964
            Self::StringV(v) => Ok(v),
62✔
1965
            _ => Err(err()),
1✔
1966
        }
1967
    }
1968

1969
    pub fn func_or_else<E, F: FnOnce() -> E>(
2✔
1970
        &self,
1971
        err: F,
1972
    ) -> std::result::Result<&FunctionSignature, E> {
1973
        match self {
2✔
1974
            Self::FuncV(v) => Ok(v),
2✔
1975
            _ => Err(err()),
×
1976
        }
1977
    }
1978

1979
    pub fn boxed_func_or_else<E, F: FnOnce() -> E>(
×
1980
        &self,
1981
        err: F,
1982
    ) -> std::result::Result<&BoxedDynFunction, E> {
1983
        match self {
×
1984
            Self::BoxedFunction(v) => Ok(v),
×
1985
            _ => Err(err()),
×
1986
        }
1987
    }
1988

1989
    // pub fn contract_or_else<E, F: FnOnce() -> E>(
1990
    //     &self,
1991
    //     err: F,
1992
    // ) -> std::result::Result<Gc<ContractType>, E> {
1993
    //     match self {
1994
    //         Self::Contract(c) => Ok(c.clone()),
1995
    //         _ => Err(err()),
1996
    //     }
1997
    // }
1998

1999
    pub fn closure_or_else<E, F: FnOnce() -> E>(
×
2000
        &self,
2001
        err: F,
2002
    ) -> std::result::Result<Gc<ByteCodeLambda>, E> {
2003
        match self {
×
2004
            Self::Closure(c) => Ok(c.clone()),
×
2005
            _ => Err(err()),
×
2006
        }
2007
    }
2008

2009
    pub fn symbol_or_else<E, F: FnOnce() -> E>(&self, err: F) -> std::result::Result<&str, E> {
2✔
2010
        match self {
2✔
2011
            Self::SymbolV(v) => Ok(v),
1✔
2012
            _ => Err(err()),
1✔
2013
        }
2014
    }
2015

2016
    pub fn clone_symbol_or_else<E, F: FnOnce() -> E>(
×
2017
        &self,
2018
        err: F,
2019
    ) -> std::result::Result<String, E> {
2020
        match self {
×
2021
            Self::SymbolV(v) => Ok(v.to_string()),
×
2022
            _ => Err(err()),
×
2023
        }
2024
    }
2025

2026
    pub fn as_isize(&self) -> Option<isize> {
×
2027
        match self {
×
2028
            Self::IntV(i) => Some(*i),
×
2029
            _ => None,
×
2030
        }
2031
    }
2032

2033
    pub fn as_usize(&self) -> Option<usize> {
×
2034
        self.as_isize()
×
2035
            .and_then(|x| if x >= 0 { Some(x as usize) } else { None })
×
2036
    }
2037

2038
    pub fn as_bool(&self) -> Option<bool> {
4✔
2039
        match self {
4✔
2040
            Self::BoolV(b) => Some(*b),
4✔
2041
            _ => None,
×
2042
        }
2043
    }
2044

2045
    pub fn as_future(&self) -> Option<Shared<BoxedFutureResult>> {
×
2046
        match self {
×
2047
            Self::FutureV(v) => Some(v.clone().unwrap().into_shared()),
×
2048
            _ => None,
×
2049
        }
2050
    }
2051

2052
    pub fn as_string(&self) -> Option<&SteelString> {
×
2053
        match self {
×
2054
            Self::StringV(s) => Some(s),
×
2055
            _ => None,
×
2056
        }
2057
    }
2058

2059
    pub fn as_symbol(&self) -> Option<&SteelString> {
63,547✔
2060
        match self {
63,547✔
2061
            Self::SymbolV(s) => Some(s),
60,578✔
2062
            _ => None,
2,969✔
2063
        }
2064
    }
2065

2066
    pub fn as_syntax_object(&self) -> Option<&Syntax> {
2,960✔
2067
        match self {
2,960✔
2068
            Self::SyntaxObject(s) => Some(s),
409✔
2069
            _ => None,
2,551✔
2070
        }
2071
    }
2072

2073
    // pub fn custom_or_else<E, F: FnOnce() -> E>(
2074
    //     &self,
2075
    //     err: F,
2076
    // ) -> std::result::Result<&Box<dyn CustomType>, E> {
2077
    //     match self {
2078
    //         Self::Custom(v) => Ok(&v),
2079
    //         _ => Err(err()),
2080
    //     }
2081
    // }
2082

2083
    // pub fn struct_or_else<E, F: FnOnce() -> E>(
2084
    //     &self,
2085
    //     err: F,
2086
    // ) -> std::result::Result<&SteelStruct, E> {
2087
    //     match self {
2088
    //         Self::StructV(v) => Ok(v),
2089
    //         _ => Err(err()),
2090
    //     }
2091
    // }
2092

2093
    pub fn closure_arity(&self) -> Option<usize> {
×
2094
        if let SteelVal::Closure(c) = self {
×
2095
            Some(c.arity())
×
2096
        } else {
2097
            None
×
2098
        }
2099
    }
2100
}
2101

2102
impl SteelVal {
2103
    pub const INT_ZERO: SteelVal = SteelVal::IntV(0);
2104
    pub const INT_ONE: SteelVal = SteelVal::IntV(1);
2105
    pub const INT_TWO: SteelVal = SteelVal::IntV(2);
2106
}
2107

2108
impl Eq for SteelVal {}
2109

2110
fn integer_float_equality(int: isize, float: f64) -> bool {
5✔
2111
    let converted = float as isize;
5✔
2112

2113
    if float == converted as f64 {
5✔
2114
        int == converted
3✔
2115
    } else {
2116
        false
2✔
2117
    }
2118
}
2119

2120
fn bignum_float_equality(bigint: &Gc<BigInt>, float: f64) -> bool {
×
2121
    if float.fract() == 0.0 {
×
2122
        if let Some(promoted) = bigint.to_f64() {
×
2123
            promoted == float
2124
        } else {
2125
            false
×
2126
        }
2127
    } else {
2128
        false
×
2129
    }
2130
}
2131

2132
#[steel_derive::function(name = "=", constant = true)]
2133
pub fn number_equality(left: &SteelVal, right: &SteelVal) -> Result<SteelVal> {
19,930,892✔
2134
    let result = match (left, right) {
39,861,784✔
2135
        (IntV(l), IntV(r)) => l == r,
19,927,882✔
2136
        (NumV(l), NumV(r)) => l == r,
3,005✔
2137
        (IntV(l), NumV(r)) | (NumV(r), IntV(l)) => integer_float_equality(*l, *r),
10✔
2138
        (Rational(l), Rational(r)) => l == r,
×
2139
        (Rational(l), NumV(r)) | (NumV(r), Rational(l)) => l.to_f64().unwrap() == *r,
×
2140
        (BigNum(l), BigNum(r)) => l == r,
×
2141
        (BigNum(l), NumV(r)) | (NumV(r), BigNum(l)) => bignum_float_equality(l, *r),
×
2142
        (BigRational(l), BigRational(r)) => l == r,
×
2143
        (BigRational(l), NumV(r)) | (NumV(r), BigRational(l)) => l.to_f64().unwrap() == *r,
×
2144
        // The below should be impossible as integers/bignums freely convert into each
2145
        // other. Similar for int/bignum/rational/bigrational.
2146
        (Rational(_), IntV(_))
2147
        | (IntV(_), Rational(_))
2148
        | (Rational(_), BigNum(_))
2149
        | (BigNum(_), Rational(_))
2150
        | (Rational(_), BigRational(_))
2151
        | (BigRational(_), Rational(_)) => false,
×
2152
        (BigRational(_), IntV(_))
2153
        | (IntV(_), BigRational(_))
2154
        | (BigRational(_), BigNum(_))
2155
        | (BigNum(_), BigRational(_)) => false,
×
2156
        (IntV(_), BigNum(_)) | (BigNum(_), IntV(_)) => false,
×
2157
        (Complex(x), Complex(y)) => {
×
2158
            number_equality(&x.re, &y.re)? == BoolV(true)
×
2159
                && number_equality(&x.im, &y.re)? == BoolV(true)
×
2160
        }
2161
        (Complex(_), _) | (_, Complex(_)) => false,
×
2162
        _ => stop!(TypeMismatch => "= expects two numbers, found: {:?} and {:?}", left, right),
×
2163
    };
2164
    Ok(BoolV(result))
2165
}
2166

2167
impl PartialOrd for SteelVal {
2168
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
107,656✔
2169
        // TODO: Attempt to avoid converting to f64 for cases below as it may lead to precision loss
2170
        // at tiny and large values.
2171
        match (self, other) {
107,656✔
2172
            // Comparison of matching `SteelVal` variants:
2173
            (IntV(x), IntV(y)) => x.partial_cmp(y),
68,592✔
2174
            (BigNum(x), BigNum(y)) => x.partial_cmp(y),
5✔
2175
            (Rational(x), Rational(y)) => x.partial_cmp(y),
×
2176
            (BigRational(x), BigRational(y)) => x.partial_cmp(y),
×
2177
            (NumV(x), NumV(y)) => x.partial_cmp(y),
21,022✔
2178
            (StringV(s), StringV(o)) => s.partial_cmp(o),
×
2179
            (CharV(l), CharV(r)) => l.partial_cmp(r),
×
2180

2181
            // Comparison of `IntV`, means promoting to the rhs type
2182
            (IntV(x), BigNum(y)) => x
2✔
2183
                .to_bigint()
2184
                .expect("integers are representable by bigint")
2185
                .partial_cmp(y),
2✔
2186
            (IntV(x), Rational(y)) => {
×
2187
                // Since we have platform-dependent type for rational conditional compilation is required to find
2188
                // the common ground
2189
                #[cfg(target_pointer_width = "32")]
2190
                {
2191
                    let x_rational = num_rational::Rational32::new_raw(*x as i32, 1);
×
2192
                    x_rational.partial_cmp(y)
×
2193
                }
2194
                #[cfg(target_pointer_width = "64")]
2195
                {
2196
                    let x_rational = num_rational::Rational64::new_raw(*x as i64, 1);
×
2197
                    x_rational.partial_cmp(&num_rational::Rational64::new_raw(
×
2198
                        *y.numer() as i64,
×
2199
                        *y.denom() as i64,
×
2200
                    ))
2201
                }
2202
            }
2203
            (IntV(x), BigRational(y)) => {
×
2204
                let x_rational = BigRational::from_integer(
2205
                    x.to_bigint().expect("integers are representable by bigint"),
×
2206
                );
2207
                x_rational.partial_cmp(y)
×
2208
            }
2209
            (IntV(x), NumV(y)) => (*x as f64).partial_cmp(y),
6✔
2210

2211
            // BigNum comparisons means promoting to BigInt for integers, BigRational for ratios,
2212
            // or Decimal otherwise
2213
            (BigNum(x), IntV(y)) => x
1✔
2214
                .as_ref()
2215
                .partial_cmp(&y.to_bigint().expect("integers are representable by bigint")),
1✔
2216
            (BigNum(x), Rational(y)) => {
×
2217
                let x_big_rational = BigRational::from_integer(x.unwrap());
×
2218
                let y_big_rational = BigRational::new_raw(
2219
                    y.numer()
×
2220
                        .to_bigint()
×
2221
                        .expect("integers are representable by bigint"),
×
2222
                    y.denom()
×
2223
                        .to_bigint()
×
2224
                        .expect("integers are representable by bigint"),
×
2225
                );
2226
                x_big_rational.partial_cmp(&y_big_rational)
×
2227
            }
2228
            (BigNum(x), BigRational(y)) => {
×
2229
                let x_big_rational = BigRational::from_integer(x.unwrap());
×
2230
                x_big_rational.partial_cmp(&y)
×
2231
            }
2232
            (BigNum(x), NumV(y)) => {
1✔
2233
                let x_decimal = BigDecimal::new(x.unwrap(), 0);
1✔
2234
                let y_decimal_opt = BigDecimal::from_f64(*y);
1✔
2235
                y_decimal_opt.and_then(|y_decimal| x_decimal.partial_cmp(&y_decimal))
3✔
2236
            }
2237

2238
            // Rationals require rationals, regular or bigger versions; for float it will be divided to float as well
2239
            (Rational(x), IntV(y)) => {
2✔
2240
                // Same as before, but opposite direction
2241
                #[cfg(target_pointer_width = "32")]
2242
                {
2243
                    let y_rational = num_rational::Rational32::new_raw(*y as i32, 1);
2✔
2244
                    x.partial_cmp(&y_rational)
2✔
2245
                }
2246
                #[cfg(target_pointer_width = "64")]
2247
                {
2248
                    let y_rational = num_rational::Rational64::new_raw(*y as i64, 1);
2✔
2249
                    num_rational::Rational64::new_raw(*x.numer() as i64, *x.denom() as i64)
2✔
2250
                        .partial_cmp(&y_rational)
2✔
2251
                }
2252
            }
2253
            (Rational(x), BigNum(y)) => {
×
2254
                let x_big_rational = BigRational::new_raw(
2255
                    x.numer()
×
2256
                        .to_bigint()
×
2257
                        .expect("integers are representable by bigint"),
×
2258
                    x.denom()
×
2259
                        .to_bigint()
×
2260
                        .expect("integers are representable by bigint"),
×
2261
                );
2262
                let y_big_rational = BigRational::from_integer(y.unwrap());
×
2263
                x_big_rational.partial_cmp(&y_big_rational)
×
2264
            }
2265
            (Rational(x), BigRational(y)) => {
×
2266
                let x_big_rational = BigRational::new_raw(
2267
                    x.numer()
×
2268
                        .to_bigint()
×
2269
                        .expect("integers are representable by bigint"),
×
2270
                    x.denom()
×
2271
                        .to_bigint()
×
2272
                        .expect("integers are representable by bigint"),
×
2273
                );
2274
                x_big_rational.partial_cmp(&y)
×
2275
            }
2276
            (Rational(x), NumV(y)) => (*x.numer() as f64 / *x.denom() as f64).partial_cmp(y),
×
2277

2278
            // The most capacious set, but need to cover float case with BigDecimal anyways
2279
            (BigRational(x), IntV(y)) => {
×
2280
                let y_rational = BigRational::from_integer(
2281
                    y.to_bigint().expect("integers are representable by bigint"),
×
2282
                );
2283
                x.as_ref().partial_cmp(&y_rational)
×
2284
            }
2285
            (BigRational(x), BigNum(y)) => {
×
2286
                let y_big_rational = BigRational::from_integer(y.unwrap());
×
2287
                x.as_ref().partial_cmp(&y_big_rational)
×
2288
            }
2289
            (BigRational(x), Rational(y)) => {
×
2290
                let y_big_rational = BigRational::new_raw(
2291
                    y.numer()
×
2292
                        .to_bigint()
×
2293
                        .expect("integers are representable by bigint"),
×
2294
                    y.denom()
×
2295
                        .to_bigint()
×
2296
                        .expect("integers are representable by bigint"),
×
2297
                );
2298
                x.as_ref().partial_cmp(&y_big_rational)
×
2299
            }
2300
            (BigRational(x), NumV(y)) => {
×
2301
                let x_decimal =
×
2302
                    BigDecimal::new(x.numer().clone(), 0) / BigDecimal::new(x.denom().clone(), 0);
×
2303
                let y_decimal_opt = BigDecimal::from_f64(*y);
×
2304
                y_decimal_opt.and_then(|y_decimal| x_decimal.partial_cmp(&y_decimal))
×
2305
            }
2306

2307
            // The opposite of all float cases above
2308
            (NumV(x), IntV(y)) => x.partial_cmp(&(*y as f64)),
18,020✔
2309
            (NumV(x), BigNum(y)) => {
2✔
2310
                let x_decimal_opt = BigDecimal::from_f64(*x);
2✔
2311
                let y_decimal = BigDecimal::new(y.unwrap(), 0);
2✔
2312
                x_decimal_opt.and_then(|x_decimal| x_decimal.partial_cmp(&y_decimal))
6✔
2313
            }
2314
            (NumV(x), Rational(y)) => x.partial_cmp(&(*y.numer() as f64 / *y.denom() as f64)),
×
2315
            (NumV(x), BigRational(y)) => {
×
2316
                let x_decimal_opt = BigDecimal::from_f64(*x);
×
2317
                let y_decimal =
×
2318
                    BigDecimal::new(y.numer().clone(), 0) / BigDecimal::new(y.denom().clone(), 0);
×
2319
                x_decimal_opt.and_then(|x_decimal| x_decimal.partial_cmp(&y_decimal))
×
2320
            }
2321

2322
            (l, r) => {
3✔
2323
                // All real numbers (not complex) should have order defined.
2324
                debug_assert!(
3✔
2325
                    !(realp(l) && realp(r)),
6✔
2326
                    "Numbers {l:?} and {r:?} should implement partial_cmp"
×
2327
                );
2328
                // Unimplemented for other types
2329
                None
3✔
2330
            }
2331
        }
2332
    }
2333
}
2334

2335
impl fmt::Display for SteelVal {
2336
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2,144✔
2337
        CycleDetector::detect_and_display_cycles(self, f)
2,144✔
2338
    }
2339
}
2340

2341
impl fmt::Debug for SteelVal {
2342
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158✔
2343
        // at the top level, print a ' if we are
2344
        // trying to print a symbol or list
2345
        match self {
158✔
2346
            SymbolV(_) | ListV(_) | VectorV(_) => write!(f, "'")?,
18✔
2347
            _ => (),
140✔
2348
        };
2349
        // display_helper(self, f)
2350

2351
        CycleDetector::detect_and_display_cycles(self, f)
158✔
2352
    }
2353
}
2354

2355
#[cfg(test)]
2356
mod or_else_tests {
2357

2358
    use super::*;
2359

2360
    #[cfg(feature = "sync")]
2361
    use im::vector;
2362

2363
    #[cfg(not(feature = "sync"))]
2364
    use im_rc::vector;
2365

2366
    #[test]
2367
    fn bool_or_else_test_good() {
2368
        let input = SteelVal::BoolV(true);
2369
        assert_eq!(input.bool_or_else(throw!(Generic => "test")).unwrap(), true);
2370
    }
2371

2372
    #[test]
2373
    fn bool_or_else_test_bad() {
2374
        let input = SteelVal::CharV('f');
2375
        assert!(input.bool_or_else(throw!(Generic => "test")).is_err());
2376
    }
2377

2378
    #[test]
2379
    fn num_or_else_test_good() {
2380
        let input = SteelVal::NumV(10.0);
2381
        assert_eq!(input.num_or_else(throw!(Generic => "test")).unwrap(), 10.0);
2382
    }
2383

2384
    #[test]
2385
    fn num_or_else_test_bad() {
2386
        let input = SteelVal::CharV('f');
2387
        assert!(input.num_or_else(throw!(Generic => "test")).is_err());
2388
    }
2389

2390
    #[test]
2391
    fn char_or_else_test_good() {
2392
        let input = SteelVal::CharV('f');
2393
        assert_eq!(input.char_or_else(throw!(Generic => "test")).unwrap(), 'f');
2394
    }
2395

2396
    #[test]
2397
    fn char_or_else_test_bad() {
2398
        let input = SteelVal::NumV(10.0);
2399
        assert!(input.char_or_else(throw!(Generic => "test")).is_err());
2400
    }
2401

2402
    #[test]
2403
    fn vector_or_else_test_good() {
2404
        let input: SteelVal = vector![SteelVal::IntV(1)].into();
2405
        assert_eq!(
2406
            input.vector_or_else(throw!(Generic => "test")).unwrap(),
2407
            vector![SteelVal::IntV(1)]
2408
        );
2409
    }
2410

2411
    #[test]
2412
    fn vector_or_else_bad() {
2413
        let input = SteelVal::CharV('f');
2414
        assert!(input.vector_or_else(throw!(Generic => "test")).is_err());
2415
    }
2416

2417
    #[test]
2418
    fn void_or_else_test_good() {
2419
        let input = SteelVal::Void;
2420
        assert_eq!(input.void_or_else(throw!(Generic => "test")).unwrap(), ())
2421
    }
2422

2423
    #[test]
2424
    fn void_or_else_test_bad() {
2425
        let input = SteelVal::StringV("foo".into());
2426
        assert!(input.void_or_else(throw!(Generic => "test")).is_err());
2427
    }
2428

2429
    #[test]
2430
    fn string_or_else_test_good() {
2431
        let input = SteelVal::StringV("foo".into());
2432
        assert_eq!(
2433
            input.string_or_else(throw!(Generic => "test")).unwrap(),
2434
            "foo".to_string()
2435
        );
2436
    }
2437

2438
    #[test]
2439
    fn string_or_else_test_bad() {
2440
        let input = SteelVal::Void;
2441
        assert!(input.string_or_else(throw!(Generic => "test")).is_err())
2442
    }
2443

2444
    #[test]
2445
    fn symbol_or_else_test_good() {
2446
        let input = SteelVal::SymbolV("foo".into());
2447
        assert_eq!(
2448
            input.symbol_or_else(throw!(Generic => "test")).unwrap(),
2449
            "foo".to_string()
2450
        );
2451
    }
2452

2453
    #[test]
2454
    fn symbol_or_else_test_bad() {
2455
        let input = SteelVal::Void;
2456
        assert!(input.symbol_or_else(throw!(Generic => "test")).is_err())
2457
    }
2458

2459
    #[test]
2460
    fn num_and_char_are_not_ordered() {
2461
        assert_eq!(SteelVal::IntV(0).partial_cmp(&SteelVal::CharV('0')), None);
2462
        assert_eq!(SteelVal::NumV(0.0).partial_cmp(&SteelVal::CharV('0')), None);
2463
        assert_eq!(
2464
            SteelVal::BigNum(Gc::new(BigInt::default())).partial_cmp(&SteelVal::CharV('0')),
2465
            None
2466
        );
2467
    }
2468

2469
    #[test]
2470
    fn number_cmp() {
2471
        let less_cases = [
2472
            (SteelVal::IntV(-10), SteelVal::IntV(1)),
2473
            (
2474
                SteelVal::IntV(-10),
2475
                SteelVal::BigNum(Gc::new(BigInt::from(1))),
2476
            ),
2477
            (SteelVal::NumV(-10.0), SteelVal::IntV(1)),
2478
            (SteelVal::IntV(-10), SteelVal::NumV(1.0)),
2479
            (
2480
                SteelVal::BigNum(Gc::new(BigInt::from(-10))),
2481
                SteelVal::BigNum(Gc::new(BigInt::from(1))),
2482
            ),
2483
            (
2484
                SteelVal::NumV(-10.0),
2485
                SteelVal::BigNum(Gc::new(BigInt::from(1))),
2486
            ),
2487
        ];
2488
        for (l, r) in less_cases {
2489
            assert_eq!(l.partial_cmp(&r), Some(Ordering::Less));
2490
            assert_eq!(r.partial_cmp(&l), Some(Ordering::Greater));
2491
        }
2492
        let equal_cases = [
2493
            SteelVal::IntV(-10),
2494
            SteelVal::NumV(-10.0),
2495
            SteelVal::BigNum(Gc::new(BigInt::from(-10))),
2496
            // Added to test that the number is equal even if it points to a different object.
2497
            SteelVal::BigNum(Gc::new(BigInt::from(-10))),
2498
        ]
2499
        .into_iter();
2500
        for (l, r) in equal_cases.clone().zip(equal_cases.clone()) {
2501
            assert_eq!(l.partial_cmp(&r), Some(Ordering::Equal));
2502
        }
2503
    }
2504
}
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