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