1use 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}