1mod utils;
2
3use crate::utils::set_panic_hook;
4use compiler::compiler::Compiler;
5use compiler::snapshot::{read_bytecode, write_bytecode};
6use compiler::snapshot_layout::describe_bytecode;
7use monkey_asm::emitter::AsmDialect;
8use monkey_asm::lower::lower_node;
9use object::builtins::BuiltIns;
10use parser::ast::Node;
11use parser::parse as parser_pase;
12use parser::validation::validate_program;
13use parser::{parse_ast_json_string, parse_ast_lossless_json_string, stringify_integer_literals};
14use wasm_bindgen::prelude::*;
15use wasm_bindgen::throw_str;
16
17const PLAYGROUND_GC_INSTRUCTION_BUDGET: usize = 10_000;
18
19#[cfg(feature = "wee_alloc")]
22#[global_allocator]
23static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
24
25#[wasm_bindgen]
26pub fn parse(input: &str) -> String {
27 set_panic_hook();
28 match parse_ast_json_string(input) {
29 Ok(node) => node.to_string(),
30 Err(e) => throw_str(format!("parse error: {}", e[0]).as_str()),
31 }
32}
33
34#[wasm_bindgen]
37pub fn parse_lossless(input: &str) -> String {
38 set_panic_hook();
39 match parse_ast_lossless_json_string(input) {
40 Ok(node) => node,
41 Err(e) => throw_str(format!("parse error: {}", e[0]).as_str()),
42 }
43}
44
45#[wasm_bindgen]
62pub fn analyze_lossless(input: &str) -> String {
63 set_panic_hook();
64
65 let envelope = match analyze_envelope(input) {
66 Ok(program) => serde_json::json!({
67 "status": "ok",
68 "program": program,
69 }),
70 Err((stage, message, span)) => serde_json::json!({
71 "status": "error",
72 "stage": stage,
73 "message": message,
74 "span": span.map(|(start, end)| serde_json::json!({ "start": start, "end": end })),
75 }),
76 };
77 serde_json::to_string(&envelope).expect("analyze envelope serialization should not fail")
78}
79
80type AnalyzeFailure = (&'static str, String, Option<(usize, usize)>);
81
82fn analyze_envelope(input: &str) -> Result<serde_json::Value, AnalyzeFailure> {
83 let node = parser_pase(input).map_err(|errors| {
84 let message = errors
85 .first()
86 .cloned()
87 .unwrap_or_else(|| "unknown parse error".to_string());
88 ("parse", message, None)
89 })?;
90 let Node::Program(program) = &node else {
91 unreachable!("parse always returns a Program node");
92 };
93
94 let predefined = BuiltIns
95 .iter()
96 .map(|builtin| builtin.name)
97 .collect::<Vec<_>>();
98 validate_program(program, &predefined)
99 .map_err(|error| ("validation", error.message, Some((error.span.start, error.span.end))))?;
100
101 let mut ast = serde_json::to_value(program).expect("AST serialization should not fail");
102 stringify_integer_literals(&mut ast);
103 Ok(ast)
104}
105
106#[wasm_bindgen]
107pub fn compile(input: &str) -> String {
108 set_panic_hook();
109
110 let program = match parser_pase(input) {
111 Ok(ast) => ast,
112 Err(e) => throw_str(format!("parse error: {}", e[0]).as_str()),
113 };
114 let mut compiler = Compiler::new();
115 match compiler.compile(&program) {
116 Ok(bytecode) => return bytecode.instructions.string(),
117 Err(e) => throw_str(format!("compile error: {}", e).as_str()),
118 }
119}
120
121#[wasm_bindgen]
122pub fn compile_detail(input: &str) -> String {
123 set_panic_hook();
124
125 let program = match parser_pase(input) {
126 Ok(ast) => ast,
127 Err(e) => throw_str(format!("parse error: {}", e[0]).as_str()),
128 };
129 let mut compiler = Compiler::new();
130 match compiler.compile(&program) {
131 Ok(bytecode) => return bytecode.string(),
132 Err(e) => throw_str(format!("compile error: {}", e).as_str()),
133 }
134}
135
136#[wasm_bindgen]
137pub fn compile_with_debug(input: &str) -> String {
138 set_panic_hook();
139
140 let program = match parser_pase(input) {
141 Ok(ast) => ast,
142 Err(e) => throw_str(format!("parse error: {}", e[0]).as_str()),
143 };
144 let mut compiler = Compiler::new();
145 match compiler.compile(&program) {
146 Ok(bytecode) => match serde_json::to_string(&bytecode.debug_view()) {
147 Ok(json) => json,
148 Err(e) => throw_str(format!("json error: {}", e).as_str()),
149 },
150 Err(e) => throw_str(format!("compile error: {}", e).as_str()),
151 }
152}
153
154#[wasm_bindgen]
159pub fn run_gc_with_report(input: &str) -> String {
160 set_panic_hook();
161
162 let envelope =
163 match gc::run_source_with_report_classified(input, PLAYGROUND_GC_INSTRUCTION_BUDGET) {
164 Ok(success) => serde_json::json!({
165 "status": "ok",
166 "result": success.result,
167 "report": success.report,
168 }),
169 Err(error) => serde_json::json!({
170 "status": "error",
171 "stage": error.stage,
172 "kind": error.kind,
173 "message": error.message,
174 "span": error.span,
175 }),
176 };
177
178 serde_json::to_string(&envelope).expect("GC run envelope serialization should not fail")
179}
180
181#[wasm_bindgen]
189pub fn compile_to_arm64(input: &str) -> String {
190 set_panic_hook();
191
192 let envelope = match arm64_envelope(input) {
193 Ok(envelope) => envelope,
194 Err((stage, message, span)) => serde_json::json!({
195 "status": "error",
196 "stage": stage,
197 "kind": stage,
198 "message": message,
199 "span": span.map(|(start, end)| serde_json::json!({ "start": start, "end": end })),
200 }),
201 };
202 serde_json::to_string(&envelope).expect("arm64 envelope serialization should not fail")
203}
204
205type Arm64Failure = (&'static str, String, Option<(usize, usize)>);
206
207fn arm64_envelope(input: &str) -> Result<serde_json::Value, Arm64Failure> {
208 let node = parser_pase(input).map_err(|errors| {
209 let message = errors
210 .first()
211 .cloned()
212 .unwrap_or_else(|| "unknown parse error".to_string());
213 ("parse", message, None)
214 })?;
215 let assembly = lower_node(input, &node, AsmDialect::LinuxElf, false)
217 .map_err(|error| ("compile", error.message, error.span))?;
218
219 let lines: Vec<serde_json::Value> = assembly
221 .text
222 .lines()
223 .zip(assembly.line_spans.iter())
224 .map(|(text, span)| {
225 serde_json::json!({
226 "text": text,
227 "kind": arm64_line_kind(text),
228 "span": span.map(|(start, end)| serde_json::json!({ "start": start, "end": end })),
229 })
230 })
231 .collect();
232 Ok(serde_json::json!({ "status": "ok", "lines": lines }))
233}
234
235fn arm64_line_kind(text: &str) -> &'static str {
238 let code = match text.find("//") {
239 Some(index) => &text[..index],
240 None => text,
241 };
242 let trimmed = code.trim();
243 if trimmed.is_empty() {
244 if text.trim().is_empty() {
245 "blank"
246 } else {
247 "comment"
248 }
249 } else if trimmed.ends_with(':') {
250 "label"
251 } else if trimmed.starts_with('.') {
252 "directive"
253 } else {
254 "code"
255 }
256}
257
258#[wasm_bindgen]
265pub fn compile_to_snapshot(input: &str, strip_debug: bool) -> String {
266 set_panic_hook();
267
268 let envelope = match snapshot_envelope(input, strip_debug) {
269 Ok(envelope) => envelope,
270 Err((stage, message)) => serde_json::json!({
271 "status": "error",
272 "stage": stage,
273 "kind": stage,
274 "message": message,
275 }),
276 };
277 serde_json::to_string(&envelope).expect("snapshot envelope serialization should not fail")
278}
279
280fn snapshot_envelope(
281 input: &str,
282 strip_debug: bool,
283) -> Result<serde_json::Value, (&'static str, String)> {
284 let program = parser_pase(input).map_err(|errors| {
285 let message = errors
286 .first()
287 .cloned()
288 .unwrap_or_else(|| "unknown parse error".to_string());
289 ("parse", message)
290 })?;
291 let mut compiler = Compiler::new();
292 let bytecode = compiler
293 .compile(&program)
294 .map_err(|message| ("compile", message))?;
295 let bytes = write_bytecode(&bytecode, strip_debug)
296 .map_err(|error| ("snapshot", format!("{:?}", error)))?;
297 let layout = describe_bytecode(&bytes).map_err(|error| ("snapshot", format!("{:?}", error)))?;
298 Ok(serde_json::json!({
299 "status": "ok",
300 "bytesHex": hex_encode(&bytes),
301 "layout": layout,
302 }))
303}
304
305fn hex_encode(bytes: &[u8]) -> String {
306 use std::fmt::Write;
307
308 let mut out = String::with_capacity(bytes.len() * 2);
309 for byte in bytes {
310 write!(out, "{:02x}", byte).expect("writing to a String cannot fail");
311 }
312 out
313}
314
315#[wasm_bindgen]
324pub fn run_snapshot(bytes: &[u8]) -> String {
325 set_panic_hook();
326
327 let envelope = match read_bytecode(bytes) {
328 Ok(bytecode) => {
329 let mut vm = gc::GcVM::new(bytecode);
330 match vm
331 .run_with_budget_classified(PLAYGROUND_GC_INSTRUCTION_BUDGET)
332 .map(|()| vm.last_result_string())
333 {
334 Ok(result) => serde_json::json!({
335 "status": "ok",
336 "result": result,
337 }),
338 Err(error) => serde_json::json!({
339 "status": "error",
340 "stage": "runtime",
341 "kind": error.kind,
342 "message": error.message,
343 "span": error.span,
344 }),
345 }
346 }
347 Err(error) => serde_json::json!({
348 "status": "error",
349 "stage": "snapshot",
350 "kind": "invalidSnapshot",
351 "message": format!("{:?}", error),
352 "span": null,
353 }),
354 };
355 serde_json::to_string(&envelope).expect("snapshot run envelope serialization should not fail")
356}
357
358#[wasm_bindgen]
362pub fn run_snapshot_with_output(bytes: &[u8]) -> String {
363 set_panic_hook();
364
365 let envelope = match read_bytecode(bytes) {
366 Ok(bytecode) => {
367 let (result, stdout) =
368 gc::run_bytecode_with_output(bytecode, PLAYGROUND_GC_INSTRUCTION_BUDGET);
369 match result {
370 Ok(result) => serde_json::json!({
371 "status": "ok",
372 "result": result,
373 "stdout": stdout,
374 }),
375 Err(error) => serde_json::json!({
376 "status": "error",
377 "stage": "runtime",
378 "kind": error.kind,
379 "message": error.message,
380 "span": error.span,
381 "stdout": stdout,
382 }),
383 }
384 }
385 Err(error) => serde_json::json!({
386 "status": "error",
387 "stage": "snapshot",
388 "kind": "invalidSnapshot",
389 "message": format!("{:?}", error),
390 "span": null,
391 "stdout": "",
392 }),
393 };
394 serde_json::to_string(&envelope).expect("snapshot run envelope serialization should not fail")
395}