chasm_rs/
lib.rs

1//! This crate is a single-pass compiler to WebAssembly for the language chasm.
2//!
3//! chasm is a very simple language created by Colin Eberhardt, to introduce the basic building
4//! blocks of compilers, and reveal some of the inner workings of WebAssembly. This is a
5//! implementation of the compiler in Rust.
6#![deny(missing_docs)]
7use std::io::Write;
8
9mod wasm_macro;
10use wasm_macro::wasm;
11
12pub(crate) mod compiler;
13pub use compiler::{Error, ErrorKind};
14
15#[cfg(test)]
16mod run_wasm;
17#[cfg(test)]
18mod test;
19
20fn write_section(w: &mut Vec<u8>, section_type: u8, f: impl Fn(&mut Vec<u8>)) {
21    // section type
22    w.write_all(&[section_type]).unwrap();
23    let section_start = w.len();
24
25    f(w);
26
27    // write the length of the section at the start
28
29    // write the length
30    let section_len = w.len() - section_start;
31    let len = leb128::write::unsigned(w, section_len as u64).unwrap();
32    // move it to the start
33    w[section_start..].rotate_right(len);
34}
35
36/// Compile the given chasm source code in a WebAssembly module.
37///
38/// The created module imports the function `"env" "print"` that received a f32 and return nothing,
39/// and a memory `"env" "memory"` with a minimal size of 1, and exports the function `"main"`, that
40/// has no argument or return, which is the code entry point.
41///
42/// At the end of the execution of the created module, the rendered 100x100 output will be in the
43/// linear memory, in the range 0..10000.
44///
45/// # Example
46/// ```
47/// let source = "
48///     var y = 0
49///     while (y < 100)
50///         var x = 0
51///         while (x < 100)
52///             c = ((y/100)*255)
53///             setpixel (x, y, c)
54///             x = (x + 1)
55///         endwhile
56///         y = (y + 1)
57///     endwhile";
58///
59/// let wasm = chasm_rs::compile(source);
60///
61/// assert!(wasm.is_ok());
62/// ```
63pub fn compile<'s>(source: &'s str) -> Result<Vec<u8>, Error<'s>> {
64    let functions = compiler::Parser::parse(source)?;
65
66    let mut binary = wasm!( new
67        (magic version)
68    );
69
70    // section type
71    write_section(&mut binary, wasm!(section_type type), |mut w| {
72        // number of types
73        leb128::write::unsigned(&mut w, 1 + functions.len() as u64).unwrap();
74        // print function type
75        wasm!(&mut w, (functype (vec f32) (vec)));
76        for f in &functions {
77            wasm!(&mut w, functype);
78            leb128::write::unsigned(&mut w, f.num_param as u64).unwrap();
79            for _ in 0..f.num_param {
80                wasm!(&mut w, f32);
81            }
82            wasm!(&mut w, (vec));
83        }
84    });
85
86    wasm!(&mut binary,
87        (section import (vec
88            (import "env" "print" (function 0x0))
89            (import "env" "memory" (memory 1))))
90    );
91
92    // (section function (vec 1))
93    write_section(&mut binary, wasm!(section_type function), |mut w| {
94        // number of functions
95        leb128::write::unsigned(&mut w, functions.len() as u64).unwrap();
96        // print function type
97        for f in &functions {
98            leb128::write::unsigned(&mut w, f.idx as u64).unwrap();
99        }
100    });
101
102    wasm!(&mut binary, (section export (vec (export "main" function 0x1))));
103
104    // section code
105    write_section(&mut binary, wasm!(section_type code), |mut w| {
106        // number of functions
107        leb128::write::unsigned(&mut w, functions.len() as u64).unwrap();
108        // print function type
109        for f in &functions {
110            leb128::write::unsigned(&mut w, f.code.len() as u64).unwrap();
111            w.write_all(&f.code).unwrap();
112        }
113    });
114
115    Ok(binary)
116}