@devaloop/devalang 0.0.1-alpha.12 → 0.0.1-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/Cargo.toml +54 -53
  2. package/README.md +1 -14
  3. package/docs/CHANGELOG.md +26 -0
  4. package/docs/TODO.md +1 -1
  5. package/examples/index.deva +10 -13
  6. package/out-tsc/bin/devalang.exe +0 -0
  7. package/package.json +1 -1
  8. package/project-version.json +3 -3
  9. package/rust/cli/build.rs +25 -2
  10. package/rust/cli/check.rs +26 -3
  11. package/rust/cli/play.rs +1 -1
  12. package/rust/core/audio/engine.rs +126 -73
  13. package/rust/core/audio/interpreter/call.rs +72 -47
  14. package/rust/core/audio/interpreter/condition.rs +14 -12
  15. package/rust/core/audio/interpreter/driver.rs +84 -127
  16. package/rust/core/audio/interpreter/function.rs +21 -0
  17. package/rust/core/audio/interpreter/load.rs +1 -1
  18. package/rust/core/audio/interpreter/loop_.rs +24 -18
  19. package/rust/core/audio/interpreter/mod.rs +2 -1
  20. package/rust/core/audio/interpreter/sleep.rs +0 -6
  21. package/rust/core/audio/interpreter/spawn.rs +78 -60
  22. package/rust/core/audio/interpreter/trigger.rs +157 -70
  23. package/rust/core/audio/loader/trigger.rs +37 -4
  24. package/rust/core/audio/player.rs +20 -10
  25. package/rust/core/audio/renderer.rs +24 -25
  26. package/rust/core/debugger/mod.rs +2 -0
  27. package/rust/core/debugger/module.rs +47 -0
  28. package/rust/core/debugger/store.rs +25 -11
  29. package/rust/core/error/mod.rs +6 -0
  30. package/rust/core/lexer/handler/driver.rs +23 -1
  31. package/rust/core/lexer/handler/identifier.rs +1 -0
  32. package/rust/core/lexer/handler/mod.rs +1 -0
  33. package/rust/core/lexer/handler/parenthesis.rs +41 -0
  34. package/rust/core/lexer/token.rs +3 -0
  35. package/rust/core/parser/driver.rs +3 -1
  36. package/rust/core/parser/handler/dot.rs +64 -127
  37. package/rust/core/parser/handler/identifier/call.rs +69 -22
  38. package/rust/core/parser/handler/identifier/function.rs +92 -0
  39. package/rust/core/parser/handler/identifier/let_.rs +13 -19
  40. package/rust/core/parser/handler/identifier/mod.rs +1 -0
  41. package/rust/core/parser/handler/identifier/spawn.rs +74 -27
  42. package/rust/core/parser/statement.rs +16 -4
  43. package/rust/core/preprocessor/loader.rs +45 -29
  44. package/rust/core/preprocessor/module.rs +3 -1
  45. package/rust/core/preprocessor/processor.rs +26 -1
  46. package/rust/core/preprocessor/resolver/call.rs +61 -84
  47. package/rust/core/preprocessor/resolver/condition.rs +11 -6
  48. package/rust/core/preprocessor/resolver/driver.rs +52 -6
  49. package/rust/core/preprocessor/resolver/function.rs +78 -0
  50. package/rust/core/preprocessor/resolver/group.rs +43 -13
  51. package/rust/core/preprocessor/resolver/let_.rs +7 -10
  52. package/rust/core/preprocessor/resolver/mod.rs +2 -1
  53. package/rust/core/preprocessor/resolver/spawn.rs +64 -30
  54. package/rust/core/preprocessor/resolver/trigger.rs +7 -3
  55. package/rust/core/preprocessor/resolver/value.rs +10 -1
  56. package/rust/core/shared/value.rs +4 -1
  57. package/rust/core/store/function.rs +34 -0
  58. package/rust/core/store/global.rs +9 -10
  59. package/rust/core/store/mod.rs +2 -1
  60. package/rust/core/store/variable.rs +6 -0
  61. package/rust/lib.rs +10 -7
  62. package/rust/utils/mod.rs +45 -1
@@ -1,20 +1,18 @@
1
- use std::collections::HashMap;
2
-
3
1
  use crate::{
4
2
  core::{
5
- parser::statement::{ Statement, StatementKind },
6
- preprocessor::{ module::Module, resolver::driver::resolve_statement },
3
+ parser::statement::{Statement, StatementKind},
4
+ preprocessor::{module::Module, resolver::driver::resolve_statement},
7
5
  shared::value::Value,
8
- store::{ global::GlobalStore, variable::VariableTable },
6
+ store::global::GlobalStore,
9
7
  },
10
- utils::logger::{ LogLevel, Logger },
8
+ utils::logger::{LogLevel, Logger},
11
9
  };
12
10
 
13
11
  pub fn resolve_group(
14
12
  stmt: &Statement,
15
13
  module: &Module,
16
14
  path: &str,
17
- global_store: &mut GlobalStore
15
+ global_store: &mut GlobalStore,
18
16
  ) -> Statement {
19
17
  let logger = Logger::new();
20
18
 
@@ -22,27 +20,59 @@ pub fn resolve_group(
22
20
  return type_error(&logger, module, stmt, "Expected a map in group statement".to_string());
23
21
  };
24
22
 
25
- let mut resolved_map = group_map.clone();
23
+ // Check for the presence of the identifier field
24
+ let identifier = match group_map.get("identifier") {
25
+ Some(Value::String(id)) => id.clone(),
26
+ _ => {
27
+ return type_error(
28
+ &logger,
29
+ module,
30
+ stmt,
31
+ "Group statement must have an 'identifier' field".to_string(),
32
+ )
33
+ }
34
+ };
26
35
 
36
+ // Ensure the identifier does not already exist
37
+ if global_store.variables.variables.contains_key(&identifier) {
38
+ return type_error(
39
+ &logger,
40
+ module,
41
+ stmt,
42
+ format!("Group identifier '{}' already exists", identifier),
43
+ );
44
+ }
45
+
46
+ // Resolve statements in the body
47
+ let mut resolved_map = group_map.clone();
27
48
  if let Some(Value::Block(body)) = group_map.get("body") {
28
49
  let resolved_body = resolve_block_statements(body, module, path, global_store);
29
50
  resolved_map.insert("body".to_string(), Value::Block(resolved_body));
30
51
  } else {
31
- logger.log_message(LogLevel::Warning, "group without a body");
52
+ logger.log_message(LogLevel::Warning, "Group without a body");
32
53
  }
33
54
 
34
- Statement {
55
+ // Build a complete Statement for the group
56
+ let resolved_group_stmt = Statement {
35
57
  kind: StatementKind::Group,
36
- value: Value::Map(resolved_map),
58
+ value: Value::Map(resolved_map.clone()),
37
59
  ..stmt.clone()
38
- }
60
+ };
61
+
62
+ // Store the Statement directly in the global variable_table
63
+ global_store
64
+ .variables
65
+ .variables
66
+ .insert(identifier.clone(), Value::Statement(Box::new(resolved_group_stmt.clone())));
67
+
68
+ resolved_group_stmt
39
69
  }
40
70
 
41
71
  fn resolve_block_statements(
42
72
  body: &[Statement],
43
73
  module: &Module,
44
74
  path: &str,
45
- global_store: &mut GlobalStore
75
+ global_store: &mut GlobalStore,
46
76
  ) -> Vec<Statement> {
47
77
  body.iter()
48
78
  .map(|stmt| resolve_statement(stmt, module, path, global_store))
@@ -1,13 +1,8 @@
1
- use crate::
2
- core::{
3
- parser::statement:: Statement ,
4
- preprocessor::{
5
- module::Module,
6
- resolver:: value::resolve_value ,
7
- },
8
- store:: global::GlobalStore ,
9
- }
10
- ;
1
+ use crate::core::{
2
+ parser::statement::Statement,
3
+ preprocessor::{ module::Module, resolver::value::resolve_value },
4
+ store::global::GlobalStore,
5
+ };
11
6
 
12
7
  pub fn resolve_let(
13
8
  stmt: &Statement,
@@ -18,6 +13,8 @@ pub fn resolve_let(
18
13
  ) -> Statement {
19
14
  let resolved_value = resolve_value(&stmt.value, module, global_store);
20
15
 
16
+ global_store.variables.set(name.to_string(), resolved_value.clone());
17
+
21
18
  if let Some(current_mod) = global_store.modules.get_mut(path) {
22
19
  current_mod.variable_table.set(name.to_string(), resolved_value.clone());
23
20
  } else {
@@ -11,4 +11,5 @@ pub mod condition;
11
11
  pub mod spawn;
12
12
  pub mod call;
13
13
  pub mod synth;
14
- pub mod let_;
14
+ pub mod let_;
15
+ pub mod function;
@@ -14,45 +14,79 @@ use crate::{
14
14
 
15
15
  pub fn resolve_spawn(
16
16
  stmt: &Statement,
17
+ name: String,
18
+ args: Vec<Value>,
17
19
  module: &Module,
18
20
  path: &str,
19
21
  global_store: &mut GlobalStore
20
22
  ) -> Statement {
21
23
  let logger = Logger::new();
22
24
 
23
- let resolved_value = resolve_value(&stmt.value, module, global_store);
25
+ // Si c'est une fonction
26
+ if let Some(func) = global_store.functions.functions.get(&name) {
27
+ let mut resolved_map = std::collections::HashMap::new();
28
+ resolved_map.insert("name".to_string(), Value::String(name.clone()));
29
+ resolved_map.insert("args".to_string(), Value::Array(args.clone()));
30
+ resolved_map.insert("body".to_string(), Value::Block(func.body.clone()));
24
31
 
25
- match resolved_value {
26
- Value::Map(mut map) => {
27
- if let Some(Value::Block(stmts)) = map.get("body") {
28
- let resolved_block = stmts
29
- .iter()
30
- .map(|s| resolve_statement(s, module, path, global_store))
31
- .collect();
32
- map.insert("body".to_string(), Value::Block(resolved_block));
33
- }
32
+ return Statement {
33
+ kind: StatementKind::Spawn { name, args },
34
+ value: Value::Map(resolved_map),
35
+ ..stmt.clone()
36
+ };
37
+ }
34
38
 
35
- Statement {
36
- kind: StatementKind::Spawn,
37
- value: Value::Map(map),
38
- ..stmt.clone()
39
- }
40
- }
39
+ // ✅ Si c'est un group dans les variables
40
+ if let Some(variable) = global_store.variables.variables.get(&name) {
41
+ if let Value::Statement(stmt_box) = variable {
42
+ if let StatementKind::Group = stmt_box.kind {
43
+ if let Value::Map(map) = &stmt_box.value {
44
+ if let Some(Value::Block(body)) = map.get("body") {
45
+ let mut resolved_map = std::collections::HashMap::new();
46
+ resolved_map.insert("identifier".to_string(), Value::String(name.clone()));
47
+ resolved_map.insert("args".to_string(), Value::Array(args.clone()));
48
+ resolved_map.insert("body".to_string(), Value::Block(body.clone()));
41
49
 
42
- _ => {
43
- let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
44
- logger.log_message(
45
- LogLevel::Error,
46
- &format!("Expected a map in spawn statement\n → at {stacktrace}")
47
- );
48
-
49
- Statement {
50
- kind: StatementKind::Error {
51
- message: "Invalid spawn value".to_string(),
52
- },
53
- value: Value::Null,
54
- ..stmt.clone()
50
+ return Statement {
51
+ kind: StatementKind::Spawn { name, args },
52
+ value: Value::Map(resolved_map),
53
+ ..stmt.clone()
54
+ };
55
+ }
56
+ }
55
57
  }
56
58
  }
57
59
  }
58
- }
60
+
61
+ // ❌ Sinon erreur
62
+ let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
63
+ logger.log_message(
64
+ LogLevel::Error,
65
+ &format!("Function or group '{}' not found for spawn\n → at {stacktrace}", name)
66
+ );
67
+
68
+ Statement {
69
+ kind: StatementKind::Error {
70
+ message: format!("Function or group '{}' not found for spawn", name),
71
+ },
72
+ value: Value::Null,
73
+ ..stmt.clone()
74
+ }
75
+ }
76
+
77
+ fn get_group_body(stmt_box: &Statement) -> Vec<Statement> {
78
+ if let Value::Block(body) = &stmt_box.value { body.clone() } else { vec![] }
79
+ }
80
+
81
+ fn error_stmt(logger: &Logger, module: &Module, stmt: &Statement, message: &str) -> Statement {
82
+ let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
83
+ logger.log_message(LogLevel::Error, &format!("{message}\n → at {stacktrace}"));
84
+
85
+ Statement {
86
+ kind: StatementKind::Error {
87
+ message: message.to_string(),
88
+ },
89
+ value: Value::Null,
90
+ ..stmt.clone()
91
+ }
92
+ }
@@ -14,6 +14,7 @@ pub fn resolve_trigger(
14
14
  stmt: &Statement,
15
15
  entity: &str,
16
16
  duration: &mut Duration,
17
+ effects: Option<Value>,
17
18
  module: &Module,
18
19
  path: &str,
19
20
  global_store: &GlobalStore
@@ -44,6 +45,8 @@ pub fn resolve_trigger(
44
45
  // Params value resolution
45
46
  final_value = match &stmt.value {
46
47
  Value::Identifier(ident) => {
48
+ println!("Resolving identifier: {}", ident);
49
+
47
50
  resolve_identifier(ident, module, global_store).unwrap_or_else(|| {
48
51
  logger.log_error_with_stacktrace(
49
52
  &format!("'{path}': value identifier '{ident}' not found"),
@@ -71,9 +74,10 @@ pub fn resolve_trigger(
71
74
  Statement {
72
75
  kind: StatementKind::Trigger {
73
76
  entity: entity.to_string(),
74
- duration: final_duration,
77
+ duration: final_duration.clone(),
78
+ effects: Some(final_value.clone()),
75
79
  },
76
- value: final_value,
80
+ value: Value::Null,
77
81
  line: stmt.line,
78
82
  column: stmt.column,
79
83
  indent: stmt.indent,
@@ -86,7 +90,7 @@ fn resolve_identifier(ident: &str, module: &Module, global_store: &GlobalStore)
86
90
  }
87
91
 
88
92
  for (_, other_mod) in &global_store.modules {
89
- if let Some(val) = other_mod.export_table.get_export(ident) {
93
+ if let Some(val) = other_mod.variable_table.get(ident) {
90
94
  return Some(resolve_value(val, other_mod, global_store));
91
95
  }
92
96
  }
@@ -16,11 +16,18 @@ fn find_export_value(name: &str, global_store: &GlobalStore) -> Option<Value> {
16
16
  return Some(val.clone());
17
17
  }
18
18
  }
19
+
19
20
  None
20
21
  }
21
22
 
22
23
  pub fn resolve_value(value: &Value, module: &Module, global_store: &mut GlobalStore) -> Value {
23
24
  match value {
25
+ Value::String(s) => {
26
+ println!("Resolving value: {}", s);
27
+
28
+ Value::String(s.clone())
29
+ },
30
+
24
31
  Value::Identifier(name) => {
25
32
  if let Some(original_val) = module.variable_table.get(name) {
26
33
  return resolve_value(original_val, module, global_store);
@@ -30,7 +37,8 @@ pub fn resolve_value(value: &Value, module: &Module, global_store: &mut GlobalSt
30
37
  return resolve_value(&export_val, module, global_store);
31
38
  }
32
39
 
33
- eprintln!("⚠️ Unresolved identifier '{}'", name);
40
+ println!("⚠️ Unresolved identifier '{}'", name);
41
+
34
42
  Value::Null
35
43
  }
36
44
 
@@ -62,6 +70,7 @@ pub fn resolve_value(value: &Value, module: &Module, global_store: &mut GlobalSt
62
70
  for (k, v) in map {
63
71
  resolved.insert(k.clone(), resolve_value(v, module, global_store));
64
72
  }
73
+
65
74
  Value::Map(resolved)
66
75
  }
67
76
 
@@ -1,12 +1,13 @@
1
1
  use std::collections::HashMap;
2
2
  use serde::{ Deserialize, Serialize };
3
3
 
4
- use crate::core::parser::statement::Statement;
4
+ use crate::core::{parser::statement::{Statement, StatementKind}, shared::duration::Duration};
5
5
 
6
6
  #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7
7
  pub enum Value {
8
8
  Boolean(bool),
9
9
  Number(f32),
10
+ Duration(Duration),
10
11
  Identifier(String),
11
12
  String(String),
12
13
  Array(Vec<Value>),
@@ -14,6 +15,8 @@ pub enum Value {
14
15
  Block(Vec<Statement>),
15
16
  Sample(String),
16
17
  Beat(String),
18
+ Statement(Box<Statement>),
19
+ StatementKind(Box<StatementKind>),
17
20
  Unknown,
18
21
  Null,
19
22
  }
@@ -0,0 +1,34 @@
1
+ use std::collections::HashMap;
2
+ use crate::core::parser::statement::Statement;
3
+
4
+ #[derive(Debug, Clone)]
5
+ pub struct FunctionDef {
6
+ pub name: String,
7
+ pub parameters: Vec<String>,
8
+ pub body: Vec<Statement>,
9
+ }
10
+
11
+ #[derive(Debug, Clone)]
12
+ pub struct FunctionTable {
13
+ pub functions: HashMap<String, FunctionDef>,
14
+ }
15
+
16
+ impl FunctionTable {
17
+ pub fn new() -> Self {
18
+ FunctionTable {
19
+ functions: HashMap::new(),
20
+ }
21
+ }
22
+
23
+ pub fn add_function(&mut self, function: FunctionDef) {
24
+ self.functions.insert(function.name.clone(), function);
25
+ }
26
+
27
+ pub fn get_function(&self, name: &str) -> Option<&FunctionDef> {
28
+ self.functions.get(name)
29
+ }
30
+
31
+ pub fn remove_function(&mut self, name: &str) -> Option<FunctionDef> {
32
+ self.functions.remove(name)
33
+ }
34
+ }
@@ -1,23 +1,22 @@
1
1
  use std::collections::HashMap;
2
- use crate::core::preprocessor::module::Module;
2
+ use crate::core::{
3
+ preprocessor::module::Module,
4
+ store::{ function::FunctionTable, variable::VariableTable },
5
+ };
3
6
 
4
- #[derive(Debug, Default, Clone)]
7
+ #[derive(Debug, Clone)]
5
8
  pub struct GlobalStore {
6
9
  pub modules: HashMap<String, Module>,
10
+ pub variables: VariableTable,
11
+ pub functions: FunctionTable,
7
12
  }
8
13
 
9
14
  impl GlobalStore {
10
15
  pub fn new() -> Self {
11
16
  GlobalStore {
12
17
  modules: HashMap::new(),
13
- }
14
- }
15
-
16
- pub fn resolve_all_imports(&mut self) {
17
- for module in self.modules.values_mut() {
18
- for (name, source_path) in &module.import_table.imports {
19
- println!("Resolving import: {} from {:?}", name, source_path);
20
- }
18
+ functions: FunctionTable::new(),
19
+ variables: VariableTable::new(),
21
20
  }
22
21
  }
23
22
 
@@ -1,4 +1,5 @@
1
1
  pub mod global;
2
2
  pub mod export;
3
3
  pub mod import;
4
- pub mod variable;
4
+ pub mod variable;
5
+ pub mod function;
@@ -5,12 +5,14 @@ use crate::core::shared::value::Value;
5
5
  #[derive(Debug, Default, Clone, PartialEq)]
6
6
  pub struct VariableTable {
7
7
  pub variables: HashMap<String, Value>,
8
+ pub parent: Option<Box<VariableTable>>,
8
9
  }
9
10
 
10
11
  impl VariableTable {
11
12
  pub fn new() -> Self {
12
13
  VariableTable {
13
14
  variables: HashMap::new(),
15
+ parent: None,
14
16
  }
15
17
  }
16
18
 
@@ -18,6 +20,10 @@ impl VariableTable {
18
20
  self.variables.insert(name, value);
19
21
  }
20
22
 
23
+ pub fn with_parent(parent: VariableTable) -> Self {
24
+ Self { variables: HashMap::new(), parent: Some(Box::new(parent)) }
25
+ }
26
+
21
27
  pub fn get(&self, name: &str) -> Option<&Value> {
22
28
  self.variables.get(name)
23
29
  }
package/rust/lib.rs CHANGED
@@ -7,11 +7,11 @@ use wasm_bindgen::prelude::*;
7
7
  use serde_wasm_bindgen::to_value;
8
8
 
9
9
  use crate::core::{
10
- audio::{ engine::AudioEngine, interpreter::driver::{ run_audio_program } },
10
+ audio::{ engine::AudioEngine, interpreter::driver::run_audio_program },
11
11
  parser::statement::{ Statement, StatementKind },
12
12
  preprocessor::loader::ModuleLoader,
13
13
  shared::value::Value,
14
- store::global::GlobalStore,
14
+ store::{ function::FunctionTable, global::GlobalStore, variable::VariableTable },
15
15
  utils::path::normalize_path,
16
16
  };
17
17
 
@@ -69,16 +69,19 @@ pub fn render_audio(user_code: &str) -> Result<js_sys::Float32Array, JsValue> {
69
69
  .ok_or(JsValue::from_str("❌ No statements found for entry module"))?
70
70
  .clone();
71
71
 
72
- let audio_engine = AudioEngine::new("wasm_output".to_string());
72
+ let mut audio_engine = AudioEngine::new("wasm_output".to_string());
73
73
 
74
- let (final_engine, _, _) = run_audio_program(
74
+ let _ = run_audio_program(
75
75
  &main_statements,
76
- audio_engine,
76
+ &mut audio_engine,
77
77
  "playground".to_string(),
78
- "wasm_output".to_string()
78
+ "wasm_output".to_string(),
79
+ VariableTable::new(),
80
+ FunctionTable::new(),
81
+ &mut global_store
79
82
  );
80
83
 
81
- let samples = final_engine.get_normalized_buffer();
84
+ let samples = audio_engine.get_normalized_buffer();
82
85
 
83
86
  if samples.is_empty() {
84
87
  return Err(JsValue::from_str("❌ Audio buffer is empty"));
package/rust/utils/mod.rs CHANGED
@@ -3,4 +3,48 @@ pub mod signature;
3
3
  pub mod spinner;
4
4
  pub mod watcher;
5
5
  pub mod file;
6
- pub mod logger;
6
+ pub mod logger;
7
+
8
+ use crate::core::parser::statement::{Statement, StatementKind};
9
+ use crate::core::error::ErrorResult;
10
+ use crate::core::shared::value::Value;
11
+
12
+ pub fn collect_errors_recursively(statements: &[Statement]) -> Vec<ErrorResult> {
13
+ let mut errors: Vec<ErrorResult> = Vec::new();
14
+
15
+ for stmt in statements {
16
+ match &stmt.kind {
17
+ StatementKind::Unknown => {
18
+ errors.push(ErrorResult {
19
+ message: format!("Unknown statement at line {}:{}", stmt.line, stmt.column),
20
+ line: stmt.line,
21
+ column: stmt.column,
22
+ });
23
+ }
24
+ StatementKind::Error { message } => {
25
+ errors.push(ErrorResult {
26
+ message: message.clone(),
27
+ line: stmt.line,
28
+ column: stmt.column,
29
+ });
30
+ }
31
+ StatementKind::Loop => {
32
+ if let Some(body_statements) = extract_loop_body_statements(&stmt.value) {
33
+ errors.extend(collect_errors_recursively(body_statements));
34
+ }
35
+ }
36
+ _ => {}
37
+ }
38
+ }
39
+
40
+ errors
41
+ }
42
+
43
+ pub fn extract_loop_body_statements(value: &Value) -> Option<&[Statement]> {
44
+ if let Value::Map(map) = value {
45
+ if let Some(Value::Block(statements)) = map.get("body") {
46
+ return Some(statements);
47
+ }
48
+ }
49
+ None
50
+ }