petr_playground/
lib.rs

1//! Basic WASM bindings for the pete compiler
2//! Nothing fancy at all, could definitely be improved over time to support better error reporting,
3//! etc
4
5use petr_api::{render_error, resolve_symbols, type_check, Formattable, FormatterContext, Lowerer, Parser, Vm};
6use wasm_bindgen::prelude::*;
7
8#[cfg(test)]
9mod tests;
10
11#[wasm_bindgen]
12extern "C" {
13    #[wasm_bindgen(js_name = setOutputContent)]
14    fn set_output_content(s: &str);
15
16    #[wasm_bindgen(js_name = setCodeEditorContent)]
17    fn set_code_editor_content(s: &str);
18}
19
20#[wasm_bindgen]
21pub fn run_snippet(code: &str) {
22    run_snippet_inner(code, set_output_content);
23}
24
25fn run_snippet_inner<F>(
26    code: &str,
27    set_output_content: F,
28) where
29    F: Fn(&str),
30{
31    let lowerer = match compile_snippet(code.to_string()) {
32        Ok(o) => o,
33        Err(e) => {
34            let err_text = errors_to_html(&e);
35            set_output_content(&err_text);
36            return;
37        },
38    };
39
40    let (data, instructions) = lowerer.finalize();
41
42    let vm = Vm::new(instructions, data);
43    let (result, _stack, logs) = match vm.run() {
44        Ok(o) => o,
45        Err(e) => {
46            set_output_content(&format!("Runtime error: {:#?}", e));
47            return;
48        },
49    };
50
51    set_output_content(&format!("Logs:<br>\t{}<br>Result: <br>\t{:#?}", logs.join("\n\t"), result.inner()));
52}
53
54fn errors_to_html(e: &[String]) -> String {
55    let mut buf = String::new();
56    buf.push_str("<div class=\"errors\">");
57    for err in e {
58        buf.push_str(&format!("<div class=\"error\">{}</div>", err));
59    }
60    buf.push_str("</div>");
61    buf
62}
63
64fn compile_snippet(input: String) -> Result<Lowerer, Vec<String>> {
65    let mut sources = petr_stdlib::stdlib();
66    sources.push(("snippet", &input));
67    let parser = petr_api::Parser::new(sources);
68    let (ast, errs, interner, source_map) = parser.into_result();
69    if !errs.is_empty() {
70        return Err(errs.into_iter().map(|err| format!("{:?}", render_error(&source_map, err))).collect());
71    }
72    let (errs, resolved) = resolve_symbols(ast, interner, Default::default());
73    if !errs.is_empty() {
74        return Err(errs.into_iter().map(|err| format!("{:?}", render_error(&source_map, err))).collect());
75    }
76
77    let (errs, type_checker) = type_check(resolved);
78
79    if !errs.is_empty() {
80        return Err(errs.into_iter().map(|err| format!("{:?}", render_error(&source_map, err))).collect());
81    }
82
83    let lowerer = match Lowerer::new(type_checker) {
84        Ok(l) => l,
85        Err(err) => panic!("lowering failed: {:?}", err),
86    };
87
88    Ok(lowerer)
89}
90
91#[wasm_bindgen]
92pub fn format(code: String) {
93    let parser = Parser::new(vec![("snippet".to_string(), code)]);
94    let (ast, errs, interner, source_map) = parser.into_result();
95    if !errs.is_empty() {
96        let errs = errs
97            .into_iter()
98            .map(|e| format!("{:?}", render_error(&source_map, e)))
99            .collect::<Vec<_>>();
100        set_output_content(&errs.join("<br>"));
101        return;
102    }
103    let mut ctx = FormatterContext::from_interner(interner).with_config(Default::default());
104    let formatted_content = ast.line_length_aware_format(&mut ctx).render();
105    set_code_editor_content(&formatted_content);
106}