@devaloop/devalang 0.0.1-alpha.3 → 0.0.1-alpha.4

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.
@@ -0,0 +1,46 @@
1
+ use crate::{core::{
2
+ parser::statement::{Statement, StatementKind},
3
+ preprocessor::module::Module,
4
+ shared::value::Value,
5
+ store::global::GlobalStore,
6
+ }, utils::logger::Logger};
7
+
8
+ pub fn resolve_bank(
9
+ stmt: &Statement,
10
+ module: &Module,
11
+ path: &str,
12
+ _global_store: &GlobalStore
13
+ ) -> Statement {
14
+ let mut new_stmt = stmt.clone();
15
+ let logger = Logger::new();
16
+
17
+ match &stmt.value {
18
+ Value::Identifier(ident) => {
19
+ if let Some(val) = module.variable_table.get(ident) {
20
+ new_stmt.value = val.clone();
21
+ } else {
22
+ let message = format!("Bank identifier '{ident}' not found in variable table");
23
+ logger.log_error_with_stacktrace(&message, &module.path);
24
+ new_stmt.kind = StatementKind::Error {
25
+ message: message.clone(),
26
+ };
27
+ new_stmt.value = Value::Null;
28
+ }
29
+ }
30
+
31
+ Value::String(_) => {
32
+ // Pas de résolution nécessaire
33
+ }
34
+
35
+ other => {
36
+ let message = format!("Expected a string or identifier for bank, found {:?}", other);
37
+ logger.log_error_with_stacktrace(&message, &module.path);
38
+ new_stmt.kind = StatementKind::Error {
39
+ message: "Expected a string or identifier for bank".to_string(),
40
+ };
41
+ new_stmt.value = Value::Null;
42
+ }
43
+ }
44
+
45
+ new_stmt
46
+ }
@@ -0,0 +1,143 @@
1
+ use crate::{
2
+ core::{
3
+ parser::statement::{ Statement, StatementKind },
4
+ preprocessor::{ module::Module, resolver::trigger::resolve_trigger },
5
+ shared::value::Value,
6
+ store::global::GlobalStore,
7
+ },
8
+ utils::logger::Logger,
9
+ };
10
+
11
+ pub fn resolve_loop(
12
+ stmt: &Statement,
13
+ module: &Module,
14
+ path: &str,
15
+ global_store: &GlobalStore
16
+ ) -> Statement {
17
+ let logger = Logger::new();
18
+
19
+ // Vérifie que stmt.value est bien une Map
20
+ if let Value::Map(value_map) = &stmt.value {
21
+ // Résolution de l'iterator
22
+ let iterator_value = match value_map.get("iterator") {
23
+ Some(Value::Identifier(ident)) => {
24
+ match module.variable_table.get(ident) {
25
+ Some(Value::Number(n)) => Value::Number(*n),
26
+ Some(_) => {
27
+ log_type_error(
28
+ &logger,
29
+ module,
30
+ stmt,
31
+ format!("Loop iterator '{ident}' must resolve to a number")
32
+ );
33
+ Value::Null
34
+ }
35
+ None => {
36
+ log_type_error(
37
+ &logger,
38
+ module,
39
+ stmt,
40
+ format!("Loop iterator '{ident}' not found")
41
+ );
42
+ Value::Null
43
+ }
44
+ }
45
+ }
46
+ Some(Value::Number(n)) => Value::Number(*n),
47
+ Some(other) => {
48
+ log_type_error(
49
+ &logger,
50
+ module,
51
+ stmt,
52
+ format!("Unexpected value for loop iterator: {:?}", other)
53
+ );
54
+ Value::Null
55
+ }
56
+ None => {
57
+ log_type_error(
58
+ &logger,
59
+ module,
60
+ stmt,
61
+ "Missing 'iterator' key in loop statement map".to_string()
62
+ );
63
+ Value::Null
64
+ }
65
+ };
66
+
67
+ // Résolution du body
68
+ let body_value = match value_map.get("body") {
69
+ Some(Value::Block(block)) => {
70
+ let mut resolved_block = Vec::new();
71
+ for ref statement in block.clone() {
72
+ match &statement.kind {
73
+ StatementKind::Trigger { entity, duration } => {
74
+ let resolved = resolve_trigger(
75
+ &mut statement.clone(),
76
+ &entity,
77
+ &mut duration.clone(),
78
+ module,
79
+ path,
80
+ global_store
81
+ );
82
+ resolved_block.push(resolved);
83
+ }
84
+ _ => {
85
+ println!("Unhandled loop body statement: {:?}", statement);
86
+ }
87
+ }
88
+ }
89
+ Value::Block(resolved_block)
90
+ }
91
+ Some(other) => {
92
+ log_type_error(
93
+ &logger,
94
+ module,
95
+ stmt,
96
+ format!("Unexpected value for loop body: {:?}", other)
97
+ );
98
+ Value::Null
99
+ }
100
+ None => {
101
+ log_type_error(
102
+ &logger,
103
+ module,
104
+ stmt,
105
+ "Missing 'body' key in loop statement map".to_string()
106
+ );
107
+ Value::Null
108
+ }
109
+ };
110
+
111
+ // ✅ Reconstruit proprement la valeur résolue
112
+ let mut resolved_map = std::collections::HashMap::new();
113
+ resolved_map.insert("iterator".to_string(), iterator_value);
114
+ resolved_map.insert("body".to_string(), body_value);
115
+
116
+ // ✅ Reconstruit le StatementLoop à partir des éléments résolus
117
+ Statement {
118
+ kind: StatementKind::Loop,
119
+ value: Value::Map(resolved_map),
120
+ ..stmt.clone()
121
+ }
122
+ } else {
123
+ log_type_error(
124
+ &logger,
125
+ module,
126
+ stmt,
127
+ format!("Expected a map for loop value, found {:?}", stmt.value)
128
+ );
129
+
130
+ Statement {
131
+ kind: StatementKind::Error {
132
+ message: "Expected a map for loop value".to_string(),
133
+ },
134
+ value: Value::Null,
135
+ ..stmt.clone()
136
+ }
137
+ }
138
+ }
139
+
140
+ fn log_type_error(logger: &Logger, module: &Module, stmt: &Statement, message: String) {
141
+ let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
142
+ logger.log_error_with_stacktrace(&message, &stacktrace);
143
+ }
@@ -0,0 +1,152 @@
1
+ pub mod trigger;
2
+ pub mod loop_;
3
+ pub mod bank;
4
+ pub mod tempo;
5
+
6
+ use std::collections::HashMap;
7
+ use crate::{
8
+ core::{
9
+ parser::statement::{ self, Statement, StatementKind },
10
+ preprocessor::{
11
+ loader::ModuleLoader,
12
+ resolver::{
13
+ bank::resolve_bank,
14
+ loop_::resolve_loop,
15
+ tempo::resolve_tempo,
16
+ trigger::resolve_trigger,
17
+ },
18
+ },
19
+ shared::{ duration::Duration, value::Value },
20
+ store::global::GlobalStore,
21
+ utils::validation::{ is_valid_entity, is_valid_identifier },
22
+ },
23
+ utils::logger::Logger,
24
+ };
25
+
26
+ pub fn resolve_all_modules(module_loader: &ModuleLoader, global_store: &mut GlobalStore) {
27
+ for module in global_store.clone().modules.values_mut() {
28
+ resolve_imports(module_loader, global_store);
29
+ }
30
+ }
31
+
32
+ pub fn resolve_imports(module_loader: &ModuleLoader, global_store: &mut GlobalStore) {
33
+ for (module_path, module) in global_store.clone().modules.iter_mut() {
34
+ for (name, source_path) in &module.import_table.imports {
35
+ match source_path {
36
+ Value::String(source_path) => {
37
+ if let Some(source_module) = global_store.modules.get(source_path) {
38
+ if let Some(value) = source_module.export_table.get_export(name) {
39
+ module.variable_table.set(name.clone(), value.clone());
40
+ } else {
41
+ println!(
42
+ "[warn] '{module_path}': '{name}' not found in exports of '{source_path}'"
43
+ );
44
+ }
45
+ } else {
46
+ println!(
47
+ "[warn] '{module_path}': cannot find source module '{source_path}'"
48
+ );
49
+ }
50
+ }
51
+ _ => {
52
+ println!(
53
+ "[warn] '{module_path}': expected string for import source, found {:?}",
54
+ source_path
55
+ );
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+
62
+ pub fn resolve_and_flatten_all_modules(
63
+ global_store: &mut GlobalStore
64
+ ) -> HashMap<String, Vec<Statement>> {
65
+ let logger = Logger::new();
66
+ let snapshot = global_store.clone(); // pour éviter les emprunts mutables
67
+
68
+ // 1. Résolution des imports
69
+ for (module_path, module) in global_store.modules.iter_mut() {
70
+ for (name, source_path) in &module.import_table.imports {
71
+ if let Value::String(source_path_str) = source_path {
72
+ match snapshot.modules.get(source_path_str) {
73
+ Some(source_module) => {
74
+ if let Some(value) = source_module.export_table.get_export(name) {
75
+ module.variable_table.set(name.clone(), value.clone());
76
+ } else {
77
+ logger.log_error_with_stacktrace(
78
+ &format!("'{name}' not found in exports of '{source_path_str}'"),
79
+ module_path
80
+ );
81
+ }
82
+ }
83
+ None => {
84
+ logger.log_error_with_stacktrace(
85
+ &format!("Cannot find source module '{source_path_str}'"),
86
+ module_path
87
+ );
88
+ }
89
+ }
90
+ } else {
91
+ logger.log_error_with_stacktrace(
92
+ &format!("Expected string for import source, found {:?}", source_path),
93
+ module_path
94
+ );
95
+ }
96
+ }
97
+ }
98
+
99
+ // 2. Résolution des statements
100
+ let mut resolved_map = HashMap::new();
101
+ let store_snapshot = global_store.clone();
102
+
103
+ for (path, module) in &store_snapshot.modules {
104
+ let mut resolved = Vec::new();
105
+
106
+ for stmt in &module.statements {
107
+ let mut stmt = stmt.clone();
108
+
109
+ match &stmt.kind {
110
+ StatementKind::Trigger { entity, duration } => {
111
+ let resolved_stmt = resolve_trigger(
112
+ &stmt,
113
+ entity.as_str(),
114
+ &mut duration.clone(),
115
+ &module,
116
+ &path,
117
+ &store_snapshot
118
+ );
119
+ resolved.push(resolved_stmt);
120
+ }
121
+
122
+ StatementKind::Loop => {
123
+ let resolved_stmt = resolve_loop(&stmt, &module, &path, &store_snapshot);
124
+ resolved.push(resolved_stmt);
125
+ }
126
+
127
+ StatementKind::Bank => {
128
+ let resolved_stmt = resolve_bank(&stmt, &module, &path, &store_snapshot);
129
+ resolved.push(resolved_stmt);
130
+ }
131
+
132
+ StatementKind::Tempo => {
133
+ let resolved_stmt = resolve_tempo(&stmt, &module, &path, &store_snapshot);
134
+ resolved.push(resolved_stmt);
135
+ }
136
+
137
+ StatementKind::Import { .. } | StatementKind::Export { .. } => {
138
+ // Rien à faire
139
+ resolved.push(stmt.clone());
140
+ }
141
+
142
+ _ => {
143
+ resolved.push(stmt);
144
+ }
145
+ }
146
+ }
147
+
148
+ resolved_map.insert(path.clone(), resolved);
149
+ }
150
+
151
+ resolved_map
152
+ }
@@ -0,0 +1,49 @@
1
+ use crate::{
2
+ core::{
3
+ parser::statement::{ Statement, StatementKind },
4
+ preprocessor::module::Module,
5
+ shared::value::Value,
6
+ store::global::GlobalStore,
7
+ },
8
+ utils::logger::Logger,
9
+ };
10
+
11
+ pub fn resolve_tempo(
12
+ stmt: &Statement,
13
+ module: &Module,
14
+ path: &str,
15
+ _global_store: &GlobalStore
16
+ ) -> Statement {
17
+ let mut new_stmt = stmt.clone();
18
+ let logger = Logger::new();
19
+
20
+ match &stmt.value {
21
+ Value::Identifier(ident) => {
22
+ if let Some(val) = module.variable_table.get(ident) {
23
+ new_stmt.value = val.clone();
24
+ } else {
25
+ let message = format!("Tempo identifier '{ident}' not found in variable table");
26
+ logger.log_error_with_stacktrace(&message, &module.path);
27
+ new_stmt.kind = StatementKind::Error {
28
+ message,
29
+ };
30
+ new_stmt.value = Value::Null;
31
+ }
32
+ }
33
+
34
+ Value::Number(_) => {
35
+ // Already resolved, no modification needed
36
+ }
37
+
38
+ other => {
39
+ let message = format!("Expected a number or identifier for tempo, found {:?}", other);
40
+ logger.log_error_with_stacktrace(&message, &module.path);
41
+ new_stmt.kind = StatementKind::Error {
42
+ message: "Expected a number or identifier for tempo".to_string(),
43
+ };
44
+ new_stmt.value = Value::Null;
45
+ }
46
+ }
47
+
48
+ new_stmt
49
+ }
@@ -0,0 +1,114 @@
1
+ use std::collections::HashMap;
2
+
3
+ use crate::{
4
+ core::{
5
+ parser::statement::{ Statement, StatementKind },
6
+ preprocessor::module::Module,
7
+ shared::{ duration::Duration, value::Value },
8
+ store::global::GlobalStore,
9
+ utils::validation::is_valid_entity,
10
+ },
11
+ utils::logger::Logger,
12
+ };
13
+
14
+ pub fn resolve_trigger(
15
+ stmt: &Statement,
16
+ entity: &str,
17
+ duration: &mut Duration,
18
+ module: &Module,
19
+ path: &str,
20
+ global_store: &GlobalStore
21
+ ) -> Statement {
22
+ let logger = Logger::new();
23
+
24
+ let mut final_duration = duration.clone();
25
+ let mut final_value = stmt.value.clone();
26
+
27
+ if !is_valid_entity(entity, module, global_store) {
28
+ let message = format!("Invalid entity '{}', expected a valid identifier", entity);
29
+ let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
30
+ logger.log_error_with_stacktrace(&message, &stacktrace);
31
+
32
+ return Statement {
33
+ kind: stmt.kind.clone(),
34
+ value: Value::Null,
35
+ line: stmt.line,
36
+ column: stmt.column,
37
+ indent: stmt.indent,
38
+ };
39
+ }
40
+
41
+ // ✅ Résolution de duration si c'est un identifiant
42
+ if let Duration::Identifier(ident) = duration {
43
+ if let Some(val) = module.variable_table.get(ident) {
44
+ match val {
45
+ Value::Number(num) => {
46
+ final_duration = Duration::Number(*num);
47
+ }
48
+ Value::String(s) => {
49
+ final_duration = Duration::Identifier(s.clone());
50
+ }
51
+ Value::Identifier(id) if id == "auto" => {
52
+ final_duration = Duration::Auto;
53
+ }
54
+ _ => {}
55
+ }
56
+ }
57
+ }
58
+
59
+ // ✅ Résolution de value (params, effets)
60
+ final_value = match &stmt.value {
61
+ Value::Identifier(ident) => {
62
+ match module.variable_table.get(ident) {
63
+ Some(val) => val.clone(),
64
+ None => {
65
+ let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
66
+ let message = format!(
67
+ "'{path}': value identifier '{ident}' not found in variable table"
68
+ );
69
+ logger.log_error_with_stacktrace(&message, &stacktrace);
70
+ Value::Null
71
+ }
72
+ }
73
+ }
74
+ Value::Map(map) => {
75
+ let mut resolved_map = HashMap::new();
76
+ for (k, v) in map.iter() {
77
+ let resolved_v = match v {
78
+ Value::Identifier(id) => {
79
+ module.variable_table.get(id).cloned().unwrap_or(Value::Null)
80
+ }
81
+ other => other.clone(),
82
+ };
83
+ resolved_map.insert(k.clone(), resolved_v);
84
+ }
85
+ Value::Map(resolved_map)
86
+ }
87
+ other => other.clone(),
88
+ };
89
+
90
+ // ✅ On reconstruit le Statement avec Trigger résolu
91
+ if let StatementKind::Trigger { entity, .. } = &stmt.kind {
92
+ return Statement {
93
+ kind: StatementKind::Trigger {
94
+ entity: entity.to_string(),
95
+ duration: final_duration,
96
+ },
97
+ value: final_value,
98
+ line: stmt.line,
99
+ column: stmt.column,
100
+ indent: stmt.indent,
101
+ };
102
+ }
103
+
104
+ return Statement {
105
+ kind: StatementKind::Trigger {
106
+ entity: entity.to_string(),
107
+ duration: final_duration,
108
+ },
109
+ value: final_value,
110
+ line: stmt.line,
111
+ column: stmt.column,
112
+ indent: stmt.indent,
113
+ };
114
+ }
package/rust/main.rs CHANGED
@@ -2,6 +2,7 @@ pub mod core;
2
2
  pub mod cli;
3
3
  pub mod utils;
4
4
  pub mod config;
5
+ pub mod audio;
5
6
 
6
7
  use std::io;
7
8
  use cli::{ Cli };
@@ -11,6 +12,7 @@ use crate::{
11
12
  build::handle_build_command,
12
13
  check::handle_check_command,
13
14
  init::handle_init_command,
15
+ play::handle_play_command,
14
16
  template::{ handle_template_info_command, handle_template_list_command },
15
17
  Commands,
16
18
  TemplateCommand,
@@ -51,6 +53,10 @@ fn main() -> io::Result<()> {
51
53
  handle_build_command(config, entry, output, watch);
52
54
  }
53
55
 
56
+ Commands::Play { entry, output, watch, repeat } => {
57
+ handle_play_command(config, entry, output, watch, repeat);
58
+ }
59
+
54
60
  _ => {}
55
61
  }
56
62
 
@@ -1,6 +1,8 @@
1
1
  use notify::{ Watcher, RecursiveMode, Config, RecommendedWatcher };
2
2
  use std::sync::mpsc::channel;
3
3
 
4
+ use std::time::{ Duration, Instant };
5
+
4
6
  pub fn watch_directory<F>(entry: String, callback: F) -> notify::Result<()>
5
7
  where F: Fn() + Send + 'static
6
8
  {
@@ -9,13 +11,19 @@ pub fn watch_directory<F>(entry: String, callback: F) -> notify::Result<()>
9
11
  let mut watcher: RecommendedWatcher = Watcher::new(tx, Config::default())?;
10
12
  watcher.watch(&entry.as_ref(), RecursiveMode::Recursive)?;
11
13
 
14
+ let mut last_trigger = Instant::now();
15
+
12
16
  loop {
13
17
  match rx.recv() {
14
18
  Ok(_) => {
15
- callback();
19
+ let now = Instant::now();
20
+ if now.duration_since(last_trigger) > Duration::from_millis(200) {
21
+ callback();
22
+ last_trigger = now;
23
+ }
16
24
  }
17
25
  Err(e) => {
18
- eprintln!("Channel error : {:?}", e);
26
+ eprintln!("Channel error: {:?}", e);
19
27
  break;
20
28
  }
21
29
  }