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}