1pub mod common;
2pub mod config;
3pub mod core;
4pub mod utils;
5
6use serde::{Deserialize, Serialize};
7use serde_wasm_bindgen::to_value;
8use wasm_bindgen::prelude::*;
9
10use crate::core::{
11 audio::{engine::AudioEngine, interpreter::driver::run_audio_program},
12 parser::statement::{Statement, StatementKind},
13 preprocessor::loader::ModuleLoader,
14 shared::value::Value,
15 store::{function::FunctionTable, global::GlobalStore, variable::VariableTable},
16 utils::path::normalize_path,
17};
18
19#[derive(Serialize, Deserialize)]
20struct ParseResult {
21 ok: bool,
22 ast: String,
23 errors: Vec<ErrorResult>,
24}
25
26#[derive(Serialize, Deserialize)]
27struct ErrorResult {
28 message: String,
29 line: usize,
30 column: usize,
31}
32
33#[wasm_bindgen]
34pub fn parse(entry_path: &str, source: &str) -> Result<JsValue, JsValue> {
35 let statements = parse_internal_from_string(entry_path, source);
36
37 match statements {
38 Ok(value) => {
39 let ast_string = value;
40 to_value(&ast_string)
41 .map_err(|e| JsValue::from_str(&format!("Error converting AST to JS value: {}", e)))
42 }
43 Err(e) => Err(JsValue::from_str(&format!("Error: {}", e))),
44 }
45}
46
47#[wasm_bindgen]
48pub fn render_audio(user_code: &str) -> Result<js_sys::Float32Array, JsValue> {
49 let entry_path = normalize_path("playground.deva");
50 let output_path = normalize_path("./temp");
51
52 let mut global_store = GlobalStore::new();
53
54 let loader =
55 ModuleLoader::from_raw_source(&entry_path, &output_path, user_code, &mut global_store);
56
57 loader
58 .load_wasm_module(&mut global_store)
59 .map_err(|e| JsValue::from_str(&format!("Module loading error: {}", e)))?;
60
61 let all_statements_map = loader.extract_statements_map(&global_store);
62
63 let main_statements = all_statements_map
64 .get(&entry_path)
65 .ok_or(JsValue::from_str("❌ No statements found for entry module"))?
66 .clone();
67
68 let mut audio_engine = AudioEngine::new("wasm_output".to_string());
69
70 let _ = run_audio_program(
71 &main_statements,
72 &mut audio_engine,
73 "playground".to_string(),
74 "wasm_output".to_string(),
75 VariableTable::new(),
76 FunctionTable::new(),
77 &mut global_store,
78 );
79
80 let samples = audio_engine.get_normalized_buffer();
81
82 if samples.is_empty() {
83 return Err(JsValue::from_str("❌ Audio buffer is empty"));
84 }
85
86 Ok(js_sys::Float32Array::from(samples.as_slice()))
87}
88
89fn parse_internal_from_string(virtual_path: &str, source: &str) -> Result<ParseResult, String> {
90 let entry_path = normalize_path(virtual_path);
91 let output_path = normalize_path("./temp");
92
93 let mut global_store = GlobalStore::new();
94 let loader =
95 ModuleLoader::from_raw_source(&entry_path, &output_path, source, &mut global_store);
96
97 let module = loader
98 .load_single_module(&mut global_store)
99 .map_err(|e| format!("Error loading module: {}", e))?;
100
101 let raw_ast = ast_to_string(module.statements.clone());
102
103 let found_errors = collect_errors_recursively(&module.statements);
104
105 let result = ParseResult {
106 ok: true,
107 ast: raw_ast,
108 errors: found_errors,
109 };
110
111 Ok(result)
112}
113
114fn collect_errors_recursively(statements: &[Statement]) -> Vec<ErrorResult> {
115 let mut errors: Vec<ErrorResult> = Vec::new();
116
117 for stmt in statements {
118 match &stmt.kind {
119 StatementKind::Unknown => {
120 errors.push(ErrorResult {
121 message: format!("Unknown statement at line {}:{}", stmt.line, stmt.column),
122 line: stmt.line,
123 column: stmt.column,
124 });
125 }
126 StatementKind::Error { message } => {
127 errors.push(ErrorResult {
128 message: message.clone(),
129 line: stmt.line,
130 column: stmt.column,
131 });
132 }
133 StatementKind::Loop => {
134 if let Some(body_statements) = extract_loop_body_statements(&stmt.value) {
135 errors.extend(collect_errors_recursively(body_statements));
136 }
137 }
138 _ => {}
139 }
140 }
141
142 errors
143}
144
145fn extract_loop_body_statements(value: &Value) -> Option<&[Statement]> {
146 if let Value::Map(map) = value {
147 if let Some(Value::Block(statements)) = map.get("body") {
148 return Some(statements);
149 }
150 }
151 None
152}
153
154fn ast_to_string(statements: Vec<Statement>) -> String {
155 serde_json::to_string_pretty(&statements).expect("Failed to serialize AST")
156}