gluon_vm/
primitives.rs

1//! Module containing functions for interacting with gluon's primitive types.
2use crate::real_std::{
3    ffi::OsStr,
4    fs, io,
5    marker::PhantomData,
6    path::{self, Path},
7    result::Result as StdResult,
8    str::FromStr,
9    string::String as StdString,
10    sync::Mutex,
11};
12
13use crate::base::types::ArcType;
14
15use crate::{
16    api::{
17        generic::{self, A, S},
18        primitive, Array, Getable, Opaque, OpaqueRef, Pushable, Pushed, RuntimeResult, ValueRef,
19        VmType, WithVM, IO,
20    },
21    gc::{DataDef, Trace, WriteOnly},
22    stack::{ExternState, StackFrame},
23    types::VmInt,
24    value::{GcStr, Repr, ValueArray},
25    vm::{Status, Thread},
26    Error, ExternModule, Result, Variants,
27};
28
29#[doc(hidden)]
30pub mod array {
31    use super::*;
32    use crate::thread::ThreadInternal;
33
34    pub fn len(array: Array<generic::A>) -> VmInt {
35        array.len() as VmInt
36    }
37
38    pub(crate) fn index<'vm>(
39        array: OpaqueRef<'vm, [generic::A]>,
40        index: VmInt,
41    ) -> RuntimeResult<OpaqueRef<'vm, generic::A>, String> {
42        match array.get(index) {
43            Some(value) => RuntimeResult::Return(value),
44            None => RuntimeResult::Panic(format!("Index {} is out of range", index)),
45        }
46    }
47
48    pub(crate) fn slice<'vm>(
49        array: Array<'vm, generic::A>,
50        start: usize,
51        end: usize,
52    ) -> RuntimeResult<Array<'vm, generic::A>, Error> {
53        if start > end {
54            return RuntimeResult::Panic(Error::Message(format!(
55                "slice index starts at {} but ends at {}",
56                start, end
57            )));
58        }
59
60        if end > array.len() {
61            return RuntimeResult::Panic(Error::Message(format!(
62                "index {} is out of range for array of length {}",
63                end,
64                array.len()
65            )));
66        }
67
68        #[derive(Trace)]
69        #[gluon(gluon_vm)]
70        struct Slice<'a> {
71            start: usize,
72            end: usize,
73            array: &'a ValueArray,
74        }
75
76        unsafe impl<'a> DataDef for Slice<'a> {
77            type Value = ValueArray;
78
79            fn size(&self) -> usize {
80                ValueArray::size_of(self.array.repr(), self.end - self.start)
81            }
82
83            fn initialize<'w>(self, mut result: WriteOnly<'w, ValueArray>) -> &'w mut ValueArray {
84                unsafe {
85                    let result = &mut *result.as_mut_ptr();
86                    result.set_repr(self.array.repr());
87                    result.initialize(
88                        self.array
89                            .iter()
90                            .skip(self.start)
91                            .take(self.end - self.start),
92                    );
93                    result
94                }
95            }
96        }
97
98        let mut context = array.vm().context();
99        let result = context.alloc(Slice {
100            start,
101            end,
102            array: &array.get_array(),
103        });
104
105        let value = match result {
106            Ok(value) => value,
107            Err(err) => return RuntimeResult::Panic(err),
108        };
109
110        RuntimeResult::Return(Getable::from_value(array.vm_(), Variants::from(value)))
111    }
112
113    pub(crate) fn append<'vm>(
114        lhs: Array<'vm, generic::A>,
115        rhs: Array<'vm, generic::A>,
116    ) -> RuntimeResult<Array<'vm, generic::A>, Error> {
117        #[derive(Trace)]
118        #[gluon(gluon_vm)]
119        struct Append<'b> {
120            lhs: &'b ValueArray,
121            rhs: &'b ValueArray,
122        }
123        impl<'b> Append<'b> {
124            fn repr(&self) -> Repr {
125                // Empty arrays don't have the correct representation set so choose the representation
126                // of `rhs` if it is empty. (And if both are empty the representation does not matter).
127                if self.lhs.len() == 0 {
128                    self.rhs.repr()
129                } else {
130                    self.lhs.repr()
131                }
132            }
133        }
134
135        unsafe impl<'b> DataDef for Append<'b> {
136            type Value = ValueArray;
137            fn size(&self) -> usize {
138                let len = self.lhs.len() + self.rhs.len();
139                ValueArray::size_of(self.repr(), len)
140            }
141            fn initialize<'w>(self, mut result: WriteOnly<'w, ValueArray>) -> &'w mut ValueArray {
142                unsafe {
143                    let result = &mut *result.as_mut_ptr();
144                    result.set_repr(self.repr());
145                    result.initialize(self.lhs.iter().chain(self.rhs.iter()));
146                    result
147                }
148            }
149        }
150        let vm = lhs.vm();
151        let mut context = vm.context();
152        let value = {
153            let result = context.alloc(Append {
154                lhs: &lhs.get_array(),
155                rhs: &rhs.get_array(),
156            });
157            match result {
158                Ok(x) => x,
159                Err(err) => return RuntimeResult::Panic(err),
160            }
161        };
162        RuntimeResult::Return(Getable::from_value(lhs.vm_(), Variants::from(value)))
163    }
164}
165
166mod int {
167    use super::*;
168    use crate::types::VmInt;
169
170    pub(crate) fn rem(dividend: VmInt, divisor: VmInt) -> RuntimeResult<VmInt, String> {
171        if divisor != 0 {
172            RuntimeResult::Return(dividend % divisor)
173        } else {
174            RuntimeResult::Panic(format!(
175                "attempted to calculate remainder of {} divided by 0",
176                dividend
177            ))
178        }
179    }
180
181    pub(crate) fn rem_euclid(dividend: VmInt, divisor: VmInt) -> RuntimeResult<VmInt, String> {
182        if divisor != 0 {
183            RuntimeResult::Return(dividend.rem_euclid(divisor))
184        } else {
185            RuntimeResult::Panic(format!(
186                "attempted to calculate euclidean remainder of {} divided by 0",
187                dividend
188            ))
189        }
190    }
191
192    pub(crate) fn wrapping_rem(dividend: VmInt, divisor: VmInt) -> RuntimeResult<VmInt, String> {
193        if divisor != 0 {
194            RuntimeResult::Return(dividend.wrapping_rem(divisor))
195        } else {
196            RuntimeResult::Panic(format!(
197                "attempted to calculate wrapping remainder of {} divided by 0",
198                dividend
199            ))
200        }
201    }
202
203    pub(crate) fn wrapping_rem_euclid(
204        dividend: VmInt,
205        divisor: VmInt,
206    ) -> RuntimeResult<VmInt, String> {
207        if divisor != 0 {
208            RuntimeResult::Return(dividend.wrapping_rem_euclid(divisor))
209        } else {
210            RuntimeResult::Panic(format!(
211                "attempted to calculate wrapping euclidean remainder of {} divided by 0",
212                dividend
213            ))
214        }
215    }
216
217    pub(crate) fn overflowing_rem(
218        dividend: VmInt,
219        divisor: VmInt,
220    ) -> RuntimeResult<(VmInt, bool), String> {
221        if divisor != 0 {
222            RuntimeResult::Return(dividend.overflowing_rem(divisor))
223        } else {
224            RuntimeResult::Panic(format!(
225                "attempted to calculate overflowing remainder of {} divided by 0",
226                dividend
227            ))
228        }
229    }
230
231    pub(crate) fn overflowing_rem_euclid(
232        dividend: VmInt,
233        divisor: VmInt,
234    ) -> RuntimeResult<(VmInt, bool), String> {
235        if divisor != 0 {
236            RuntimeResult::Return(dividend.overflowing_rem_euclid(divisor))
237        } else {
238            RuntimeResult::Panic(format!(
239                "attempted to calculate overflowing euclidean remainder of {} divided by 0",
240                dividend
241            ))
242        }
243    }
244}
245
246mod string {
247    use super::*;
248    use crate::value::ValueStr;
249
250    pub(crate) fn append(lhs: WithVM<&str>, rhs: &str) -> RuntimeResult<Pushed<String>, Error> {
251        #[derive(Trace)]
252        #[gluon(gluon_vm)]
253        struct StrAppend<'b> {
254            lhs: &'b str,
255            rhs: &'b str,
256        }
257
258        unsafe impl<'b> DataDef for StrAppend<'b> {
259            type Value = ValueStr;
260            fn size(&self) -> usize {
261                use crate::real_std::mem::size_of;
262                size_of::<ValueStr>() + (self.lhs.len() + self.rhs.len()) * size_of::<u8>()
263            }
264            fn initialize<'w>(self, mut result: WriteOnly<'w, ValueStr>) -> &'w mut ValueStr {
265                unsafe {
266                    let result = &mut *result.as_mut_ptr();
267                    result.as_mut_array().set_repr(Repr::Byte);
268                    result.as_mut_array().unsafe_array_mut::<u8>().initialize(
269                        self.lhs
270                            .as_bytes()
271                            .iter()
272                            .chain(self.rhs.as_bytes())
273                            .cloned(),
274                    );
275                    result
276                }
277            }
278        }
279
280        let vm = lhs.vm;
281        let lhs = lhs.value;
282
283        let mut context = vm.current_context();
284        let mut context = context.context();
285        let value = match alloc!(context, StrAppend { lhs: lhs, rhs: rhs }) {
286            Ok(x) => x,
287            Err(err) => return RuntimeResult::Panic(err),
288        };
289        context.stack.push(Variants::from(value));
290        RuntimeResult::Return(Pushed::default())
291    }
292
293    pub(crate) fn append_char(
294        lhs: WithVM<&str>,
295        rhs: char,
296    ) -> RuntimeResult<Pushed<String>, Error> {
297        append(lhs, rhs.encode_utf8(&mut [0; 4]))
298    }
299
300    pub(crate) fn from_char(c: WithVM<char>) -> RuntimeResult<Pushed<String>, Error> {
301        append_char(
302            WithVM {
303                vm: c.vm,
304                value: "",
305            },
306            c.value,
307        )
308    }
309
310    pub fn split_at(s: &str, index: usize) -> RuntimeResult<(&str, &str), String> {
311        if !s.is_char_boundary(index) {
312            // Limit the amount of characters to print in the error message
313            let mut iter = s.chars();
314            for _ in iter.by_ref().take(256) {}
315            RuntimeResult::Panic(format!(
316                "index {} in `{}` does not lie on a character \
317                 boundary",
318                index,
319                &s[..(s.len() - iter.as_str().len())]
320            ))
321        } else {
322            RuntimeResult::Return(s.split_at(index))
323        }
324    }
325
326    pub fn slice(s: &str, start: usize, end: usize) -> RuntimeResult<&str, String> {
327        if s.is_char_boundary(start) && s.is_char_boundary(end) {
328            RuntimeResult::Return(&s[start..end])
329        } else {
330            // Limit the amount of characters to print in the error message
331            let mut iter = s.chars();
332            for _ in iter.by_ref().take(256) {}
333            RuntimeResult::Panic(format!(
334                "index {} and/or {} in `{}` does not lie on a character \
335                 boundary",
336                start,
337                end,
338                &s[..(s.len() - iter.as_str().len())]
339            ))
340        }
341    }
342
343    pub fn from_utf8<'a>(array: OpaqueRef<'a, [u8]>) -> StdResult<OpaqueRef<'a, str>, ()> {
344        Ok(Opaque::from_value(Variants::from(GcStr::from_utf8(
345            array.get_array(),
346        )?)))
347    }
348
349    pub fn char_at(s: &str, index: usize) -> RuntimeResult<char, String> {
350        if s.is_char_boundary(index) {
351            if let Some(c) = s[index..].chars().next() {
352                return RuntimeResult::Return(c);
353            }
354        }
355        let mut iter = s.chars();
356        for _ in iter.by_ref().take(256) {}
357        RuntimeResult::Panic(format!(
358            "index {} in `{}` does not lie on a character boundary",
359            index,
360            &s[..(s.len() - iter.as_str().len())]
361        ))
362    }
363}
364
365fn parse<T>(s: &str) -> StdResult<T, ()>
366where
367    T: FromStr,
368{
369    s.parse().map_err(|_| ())
370}
371
372fn show_int(i: VmInt) -> String {
373    format!("{}", i)
374}
375
376fn show_float(f: f64) -> String {
377    format!("{}", f)
378}
379
380fn show_char(c: char) -> String {
381    format!("{:?}", c)
382}
383
384fn show_byte(c: u8) -> String {
385    format!("{}", c)
386}
387
388extern "C" fn error(_: &Thread) -> Status {
389    // We expect a string as an argument to this function but we only return Status::Error
390    // and let the caller take care of printing the message
391    Status::Error
392}
393
394extern "C" fn discriminant_value(thread: &Thread) -> Status {
395    let mut context = thread.current_context();
396    let tag = {
397        let stack = StackFrame::<ExternState>::current(context.stack());
398        let value = stack.get_variant(0).unwrap();
399        match value.as_ref() {
400            ValueRef::Data(data) => data.tag(),
401            _ => 0,
402        }
403    };
404    tag.vm_push(&mut context).unwrap();
405    Status::Ok
406}
407
408#[allow(non_camel_case_types)]
409mod std {
410
411    macro_rules! bit_const_inner {
412        ($typ: ty, $($trait_: ident :: $name: ident,)*) => {
413            $(
414                #[allow(non_upper_case_globals)]
415                pub const $name: fn(l: $typ, r: $typ) -> $typ = ::std::ops::$trait_::$name;
416            )*
417        }
418    }
419
420    macro_rules! bit_const {
421        ($typ: ty) => {
422            bit_const_inner! {
423                $typ,
424                BitAnd::bitand,
425                BitOr::bitor,
426                BitXor::bitxor,
427                Shl::shl,
428                Shr::shr,
429            }
430        };
431    }
432
433    pub use crate::primitives as prim;
434
435    pub mod string {
436        pub type prim = str;
437    }
438    pub mod char {
439        pub type prim = char;
440    }
441    pub mod array {
442        pub use crate::primitives::array as prim;
443    }
444
445    pub mod byte {
446        pub type prim = u8;
447
448        bit_const! { u8 }
449    }
450    pub mod int {
451        use crate::types::VmInt;
452
453        pub type prim = VmInt;
454
455        bit_const! { VmInt }
456
457        #[allow(non_upper_case_globals)]
458        pub const arithmetic_shr: fn(l: VmInt, r: VmInt) -> VmInt = shr;
459        #[allow(non_upper_case_globals)]
460        pub const logical_shr: fn(l: u64, r: u64) -> u64 = ::std::ops::Shr::shr;
461    }
462    pub mod float {
463        pub type prim = f64;
464    }
465    pub mod path {
466        pub type prim = ::std::path::Path;
467    }
468    pub mod effect {
469        pub mod st {
470            pub mod string {
471                pub use crate::primitives::st_string as prim;
472            }
473        }
474    }
475}
476
477#[allow(non_camel_case_types)]
478pub fn load_float(thread: &Thread) -> Result<ExternModule> {
479    use crate::real_std::f64;
480
481    ExternModule::new(
482        thread,
483        record! {
484            digits => f64::DIGITS,
485            epsilon => f64::EPSILON,
486            infinity => f64::INFINITY,
487            mantissa_digits => f64::MANTISSA_DIGITS,
488            max_ => f64::MAX,
489            max_10_exp => f64::MAX_10_EXP,
490            max_exp => f64::MAX_EXP,
491            min_ => f64::MIN,
492            min_10_exp => f64::MIN_10_EXP,
493            min_exp => f64::MIN_EXP,
494            min_positive => f64::MIN_POSITIVE,
495            nan => f64::NAN,
496            neg_infinity => f64::NEG_INFINITY,
497            e => f64::consts::E,
498            pi => f64::consts::PI,
499            radix => f64::RADIX,
500            is_nan => primitive!(1, std::float::prim::is_nan),
501            is_infinite => primitive!(1, std::float::prim::is_infinite),
502            is_finite => primitive!(1, std::float::prim::is_finite),
503            is_normal => primitive!(1, std::float::prim::is_normal),
504            floor => primitive!(1, std::float::prim::floor),
505            ceil => primitive!(1, std::float::prim::ceil),
506            round => primitive!(1, std::float::prim::round),
507            trunc => primitive!(1, std::float::prim::trunc),
508            fract => primitive!(1, std::float::prim::fract),
509            abs => primitive!(1, std::float::prim::abs),
510            signum => primitive!(1, std::float::prim::signum),
511            is_sign_positive => primitive!(1, std::float::prim::is_sign_positive),
512            is_sign_negative => primitive!(1, std::float::prim::is_sign_negative),
513            mul_add => primitive!(3, std::float::prim::mul_add),
514            recip => primitive!(1, std::float::prim::recip),
515            rem => primitive!(2, "std::float::prim::rem", |a: f64, b: f64| a % b),
516            rem_euclid => primitive!(2, std::float::prim::rem_euclid),
517            powi => primitive!(2, std::float::prim::powi),
518            powf => primitive!(2, std::float::prim::powf),
519            sqrt => primitive!(1, std::float::prim::sqrt),
520            exp => primitive!(1, std::float::prim::exp),
521            exp2 => primitive!(1, std::float::prim::exp2),
522            ln => primitive!(1, std::float::prim::ln),
523            log2 => primitive!(1, std::float::prim::log2),
524            log10 => primitive!(1, std::float::prim::log10),
525            to_degrees => primitive!(1, std::float::prim::to_degrees),
526            to_radians => primitive!(1, std::float::prim::to_radians),
527            max => primitive!(2, std::float::prim::max),
528            min => primitive!(2, std::float::prim::min),
529            cbrt => primitive!(1, std::float::prim::cbrt),
530            hypot => primitive!(2, std::float::prim::hypot),
531            sin => primitive!(1, std::float::prim::sin),
532            cos => primitive!(1, std::float::prim::cos),
533            tan => primitive!(1, std::float::prim::tan),
534            acos => primitive!(1, std::float::prim::acos),
535            atan => primitive!(1, std::float::prim::atan),
536            atan2 => primitive!(2, std::float::prim::atan2),
537            sin_cos => primitive!(1, std::float::prim::sin_cos),
538            exp_m1 => primitive!(1, std::float::prim::exp_m1),
539            ln_1p => primitive!(1, std::float::prim::ln_1p),
540            sinh => primitive!(1, std::float::prim::sinh),
541            cosh => primitive!(1, std::float::prim::cosh),
542            tanh => primitive!(1, std::float::prim::tanh),
543            acosh => primitive!(1, std::float::prim::acosh),
544            atanh => primitive!(1, std::float::prim::atanh),
545            from_int => primitive!(1, "std.float.prim.from_int", |i: VmInt| i as f64),
546            parse => primitive!(1, "std.float.prim.parse", parse::<f64>),
547        },
548    )
549}
550
551#[allow(non_camel_case_types)]
552pub fn load_byte(vm: &Thread) -> Result<ExternModule> {
553    ExternModule::new(
554        vm,
555        record! {
556            min_value => std::byte::prim::min_value(),
557            max_value => std::byte::prim::max_value(),
558            shl => primitive!(2, std::byte::shl),
559            shr => primitive!(2, std::byte::shr),
560            bitxor => primitive!(2, std::byte::bitxor),
561            bitand => primitive!(2, std::byte::bitand),
562            bitor => primitive!(2, std::byte::bitor),
563            count_ones => primitive!(1, std::byte::prim::count_ones),
564            count_zeros => primitive!(1, std::byte::prim::count_zeros),
565            leading_zeros => primitive!(1, std::byte::prim::leading_zeros),
566            trailing_zeros => primitive!(1, std::byte::prim::trailing_zeros),
567            rotate_left => primitive!(2, std::byte::prim::rotate_left),
568            rotate_right => primitive!(2, std::byte::prim::rotate_right),
569            swap_bytes => primitive!(1, std::byte::prim::swap_bytes),
570            from_be => primitive!(1, std::byte::prim::from_be),
571            from_le => primitive!(1, std::byte::prim::from_le),
572            to_be => primitive!(1, std::byte::prim::to_be),
573            to_le => primitive!(1, std::byte::prim::to_le),
574            pow => primitive!(2, std::byte::prim::pow),
575            saturating_add => primitive!(2, std::byte::prim::saturating_add),
576            saturating_sub => primitive!(2, std::byte::prim::saturating_sub),
577            saturating_mul => primitive!(2, std::byte::prim::saturating_mul),
578            wrapping_add => primitive!(2, std::byte::prim::wrapping_add),
579            wrapping_sub => primitive!(2, std::byte::prim::wrapping_sub),
580            wrapping_mul => primitive!(2, std::byte::prim::wrapping_mul),
581            wrapping_div => primitive!(2, std::byte::prim::wrapping_div),
582            overflowing_add => primitive!(2, std::byte::prim::overflowing_add),
583            overflowing_sub => primitive!(2, std::byte::prim::overflowing_sub),
584            overflowing_mul => primitive!(2, std::byte::prim::overflowing_mul),
585            overflowing_div => primitive!(2, std::byte::prim::overflowing_div),
586            from_int => primitive!(1, "std.byte.prim.from_int", |i: VmInt| i as u8),
587            parse => primitive!(1, "std.byte.prim.parse", parse::<u8>),
588        },
589    )
590}
591
592#[allow(non_camel_case_types)]
593pub fn load_int(vm: &Thread) -> Result<ExternModule> {
594    ExternModule::new(
595        vm,
596        record! {
597            min_value => std::int::prim::min_value(),
598            max_value => std::int::prim::max_value(),
599            from_str_radix => primitive!(
600                2,
601                "std.int.prim.from_str_radix",
602                |src, radix| std::int::prim::from_str_radix(src, radix).map_err(|_| ())
603            ),
604            shl => primitive!(2, std::int::shl),
605            arithmetic_shr => primitive!(2, std::int::arithmetic_shr),
606            logical_shr => primitive!(2, std::int::logical_shr),
607            bitxor => primitive!(2, std::int::bitxor),
608            bitand => primitive!(2, std::int::bitand),
609            bitor => primitive!(2, std::int::bitor),
610            count_ones => primitive!(1, std::int::prim::count_ones),
611            count_zeros => primitive!(1, std::int::prim::count_zeros),
612            leading_zeros => primitive!(1, std::int::prim::leading_zeros),
613            trailing_zeros => primitive!(1, std::int::prim::trailing_zeros),
614            rotate_left => primitive!(2, std::int::prim::rotate_left),
615            rotate_right => primitive!(2, std::int::prim::rotate_right),
616            swap_bytes => primitive!(1, std::int::prim::swap_bytes),
617            from_be => primitive!(1, std::int::prim::from_be),
618            from_le => primitive!(1, std::int::prim::from_le),
619            to_be => primitive!(1, std::int::prim::to_be),
620            to_le => primitive!(1, std::int::prim::to_le),
621            pow => primitive!(2, std::int::prim::pow),
622            abs => primitive!(1, std::int::prim::abs),
623            rem => primitive!(2, "std::int::prim::rem", int::rem),
624            rem_euclid => primitive!(2, "std::int::prim::rem_euclid", int::rem_euclid),
625            checked_rem => primitive!(2, std::int::prim::checked_rem),
626            checked_rem_euclid => primitive!(2, std::int::prim::checked_rem_euclid),
627            saturating_add => primitive!(2, std::int::prim::saturating_add),
628            saturating_sub => primitive!(2, std::int::prim::saturating_sub),
629            saturating_mul => primitive!(2, std::int::prim::saturating_mul),
630            wrapping_add => primitive!(2, std::int::prim::wrapping_add),
631            wrapping_sub => primitive!(2, std::int::prim::wrapping_sub),
632            wrapping_mul => primitive!(2, std::int::prim::wrapping_mul),
633            wrapping_div => primitive!(2, std::int::prim::wrapping_div),
634            wrapping_abs => primitive!(1, std::int::prim::wrapping_abs),
635            wrapping_rem => primitive!(2, "std::int::prim::wrapping_rem", int::wrapping_rem),
636            wrapping_rem_euclid => primitive!(2, "std::int::prim::wrapping_rem", int::wrapping_rem_euclid),
637            wrapping_negate => primitive!(1, "std.int.prim.wrapping_negate", std::int::prim::wrapping_neg),
638            overflowing_add => primitive!(2, std::int::prim::overflowing_add),
639            overflowing_sub => primitive!(2, std::int::prim::overflowing_sub),
640            overflowing_mul => primitive!(2, std::int::prim::overflowing_mul),
641            overflowing_div => primitive!(2, std::int::prim::overflowing_div),
642            overflowing_abs => primitive!(1, std::int::prim::overflowing_abs),
643            overflowing_rem => primitive!(2, "std::int::prim::overflowing_rem", int::overflowing_rem),
644            overflowing_rem_euclid => primitive!(2, "std::int::prim::overflowing_rem_euclid", int::overflowing_rem_euclid),
645            overflowing_negate => primitive!(1, "std.int.prim.overflowing_negate", std::int::prim::overflowing_neg),
646            signum => primitive!(1, std::int::prim::signum),
647            is_positive => primitive!(1, std::int::prim::is_positive),
648            is_negative => primitive!(1, std::int::prim::is_negative),
649            from_byte => primitive!(1, "std.int.prim.from_byte", |b: u8| b as VmInt),
650            from_float => primitive!(1, "std.int.prim.from_float", |f: f64| f as VmInt),
651            parse => primitive!(1, "std.int.prim.parse", parse::<VmInt>)
652        },
653    )
654}
655
656#[allow(non_camel_case_types)]
657pub fn load_array(vm: &Thread) -> Result<ExternModule> {
658    ExternModule::new(
659        vm,
660        record! {
661            type Array a => Array<A>,
662            len => primitive!(1, std::array::prim::len),
663            index => primitive!(2, std::array::prim::index),
664            append => primitive!(2, std::array::prim::append),
665            slice => primitive!(3, std::array::prim::slice)
666        },
667    )
668}
669
670pub fn load_string(vm: &Thread) -> Result<ExternModule> {
671    ExternModule::new(
672        vm,
673        record! {
674            len => primitive!(1, std::string::prim::len),
675            is_empty => primitive!(1, std::string::prim::is_empty),
676            is_char_boundary => primitive!(2, std::string::prim::is_char_boundary),
677            as_bytes => primitive!(1, std::string::prim::as_bytes),
678            split_at => primitive!(2, "std.string.prim.split_at", string::split_at),
679            contains => primitive!(2, std::string::prim::contains::<&str>),
680            starts_with => primitive!(2, std::string::prim::starts_with::<&str>),
681            ends_with => primitive!(2, std::string::prim::ends_with::<&str>),
682            find => primitive!(2, std::string::prim::find::<&str>),
683            rfind => primitive!(2, std::string::prim::rfind::<&str>),
684            trim => primitive!(1, std::string::prim::trim),
685            trim_start => primitive!(1, std::string::prim::trim_start),
686            trim_start_matches => primitive!(2, std::string::prim::trim_start_matches::<&str>),
687            trim_end => primitive!(1, std::string::prim::trim_end),
688            trim_end_matches => primitive!(2, std::string::prim::trim_end_matches::<&str>),
689            append => primitive!(2, "std.string.prim.append", string::append),
690            append_char => primitive!(2, "std.string.prim.append_char", string::append_char),
691            from_char => primitive!(1, "std.string.prim.from_char", string::from_char),
692            slice => primitive!(3, "std.string.prim.slice", string::slice),
693            from_utf8 => primitive!(
694                1,
695                "std.string.prim.from_utf8",
696                string::from_utf8
697            ),
698            char_at => primitive!(2, "std.string.prim.char_at", string::char_at)
699        },
700    )
701}
702
703impl<'a> VmType for path::Component<'a> {
704    type Type = Component<'static>;
705    fn make_type(vm: &Thread) -> ArcType {
706        Component::make_type(vm)
707    }
708}
709
710#[derive(Pushable, VmType)]
711#[gluon(vm_type = "std.path.types.Component")]
712#[gluon(gluon_vm)]
713pub enum Component<'a> {
714    Prefix,
715    RootDir,
716    CurDir,
717    ParentDir,
718    Normal(&'a OsStr),
719}
720
721#[derive(Userdata, Debug, VmType)]
722#[gluon(vm_type = "std.fs.Metadata")]
723#[gluon(gluon_vm)]
724pub struct Metadata(fs::Metadata);
725
726unsafe impl Trace for Metadata {
727    impl_trace! { self, _gc, { } }
728}
729
730#[derive(Userdata, Debug, VmType)]
731#[gluon(vm_type = "std.fs.DirEntry")]
732#[gluon(gluon_vm)]
733pub struct DirEntry(fs::DirEntry);
734
735unsafe impl Trace for DirEntry {
736    impl_trace! { self, _gc, { } }
737}
738
739pub fn load_fs(vm: &Thread) -> Result<ExternModule> {
740    vm.register_type::<Metadata>("std.fs.Metadata", &[])?;
741    vm.register_type::<DirEntry>("std.fs.DirEntry", &[])?;
742
743    ExternModule::new(
744        vm,
745        record! {
746            type Metadata => Metadata,
747            type DirEntry => DirEntry,
748
749            read_dir => primitive!(1, "std.fs.prim.read_dir", |p: &Path| {
750                IO::from(fs::read_dir(p).and_then(|iter| iter.map(|result| result.map(DirEntry)).collect::<io::Result<Vec<_>>>()))
751            }),
752
753            dir_entry => record! {
754                path => primitive!(1, "std.fs.prim.dir_entry.path", |m: &DirEntry| m.0.path()),
755                metadata => primitive!(1, "std.fs.prim.dir_entry.metadata", |m: &DirEntry| IO::from(m.0.metadata().map(Metadata))),
756                file_name => primitive!(1, "std.fs.prim.dir_entry.file_name", |m: &DirEntry| m.0.file_name()),
757            },
758
759            metadata => record! {
760                is_dir => primitive!(1, "std.fs.prim.metadata.is_dir", |m: &Metadata| m.0.is_dir()),
761                is_file => primitive!(1, "std.fs.prim.metadata.is_file", |m: &Metadata| m.0.is_file()),
762                len => primitive!(1, "std.fs.prim.metadata.len", |m: &Metadata| m.0.len()),
763            },
764        },
765    )
766}
767
768pub fn load_path(vm: &Thread) -> Result<ExternModule> {
769    ExternModule::new(
770        vm,
771        record! {
772            is_absolute => primitive!(1, std::path::prim::is_absolute),
773            is_relative => primitive!(1, std::path::prim::is_relative),
774            has_root => primitive!(1, std::path::prim::has_root),
775            parent => primitive!(1, std::path::prim::parent),
776            ancestors => primitive!(1, "std.path.prim.ancestors", |p: &Path| p.ancestors().collect::<Vec<_>>()),
777            file_name => primitive!(1, std::path::prim::file_name),
778            strip_prefix => primitive!(2, "std.path.prim.strip_prefix", |p: &Path, b: &Path| p.strip_prefix(b).ok()),
779            starts_with => primitive!(2, "std.path.prim.starts_with", |p: &Path, b: &Path| p.starts_with(b)),
780            ends_with => primitive!(2, "std.path.prim.ends_with", |p: &Path, b: &Path| p.ends_with(b)),
781            file_stem => primitive!(1, std::path::prim::file_stem),
782            extension => primitive!(1, std::path::prim::extension),
783            join => primitive!(2, "std.path.prim.join", std::path::prim::join::<&Path>),
784            with_file_name => primitive!(2, std::path::prim::with_file_name::<&Path>),
785            with_extension => primitive!(2, std::path::prim::with_extension::<&Path>),
786            components => primitive!(1, "std.path.prim.components", |p: &Path| {
787                p.components()
788                    .map(|c| match c {
789                        path::Component::Prefix(..) => Component::Prefix,
790                        path::Component::RootDir => Component::RootDir,
791                        path::Component::CurDir => Component::CurDir,
792                        path::Component::ParentDir => Component::ParentDir,
793                        path::Component::Normal(p) => Component::Normal(p),
794                    })
795                    .collect::<Vec<_>>()
796            }),
797            metadata => primitive!(1, "std.path.prim.metadata", |p: &Path| IO::from(p.metadata().map(Metadata))),
798            symlink_metadata => primitive!(1, "std.path.prim.symlink_metadata", |p: &Path| IO::from(p.symlink_metadata().map(Metadata))),
799            canonicalize => primitive!(1, "std.path.prim.canonicalize", |p: &Path| IO::from(p.canonicalize())),
800            read_link => primitive!(1, "std.path.prim.read_link", |p: &Path| IO::from(p.read_link())),
801            read_dir => primitive!(
802                1,
803                "std.path.prim.read_dir",
804                |p: &Path| IO::from(
805                        p.read_dir()
806                            .and_then(|iter| iter.map(|result| Ok(result?.path())).collect::<StdResult<Vec<_>, _>>())
807                            .map_err(|err| Error::Message(err.to_string()))
808                    )
809            ),
810            exists => primitive!(1, "std.path.prim.exists", |p: &Path| IO::Value(p.exists())),
811            is_file => primitive!(1, "std.path.prim.is_file", |p: &Path| IO::Value(p.is_file())),
812            is_dir => primitive!(1, "std.path.prim.is_file", |p: &Path| IO::Value(p.is_dir())),
813        },
814    )
815}
816
817#[allow(non_camel_case_types)]
818pub fn load_char(vm: &Thread) -> Result<ExternModule> {
819    ExternModule::new(
820        vm,
821        record! {
822            from_int => primitive!(1, "std.char.prim.from_int", ::std::char::from_u32),
823            to_int => primitive!(1, "std.char.prim.to_int", |c: char| c as u32),
824            is_digit => primitive!(2, std::char::prim::is_digit),
825            to_digit => primitive!(2, std::char::prim::to_digit),
826            len_utf8 => primitive!(1, std::char::prim::len_utf8),
827            len_utf16 => primitive!(1, std::char::prim::len_utf16),
828            is_alphabetic => primitive!(1, std::char::prim::is_alphabetic),
829            is_lowercase => primitive!(1, std::char::prim::is_lowercase),
830            is_uppercase => primitive!(1, std::char::prim::is_uppercase),
831            is_whitespace => primitive!(1, std::char::prim::is_whitespace),
832            is_alphanumeric => primitive!(1, std::char::prim::is_alphanumeric),
833            is_control => primitive!(1, std::char::prim::is_control),
834            is_numeric => primitive!(1, std::char::prim::is_numeric),
835        },
836    )
837}
838
839pub mod st_string {
840    use super::*;
841
842    pub(crate) fn len(buf: &StringBuf<S>) -> usize {
843        buf.0.lock().unwrap().len()
844    }
845
846    pub(crate) fn slice(
847        buf: &StringBuf<S>,
848        start: usize,
849        end: usize,
850    ) -> RuntimeResult<String, String> {
851        string::slice(&buf.0.lock().unwrap(), start, end).map(|s| s.to_string())
852    }
853
854    pub(crate) fn pop(buf: &StringBuf<S>) -> Option<char> {
855        buf.0.lock().unwrap().pop()
856    }
857
858    pub(crate) fn push_str(buf: &StringBuf<S>, s: &str) {
859        buf.0.lock().unwrap().push_str(s)
860    }
861}
862
863#[derive(Debug, Default, VmType, Userdata, Trace)]
864#[gluon(vm_type = "std.effect.st.string.StringBuf")]
865#[gluon(gluon_vm)]
866pub(crate) struct StringBuf<S>(Mutex<String>, PhantomData<S>);
867
868pub fn load_string_buf(vm: &Thread) -> Result<ExternModule> {
869    vm.register_type::<StringBuf<S>>("std.effect.st.string.StringBuf", &["s"])?;
870
871    ExternModule::new(
872        vm,
873        record! {
874            type StringBuf s => StringBuf<S>,
875            len => primitive!(1, std::effect::st::string::prim::len),
876            new => primitive!(1, "std.effect.st.string.new", |()| StringBuf(Default::default(), PhantomData::<S>)),
877            slice => primitive!(3, std::effect::st::string::prim::slice),
878            pop => primitive!(1, std::effect::st::string::prim::pop),
879            push_str => primitive!(2, std::effect::st::string::prim::push_str)
880        },
881    )
882}
883
884#[allow(non_camel_case_types, deprecated)]
885pub fn load<'vm>(vm: &'vm Thread) -> Result<ExternModule> {
886    ExternModule::new(
887        vm,
888        record! {
889            show_int => primitive!(1, std::prim::show_int),
890            show_float => primitive!(1, std::prim::show_float),
891            show_byte => primitive!(1, std::prim::show_byte),
892            show_char => primitive!(1, std::prim::show_char),
893            string_compare => primitive!(2, "std.prim.string_compare", str::cmp),
894            string_eq => primitive!(2, "std.prim.string_eq", <str as PartialEq>::eq),
895            error => primitive::<fn(StdString) -> Pushed<A>>("std.prim.error", std::prim::error),
896            discriminant_value => primitive::<fn(OpaqueRef<'vm, A>) -> VmInt>(
897                "std.prim.discriminant_value",
898                std::prim::discriminant_value
899            ),
900        },
901    )
902}