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 parser::parse as parser_pase;
10use parser::parse_ast_json_string;
11use wasm_bindgen::prelude::*;
12use wasm_bindgen::throw_str;
13
14const PLAYGROUND_GC_INSTRUCTION_BUDGET: usize = 10_000;
15
16#[cfg(feature = "wee_alloc")]
19#[global_allocator]
20static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
21
22#[wasm_bindgen]
23pub fn parse(input: &str) -> String {
24 set_panic_hook();
25 match parse_ast_json_string(input) {
26 Ok(node) => node.to_string(),
27 Err(e) => throw_str(format!("parse error: {}", e[0]).as_str()),
28 }
29}
30
31#[wasm_bindgen]
32pub fn compile(input: &str) -> String {
33 set_panic_hook();
34
35 let program = match parser_pase(input) {
36 Ok(ast) => ast,
37 Err(e) => throw_str(format!("parse error: {}", e[0]).as_str()),
38 };
39 let mut compiler = Compiler::new();
40 match compiler.compile(&program) {
41 Ok(bytecode) => return bytecode.instructions.string(),
42 Err(e) => throw_str(format!("compile error: {}", e).as_str()),
43 }
44}
45
46#[wasm_bindgen]
47pub fn compile_detail(input: &str) -> String {
48 set_panic_hook();
49
50 let program = match parser_pase(input) {
51 Ok(ast) => ast,
52 Err(e) => throw_str(format!("parse error: {}", e[0]).as_str()),
53 };
54 let mut compiler = Compiler::new();
55 match compiler.compile(&program) {
56 Ok(bytecode) => return bytecode.string(),
57 Err(e) => throw_str(format!("compile error: {}", e).as_str()),
58 }
59}
60
61#[wasm_bindgen]
62pub fn compile_with_debug(input: &str) -> String {
63 set_panic_hook();
64
65 let program = match parser_pase(input) {
66 Ok(ast) => ast,
67 Err(e) => throw_str(format!("parse error: {}", e[0]).as_str()),
68 };
69 let mut compiler = Compiler::new();
70 match compiler.compile(&program) {
71 Ok(bytecode) => match serde_json::to_string(&bytecode.debug_view()) {
72 Ok(json) => json,
73 Err(e) => throw_str(format!("json error: {}", e).as_str()),
74 },
75 Err(e) => throw_str(format!("compile error: {}", e).as_str()),
76 }
77}
78
79#[wasm_bindgen]
84pub fn run_gc_with_report(input: &str) -> String {
85 set_panic_hook();
86
87 let envelope = match gc::run_source_with_report(input, PLAYGROUND_GC_INSTRUCTION_BUDGET) {
88 Ok(success) => serde_json::json!({
89 "status": "ok",
90 "result": success.result,
91 "report": success.report,
92 }),
93 Err(error) => serde_json::json!({
94 "status": "error",
95 "stage": error.stage,
96 "message": error.message,
97 "span": error.span,
98 }),
99 };
100
101 serde_json::to_string(&envelope).expect("GC run envelope serialization should not fail")
102}
103
104#[wasm_bindgen]
112pub fn compile_to_arm64(input: &str) -> String {
113 set_panic_hook();
114
115 let envelope = match arm64_envelope(input) {
116 Ok(envelope) => envelope,
117 Err((stage, message, span)) => serde_json::json!({
118 "status": "error",
119 "stage": stage,
120 "message": message,
121 "span": span.map(|(start, end)| serde_json::json!({ "start": start, "end": end })),
122 }),
123 };
124 serde_json::to_string(&envelope).expect("arm64 envelope serialization should not fail")
125}
126
127type Arm64Failure = (&'static str, String, Option<(usize, usize)>);
128
129fn arm64_envelope(input: &str) -> Result<serde_json::Value, Arm64Failure> {
130 let node = parser_pase(input).map_err(|errors| {
131 let message = errors
132 .first()
133 .cloned()
134 .unwrap_or_else(|| "unknown parse error".to_string());
135 ("parse", message, None)
136 })?;
137 let assembly = lower_node(input, &node, AsmDialect::LinuxElf, false)
139 .map_err(|error| ("compile", error.message, error.span))?;
140
141 let lines: Vec<serde_json::Value> = assembly
143 .text
144 .lines()
145 .zip(assembly.line_spans.iter())
146 .map(|(text, span)| {
147 serde_json::json!({
148 "text": text,
149 "kind": arm64_line_kind(text),
150 "span": span.map(|(start, end)| serde_json::json!({ "start": start, "end": end })),
151 })
152 })
153 .collect();
154 Ok(serde_json::json!({ "status": "ok", "lines": lines }))
155}
156
157fn arm64_line_kind(text: &str) -> &'static str {
160 let code = match text.find("//") {
161 Some(index) => &text[..index],
162 None => text,
163 };
164 let trimmed = code.trim();
165 if trimmed.is_empty() {
166 if text.trim().is_empty() {
167 "blank"
168 } else {
169 "comment"
170 }
171 } else if trimmed.ends_with(':') {
172 "label"
173 } else if trimmed.starts_with('.') {
174 "directive"
175 } else {
176 "code"
177 }
178}
179
180#[wasm_bindgen]
187pub fn compile_to_snapshot(input: &str, strip_debug: bool) -> String {
188 set_panic_hook();
189
190 let envelope = match snapshot_envelope(input, strip_debug) {
191 Ok(envelope) => envelope,
192 Err((stage, message)) => serde_json::json!({
193 "status": "error",
194 "stage": stage,
195 "message": message,
196 }),
197 };
198 serde_json::to_string(&envelope).expect("snapshot envelope serialization should not fail")
199}
200
201fn snapshot_envelope(
202 input: &str,
203 strip_debug: bool,
204) -> Result<serde_json::Value, (&'static str, String)> {
205 let program = parser_pase(input).map_err(|errors| {
206 let message = errors
207 .first()
208 .cloned()
209 .unwrap_or_else(|| "unknown parse error".to_string());
210 ("parse", message)
211 })?;
212 let mut compiler = Compiler::new();
213 let bytecode = compiler
214 .compile(&program)
215 .map_err(|message| ("compile", message))?;
216 let bytes = write_bytecode(&bytecode, strip_debug)
217 .map_err(|error| ("snapshot", format!("{:?}", error)))?;
218 let layout = describe_bytecode(&bytes).map_err(|error| ("snapshot", format!("{:?}", error)))?;
219 Ok(serde_json::json!({
220 "status": "ok",
221 "bytesHex": hex_encode(&bytes),
222 "layout": layout,
223 }))
224}
225
226fn hex_encode(bytes: &[u8]) -> String {
227 use std::fmt::Write;
228
229 let mut out = String::with_capacity(bytes.len() * 2);
230 for byte in bytes {
231 write!(out, "{:02x}", byte).expect("writing to a String cannot fail");
232 }
233 out
234}
235
236#[wasm_bindgen]
244pub fn run_snapshot(bytes: &[u8]) -> String {
245 set_panic_hook();
246
247 let envelope = match read_bytecode(bytes) {
248 Ok(bytecode) => match gc::run_bytecode(bytecode, PLAYGROUND_GC_INSTRUCTION_BUDGET) {
249 Ok(result) => serde_json::json!({
250 "status": "ok",
251 "result": result,
252 }),
253 Err(error) => serde_json::json!({
254 "status": "error",
255 "stage": "runtime",
256 "message": error.message,
257 "span": error.span,
258 }),
259 },
260 Err(error) => serde_json::json!({
261 "status": "error",
262 "stage": "snapshot",
263 "message": format!("{:?}", error),
264 "span": null,
265 }),
266 };
267 serde_json::to_string(&envelope).expect("snapshot run envelope serialization should not fail")
268}