devalang_core/
lib.rs

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}