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