@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.
- package/Cargo.toml +54 -53
- package/README.md +1 -14
- package/docs/CHANGELOG.md +26 -0
- package/docs/TODO.md +1 -1
- package/examples/index.deva +10 -13
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +1 -1
- package/project-version.json +3 -3
- package/rust/cli/build.rs +25 -2
- package/rust/cli/check.rs +26 -3
- package/rust/cli/play.rs +1 -1
- package/rust/core/audio/engine.rs +126 -73
- package/rust/core/audio/interpreter/call.rs +72 -47
- package/rust/core/audio/interpreter/condition.rs +14 -12
- package/rust/core/audio/interpreter/driver.rs +84 -127
- package/rust/core/audio/interpreter/function.rs +21 -0
- package/rust/core/audio/interpreter/load.rs +1 -1
- package/rust/core/audio/interpreter/loop_.rs +24 -18
- package/rust/core/audio/interpreter/mod.rs +2 -1
- package/rust/core/audio/interpreter/sleep.rs +0 -6
- package/rust/core/audio/interpreter/spawn.rs +78 -60
- package/rust/core/audio/interpreter/trigger.rs +157 -70
- package/rust/core/audio/loader/trigger.rs +37 -4
- package/rust/core/audio/player.rs +20 -10
- package/rust/core/audio/renderer.rs +24 -25
- package/rust/core/debugger/mod.rs +2 -0
- package/rust/core/debugger/module.rs +47 -0
- package/rust/core/debugger/store.rs +25 -11
- package/rust/core/error/mod.rs +6 -0
- package/rust/core/lexer/handler/driver.rs +23 -1
- package/rust/core/lexer/handler/identifier.rs +1 -0
- package/rust/core/lexer/handler/mod.rs +1 -0
- package/rust/core/lexer/handler/parenthesis.rs +41 -0
- package/rust/core/lexer/token.rs +3 -0
- package/rust/core/parser/driver.rs +3 -1
- package/rust/core/parser/handler/dot.rs +64 -127
- package/rust/core/parser/handler/identifier/call.rs +69 -22
- package/rust/core/parser/handler/identifier/function.rs +92 -0
- package/rust/core/parser/handler/identifier/let_.rs +13 -19
- package/rust/core/parser/handler/identifier/mod.rs +1 -0
- package/rust/core/parser/handler/identifier/spawn.rs +74 -27
- package/rust/core/parser/statement.rs +16 -4
- package/rust/core/preprocessor/loader.rs +45 -29
- package/rust/core/preprocessor/module.rs +3 -1
- package/rust/core/preprocessor/processor.rs +26 -1
- package/rust/core/preprocessor/resolver/call.rs +61 -84
- package/rust/core/preprocessor/resolver/condition.rs +11 -6
- package/rust/core/preprocessor/resolver/driver.rs +52 -6
- package/rust/core/preprocessor/resolver/function.rs +78 -0
- package/rust/core/preprocessor/resolver/group.rs +43 -13
- package/rust/core/preprocessor/resolver/let_.rs +7 -10
- package/rust/core/preprocessor/resolver/mod.rs +2 -1
- package/rust/core/preprocessor/resolver/spawn.rs +64 -30
- package/rust/core/preprocessor/resolver/trigger.rs +7 -3
- package/rust/core/preprocessor/resolver/value.rs +10 -1
- package/rust/core/shared/value.rs +4 -1
- package/rust/core/store/function.rs +34 -0
- package/rust/core/store/global.rs +9 -10
- package/rust/core/store/mod.rs +2 -1
- package/rust/core/store/variable.rs +6 -0
- package/rust/lib.rs +10 -7
- package/rust/utils/mod.rs +45 -1
|
@@ -15,105 +15,192 @@ pub fn interprete_trigger_statement(
|
|
|
15
15
|
cursor_time: f32,
|
|
16
16
|
max_end_time: f32
|
|
17
17
|
) -> Option<(f32, f32, AudioEngine)> {
|
|
18
|
-
if let StatementKind::Trigger { entity, duration } = &stmt.kind {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
if let StatementKind::Trigger { entity, duration, effects } = &stmt.kind {
|
|
19
|
+
let mut trigger_val = Value::String(entity.clone());
|
|
20
|
+
let mut trigger_src = String::new();
|
|
21
|
+
|
|
22
|
+
match variable_table.variables.get(entity) {
|
|
23
|
+
Some(Value::Identifier(id)) => {
|
|
24
|
+
// Get real value from global variable table
|
|
25
|
+
if let Some(global_table) = &variable_table.parent {
|
|
26
|
+
if let Some(val) = global_table.get(id) {
|
|
27
|
+
trigger_val = val.clone();
|
|
26
28
|
} else {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Some(Value::Identifier(other)) if other == "auto" => 1.0,
|
|
30
|
-
Some(other) => {
|
|
31
|
-
eprintln!(
|
|
32
|
-
"❌ Invalid duration reference '{}': expected number, got {:?}",
|
|
33
|
-
id,
|
|
34
|
-
other
|
|
35
|
-
);
|
|
36
|
-
return None;
|
|
37
|
-
}
|
|
38
|
-
None => {
|
|
39
|
-
eprintln!("❌ Duration identifier '{}' not found", id);
|
|
40
|
-
return None;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
29
|
+
eprintln!("❌ Trigger entity '{}' not found in global variable table", id);
|
|
30
|
+
return None;
|
|
43
31
|
}
|
|
32
|
+
} else if let Some(val) = variable_table.get(id) {
|
|
33
|
+
trigger_val = val.clone();
|
|
34
|
+
} else {
|
|
35
|
+
eprintln!("❌ Trigger entity '{}' not found in variable table", id);
|
|
36
|
+
return None;
|
|
44
37
|
}
|
|
38
|
+
}
|
|
39
|
+
Some(Value::Sample(sample_src)) => {
|
|
40
|
+
// If the entity is a sample, we use its path directly
|
|
41
|
+
trigger_src = sample_src.clone();
|
|
42
|
+
}
|
|
43
|
+
Some(Value::Map(map)) => {
|
|
44
|
+
// If the entity is a map, we assume it contains an "entity" key
|
|
45
|
+
if let Some(Value::String(src)) = map.get("entity") {
|
|
46
|
+
trigger_val = Value::String(src.clone());
|
|
47
|
+
} else if let Some(Value::Identifier(src)) = map.get("entity") {
|
|
48
|
+
trigger_val = Value::Identifier(src.clone());
|
|
49
|
+
} else {
|
|
50
|
+
eprintln!(
|
|
51
|
+
"❌ Trigger map must contain an 'entity' key with a string or identifier value."
|
|
52
|
+
);
|
|
53
|
+
return None;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
_ => {
|
|
57
|
+
trigger_val = Value::String(entity.clone());
|
|
58
|
+
}
|
|
59
|
+
}
|
|
45
60
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
61
|
+
let duration_secs = match duration {
|
|
62
|
+
Duration::Number(n) => *n,
|
|
63
|
+
|
|
64
|
+
Duration::Identifier(id) => {
|
|
65
|
+
if id == "auto" {
|
|
66
|
+
1.0
|
|
67
|
+
} else {
|
|
68
|
+
match variable_table.get(id) {
|
|
69
|
+
Some(Value::Number(n)) => *n,
|
|
70
|
+
Some(Value::Identifier(other)) if other == "auto" => 1.0,
|
|
71
|
+
Some(other) => {
|
|
72
|
+
eprintln!(
|
|
73
|
+
"❌ Invalid duration reference '{}': expected number, got {:?}",
|
|
74
|
+
id,
|
|
75
|
+
other
|
|
76
|
+
);
|
|
77
|
+
return None;
|
|
78
|
+
}
|
|
79
|
+
None => {
|
|
80
|
+
eprintln!("❌ Duration identifier '{}' not found", id);
|
|
81
|
+
return None;
|
|
82
|
+
}
|
|
53
83
|
}
|
|
54
|
-
|
|
55
|
-
let numerator: f32 = parts[0].parse().unwrap_or(1.0);
|
|
56
|
-
let denominator: f32 = parts[1].parse().unwrap_or(1.0);
|
|
57
|
-
numerator / denominator
|
|
58
84
|
}
|
|
85
|
+
}
|
|
59
86
|
|
|
60
|
-
|
|
61
|
-
|
|
87
|
+
Duration::Beat(beat_str) => {
|
|
88
|
+
let parts: Vec<&str> = beat_str.split('/').collect();
|
|
89
|
+
if parts.len() != 2 {
|
|
90
|
+
eprintln!("❌ Invalid beat duration format: {}", beat_str);
|
|
91
|
+
return None;
|
|
92
|
+
}
|
|
62
93
|
|
|
63
|
-
|
|
94
|
+
let numerator: f32 = parts[0].parse().unwrap_or(1.0);
|
|
95
|
+
let denominator: f32 = parts[1].parse().unwrap_or(4.0);
|
|
64
96
|
|
|
65
|
-
|
|
66
|
-
trigger_val,
|
|
67
|
-
duration,
|
|
68
|
-
base_duration,
|
|
69
|
-
variable_table.clone()
|
|
70
|
-
);
|
|
97
|
+
let beats = (numerator / denominator) * 4.0;
|
|
71
98
|
|
|
72
|
-
|
|
73
|
-
audio_engine.insert_sample(&src, cursor_time, duration_final, Some(effects));
|
|
74
|
-
} else {
|
|
75
|
-
audio_engine.insert_sample(&src, cursor_time, duration_final, None);
|
|
99
|
+
beats * base_duration
|
|
76
100
|
}
|
|
77
101
|
|
|
78
|
-
|
|
102
|
+
Duration::Auto => base_duration,
|
|
103
|
+
};
|
|
79
104
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
105
|
+
let final_variable_table = if let Some(parent) = &variable_table.parent {
|
|
106
|
+
VariableTable {
|
|
107
|
+
variables: parent.variables.clone(),
|
|
108
|
+
parent: None,
|
|
109
|
+
}
|
|
84
110
|
} else {
|
|
85
|
-
|
|
111
|
+
variable_table.clone()
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
let (src, sample_length) = load_trigger(
|
|
115
|
+
&trigger_val,
|
|
116
|
+
duration,
|
|
117
|
+
effects,
|
|
118
|
+
base_duration,
|
|
119
|
+
final_variable_table.clone()
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if trigger_src.is_empty() {
|
|
123
|
+
trigger_src = src;
|
|
86
124
|
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
None
|
|
90
|
-
}
|
|
91
125
|
|
|
92
|
-
|
|
93
|
-
|
|
126
|
+
let effects = extract_effects(stmt.value.clone());
|
|
127
|
+
let one_shot = effects
|
|
128
|
+
.as_ref()
|
|
129
|
+
.and_then(|map| map.get("one_shot"))
|
|
130
|
+
.and_then(|v| {
|
|
131
|
+
match v {
|
|
132
|
+
Value::Identifier(id) if id == "true" => Some(true),
|
|
133
|
+
Value::String(s) if s == "true" => Some(true),
|
|
134
|
+
_ => None,
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
.unwrap_or(false);
|
|
94
138
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
current = variables.get(part);
|
|
139
|
+
let play_length = if one_shot {
|
|
140
|
+
sample_length // play entire sample
|
|
98
141
|
} else {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
142
|
+
duration_secs.min(sample_length)
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
let trigger_src = match trigger_val.get("entity") {
|
|
146
|
+
Some(Value::String(src)) => src.clone(),
|
|
147
|
+
Some(Value::Identifier(id)) => id.clone(),
|
|
148
|
+
Some(Value::Statement(stmt)) => {
|
|
149
|
+
if let StatementKind::Trigger { entity, .. } = &stmt.kind {
|
|
150
|
+
entity.clone()
|
|
151
|
+
} else {
|
|
152
|
+
eprintln!("❌ Invalid trigger statement in map: expected 'Trigger' kind");
|
|
102
153
|
return None;
|
|
103
154
|
}
|
|
104
|
-
}
|
|
155
|
+
}
|
|
156
|
+
_ => trigger_src,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
if let Some(effects_map) = effects {
|
|
160
|
+
audio_engine.insert_sample(
|
|
161
|
+
&trigger_src,
|
|
162
|
+
cursor_time,
|
|
163
|
+
play_length,
|
|
164
|
+
Some(effects_map),
|
|
165
|
+
&final_variable_table
|
|
166
|
+
);
|
|
167
|
+
} else {
|
|
168
|
+
audio_engine.insert_sample(
|
|
169
|
+
&trigger_src,
|
|
170
|
+
cursor_time,
|
|
171
|
+
play_length,
|
|
172
|
+
None,
|
|
173
|
+
&final_variable_table
|
|
174
|
+
);
|
|
105
175
|
}
|
|
176
|
+
|
|
177
|
+
let new_cursor_time = cursor_time + duration_secs; // advance by beat duration
|
|
178
|
+
let new_max_end_time = (cursor_time + play_length).max(max_end_time);
|
|
179
|
+
|
|
180
|
+
let updated_engine = audio_engine.clone();
|
|
181
|
+
|
|
182
|
+
return Some((new_cursor_time, new_max_end_time, updated_engine));
|
|
106
183
|
}
|
|
107
184
|
|
|
108
|
-
|
|
185
|
+
None
|
|
109
186
|
}
|
|
110
187
|
|
|
111
188
|
fn extract_effects(value: Value) -> Option<HashMap<String, Value>> {
|
|
112
|
-
if let Value::Map(map) = value
|
|
189
|
+
if let Value::Map(map) = value {
|
|
113
190
|
let mut effects = HashMap::new();
|
|
114
191
|
|
|
115
192
|
for (key, val) in map {
|
|
116
|
-
|
|
193
|
+
if key == "effects" {
|
|
194
|
+
if let Value::Map(effect_map) = val {
|
|
195
|
+
for (effect_key, effect_val) in effect_map {
|
|
196
|
+
effects.insert(effect_key, effect_val);
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
return None; // effects must be a map
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
return Some(effects);
|
|
203
|
+
}
|
|
117
204
|
}
|
|
118
205
|
|
|
119
206
|
Some(effects)
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
use crate::core::{
|
|
1
|
+
use crate::core::{
|
|
2
|
+
parser::statement::StatementKind,
|
|
3
|
+
shared::{ duration::Duration, value::Value },
|
|
4
|
+
store::variable::VariableTable,
|
|
5
|
+
};
|
|
2
6
|
|
|
3
7
|
pub fn load_trigger(
|
|
4
8
|
trigger: &Value,
|
|
5
9
|
duration: &Duration,
|
|
10
|
+
effects: &Option<Value>,
|
|
6
11
|
base_duration: f32,
|
|
7
12
|
variable_table: VariableTable
|
|
8
13
|
) -> (String, f32) {
|
|
@@ -13,8 +18,36 @@ pub fn load_trigger(
|
|
|
13
18
|
Value::String(src) => {
|
|
14
19
|
trigger_path = src.to_string();
|
|
15
20
|
}
|
|
21
|
+
Value::Identifier(src) => {
|
|
22
|
+
trigger_path = src.to_string();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
Value::Map(map) => {
|
|
26
|
+
if let Some(Value::String(src)) = map.get("entity") {
|
|
27
|
+
trigger_path = format!("devalang://bank/{}", src.to_string());
|
|
28
|
+
} else if let Some(Value::Identifier(src)) = map.get("entity") {
|
|
29
|
+
trigger_path = format!("devalang://bank/{}", src.to_string());
|
|
30
|
+
} else {
|
|
31
|
+
eprintln!(
|
|
32
|
+
"❌ Trigger map must contain an 'entity' key with a string or identifier value."
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
Value::Sample(src) => {
|
|
37
|
+
trigger_path = src.to_string();
|
|
38
|
+
}
|
|
39
|
+
Value::Statement(stmt) => {
|
|
40
|
+
if let StatementKind::Trigger { entity, duration, effects } = &stmt.kind {
|
|
41
|
+
trigger_path = entity.clone();
|
|
42
|
+
} else {
|
|
43
|
+
eprintln!("❌ Trigger statement must be of type 'Trigger', found: {:?}", stmt.kind);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
16
46
|
_ => {
|
|
17
|
-
eprintln!(
|
|
47
|
+
eprintln!(
|
|
48
|
+
"❌ Invalid trigger type. Expected a string or identifier variable, found: {:?}",
|
|
49
|
+
trigger
|
|
50
|
+
);
|
|
18
51
|
}
|
|
19
52
|
}
|
|
20
53
|
|
|
@@ -45,11 +78,11 @@ pub fn load_trigger(
|
|
|
45
78
|
|
|
46
79
|
Duration::Beat(beat_str) => {
|
|
47
80
|
let parts: Vec<&str> = beat_str.split('/').collect();
|
|
48
|
-
|
|
81
|
+
|
|
49
82
|
if parts.len() == 2 {
|
|
50
83
|
let numerator: f32 = parts[0].parse().unwrap_or(1.0);
|
|
51
84
|
let denominator: f32 = parts[1].parse().unwrap_or(1.0);
|
|
52
|
-
duration_as_secs = numerator / denominator * base_duration;
|
|
85
|
+
duration_as_secs = (numerator / denominator) * base_duration;
|
|
53
86
|
} else {
|
|
54
87
|
eprintln!("❌ Invalid beat duration format: {}", beat_str);
|
|
55
88
|
}
|
|
@@ -21,23 +21,33 @@ impl AudioPlayer {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
fn load_source(&self, path: &str) -> impl Source<Item = f32> + Send + 'static {
|
|
25
|
-
let file = File::open(path)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
fn load_source(&self, path: &str) -> Option<impl Source<Item = f32> + Send + 'static> {
|
|
25
|
+
if let Ok(file) = File::open(path) {
|
|
26
|
+
let reader = BufReader::new(file);
|
|
27
|
+
match Decoder::new(reader) {
|
|
28
|
+
Ok(decoder) => Some(decoder.convert_samples()),
|
|
29
|
+
Err(e) => {
|
|
30
|
+
eprintln!("❌ Failed to decode audio file '{}': {}", path, e);
|
|
31
|
+
None
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
eprintln!("❌ Could not open audio file: {}", path);
|
|
36
|
+
None
|
|
37
|
+
}
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
pub fn play_file_once(&mut self, path: &str) {
|
|
32
41
|
self.sink.stop();
|
|
33
42
|
self.sink = Sink::try_new(&self.handle).unwrap();
|
|
34
|
-
|
|
35
43
|
self.sink.set_volume(1.0);
|
|
36
44
|
|
|
37
|
-
let source = self.load_source(path)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
if let Some(source) = self.load_source(path) {
|
|
46
|
+
self.sink.append(source);
|
|
47
|
+
self.last_path = Some(path.to_string());
|
|
48
|
+
} else {
|
|
49
|
+
eprintln!("⚠️ Skipping playback: failed to load '{}'", path);
|
|
50
|
+
}
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
pub fn replay_last(&mut self) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
|
-
|
|
3
2
|
use crate::{
|
|
4
3
|
core::{
|
|
5
4
|
audio::{ engine::AudioEngine, interpreter::driver::run_audio_program },
|
|
@@ -18,36 +17,36 @@ pub fn render_audio_with_modules(
|
|
|
18
17
|
|
|
19
18
|
for (module_name, statements) in modules {
|
|
20
19
|
let mut global_max_end_time: f32 = 0.0;
|
|
21
|
-
let mut
|
|
20
|
+
let mut audio_engine = AudioEngine::new(module_name.clone());
|
|
22
21
|
|
|
23
22
|
// Apply global variables to the initial engine
|
|
24
23
|
if let Some(module) = global_store.get_module(&module_name) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
// Verify if the buffer is silent (all samples are zero)
|
|
37
|
-
if updated_engine.buffer.iter().all(|&s| s == 0) {
|
|
38
|
-
let logger = Logger::new();
|
|
39
|
-
logger.log_message(
|
|
40
|
-
LogLevel::Warning,
|
|
41
|
-
&format!("Module '{}' ignored: silent buffer (no non-zero samples)", module_name)
|
|
24
|
+
// interprete statements to fill the audio buffer
|
|
25
|
+
let (module_max_end_time, cursor_time) = run_audio_program(
|
|
26
|
+
&statements,
|
|
27
|
+
&mut audio_engine,
|
|
28
|
+
module_name.clone(),
|
|
29
|
+
output_dir.to_string(),
|
|
30
|
+
module.variable_table.clone(),
|
|
31
|
+
module.function_table.clone(),
|
|
32
|
+
global_store
|
|
42
33
|
);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
34
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
35
|
+
// Verify if the buffer is silent (all samples are zero)
|
|
36
|
+
if audio_engine.buffer.iter().all(|&s| s == 0) {
|
|
37
|
+
let logger = Logger::new();
|
|
38
|
+
logger.log_message(
|
|
39
|
+
LogLevel::Warning,
|
|
40
|
+
&format!("Module '{}' ignored: silent buffer (no non-zero samples)", module_name)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Determines the maximum end time for the module
|
|
45
|
+
global_max_end_time = global_max_end_time.max(module_max_end_time);
|
|
46
|
+
audio_engine.set_duration(global_max_end_time);
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
result.insert(module_name, audio_engine);
|
|
49
|
+
}
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
result
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
pub mod preprocessor;
|
|
2
2
|
pub mod lexer;
|
|
3
3
|
pub mod store;
|
|
4
|
+
pub mod module;
|
|
4
5
|
|
|
5
6
|
use std::io::Write;
|
|
6
7
|
|
|
@@ -13,6 +14,7 @@ impl Debugger {
|
|
|
13
14
|
|
|
14
15
|
pub fn write_log_file(&self, path: &str, filename: &str, content: &str) {
|
|
15
16
|
std::fs::create_dir_all(path).expect("Failed to create directory");
|
|
17
|
+
|
|
16
18
|
let file_path = format!("{}/{}", path, filename);
|
|
17
19
|
let mut file = std::fs::File::create(file_path).expect("Failed to create file");
|
|
18
20
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
use std::{ fs::create_dir_all };
|
|
2
|
+
use crate::core::{
|
|
3
|
+
debugger::Debugger,
|
|
4
|
+
store::{ function::FunctionTable, variable::VariableTable },
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
pub fn write_module_variable_log_file(
|
|
8
|
+
output_dir: &str,
|
|
9
|
+
module_path: &str,
|
|
10
|
+
variable_table: &VariableTable
|
|
11
|
+
) {
|
|
12
|
+
let debugger = Debugger::new();
|
|
13
|
+
let mut content = String::new();
|
|
14
|
+
let module_name = module_path.split('/').last().unwrap_or("index").replace(".deva", "");
|
|
15
|
+
|
|
16
|
+
let log_directory = format!("{}/logs/modules/{}", output_dir, module_name);
|
|
17
|
+
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
18
|
+
|
|
19
|
+
for (var_name, var_data) in &variable_table.variables {
|
|
20
|
+
content.push_str(&format!("{:?} = {:?}\n", var_name, var_data));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
content.push_str("\n");
|
|
24
|
+
|
|
25
|
+
debugger.write_log_file(&log_directory, "variables.log", &content);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
pub fn write_module_function_log_file(
|
|
29
|
+
output_dir: &str,
|
|
30
|
+
module_path: &str,
|
|
31
|
+
function_table: &FunctionTable
|
|
32
|
+
) {
|
|
33
|
+
let debugger = Debugger::new();
|
|
34
|
+
let mut content = String::new();
|
|
35
|
+
let module_name = module_path.split('/').last().unwrap_or("index").replace(".deva", "");
|
|
36
|
+
|
|
37
|
+
let log_directory = format!("{}/logs/modules/{}", output_dir, module_name);
|
|
38
|
+
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
39
|
+
|
|
40
|
+
for (func_name, func_data) in &function_table.functions {
|
|
41
|
+
content.push_str(&format!("{:?} = {:?}\n", func_name, func_data));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
content.push_str("\n");
|
|
45
|
+
|
|
46
|
+
debugger.write_log_file(&log_directory, "functions.log", &content);
|
|
47
|
+
}
|
|
@@ -1,25 +1,39 @@
|
|
|
1
|
-
use std::{
|
|
2
|
-
use crate::core::{
|
|
1
|
+
use std::{ fs::create_dir_all };
|
|
2
|
+
use crate::core::{
|
|
3
|
+
debugger::Debugger,
|
|
4
|
+
store::{ function::FunctionTable, variable::VariableTable },
|
|
5
|
+
};
|
|
3
6
|
|
|
4
|
-
pub fn
|
|
7
|
+
pub fn write_variables_log_file(output_dir: &str, file_name: &str, variables: VariableTable) {
|
|
5
8
|
let debugger = Debugger::new();
|
|
6
9
|
let mut content = String::new();
|
|
7
10
|
|
|
8
11
|
let log_directory = format!("{}/logs", output_dir);
|
|
9
12
|
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
10
13
|
|
|
11
|
-
for (
|
|
12
|
-
content.push_str(&format!("
|
|
14
|
+
for (var_name, var_data) in variables.variables {
|
|
15
|
+
content.push_str(&format!("{:?} = {:?}\n", var_name, var_data));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
content.push_str("\n");
|
|
19
|
+
|
|
20
|
+
debugger.write_log_file(&log_directory, file_name, &content);
|
|
21
|
+
}
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
pub fn write_function_log_file(output_dir: &str, file_name: &str, functions: FunctionTable) {
|
|
24
|
+
let debugger = Debugger::new();
|
|
25
|
+
let mut content = String::new();
|
|
17
26
|
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
let log_directory = format!("{}/logs", output_dir);
|
|
28
|
+
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
for (index, function) in functions.functions {
|
|
31
|
+
content.push_str(
|
|
32
|
+
&format!("'{}' = [{:?}] => {:?}\n", function.name, function.parameters, function.body)
|
|
33
|
+
);
|
|
22
34
|
}
|
|
23
35
|
|
|
36
|
+
content.push_str("\n");
|
|
37
|
+
|
|
24
38
|
debugger.write_log_file(&log_directory, file_name, &content);
|
|
25
39
|
}
|
package/rust/core/error/mod.rs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
use crate::core::lexer::{
|
|
2
2
|
handler::{
|
|
3
|
-
arrow::handle_arrow_lexer, at::handle_at_lexer, brace::{ handle_lbrace_lexer, handle_rbrace_lexer }, colon::handle_colon_lexer, comment::handle_comment_lexer, dot::handle_dot_lexer, identifier::handle_identifier_lexer, indent::handle_indent_lexer, newline::handle_newline_lexer, number::handle_number_lexer, operator::handle_operator_lexer, slash::handle_slash_lexer, string::handle_string_lexer
|
|
3
|
+
arrow::handle_arrow_lexer, at::handle_at_lexer, brace::{ handle_lbrace_lexer, handle_rbrace_lexer }, colon::handle_colon_lexer, comment::handle_comment_lexer, dot::handle_dot_lexer, identifier::handle_identifier_lexer, indent::handle_indent_lexer, newline::handle_newline_lexer, number::handle_number_lexer, operator::handle_operator_lexer, parenthesis::{handle_lparen_lexer, handle_rparen_lexer}, slash::handle_slash_lexer, string::handle_string_lexer
|
|
4
4
|
},
|
|
5
5
|
token::{ Token, TokenKind },
|
|
6
6
|
};
|
|
@@ -145,6 +145,28 @@ pub fn handle_content_lexing(content: String) -> Result<Vec<Token>, String> {
|
|
|
145
145
|
&mut column
|
|
146
146
|
);
|
|
147
147
|
}
|
|
148
|
+
'(' => {
|
|
149
|
+
handle_lparen_lexer(
|
|
150
|
+
ch,
|
|
151
|
+
&mut chars,
|
|
152
|
+
&mut current_indent,
|
|
153
|
+
&mut indent_stack,
|
|
154
|
+
&mut tokens,
|
|
155
|
+
&mut line,
|
|
156
|
+
&mut column
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
')' => {
|
|
160
|
+
handle_rparen_lexer(
|
|
161
|
+
ch,
|
|
162
|
+
&mut chars,
|
|
163
|
+
&mut current_indent,
|
|
164
|
+
&mut indent_stack,
|
|
165
|
+
&mut tokens,
|
|
166
|
+
&mut line,
|
|
167
|
+
&mut column
|
|
168
|
+
);
|
|
169
|
+
}
|
|
148
170
|
'.' => {
|
|
149
171
|
handle_dot_lexer(
|
|
150
172
|
ch,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
use crate::core::lexer::token::{ Token, TokenKind };
|
|
2
|
+
|
|
3
|
+
pub fn handle_rparen_lexer(
|
|
4
|
+
char: char,
|
|
5
|
+
chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
6
|
+
current_indent: &mut usize,
|
|
7
|
+
indent_stack: &mut Vec<usize>,
|
|
8
|
+
tokens: &mut Vec<Token>,
|
|
9
|
+
line: &mut usize,
|
|
10
|
+
column: &mut usize
|
|
11
|
+
) {
|
|
12
|
+
tokens.push(Token {
|
|
13
|
+
kind: TokenKind::RParen,
|
|
14
|
+
lexeme: char.to_string(),
|
|
15
|
+
line: *line,
|
|
16
|
+
column: *column,
|
|
17
|
+
indent: *current_indent,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
*column += 1;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
pub fn handle_lparen_lexer(
|
|
24
|
+
char: char,
|
|
25
|
+
chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
26
|
+
current_indent: &mut usize,
|
|
27
|
+
indent_stack: &mut Vec<usize>,
|
|
28
|
+
tokens: &mut Vec<Token>,
|
|
29
|
+
line: &mut usize,
|
|
30
|
+
column: &mut usize
|
|
31
|
+
) {
|
|
32
|
+
tokens.push(Token {
|
|
33
|
+
kind: TokenKind::LParen,
|
|
34
|
+
lexeme: char.to_string(),
|
|
35
|
+
line: *line,
|
|
36
|
+
column: *column,
|
|
37
|
+
indent: *current_indent,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
*column += 1;
|
|
41
|
+
}
|