@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.
- package/Cargo.toml +1 -1
- package/README.md +31 -16
- package/docs/CHANGELOG.md +49 -2
- package/docs/ROADMAP.md +2 -2
- package/docs/SYNTAX.md +41 -7
- package/docs/TODO.md +3 -3
- package/examples/condition.deva +20 -0
- package/examples/group.deva +3 -3
- package/examples/index.deva +9 -8
- package/examples/loop.deva +10 -8
- package/examples/synth.deva +14 -0
- package/examples/variables.deva +2 -2
- package/out-tsc/bin/devalang.exe +0 -0
- package/out-tsc/scripts/version/fetch.js +1 -5
- package/package.json +1 -1
- package/project-version.json +3 -3
- package/rust/cli/build.rs +6 -1
- package/rust/core/audio/engine.rs +89 -12
- package/rust/core/audio/evaluator.rs +31 -0
- package/rust/core/audio/interpreter/arrow_call.rs +129 -0
- package/rust/core/audio/interpreter/call.rs +64 -0
- package/rust/core/audio/interpreter/condition.rs +69 -0
- package/rust/core/audio/interpreter/driver.rs +216 -0
- package/rust/core/audio/interpreter/let_.rs +19 -0
- package/rust/core/audio/interpreter/load.rs +18 -0
- package/rust/core/audio/interpreter/loop_.rs +67 -0
- package/rust/core/audio/interpreter/mod.rs +12 -0
- package/rust/core/audio/interpreter/sleep.rs +36 -0
- package/rust/core/audio/interpreter/spawn.rs +66 -0
- package/rust/core/audio/interpreter/tempo.rs +16 -0
- package/rust/core/audio/interpreter/trigger.rs +69 -0
- package/rust/core/audio/loader/mod.rs +1 -0
- package/rust/core/audio/{loader.rs → loader/trigger.rs} +3 -1
- package/rust/core/audio/mod.rs +2 -1
- package/rust/core/audio/renderer.rs +54 -0
- package/rust/core/builder/mod.rs +1 -1
- package/rust/core/debugger/lexer.rs +1 -1
- package/rust/core/debugger/mod.rs +1 -0
- package/rust/core/debugger/store.rs +25 -0
- package/rust/core/error/mod.rs +1 -1
- package/rust/core/lexer/handler/arrow.rs +31 -0
- package/rust/core/lexer/handler/driver.rs +226 -0
- package/rust/core/lexer/handler/identifier.rs +3 -0
- package/rust/core/lexer/handler/mod.rs +4 -227
- package/rust/core/lexer/handler/operator.rs +44 -0
- package/rust/core/lexer/mod.rs +25 -4
- package/rust/core/lexer/token.rs +40 -9
- package/rust/core/parser/driver.rs +331 -0
- package/rust/core/parser/handler/arrow_call.rs +126 -0
- package/rust/core/parser/handler/at.rs +3 -7
- package/rust/core/parser/handler/bank.rs +5 -2
- package/rust/core/parser/handler/condition.rs +74 -0
- package/rust/core/parser/handler/dot.rs +1 -1
- package/rust/core/parser/handler/identifier/call.rs +41 -0
- package/rust/core/parser/handler/identifier/group.rs +75 -0
- package/rust/core/parser/handler/identifier/let_.rs +133 -0
- package/rust/core/parser/handler/identifier/mod.rs +51 -0
- package/rust/core/parser/handler/identifier/sleep.rs +33 -0
- package/rust/core/parser/handler/identifier/spawn.rs +41 -0
- package/rust/core/parser/handler/identifier/synth.rs +65 -0
- package/rust/core/parser/handler/loop_.rs +25 -19
- package/rust/core/parser/handler/mod.rs +3 -1
- package/rust/core/parser/handler/tempo.rs +1 -1
- package/rust/core/parser/mod.rs +3 -237
- package/rust/core/parser/statement.rs +36 -35
- package/rust/core/preprocessor/loader.rs +64 -49
- package/rust/core/preprocessor/module.rs +3 -6
- package/rust/core/preprocessor/processor.rs +13 -4
- package/rust/core/preprocessor/resolver/call.rs +123 -0
- package/rust/core/preprocessor/resolver/condition.rs +92 -0
- package/rust/core/preprocessor/resolver/driver.rs +227 -0
- package/rust/core/preprocessor/resolver/group.rs +35 -87
- package/rust/core/preprocessor/resolver/let_.rs +31 -0
- package/rust/core/preprocessor/resolver/loop_.rs +62 -116
- package/rust/core/preprocessor/resolver/mod.rs +9 -153
- package/rust/core/preprocessor/resolver/spawn.rs +58 -0
- package/rust/core/preprocessor/resolver/synth.rs +50 -0
- package/rust/core/preprocessor/resolver/trigger.rs +51 -50
- package/rust/core/preprocessor/resolver/value.rs +78 -0
- package/rust/core/utils/path.rs +17 -32
- package/rust/core/utils/validation.rs +30 -28
- package/typescript/scripts/version/fetch.ts +1 -6
- package/rust/core/audio/interpreter.rs +0 -317
- package/rust/core/audio/render.rs +0 -53
- package/rust/core/lexer/handler/equal.rs +0 -32
- package/rust/core/parser/handler/identifier.rs +0 -260
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
audio::{ engine::AudioEngine, interpreter::driver::execute_audio_block },
|
|
3
|
+
parser::statement::Statement,
|
|
4
|
+
shared::value::Value,
|
|
5
|
+
store::variable::VariableTable,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub fn interprete_spawn_statement(
|
|
9
|
+
stmt: &Statement,
|
|
10
|
+
audio_engine: AudioEngine,
|
|
11
|
+
variable_table: &VariableTable,
|
|
12
|
+
base_bpm: f32,
|
|
13
|
+
base_duration: f32,
|
|
14
|
+
cursor_time: f32,
|
|
15
|
+
max_end_time: f32
|
|
16
|
+
) -> Option<(f32, f32, AudioEngine)> {
|
|
17
|
+
match &stmt.value {
|
|
18
|
+
Value::String(identifier) | Value::Identifier(identifier) => {
|
|
19
|
+
handle_spawn_identifier(
|
|
20
|
+
identifier,
|
|
21
|
+
audio_engine,
|
|
22
|
+
variable_table,
|
|
23
|
+
base_bpm,
|
|
24
|
+
base_duration,
|
|
25
|
+
cursor_time,
|
|
26
|
+
max_end_time
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_ => {
|
|
31
|
+
eprintln!("❌ Invalid spawn statement: expected identifier, found {:?}", stmt.value);
|
|
32
|
+
None
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fn handle_spawn_identifier(
|
|
38
|
+
identifier: &str,
|
|
39
|
+
audio_engine: AudioEngine,
|
|
40
|
+
variable_table: &VariableTable,
|
|
41
|
+
base_bpm: f32,
|
|
42
|
+
base_duration: f32,
|
|
43
|
+
cursor_time: f32,
|
|
44
|
+
max_end_time: f32
|
|
45
|
+
) -> Option<(f32, f32, AudioEngine)> {
|
|
46
|
+
if let Some(Value::Map(map)) = variable_table.get(identifier) {
|
|
47
|
+
if let Some(Value::Block(block)) = map.get("body") {
|
|
48
|
+
let (eng, _, end_time) = execute_audio_block(
|
|
49
|
+
audio_engine.clone(),
|
|
50
|
+
variable_table.clone(),
|
|
51
|
+
block.clone(),
|
|
52
|
+
base_bpm,
|
|
53
|
+
base_duration,
|
|
54
|
+
max_end_time,
|
|
55
|
+
cursor_time
|
|
56
|
+
);
|
|
57
|
+
return Some((max_end_time.max(end_time), end_time, eng));
|
|
58
|
+
} else {
|
|
59
|
+
eprintln!("❌ Spawn group '{}' has no 'body' block", identifier);
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
eprintln!("❌ Spawn group '{}' not found or not a map", identifier);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
None
|
|
66
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
use crate::core::{ parser::statement::{ Statement, StatementKind }, shared::value::Value };
|
|
2
|
+
|
|
3
|
+
pub fn interprete_tempo_statement(stmt: &Statement) -> Option<(f32, f32)> {
|
|
4
|
+
if let StatementKind::Tempo = &stmt.kind {
|
|
5
|
+
if let Value::Number(bpm) = &stmt.value {
|
|
6
|
+
let bpm = *bpm as f32;
|
|
7
|
+
let duration = 60.0 / bpm;
|
|
8
|
+
|
|
9
|
+
return Some((bpm, duration));
|
|
10
|
+
} else {
|
|
11
|
+
eprintln!("❌ Invalid tempo value: {:?}", stmt.value);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
None
|
|
16
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
audio::{ engine::AudioEngine, loader::trigger::load_trigger },
|
|
3
|
+
parser::statement::{ Statement, StatementKind },
|
|
4
|
+
shared::{ duration::Duration, value::Value },
|
|
5
|
+
store::variable::VariableTable,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub fn interprete_trigger_statement(
|
|
9
|
+
stmt: &Statement,
|
|
10
|
+
audio_engine: &mut AudioEngine,
|
|
11
|
+
variable_table: &VariableTable,
|
|
12
|
+
base_duration: f32,
|
|
13
|
+
cursor_time: f32,
|
|
14
|
+
max_end_time: f32
|
|
15
|
+
) -> Option<(f32, f32, AudioEngine)> {
|
|
16
|
+
if let StatementKind::Trigger { entity, duration } = &stmt.kind {
|
|
17
|
+
if let Some(trigger_val) = variable_table.get(entity) {
|
|
18
|
+
let duration_secs = match duration {
|
|
19
|
+
Duration::Number(n) => *n,
|
|
20
|
+
|
|
21
|
+
Duration::Identifier(id) => {
|
|
22
|
+
if id == "auto" {
|
|
23
|
+
1.0
|
|
24
|
+
} else {
|
|
25
|
+
match variable_table.get(id) {
|
|
26
|
+
Some(Value::Number(n)) => *n,
|
|
27
|
+
Some(Value::Identifier(other)) if other == "auto" => 1.0,
|
|
28
|
+
Some(other) => {
|
|
29
|
+
eprintln!(
|
|
30
|
+
"❌ Invalid duration reference '{}': expected number, got {:?}",
|
|
31
|
+
id,
|
|
32
|
+
other
|
|
33
|
+
);
|
|
34
|
+
return None;
|
|
35
|
+
}
|
|
36
|
+
None => {
|
|
37
|
+
eprintln!("❌ Duration identifier '{}' not found", id);
|
|
38
|
+
return None;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
Duration::Auto => 1.0,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
let duration_final = duration_secs * base_duration;
|
|
48
|
+
|
|
49
|
+
let (src, _) = load_trigger(
|
|
50
|
+
trigger_val,
|
|
51
|
+
duration,
|
|
52
|
+
base_duration,
|
|
53
|
+
variable_table.clone()
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
let mut updated_engine = audio_engine.clone();
|
|
57
|
+
updated_engine.insert_sample(&src, cursor_time, duration_final, None);
|
|
58
|
+
|
|
59
|
+
let new_cursor_time = cursor_time + duration_final;
|
|
60
|
+
let new_max_end_time = new_cursor_time.max(max_end_time);
|
|
61
|
+
|
|
62
|
+
return Some((new_cursor_time, new_max_end_time, updated_engine));
|
|
63
|
+
} else {
|
|
64
|
+
eprintln!("❌ Unknown trigger entity: {}", entity);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
None
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pub mod trigger;
|
|
@@ -26,7 +26,9 @@ pub fn load_trigger(
|
|
|
26
26
|
duration_as_secs = *num;
|
|
27
27
|
} else if let Some(Value::String(num_str)) = variable_table.get(duration_identifier) {
|
|
28
28
|
duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
|
|
29
|
-
} else if
|
|
29
|
+
} else if
|
|
30
|
+
let Some(Value::Identifier(num_str)) = variable_table.get(duration_identifier)
|
|
31
|
+
{
|
|
30
32
|
duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
|
|
31
33
|
} else {
|
|
32
34
|
eprintln!("❌ Invalid duration identifier: {}", duration_identifier);
|
package/rust/core/audio/mod.rs
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
|
|
3
|
+
use crate::{
|
|
4
|
+
core::{
|
|
5
|
+
audio::{ engine::AudioEngine, interpreter::driver::run_audio_program },
|
|
6
|
+
parser::statement::Statement,
|
|
7
|
+
store::global::GlobalStore,
|
|
8
|
+
},
|
|
9
|
+
utils::logger::{ LogLevel, Logger },
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
pub fn render_audio_with_modules(
|
|
13
|
+
modules: HashMap<String, Vec<Statement>>,
|
|
14
|
+
output_dir: &str,
|
|
15
|
+
global_store: &mut GlobalStore
|
|
16
|
+
) -> HashMap<String, AudioEngine> {
|
|
17
|
+
let mut result = HashMap::new();
|
|
18
|
+
|
|
19
|
+
for (module_name, statements) in modules {
|
|
20
|
+
let mut global_max_end_time: f32 = 0.0;
|
|
21
|
+
let mut initial_engine = AudioEngine::new(module_name.clone());
|
|
22
|
+
|
|
23
|
+
// Apply global variables to the initial engine
|
|
24
|
+
if let Some(module) = global_store.get_module(&module_name) {
|
|
25
|
+
initial_engine.set_variables(module.variable_table.clone());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// interprete statements to fill the audio buffer
|
|
29
|
+
let (mut updated_engine, _bpm, module_max_end_time) = run_audio_program(
|
|
30
|
+
&statements,
|
|
31
|
+
initial_engine,
|
|
32
|
+
module_name.clone(),
|
|
33
|
+
output_dir.to_string()
|
|
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)
|
|
42
|
+
);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Determines the maximum end time for the module
|
|
47
|
+
global_max_end_time = global_max_end_time.max(module_max_end_time);
|
|
48
|
+
updated_engine.set_duration(global_max_end_time);
|
|
49
|
+
|
|
50
|
+
result.insert(module_name, updated_engine);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
result
|
|
54
|
+
}
|
package/rust/core/builder/mod.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use crate::core::audio::
|
|
1
|
+
use crate::core::audio::renderer::render_audio_with_modules;
|
|
2
2
|
use crate::core::parser::statement::Statement;
|
|
3
3
|
use crate::core::store::global::GlobalStore;
|
|
4
4
|
use std::{ collections::HashMap, fs::create_dir_all };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
use std::{ collections::HashMap, fs::create_dir_all };
|
|
2
|
+
use crate::core::{ debugger::Debugger, preprocessor::module::Module };
|
|
3
|
+
|
|
4
|
+
pub fn write_store_log_file(output_dir: &str, file_name: &str, modules: HashMap<String, Module>) {
|
|
5
|
+
let debugger = Debugger::new();
|
|
6
|
+
let mut content = String::new();
|
|
7
|
+
|
|
8
|
+
let log_directory = format!("{}/logs", output_dir);
|
|
9
|
+
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
10
|
+
|
|
11
|
+
for (path, module) in modules {
|
|
12
|
+
content.push_str(&format!("--- Module: {} ---\n", path));
|
|
13
|
+
|
|
14
|
+
for (index, var_value) in module.variable_table.variables.iter().enumerate() {
|
|
15
|
+
let var_name = var_value.0.clone();
|
|
16
|
+
let var_data = var_value.1;
|
|
17
|
+
|
|
18
|
+
content.push_str(&format!("{}: {:?} = {:?}\n", index + 1, var_name, var_data));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
content.push_str("\n");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
debugger.write_log_file(&log_directory, file_name, &content);
|
|
25
|
+
}
|
package/rust/core/error/mod.rs
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
use crate::core::lexer::token::{ Token, TokenKind };
|
|
2
|
+
|
|
3
|
+
pub fn handle_arrow_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
|
+
let mut arrow_call = char.to_string();
|
|
13
|
+
|
|
14
|
+
while let Some(&c) = chars.peek() {
|
|
15
|
+
if c == '>' {
|
|
16
|
+
chars.next();
|
|
17
|
+
arrow_call.push(c);
|
|
18
|
+
*column += 1;
|
|
19
|
+
} else {
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
tokens.push(Token {
|
|
25
|
+
kind: TokenKind::Arrow,
|
|
26
|
+
lexeme: arrow_call,
|
|
27
|
+
line: *line,
|
|
28
|
+
column: *column,
|
|
29
|
+
indent: *current_indent,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
use crate::core::lexer::{
|
|
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, string::handle_string_lexer
|
|
4
|
+
},
|
|
5
|
+
token::{ Token, TokenKind },
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
fn advance_char<I: Iterator<Item = char>>(
|
|
9
|
+
chars: &mut std::iter::Peekable<I>,
|
|
10
|
+
line: &mut usize,
|
|
11
|
+
column: &mut usize
|
|
12
|
+
) -> Option<char> {
|
|
13
|
+
let c = chars.next()?;
|
|
14
|
+
if c == '\n' {
|
|
15
|
+
// Do not increment column on newline here
|
|
16
|
+
} else {
|
|
17
|
+
*column += 1;
|
|
18
|
+
}
|
|
19
|
+
Some(c)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pub fn handle_content_lexing(content: String) -> Result<Vec<Token>, String> {
|
|
23
|
+
let mut tokens = Vec::new();
|
|
24
|
+
|
|
25
|
+
let mut line = 1;
|
|
26
|
+
let mut column = 1;
|
|
27
|
+
|
|
28
|
+
let mut indent_stack: Vec<usize> = vec![0];
|
|
29
|
+
let mut current_indent = 0;
|
|
30
|
+
let mut at_line_start = true;
|
|
31
|
+
|
|
32
|
+
let mut chars = content.chars().peekable();
|
|
33
|
+
|
|
34
|
+
while chars.peek().is_some() {
|
|
35
|
+
if at_line_start {
|
|
36
|
+
handle_indent_lexer(
|
|
37
|
+
&mut chars,
|
|
38
|
+
&mut current_indent,
|
|
39
|
+
&mut indent_stack,
|
|
40
|
+
&mut tokens,
|
|
41
|
+
&mut line,
|
|
42
|
+
&mut column
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
at_line_start = false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let Some(ch) = advance_char(&mut chars, &mut line, &mut column) else {
|
|
49
|
+
break;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
match ch {
|
|
53
|
+
'\n' => {
|
|
54
|
+
handle_newline_lexer(
|
|
55
|
+
ch,
|
|
56
|
+
&mut chars,
|
|
57
|
+
&mut tokens,
|
|
58
|
+
&mut line,
|
|
59
|
+
&mut column,
|
|
60
|
+
&mut at_line_start,
|
|
61
|
+
&mut current_indent
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
' ' | '\t' => {
|
|
65
|
+
// Already handled by indent_lexer
|
|
66
|
+
}
|
|
67
|
+
'#' => {
|
|
68
|
+
handle_comment_lexer(
|
|
69
|
+
ch,
|
|
70
|
+
&mut chars,
|
|
71
|
+
&mut current_indent,
|
|
72
|
+
&mut indent_stack,
|
|
73
|
+
&mut tokens,
|
|
74
|
+
&mut line,
|
|
75
|
+
&mut column
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
':' => {
|
|
79
|
+
handle_colon_lexer(
|
|
80
|
+
ch,
|
|
81
|
+
&mut chars,
|
|
82
|
+
&mut current_indent,
|
|
83
|
+
&mut indent_stack,
|
|
84
|
+
&mut tokens,
|
|
85
|
+
&mut line,
|
|
86
|
+
&mut column
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
'=' | '!' | '<' | '>' => {
|
|
90
|
+
handle_operator_lexer(
|
|
91
|
+
ch,
|
|
92
|
+
&mut chars,
|
|
93
|
+
&mut current_indent,
|
|
94
|
+
&mut indent_stack,
|
|
95
|
+
&mut tokens,
|
|
96
|
+
&mut line,
|
|
97
|
+
&mut column
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
'-' => {
|
|
101
|
+
handle_arrow_lexer(
|
|
102
|
+
ch,
|
|
103
|
+
&mut chars,
|
|
104
|
+
&mut current_indent,
|
|
105
|
+
&mut indent_stack,
|
|
106
|
+
&mut tokens,
|
|
107
|
+
&mut line,
|
|
108
|
+
&mut column
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
'{' => {
|
|
112
|
+
handle_lbrace_lexer(
|
|
113
|
+
ch,
|
|
114
|
+
&mut chars,
|
|
115
|
+
&mut current_indent,
|
|
116
|
+
&mut indent_stack,
|
|
117
|
+
&mut tokens,
|
|
118
|
+
&mut line,
|
|
119
|
+
&mut column
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
'}' => {
|
|
123
|
+
handle_rbrace_lexer(
|
|
124
|
+
ch,
|
|
125
|
+
&mut chars,
|
|
126
|
+
&mut current_indent,
|
|
127
|
+
&mut indent_stack,
|
|
128
|
+
&mut tokens,
|
|
129
|
+
&mut line,
|
|
130
|
+
&mut column
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
'.' => {
|
|
134
|
+
handle_dot_lexer(
|
|
135
|
+
ch,
|
|
136
|
+
&mut chars,
|
|
137
|
+
&mut current_indent,
|
|
138
|
+
&mut indent_stack,
|
|
139
|
+
&mut tokens,
|
|
140
|
+
&mut line,
|
|
141
|
+
&mut column
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
'@' => {
|
|
145
|
+
handle_at_lexer(
|
|
146
|
+
ch,
|
|
147
|
+
&mut chars,
|
|
148
|
+
&mut current_indent,
|
|
149
|
+
&mut indent_stack,
|
|
150
|
+
&mut tokens,
|
|
151
|
+
&mut line,
|
|
152
|
+
&mut column
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
'\"' | '\'' => {
|
|
156
|
+
handle_string_lexer(
|
|
157
|
+
ch,
|
|
158
|
+
&mut chars,
|
|
159
|
+
&mut current_indent,
|
|
160
|
+
&mut indent_stack,
|
|
161
|
+
&mut tokens,
|
|
162
|
+
&mut line,
|
|
163
|
+
&mut column
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
c if c.is_ascii_digit() => {
|
|
167
|
+
handle_number_lexer(
|
|
168
|
+
c,
|
|
169
|
+
&mut chars,
|
|
170
|
+
&mut current_indent,
|
|
171
|
+
&mut indent_stack,
|
|
172
|
+
&mut tokens,
|
|
173
|
+
&mut line,
|
|
174
|
+
&mut column
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
c if c.is_ascii_alphabetic() => {
|
|
178
|
+
handle_identifier_lexer(
|
|
179
|
+
c,
|
|
180
|
+
&mut chars,
|
|
181
|
+
&mut current_indent,
|
|
182
|
+
&mut indent_stack,
|
|
183
|
+
&mut tokens,
|
|
184
|
+
&mut line,
|
|
185
|
+
&mut column
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
_ => {
|
|
189
|
+
// Ignore unknown char
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
while indent_stack.len() > 1 {
|
|
195
|
+
indent_stack.pop();
|
|
196
|
+
current_indent = *indent_stack.last().unwrap();
|
|
197
|
+
tokens.push(Token {
|
|
198
|
+
kind: TokenKind::Dedent,
|
|
199
|
+
lexeme: String::new(),
|
|
200
|
+
line,
|
|
201
|
+
column,
|
|
202
|
+
indent: current_indent,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
tokens.push(Token {
|
|
207
|
+
kind: TokenKind::EOF,
|
|
208
|
+
lexeme: String::new(),
|
|
209
|
+
line: line + 1,
|
|
210
|
+
column: 0,
|
|
211
|
+
indent: 0,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// NOTE: Debug only
|
|
215
|
+
// for token in &tokens {
|
|
216
|
+
// println!(
|
|
217
|
+
// "{:?} @ line {}, col {}, indent {}",
|
|
218
|
+
// token.kind,
|
|
219
|
+
// token.line,
|
|
220
|
+
// token.column,
|
|
221
|
+
// token.indent
|
|
222
|
+
// );
|
|
223
|
+
// }
|
|
224
|
+
|
|
225
|
+
Ok(tokens)
|
|
226
|
+
}
|
|
@@ -22,9 +22,12 @@ pub fn handle_identifier_lexer(
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
let kind = match ident.as_str() {
|
|
25
|
+
"if" => TokenKind::If,
|
|
26
|
+
"else" => TokenKind::Else,
|
|
25
27
|
"bank" => TokenKind::Bank,
|
|
26
28
|
"bpm" => TokenKind::Tempo,
|
|
27
29
|
"loop" => TokenKind::Loop,
|
|
30
|
+
"synth" => TokenKind::Synth,
|
|
28
31
|
_ => TokenKind::Identifier,
|
|
29
32
|
};
|
|
30
33
|
|