@devaloop/devalang 0.0.1-alpha.7 → 0.0.1-alpha.9

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 (86) hide show
  1. package/Cargo.toml +1 -1
  2. package/README.md +31 -16
  3. package/docs/CHANGELOG.md +49 -2
  4. package/docs/ROADMAP.md +2 -2
  5. package/docs/SYNTAX.md +41 -7
  6. package/docs/TODO.md +3 -3
  7. package/examples/condition.deva +20 -0
  8. package/examples/group.deva +3 -3
  9. package/examples/index.deva +9 -8
  10. package/examples/loop.deva +10 -8
  11. package/examples/synth.deva +14 -0
  12. package/examples/variables.deva +2 -2
  13. package/out-tsc/bin/devalang.exe +0 -0
  14. package/out-tsc/scripts/version/fetch.js +1 -5
  15. package/package.json +1 -1
  16. package/project-version.json +3 -3
  17. package/rust/cli/build.rs +6 -1
  18. package/rust/core/audio/engine.rs +89 -12
  19. package/rust/core/audio/evaluator.rs +31 -0
  20. package/rust/core/audio/interpreter/arrow_call.rs +129 -0
  21. package/rust/core/audio/interpreter/call.rs +64 -0
  22. package/rust/core/audio/interpreter/condition.rs +69 -0
  23. package/rust/core/audio/interpreter/driver.rs +216 -0
  24. package/rust/core/audio/interpreter/let_.rs +19 -0
  25. package/rust/core/audio/interpreter/load.rs +18 -0
  26. package/rust/core/audio/interpreter/loop_.rs +67 -0
  27. package/rust/core/audio/interpreter/mod.rs +12 -0
  28. package/rust/core/audio/interpreter/sleep.rs +36 -0
  29. package/rust/core/audio/interpreter/spawn.rs +66 -0
  30. package/rust/core/audio/interpreter/tempo.rs +16 -0
  31. package/rust/core/audio/interpreter/trigger.rs +69 -0
  32. package/rust/core/audio/loader/mod.rs +1 -0
  33. package/rust/core/audio/{loader.rs → loader/trigger.rs} +3 -1
  34. package/rust/core/audio/mod.rs +2 -1
  35. package/rust/core/audio/renderer.rs +54 -0
  36. package/rust/core/builder/mod.rs +1 -1
  37. package/rust/core/debugger/lexer.rs +1 -1
  38. package/rust/core/debugger/mod.rs +1 -0
  39. package/rust/core/debugger/store.rs +25 -0
  40. package/rust/core/error/mod.rs +1 -1
  41. package/rust/core/lexer/handler/arrow.rs +31 -0
  42. package/rust/core/lexer/handler/driver.rs +226 -0
  43. package/rust/core/lexer/handler/identifier.rs +3 -0
  44. package/rust/core/lexer/handler/mod.rs +4 -227
  45. package/rust/core/lexer/handler/operator.rs +44 -0
  46. package/rust/core/lexer/mod.rs +25 -4
  47. package/rust/core/lexer/token.rs +40 -9
  48. package/rust/core/parser/driver.rs +331 -0
  49. package/rust/core/parser/handler/arrow_call.rs +126 -0
  50. package/rust/core/parser/handler/at.rs +3 -7
  51. package/rust/core/parser/handler/bank.rs +5 -2
  52. package/rust/core/parser/handler/condition.rs +74 -0
  53. package/rust/core/parser/handler/dot.rs +1 -1
  54. package/rust/core/parser/handler/identifier/call.rs +41 -0
  55. package/rust/core/parser/handler/identifier/group.rs +75 -0
  56. package/rust/core/parser/handler/identifier/let_.rs +133 -0
  57. package/rust/core/parser/handler/identifier/mod.rs +51 -0
  58. package/rust/core/parser/handler/identifier/sleep.rs +33 -0
  59. package/rust/core/parser/handler/identifier/spawn.rs +41 -0
  60. package/rust/core/parser/handler/identifier/synth.rs +65 -0
  61. package/rust/core/parser/handler/loop_.rs +25 -19
  62. package/rust/core/parser/handler/mod.rs +3 -1
  63. package/rust/core/parser/handler/tempo.rs +1 -1
  64. package/rust/core/parser/mod.rs +3 -237
  65. package/rust/core/parser/statement.rs +36 -35
  66. package/rust/core/preprocessor/loader.rs +64 -49
  67. package/rust/core/preprocessor/module.rs +3 -6
  68. package/rust/core/preprocessor/processor.rs +13 -4
  69. package/rust/core/preprocessor/resolver/call.rs +123 -0
  70. package/rust/core/preprocessor/resolver/condition.rs +92 -0
  71. package/rust/core/preprocessor/resolver/driver.rs +227 -0
  72. package/rust/core/preprocessor/resolver/group.rs +35 -87
  73. package/rust/core/preprocessor/resolver/let_.rs +31 -0
  74. package/rust/core/preprocessor/resolver/loop_.rs +62 -116
  75. package/rust/core/preprocessor/resolver/mod.rs +9 -153
  76. package/rust/core/preprocessor/resolver/spawn.rs +58 -0
  77. package/rust/core/preprocessor/resolver/synth.rs +50 -0
  78. package/rust/core/preprocessor/resolver/trigger.rs +51 -50
  79. package/rust/core/preprocessor/resolver/value.rs +78 -0
  80. package/rust/core/utils/path.rs +17 -32
  81. package/rust/core/utils/validation.rs +30 -28
  82. package/typescript/scripts/version/fetch.ts +1 -6
  83. package/rust/core/audio/interpreter.rs +0 -317
  84. package/rust/core/audio/render.rs +0 -53
  85. package/rust/core/lexer/handler/equal.rs +0 -32
  86. package/rust/core/parser/handler/identifier.rs +0 -260
@@ -0,0 +1,227 @@
1
+ use std::collections::HashMap;
2
+ use crate::{
3
+ core::{
4
+ parser::statement::{ Statement, StatementKind },
5
+ preprocessor::{
6
+ loader::ModuleLoader,
7
+ module::Module,
8
+ resolver::{
9
+ bank::resolve_bank,
10
+ call::resolve_call,
11
+ condition::resolve_condition,
12
+ group::resolve_group,
13
+ let_::resolve_let,
14
+ loop_::resolve_loop,
15
+ spawn::resolve_spawn,
16
+ tempo::resolve_tempo,
17
+ trigger::resolve_trigger,
18
+ },
19
+ },
20
+ shared::value::Value,
21
+ store::global::GlobalStore,
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_statement(
33
+ stmt: &Statement,
34
+ module: &Module,
35
+ path: &str,
36
+ global_store: &mut GlobalStore
37
+ ) -> Statement {
38
+ match &stmt.kind {
39
+ StatementKind::Trigger { entity, duration } =>
40
+ resolve_trigger(stmt, entity, &mut duration.clone(), module, path, global_store),
41
+ StatementKind::If => resolve_condition(stmt, module, path, global_store),
42
+ StatementKind::Group => resolve_group(stmt, module, path, global_store),
43
+ StatementKind::Call => resolve_call(stmt, module, path, global_store),
44
+ StatementKind::Spawn => resolve_spawn(stmt, module, path, global_store),
45
+ StatementKind::Bank => resolve_bank(stmt, module, path, global_store),
46
+ StatementKind::Tempo => resolve_tempo(stmt, module, path, global_store),
47
+ StatementKind::Loop => resolve_loop(stmt, module, path, global_store),
48
+ StatementKind::Let { name, .. } => resolve_let(stmt, name, module, path, global_store),
49
+
50
+ _ => {
51
+ let resolved_value = resolve_value(&stmt.value, module, global_store);
52
+
53
+ Statement {
54
+ value: resolved_value,
55
+ ..stmt.clone()
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ fn resolve_value(value: &Value, module: &Module, global_store: &mut GlobalStore) -> Value {
62
+ match value {
63
+ Value::Identifier(name) => {
64
+ if let Some(original_val) = module.variable_table.get(name) {
65
+ return resolve_value(original_val, module, global_store);
66
+ }
67
+
68
+ if let Some(export_val) = find_export_value(name, global_store) {
69
+ return resolve_value(&export_val, module, global_store);
70
+ }
71
+
72
+ eprintln!("⚠️ Unresolved identifier '{}'", name);
73
+ Value::Null
74
+ }
75
+
76
+ Value::Map(map) => {
77
+ let mut resolved = HashMap::new();
78
+ for (k, v) in map {
79
+ resolved.insert(k.clone(), resolve_value(v, module, global_store));
80
+ }
81
+ Value::Map(resolved)
82
+ }
83
+
84
+ Value::Block(stmts) => {
85
+ let resolved_stmts = stmts
86
+ .iter()
87
+ .map(|stmt| resolve_statement(stmt, module, &module.path, global_store))
88
+ .collect();
89
+ Value::Block(resolved_stmts)
90
+ }
91
+
92
+ other => other.clone(),
93
+ }
94
+ }
95
+
96
+ fn find_export_value(name: &str, global_store: &GlobalStore) -> Option<Value> {
97
+ for (_path, module) in &global_store.modules {
98
+ if let Some(val) = module.export_table.get_export(name) {
99
+ return Some(val.clone());
100
+ }
101
+ }
102
+ None
103
+ }
104
+
105
+ pub fn resolve_imports(module_loader: &ModuleLoader, global_store: &mut GlobalStore) {
106
+ for (module_path, module) in global_store.clone().modules.iter_mut() {
107
+ for (name, source_path) in &module.import_table.imports {
108
+ match source_path {
109
+ Value::String(source_path) => {
110
+ if let Some(source_module) = global_store.modules.get(source_path) {
111
+ if let Some(value) = source_module.export_table.get_export(name) {
112
+ module.variable_table.set(name.clone(), value.clone());
113
+ } else {
114
+ println!(
115
+ "[warn] '{module_path}': '{name}' not found in exports of '{source_path}'"
116
+ );
117
+ }
118
+ } else {
119
+ println!(
120
+ "[warn] '{module_path}': cannot find source module '{source_path}'"
121
+ );
122
+ }
123
+ }
124
+ _ => {
125
+ println!(
126
+ "[warn] '{module_path}': expected string for import source, found {:?}",
127
+ source_path
128
+ );
129
+ }
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ pub fn resolve_and_flatten_all_modules(
136
+ global_store: &mut GlobalStore
137
+ ) -> HashMap<String, Vec<Statement>> {
138
+ let logger = Logger::new();
139
+ let snapshot = global_store.clone();
140
+
141
+ // 1. Imports resolution
142
+ for (module_path, module) in global_store.modules.iter_mut() {
143
+ for (name, source_path) in &module.import_table.imports {
144
+ if let Value::String(source_path_str) = source_path {
145
+ match snapshot.modules.get(source_path_str) {
146
+ Some(source_module) => {
147
+ if let Some(value) = source_module.export_table.get_export(name) {
148
+ module.variable_table.set(name.clone(), value.clone());
149
+ } else {
150
+ logger.log_error_with_stacktrace(
151
+ &format!("'{name}' not found in exports of '{source_path_str}'"),
152
+ module_path
153
+ );
154
+ }
155
+ }
156
+ None => {
157
+ logger.log_error_with_stacktrace(
158
+ &format!("Cannot find source module '{source_path_str}'"),
159
+ module_path
160
+ );
161
+ }
162
+ }
163
+ } else {
164
+ logger.log_error_with_stacktrace(
165
+ &format!("Expected string for import source, found {:?}", source_path),
166
+ module_path
167
+ );
168
+ }
169
+ }
170
+ }
171
+
172
+ // 2. Statements resolution
173
+ let mut resolved_map: HashMap<String, Vec<Statement>> = HashMap::new();
174
+ for (path, module) in global_store.modules.clone() {
175
+ let mut resolved = Vec::new();
176
+
177
+ for stmt in &module.statements {
178
+ let mut stmt = stmt.clone();
179
+
180
+ match &stmt.kind {
181
+ StatementKind::Trigger { entity, duration } => {
182
+ let resolved_stmt = resolve_trigger(
183
+ &stmt,
184
+ entity.as_str(),
185
+ &mut duration.clone(),
186
+ &module,
187
+ &path,
188
+ global_store
189
+ );
190
+ resolved.push(resolved_stmt);
191
+ }
192
+
193
+ StatementKind::Loop => {
194
+ let resolved_stmt = resolve_loop(&stmt, &module, &path, global_store);
195
+ resolved.push(resolved_stmt);
196
+ }
197
+
198
+ StatementKind::Bank => {
199
+ let resolved_stmt = resolve_bank(&stmt, &module, &path, global_store);
200
+ resolved.push(resolved_stmt);
201
+ }
202
+
203
+ StatementKind::Tempo => {
204
+ let resolved_stmt = resolve_tempo(&stmt, &module, &path, global_store);
205
+ resolved.push(resolved_stmt);
206
+ }
207
+
208
+ StatementKind::Import { .. } | StatementKind::Export { .. } => {
209
+ resolved.push(stmt.clone());
210
+ }
211
+
212
+ StatementKind::Group => {
213
+ let resolved_stmt = resolve_group(&stmt, &module, &path, global_store);
214
+ resolved.push(resolved_stmt);
215
+ }
216
+
217
+ _ => {
218
+ resolved.push(stmt);
219
+ }
220
+ }
221
+ }
222
+
223
+ resolved_map.insert(path.clone(), resolved);
224
+ }
225
+
226
+ resolved_map
227
+ }
@@ -3,111 +3,59 @@ use std::collections::HashMap;
3
3
  use crate::{
4
4
  core::{
5
5
  parser::statement::{ Statement, StatementKind },
6
- preprocessor::{ module::Module, resolver::trigger::resolve_trigger },
6
+ preprocessor::{ module::Module, resolver::driver::resolve_statement },
7
7
  shared::value::Value,
8
- store::global::GlobalStore,
8
+ store::{ global::GlobalStore, variable::VariableTable },
9
9
  },
10
- utils::logger::Logger,
10
+ utils::logger::{ LogLevel, Logger },
11
11
  };
12
12
 
13
13
  pub fn resolve_group(
14
14
  stmt: &Statement,
15
15
  module: &Module,
16
16
  path: &str,
17
- global_store: &GlobalStore
17
+ global_store: &mut GlobalStore
18
18
  ) -> Statement {
19
19
  let logger = Logger::new();
20
20
 
21
- if let Value::Map(value_map) = &stmt.value {
22
- let group_name = match value_map.get("identifier") {
23
- Some(Value::String(name)) => name.clone(),
24
- Some(other) => {
25
- log_type_error(
26
- &logger,
27
- module,
28
- stmt,
29
- format!("Group name must be a string, found {:?}", other)
30
- );
31
- return stmt.clone();
32
- }
33
- None => {
34
- log_type_error(&logger, module, stmt, "Group name is required".to_string());
35
- return stmt.clone();
36
- }
37
- };
21
+ let Value::Map(group_map) = &stmt.value else {
22
+ return type_error(&logger, module, stmt, "Expected a map in group statement".to_string());
23
+ };
38
24
 
39
- let body_value = match value_map.get("body") {
40
- Some(Value::Block(block)) => {
41
- let mut resolved_block = Vec::new();
42
- for ref statement in block.clone() {
43
- match &statement.kind {
44
- StatementKind::Trigger { entity, duration } => {
45
- let resolved = resolve_trigger(
46
- &mut statement.clone(),
47
- &entity,
48
- &mut duration.clone(),
49
- module,
50
- path,
51
- global_store
52
- );
53
- resolved_block.push(resolved);
54
- }
55
- _ => {
56
- println!("Unhandled group body statement: {:?}", statement);
57
- }
58
- }
59
- }
60
- Value::Block(resolved_block)
61
- }
62
- Some(other) => {
63
- log_type_error(
64
- &logger,
65
- module,
66
- stmt,
67
- format!("Unexpected value for group body: {:?}", other)
68
- );
69
- Value::Null
70
- }
71
- None => {
72
- log_type_error(
73
- &logger,
74
- module,
75
- stmt,
76
- "Missing 'body' key in group statement map".to_string()
77
- );
78
- Value::Null
79
- }
80
- };
25
+ let mut resolved_map = group_map.clone();
81
26
 
82
- let mut resolved_map = HashMap::new();
83
-
84
- resolved_map.insert("identifier".to_string(), Value::String(group_name));
85
- resolved_map.insert("body".to_string(), body_value);
86
-
87
- return Statement {
88
- kind: StatementKind::Group,
89
- value: Value::Map(resolved_map),
90
- ..stmt.clone()
91
- };
27
+ if let Some(Value::Block(body)) = group_map.get("body") {
28
+ let resolved_body = resolve_block_statements(body, module, path, global_store);
29
+ resolved_map.insert("body".to_string(), Value::Block(resolved_body));
92
30
  } else {
93
- log_type_error(
94
- &logger,
95
- module,
96
- stmt,
97
- format!("Expected Map for group statement, found {:?}", stmt.value)
98
- );
31
+ logger.log_message(LogLevel::Warning, "group without a body");
32
+ }
99
33
 
100
- Statement {
101
- kind: StatementKind::Error {
102
- message: "Expected a map for group statement".to_string(),
103
- },
104
- value: Value::Null,
105
- ..stmt.clone()
106
- }
34
+ Statement {
35
+ kind: StatementKind::Group,
36
+ value: Value::Map(resolved_map),
37
+ ..stmt.clone()
107
38
  }
108
39
  }
109
40
 
110
- fn log_type_error(logger: &Logger, module: &Module, stmt: &Statement, message: String) {
41
+ fn resolve_block_statements(
42
+ body: &[Statement],
43
+ module: &Module,
44
+ path: &str,
45
+ global_store: &mut GlobalStore
46
+ ) -> Vec<Statement> {
47
+ body.iter()
48
+ .map(|stmt| resolve_statement(stmt, module, path, global_store))
49
+ .collect()
50
+ }
51
+
52
+ fn type_error(logger: &Logger, module: &Module, stmt: &Statement, message: String) -> Statement {
111
53
  let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
112
54
  logger.log_error_with_stacktrace(&message, &stacktrace);
55
+
56
+ Statement {
57
+ kind: StatementKind::Error { message },
58
+ value: Value::Null,
59
+ ..stmt.clone()
60
+ }
113
61
  }
@@ -0,0 +1,31 @@
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
+ ;
11
+
12
+ pub fn resolve_let(
13
+ stmt: &Statement,
14
+ name: &str,
15
+ module: &Module,
16
+ path: &str,
17
+ global_store: &mut GlobalStore
18
+ ) -> Statement {
19
+ let resolved_value = resolve_value(&stmt.value, module, global_store);
20
+
21
+ if let Some(current_mod) = global_store.modules.get_mut(path) {
22
+ current_mod.variable_table.set(name.to_string(), resolved_value.clone());
23
+ } else {
24
+ eprintln!("[resolve_statement] ❌ Module path not found: {path}");
25
+ }
26
+
27
+ Statement {
28
+ value: resolved_value,
29
+ ..stmt.clone()
30
+ }
31
+ }
@@ -3,7 +3,10 @@ use std::collections::HashMap;
3
3
  use crate::{
4
4
  core::{
5
5
  parser::statement::{ Statement, StatementKind },
6
- preprocessor::{ module::Module, resolver::trigger::resolve_trigger },
6
+ preprocessor::{
7
+ module::Module,
8
+ resolver::{ driver::resolve_statement, value::resolve_value },
9
+ },
7
10
  shared::value::Value,
8
11
  store::global::GlobalStore,
9
12
  },
@@ -14,132 +17,75 @@ pub fn resolve_loop(
14
17
  stmt: &Statement,
15
18
  module: &Module,
16
19
  path: &str,
17
- global_store: &GlobalStore
20
+ global_store: &mut GlobalStore
18
21
  ) -> Statement {
19
22
  let logger = Logger::new();
20
23
 
21
- if let Value::Map(value_map) = &stmt.value {
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
- // Value is not a variable so we assume it's a number
37
- if let Ok(n) = ident.parse::<f32>() {
38
- Value::Number(n)
39
- } else {
40
- log_type_error(
41
- &logger,
42
- module,
43
- stmt,
44
- format!("Loop iterator '{ident}' is not a valid number")
45
- );
46
- Value::Null
47
- }
48
- }
49
- }
50
- }
51
- Some(Value::Number(n)) => Value::Number(*n),
52
- Some(other) => {
53
- log_type_error(
54
- &logger,
55
- module,
56
- stmt,
57
- format!("Unexpected value for loop iterator: {:?}", other)
58
- );
59
- Value::Null
60
- }
61
- None => {
62
- log_type_error(
63
- &logger,
64
- module,
65
- stmt,
66
- "Missing 'iterator' key in loop statement map".to_string()
67
- );
68
- Value::Null
69
- }
70
- };
24
+ let resolved_value = resolve_value(&stmt.value, module, global_store);
71
25
 
72
- let body_value = match value_map.get("body") {
73
- Some(Value::Block(block)) => {
74
- let mut resolved_block = Vec::new();
75
- for ref statement in block.clone() {
76
- match &statement.kind {
77
- StatementKind::Trigger { entity, duration } => {
78
- let resolved = resolve_trigger(
79
- &mut statement.clone(),
80
- &entity,
81
- &mut duration.clone(),
82
- module,
83
- path,
84
- global_store
85
- );
86
- resolved_block.push(resolved);
87
- }
88
- _ => {
89
- println!("Unhandled loop body statement: {:?}", statement);
90
- }
91
- }
92
- }
93
- Value::Block(resolved_block)
94
- }
95
- Some(other) => {
96
- log_type_error(
97
- &logger,
98
- module,
99
- stmt,
100
- format!("Unexpected value for loop body: {:?}", other)
101
- );
102
- Value::Null
103
- }
104
- None => {
105
- log_type_error(
106
- &logger,
107
- module,
108
- stmt,
109
- "Missing 'body' key in loop statement map".to_string()
110
- );
111
- Value::Null
112
- }
113
- };
26
+ let Value::Map(value_map) = &resolved_value else {
27
+ return error_stmt(&logger, module, stmt, "Expected a map for loop value");
28
+ };
114
29
 
115
- let mut resolved_map = HashMap::new();
116
- resolved_map.insert("iterator".to_string(), iterator_value);
117
- resolved_map.insert("body".to_string(), body_value);
30
+ let mut resolved_map: HashMap<String, Value> = HashMap::new();
31
+ for (key, val) in value_map {
32
+ resolved_map.insert(key.clone(), resolve_value(val, module, global_store));
33
+ }
118
34
 
119
- Statement {
120
- kind: StatementKind::Loop,
121
- value: Value::Map(resolved_map),
122
- ..stmt.clone()
35
+ let iterator_value = match resolved_map.get("iterator") {
36
+ Some(Value::Number(n)) => Value::Number(*n),
37
+ Some(other) => {
38
+ error_value(
39
+ &logger,
40
+ module,
41
+ stmt,
42
+ &format!("Loop iterator must be a number, found: {:?}", other)
43
+ );
44
+ Value::Number(1.0)
45
+ }
46
+ None => {
47
+ error_value(&logger, module, stmt, "Missing 'iterator' in loop");
48
+ Value::Number(1.0)
123
49
  }
124
- } else {
125
- log_type_error(
126
- &logger,
127
- module,
128
- stmt,
129
- format!("Expected a map for loop value, found {:?}", stmt.value)
130
- );
50
+ };
131
51
 
132
- Statement {
133
- kind: StatementKind::Error {
134
- message: "Expected a map for loop value".to_string(),
135
- },
136
- value: Value::Null,
137
- ..stmt.clone()
52
+ let body_value = match resolved_map.remove("body") {
53
+ Some(Value::Block(stmts)) => {
54
+ let resolved = stmts
55
+ .iter()
56
+ .map(|s| resolve_statement(s, module, path, global_store))
57
+ .collect();
58
+ Value::Block(resolved)
138
59
  }
60
+ _ => {
61
+ error_value(&logger, module, stmt, "Invalid or missing loop body");
62
+ Value::Block(vec![])
63
+ }
64
+ };
65
+
66
+ let mut final_map = HashMap::new();
67
+ final_map.insert("iterator".to_string(), iterator_value);
68
+ final_map.insert("body".to_string(), body_value);
69
+
70
+ Statement {
71
+ kind: StatementKind::Loop,
72
+ value: Value::Map(final_map),
73
+ ..stmt.clone()
139
74
  }
140
75
  }
141
76
 
142
- fn log_type_error(logger: &Logger, module: &Module, stmt: &Statement, message: String) {
77
+ fn error_value(logger: &Logger, module: &Module, stmt: &Statement, msg: &str) {
143
78
  let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
144
- logger.log_error_with_stacktrace(&message, &stacktrace);
79
+ logger.log_error_with_stacktrace(msg, &stacktrace);
80
+ }
81
+
82
+ fn error_stmt(logger: &Logger, module: &Module, stmt: &Statement, msg: &str) -> Statement {
83
+ error_value(logger, module, stmt, msg);
84
+ Statement {
85
+ kind: StatementKind::Error {
86
+ message: msg.to_string(),
87
+ },
88
+ value: Value::Null,
89
+ ..stmt.clone()
90
+ }
145
91
  }