Skip to main content

stak_sac/
lib.rs

1//! Utilities to build executable binaries from bytecode files.
2
3#![cfg_attr(all(doc, not(doctest)), feature(doc_cfg))]
4#![no_std]
5
6#[cfg(feature = "libc")]
7#[doc(hidden)]
8pub extern crate alloc;
9#[cfg(feature = "std")]
10#[doc(hidden)]
11pub extern crate std;
12
13#[doc(hidden)]
14pub mod __private {
15    #[cfg(feature = "libc")]
16    pub use alloc;
17    #[cfg(feature = "std")]
18    pub use clap;
19    #[cfg(feature = "libc")]
20    pub use dlmalloc;
21    #[cfg(feature = "std")]
22    pub use main_error;
23    #[cfg(feature = "libc")]
24    pub use origin;
25    pub use stak_configuration;
26    pub use stak_device;
27    pub use stak_file;
28    pub use stak_macro;
29    pub use stak_process_context;
30    pub use stak_r7rs;
31    pub use stak_time;
32    pub use stak_vm;
33    #[cfg(feature = "std")]
34    pub use std;
35}
36
37/// Defines a `main` function that runs a given source file.
38///
39/// The R7RS standard libraries are based on [the `std` crate](https://doc.rust-lang.org/std/).
40///
41/// The given source file is compiled into bytecode and bundled into a
42/// resulting binary.
43///
44/// # Examples
45///
46/// ```rust
47/// use stak::sac::main;
48///
49/// main!("main.scm");
50/// ```
51#[cfg(feature = "std")]
52#[macro_export]
53macro_rules! main {
54    ($path:expr) => {
55        $crate::main!(
56            $path,
57            $crate::__private::stak_configuration::DEFAULT_HEAP_SIZE
58        );
59    };
60    ($path:expr, $heap_size:expr) => {
61        use $crate::__private::{
62            clap::{self, Parser},
63            main_error::MainError,
64            stak_device::StdioDevice,
65            stak_file::OsFileSystem,
66            stak_macro::include_r7rs,
67            stak_process_context::OsProcessContext,
68            stak_r7rs::SmallPrimitiveSet,
69            stak_time::OsClock,
70            stak_vm::Vm,
71        };
72
73        #[derive(clap::Parser)]
74        #[command(disable_help_flag = true, ignore_errors = true, version)]
75        struct Arguments {
76            #[arg()]
77            arguments: Vec<String>,
78            #[arg(short = 's', long, default_value_t = $heap_size)]
79            heap_size: usize,
80        }
81
82        fn main() -> Result<(), MainError> {
83            let arguments = Arguments::parse();
84
85            Vm::new(
86                vec![Default::default(); arguments.heap_size],
87                SmallPrimitiveSet::new(
88                    StdioDevice::new(),
89                    OsFileSystem::new(),
90                    OsProcessContext::new(),
91                    OsClock::new(),
92                ),
93            )?
94            .run(include_r7rs!($path).iter().copied())?;
95
96            Ok(())
97        }
98    };
99}
100
101/// Defines a `main` function that runs a given source file.
102///
103/// The R7RS standard libraries are based on [the `libc` crate](https://docs.rs/libc).
104///
105/// The given source file is compiled into bytecode and bundled into a
106/// resulting binary.
107#[cfg(feature = "libc")]
108#[macro_export]
109macro_rules! libc_main {
110    ($path:expr) => {
111        $crate::libc_main!(
112            $path,
113            $crate::__private::stak_configuration::DEFAULT_HEAP_SIZE
114        );
115    };
116    ($path:expr, $heap_size:expr) => {
117        use $crate::__private::{
118            alloc::vec,
119            dlmalloc::GlobalDlmalloc,
120            origin::program::exit,
121            stak_device::libc::{ReadWriteDevice, Stderr, Stdin, Stdout},
122            stak_file::LibcFileSystem,
123            stak_macro::include_r7rs,
124            stak_process_context::LibcProcessContext,
125            stak_r7rs::SmallPrimitiveSet,
126            stak_time::LibcClock,
127            stak_vm::Vm,
128        };
129
130        #[global_allocator]
131        static GLOBAL_ALLOCATOR: GlobalDlmalloc = GlobalDlmalloc;
132
133        #[cfg(not(test))]
134        #[panic_handler]
135        fn panic(_info: &core::panic::PanicInfo) -> ! {
136            exit(1)
137        }
138
139        #[cfg_attr(not(test), unsafe(no_mangle))]
140        extern "C" fn main(argc: isize, argv: *const *const i8) {
141            Vm::new(
142                vec![Default::default(); $heap_size],
143                SmallPrimitiveSet::new(
144                    ReadWriteDevice::new(Stdin::new(), Stdout::new(), Stderr::new()),
145                    LibcFileSystem::new(),
146                    unsafe { LibcProcessContext::new(argc, argv) },
147                    LibcClock::new(),
148                ),
149            )
150            .unwrap()
151            .run(include_r7rs!($path).iter().copied())
152            .unwrap();
153
154            exit(0);
155        }
156    };
157}