devalang_core/
lib.rs

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}