1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//! Stak Scheme bytecode compiler.

mod error;

pub use self::error::CompileError;
use stak_device::ReadWriteDevice;
use stak_primitive::SmallPrimitiveSet;
use stak_vm::Vm;
use std::io::{empty, Read, Write};

const DEFAULT_HEAP_SIZE: usize = 1 << 20;
const PRELUDE_SOURCE: &str = include_str!("prelude.scm");
const COMPILER_BYTECODES: &[u8] = include_bytes!(std::env!("STAK_BYTECODE_FILE"));

/// Compiles a program in R7RS Scheme into bytecodes.
///
/// # Examples
///
/// ```rust
/// let source = "(define x 42)";
/// let mut target = vec![];
///
/// stak_compiler::compile_r7rs(source.as_bytes(), &mut target).unwrap();
/// ```
pub fn compile_r7rs(source: impl Read, target: impl Write) -> Result<(), CompileError> {
    compile_bare(PRELUDE_SOURCE.as_bytes().chain(source), target)
}

/// Compiles a program in Scheme into bytecodes with only built-ins.
///
/// # Examples
///
/// ```rust
/// let source = "($$define x 42)";
/// let mut target = vec![];
///
/// stak_compiler::compile_bare(source.as_bytes(), &mut target).unwrap();
/// ```
pub fn compile_bare(source: impl Read, target: impl Write) -> Result<(), CompileError> {
    let mut heap = vec![Default::default(); DEFAULT_HEAP_SIZE];
    let device = ReadWriteDevice::new(source, target, empty());
    let mut vm = Vm::new(&mut heap, SmallPrimitiveSet::new(device))?;

    vm.initialize(COMPILER_BYTECODES.iter().copied())?;
    vm.run()?;

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    mod r7rs {
        use super::*;

        #[test]
        fn compile_nothing() {
            compile_r7rs(b"".as_slice(), &mut vec![]).unwrap();
        }

        #[test]
        fn compile_define() {
            compile_r7rs(b"(define x 42)".as_slice(), &mut vec![]).unwrap();
        }
    }

    mod bare {
        use super::*;

        #[test]
        fn compile_nothing() {
            compile_bare(b"".as_slice(), &mut vec![]).unwrap();
        }

        #[test]
        fn compile_define() {
            compile_bare(b"($$define x 42)".as_slice(), &mut vec![]).unwrap();
        }
    }
}