@devaloop/devalang 0.0.1-alpha.14 → 0.0.1-alpha.15
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/.devalang +8 -8
- package/Cargo.toml +2 -2
- package/README.md +31 -14
- package/docs/CHANGELOG.md +59 -0
- package/docs/ROADMAP.md +1 -1
- package/examples/automation.deva +44 -0
- package/examples/index.deva +41 -25
- package/examples/plugin.deva +15 -0
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +1 -1
- package/project-version.json +3 -3
- package/rust/cli/bank.rs +14 -15
- package/rust/cli/check.rs +1 -1
- package/rust/cli/play.rs +1 -1
- package/rust/cli/update.rs +1 -1
- package/rust/common/api.rs +3 -6
- package/rust/common/cdn.rs +3 -6
- package/rust/common/sso.rs +3 -6
- package/rust/core/audio/engine.rs +215 -74
- package/rust/core/audio/evaluator.rs +101 -0
- package/rust/core/audio/interpreter/arrow_call.rs +27 -1
- package/rust/core/audio/interpreter/automate.rs +18 -0
- package/rust/core/audio/interpreter/call.rs +2 -2
- package/rust/core/audio/interpreter/condition.rs +3 -3
- package/rust/core/audio/interpreter/driver.rs +49 -16
- package/rust/core/audio/interpreter/let_.rs +14 -7
- package/rust/core/audio/interpreter/loop_.rs +39 -6
- package/rust/core/audio/interpreter/mod.rs +2 -1
- package/rust/core/audio/interpreter/sleep.rs +2 -4
- package/rust/core/audio/interpreter/spawn.rs +2 -2
- package/rust/core/audio/loader/trigger.rs +2 -5
- package/rust/core/audio/mod.rs +2 -1
- package/rust/core/audio/renderer.rs +1 -1
- package/rust/core/audio/special/easing.rs +120 -0
- package/rust/core/audio/special/env.rs +41 -0
- package/rust/core/audio/special/math.rs +92 -0
- package/rust/core/audio/special/mod.rs +9 -0
- package/rust/core/audio/special/modulator.rs +120 -0
- package/rust/core/debugger/store.rs +1 -1
- package/rust/core/error/mod.rs +4 -1
- package/rust/core/lexer/handler/arrow.rs +60 -9
- package/rust/core/lexer/handler/at.rs +4 -4
- package/rust/core/lexer/handler/brace.rs +8 -8
- package/rust/core/lexer/handler/colon.rs +4 -4
- package/rust/core/lexer/handler/comment.rs +2 -2
- package/rust/core/lexer/handler/dot.rs +4 -4
- package/rust/core/lexer/handler/driver.rs +42 -13
- package/rust/core/lexer/handler/identifier.rs +5 -4
- package/rust/core/lexer/handler/newline.rs +1 -1
- package/rust/core/lexer/handler/number.rs +3 -3
- package/rust/core/lexer/handler/operator.rs +3 -1
- package/rust/core/lexer/handler/parenthesis.rs +8 -8
- package/rust/core/lexer/handler/slash.rs +5 -5
- package/rust/core/lexer/handler/string.rs +1 -1
- package/rust/core/lexer/mod.rs +1 -1
- package/rust/core/lexer/token.rs +3 -0
- package/rust/core/parser/driver.rs +94 -12
- package/rust/core/parser/handler/arrow_call.rs +105 -89
- package/rust/core/parser/handler/at.rs +1 -1
- package/rust/core/parser/handler/dot.rs +3 -3
- package/rust/core/parser/handler/identifier/automate.rs +194 -0
- package/rust/core/parser/handler/identifier/function.rs +2 -3
- package/rust/core/parser/handler/identifier/let_.rs +16 -0
- package/rust/core/parser/handler/identifier/mod.rs +14 -10
- package/rust/core/parser/handler/identifier/print.rs +29 -0
- package/rust/core/parser/handler/identifier/sleep.rs +1 -1
- package/rust/core/parser/handler/identifier/synth.rs +7 -9
- package/rust/core/parser/handler/loop_.rs +60 -43
- package/rust/core/parser/statement.rs +5 -0
- package/rust/core/preprocessor/loader.rs +1 -1
- package/rust/core/preprocessor/processor.rs +4 -4
- package/rust/core/preprocessor/resolver/bank.rs +1 -2
- package/rust/core/preprocessor/resolver/call.rs +19 -18
- package/rust/core/preprocessor/resolver/driver.rs +7 -5
- package/rust/core/preprocessor/resolver/function.rs +3 -13
- package/rust/core/preprocessor/resolver/loop_.rs +31 -1
- package/rust/core/preprocessor/resolver/spawn.rs +3 -22
- package/rust/core/preprocessor/resolver/tempo.rs +1 -1
- package/rust/core/preprocessor/resolver/trigger.rs +2 -3
- package/rust/core/preprocessor/resolver/value.rs +6 -12
- package/rust/core/shared/bank.rs +1 -1
- package/rust/core/utils/path.rs +1 -1
- package/rust/core/utils/validation.rs +0 -1
- package/rust/installer/bank.rs +1 -1
- package/rust/installer/plugin.rs +2 -2
- package/rust/main.rs +0 -1
- package/rust/utils/error.rs +51 -0
- package/rust/utils/logger.rs +4 -0
- package/rust/utils/mod.rs +1 -44
- package/rust/utils/spinner.rs +1 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
use crate::core::store::variable::VariableTable;
|
|
2
|
+
|
|
3
|
+
fn lfo_sine(rate_per_beat: f32, beat: f32) -> f32 {
|
|
4
|
+
// Output in [-1,1]
|
|
5
|
+
(2.0 * std::f32::consts::PI * rate_per_beat * beat).sin()
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
fn lfo_triangle(rate_per_beat: f32, beat: f32) -> f32 {
|
|
9
|
+
// Triangle in [-1,1]
|
|
10
|
+
let phase = (rate_per_beat * beat).fract();
|
|
11
|
+
// Map [0,1]->[-1,1] tri
|
|
12
|
+
4.0 * (phase - 0.5).abs() - 1.0
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
fn adsr_envelope_value_t(attack: f32, decay: f32, sustain: f32, release: f32, t: f32) -> f32 {
|
|
16
|
+
let a = attack.max(0.0);
|
|
17
|
+
let d = decay.max(0.0);
|
|
18
|
+
let r = release.max(0.0);
|
|
19
|
+
let s = sustain.clamp(0.0, 1.0);
|
|
20
|
+
|
|
21
|
+
// Normalize phases so that the whole ADSR spans t in [0,1]
|
|
22
|
+
let total = (a + d + r).max(1e-6);
|
|
23
|
+
let ap = a / total; let dp = d / total; let rp = r / total;
|
|
24
|
+
|
|
25
|
+
if t < ap {
|
|
26
|
+
// attack (0->1)
|
|
27
|
+
if ap > 0.0 { t / ap } else { 1.0 }
|
|
28
|
+
} else if t < ap + dp {
|
|
29
|
+
// decay (1->sustain)
|
|
30
|
+
let u = (t - ap) / dp.max(1e-6);
|
|
31
|
+
1.0 - (1.0 - s) * u
|
|
32
|
+
} else if t < 1.0 - rp {
|
|
33
|
+
// sustain
|
|
34
|
+
s
|
|
35
|
+
} else {
|
|
36
|
+
// release (sustain->0)
|
|
37
|
+
let u = (t - (1.0 - rp)) / rp.max(1e-6);
|
|
38
|
+
s * (1.0 - u)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fn eval_mod_func(func: &str, args: &[f32], beat: f32) -> Option<f32> {
|
|
43
|
+
match func {
|
|
44
|
+
"lfo.sine" => {
|
|
45
|
+
let rate = args.get(0).copied().unwrap_or(1.0);
|
|
46
|
+
Some(lfo_sine(rate, beat))
|
|
47
|
+
}
|
|
48
|
+
"lfo.tri" | "lfo.triangle" => {
|
|
49
|
+
let rate = args.get(0).copied().unwrap_or(1.0);
|
|
50
|
+
Some(lfo_triangle(rate, beat))
|
|
51
|
+
}
|
|
52
|
+
// ADSR envelope normalized over t in [0,1]
|
|
53
|
+
// $mod.envelope(attack, decay, sustain, release, t)
|
|
54
|
+
"envelope" | "mod.envelope" => {
|
|
55
|
+
if args.len() >= 5 {
|
|
56
|
+
Some(adsr_envelope_value_t(args[0], args[1], args[2], args[3], args[4].clamp(0.0, 1.0)))
|
|
57
|
+
} else { None }
|
|
58
|
+
}
|
|
59
|
+
_ => None,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
fn parse_top_level_args(s: &str) -> Vec<&str> {
|
|
64
|
+
let mut args = Vec::new();
|
|
65
|
+
let mut depth = 0i32;
|
|
66
|
+
let mut start = 0usize;
|
|
67
|
+
for (i, ch) in s.char_indices() {
|
|
68
|
+
match ch {
|
|
69
|
+
'(' => depth += 1,
|
|
70
|
+
')' => depth -= 1,
|
|
71
|
+
',' if depth == 0 => { args.push(s[start..i].trim()); start = i + 1; }
|
|
72
|
+
_ => {}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
let last = s[start..].trim();
|
|
76
|
+
if !last.is_empty() { args.push(last); }
|
|
77
|
+
args
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Find and evaluate the first $mod.<fn>(...) occurrence in the string.
|
|
81
|
+
pub fn find_and_eval_first_mod_call<EvalFn>(
|
|
82
|
+
s: &str,
|
|
83
|
+
eval: EvalFn,
|
|
84
|
+
vars: &VariableTable,
|
|
85
|
+
bpm: f32,
|
|
86
|
+
beat: f32,
|
|
87
|
+
) -> Option<String>
|
|
88
|
+
where
|
|
89
|
+
EvalFn: Fn(&str, &VariableTable, f32, f32) -> Option<f32>,
|
|
90
|
+
{
|
|
91
|
+
let start = s.find("$mod.")?;
|
|
92
|
+
let open_rel = s[start..].find('(')?;
|
|
93
|
+
let open = start + open_rel;
|
|
94
|
+
let func = &s[start + 5..open];
|
|
95
|
+
|
|
96
|
+
// matching close
|
|
97
|
+
let mut depth: i32 = 0;
|
|
98
|
+
let mut close_abs: Option<usize> = None;
|
|
99
|
+
for (i, ch) in s[open..].char_indices() {
|
|
100
|
+
match ch {
|
|
101
|
+
'(' => depth += 1,
|
|
102
|
+
')' => { depth -= 1; if depth == 0 { close_abs = Some(open + i); break; } }
|
|
103
|
+
_ => {}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
let close = close_abs?;
|
|
107
|
+
|
|
108
|
+
let inner = &s[open + 1..close];
|
|
109
|
+
let raw_args = parse_top_level_args(inner);
|
|
110
|
+
let mut args: Vec<f32> = Vec::with_capacity(raw_args.len());
|
|
111
|
+
for a in raw_args { args.push(eval(a, vars, bpm, beat)?); }
|
|
112
|
+
|
|
113
|
+
let result = eval_mod_func(func, &args, beat)?;
|
|
114
|
+
|
|
115
|
+
let mut replaced = String::new();
|
|
116
|
+
replaced.push_str(&s[..start]);
|
|
117
|
+
replaced.push_str(&result.to_string());
|
|
118
|
+
replaced.push_str(&s[close + 1..]);
|
|
119
|
+
Some(replaced)
|
|
120
|
+
}
|
|
@@ -27,7 +27,7 @@ pub fn write_function_log_file(output_dir: &str, file_name: &str, functions: Fun
|
|
|
27
27
|
let log_directory = format!("{}/logs", output_dir);
|
|
28
28
|
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
29
29
|
|
|
30
|
-
for (
|
|
30
|
+
for (_index, function) in functions.functions {
|
|
31
31
|
content.push_str(
|
|
32
32
|
&format!("'{}' = [{:?}] => {:?}\n", function.name, function.parameters, function.body)
|
|
33
33
|
);
|
package/rust/core/error/mod.rs
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
use crate::core::parser::{ statement::{ Statement, StatementKind }, driver::Parser };
|
|
2
|
+
use serde::{Serialize, Deserialize};
|
|
2
3
|
|
|
3
4
|
pub struct ErrorHandler {
|
|
4
5
|
errors: Vec<Error>,
|
|
5
6
|
}
|
|
6
7
|
|
|
8
|
+
#[derive(Serialize, Deserialize, Clone)]
|
|
7
9
|
pub struct ErrorResult {
|
|
8
10
|
pub message: String,
|
|
9
11
|
pub line: usize,
|
|
10
12
|
pub column: usize,
|
|
11
13
|
}
|
|
12
14
|
|
|
15
|
+
#[derive(Clone)]
|
|
13
16
|
pub struct Error {
|
|
14
17
|
pub message: String,
|
|
15
18
|
pub line: usize,
|
|
@@ -42,7 +45,7 @@ impl ErrorHandler {
|
|
|
42
45
|
self.errors.clear();
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
pub fn detect_from_statements(&mut self,
|
|
48
|
+
pub fn detect_from_statements(&mut self, _parser: &mut Parser, statements: &[Statement]) {
|
|
46
49
|
for stmt in statements {
|
|
47
50
|
match &stmt.kind {
|
|
48
51
|
StatementKind::Unknown => {
|
|
@@ -1,29 +1,80 @@
|
|
|
1
1
|
use crate::core::lexer::token::{ Token, TokenKind };
|
|
2
2
|
|
|
3
3
|
pub fn handle_arrow_lexer(
|
|
4
|
-
|
|
4
|
+
ch: char,
|
|
5
5
|
chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
6
6
|
current_indent: &mut usize,
|
|
7
|
-
|
|
7
|
+
_indent_stack: &mut Vec<usize>,
|
|
8
8
|
tokens: &mut Vec<Token>,
|
|
9
9
|
line: &mut usize,
|
|
10
10
|
column: &mut usize
|
|
11
11
|
) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
while let Some(&c) = chars.peek() {
|
|
12
|
+
// If next char is '>', this is an arrow '->'.
|
|
13
|
+
if let Some(&c) = chars.peek() {
|
|
15
14
|
if c == '>' {
|
|
15
|
+
let mut arrow_call = ch.to_string();
|
|
16
16
|
chars.next();
|
|
17
17
|
arrow_call.push(c);
|
|
18
18
|
*column += 1;
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
|
|
20
|
+
tokens.push(Token {
|
|
21
|
+
kind: TokenKind::Arrow,
|
|
22
|
+
lexeme: arrow_call,
|
|
23
|
+
line: *line,
|
|
24
|
+
column: *column,
|
|
25
|
+
indent: *current_indent,
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Otherwise, treat '-' as the start of a negative number if followed by digits.
|
|
32
|
+
let mut lexeme = String::from("-");
|
|
33
|
+
if let Some(&next) = chars.peek() {
|
|
34
|
+
if next.is_ascii_digit() {
|
|
35
|
+
// consume digits
|
|
36
|
+
while let Some(&d) = chars.peek() {
|
|
37
|
+
if d.is_ascii_digit() {
|
|
38
|
+
chars.next();
|
|
39
|
+
lexeme.push(d);
|
|
40
|
+
*column += 1;
|
|
41
|
+
} else {
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// optional decimal part
|
|
46
|
+
if let Some(&dot) = chars.peek() {
|
|
47
|
+
if dot == '.' {
|
|
48
|
+
chars.next();
|
|
49
|
+
lexeme.push(dot);
|
|
50
|
+
*column += 1;
|
|
51
|
+
while let Some(&d) = chars.peek() {
|
|
52
|
+
if d.is_ascii_digit() {
|
|
53
|
+
chars.next();
|
|
54
|
+
lexeme.push(d);
|
|
55
|
+
*column += 1;
|
|
56
|
+
} else {
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
tokens.push(Token {
|
|
64
|
+
kind: TokenKind::Number,
|
|
65
|
+
lexeme,
|
|
66
|
+
line: *line,
|
|
67
|
+
column: *column,
|
|
68
|
+
indent: *current_indent,
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
21
71
|
}
|
|
22
72
|
}
|
|
23
73
|
|
|
74
|
+
// Fallback: lone '-' not part of '->' or a number; emit Unknown to avoid mis-parsing as Arrow
|
|
24
75
|
tokens.push(Token {
|
|
25
|
-
kind: TokenKind::
|
|
26
|
-
lexeme:
|
|
76
|
+
kind: TokenKind::Unknown,
|
|
77
|
+
lexeme: "-".to_string(),
|
|
27
78
|
line: *line,
|
|
28
79
|
column: *column,
|
|
29
80
|
indent: *current_indent,
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
use crate::core::lexer::token::{Token, TokenKind};
|
|
2
2
|
|
|
3
3
|
pub fn handle_at_lexer(
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
ch: char,
|
|
5
|
+
_chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
6
6
|
current_indent: &mut usize,
|
|
7
|
-
|
|
7
|
+
_indent_stack: &mut Vec<usize>,
|
|
8
8
|
tokens: &mut Vec<Token>,
|
|
9
9
|
line: &mut usize,
|
|
10
10
|
column: &mut usize
|
|
11
11
|
) {
|
|
12
12
|
tokens.push(Token {
|
|
13
13
|
kind: TokenKind::At,
|
|
14
|
-
lexeme:
|
|
14
|
+
lexeme: ch.to_string(),
|
|
15
15
|
line: *line,
|
|
16
16
|
column: *column,
|
|
17
17
|
indent: *current_indent,
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
use crate::core::lexer::token::{ Token, TokenKind };
|
|
2
2
|
|
|
3
3
|
pub fn handle_rbrace_lexer(
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
ch: char,
|
|
5
|
+
_chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
6
6
|
current_indent: &mut usize,
|
|
7
|
-
|
|
7
|
+
_indent_stack: &mut Vec<usize>,
|
|
8
8
|
tokens: &mut Vec<Token>,
|
|
9
9
|
line: &mut usize,
|
|
10
10
|
column: &mut usize
|
|
11
11
|
) {
|
|
12
12
|
tokens.push(Token {
|
|
13
13
|
kind: TokenKind::RBrace,
|
|
14
|
-
lexeme:
|
|
14
|
+
lexeme: ch.to_string(),
|
|
15
15
|
line: *line,
|
|
16
16
|
column: *column,
|
|
17
17
|
indent: *current_indent,
|
|
@@ -21,17 +21,17 @@ pub fn handle_rbrace_lexer(
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
pub fn handle_lbrace_lexer(
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
ch: char,
|
|
25
|
+
_chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
26
26
|
current_indent: &mut usize,
|
|
27
|
-
|
|
27
|
+
_indent_stack: &mut Vec<usize>,
|
|
28
28
|
tokens: &mut Vec<Token>,
|
|
29
29
|
line: &mut usize,
|
|
30
30
|
column: &mut usize
|
|
31
31
|
) {
|
|
32
32
|
tokens.push(Token {
|
|
33
33
|
kind: TokenKind::LBrace,
|
|
34
|
-
lexeme:
|
|
34
|
+
lexeme: ch.to_string(),
|
|
35
35
|
line: *line,
|
|
36
36
|
column: *column,
|
|
37
37
|
indent: *current_indent,
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
use crate::core::lexer::token::{Token, TokenKind};
|
|
2
2
|
|
|
3
3
|
pub fn handle_colon_lexer(
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
ch: char,
|
|
5
|
+
_chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
6
6
|
current_indent: &mut usize,
|
|
7
|
-
|
|
7
|
+
_indent_stack: &mut Vec<usize>,
|
|
8
8
|
tokens: &mut Vec<Token>,
|
|
9
9
|
line: &mut usize,
|
|
10
10
|
column: &mut usize
|
|
11
11
|
) {
|
|
12
12
|
tokens.push(Token {
|
|
13
13
|
kind: TokenKind::Colon,
|
|
14
|
-
lexeme:
|
|
14
|
+
lexeme: ch.to_string(),
|
|
15
15
|
line: *line,
|
|
16
16
|
column: *column,
|
|
17
17
|
indent: *current_indent,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
use crate::core::lexer::token::{Token, TokenKind};
|
|
2
2
|
|
|
3
3
|
pub fn handle_comment_lexer(
|
|
4
|
-
|
|
4
|
+
_char: char,
|
|
5
5
|
chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
6
6
|
current_indent: &mut usize,
|
|
7
|
-
|
|
7
|
+
_indent_stack: &mut Vec<usize>,
|
|
8
8
|
tokens: &mut Vec<Token>,
|
|
9
9
|
line: &mut usize,
|
|
10
10
|
column: &mut usize
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
use crate::core::lexer::token::{ Token, TokenKind };
|
|
2
2
|
|
|
3
3
|
pub fn handle_dot_lexer(
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
ch: char,
|
|
5
|
+
_chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
6
6
|
current_indent: &mut usize,
|
|
7
|
-
|
|
7
|
+
_indent_stack: &mut Vec<usize>,
|
|
8
8
|
tokens: &mut Vec<Token>,
|
|
9
9
|
line: &mut usize,
|
|
10
10
|
column: &mut usize
|
|
11
11
|
) {
|
|
12
12
|
tokens.push(Token {
|
|
13
13
|
kind: TokenKind::Dot,
|
|
14
|
-
lexeme:
|
|
14
|
+
lexeme: ch.to_string(),
|
|
15
15
|
line: *line,
|
|
16
16
|
column: *column,
|
|
17
17
|
indent: *current_indent,
|
|
@@ -7,7 +7,7 @@ use crate::core::lexer::{
|
|
|
7
7
|
|
|
8
8
|
fn advance_char<I: Iterator<Item = char>>(
|
|
9
9
|
chars: &mut std::iter::Peekable<I>,
|
|
10
|
-
|
|
10
|
+
_line: &mut usize,
|
|
11
11
|
column: &mut usize
|
|
12
12
|
) -> Option<char> {
|
|
13
13
|
while let Some(c) = chars.next() {
|
|
@@ -90,7 +90,7 @@ pub fn handle_content_lexing(content: String) -> Result<Vec<Token>, String> {
|
|
|
90
90
|
&mut column
|
|
91
91
|
);
|
|
92
92
|
}
|
|
93
|
-
'=' | '!' | '<' | '>' => {
|
|
93
|
+
'=' | '!' | '<' | '>' | '+' | '*' => {
|
|
94
94
|
handle_operator_lexer(
|
|
95
95
|
ch,
|
|
96
96
|
&mut chars,
|
|
@@ -122,6 +122,20 @@ pub fn handle_content_lexing(content: String) -> Result<Vec<Token>, String> {
|
|
|
122
122
|
&mut line,
|
|
123
123
|
&mut column
|
|
124
124
|
);
|
|
125
|
+
// If not parsed as arrow or number, fallback as Minus token
|
|
126
|
+
if let Some(last) = tokens.last() {
|
|
127
|
+
if last.kind == TokenKind::Unknown && last.lexeme == "-" {
|
|
128
|
+
// replace last with Minus
|
|
129
|
+
let _ = tokens.pop();
|
|
130
|
+
tokens.push(Token {
|
|
131
|
+
kind: TokenKind::Minus,
|
|
132
|
+
lexeme: "-".to_string(),
|
|
133
|
+
line,
|
|
134
|
+
column,
|
|
135
|
+
indent: current_indent,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
125
139
|
}
|
|
126
140
|
'{' => {
|
|
127
141
|
handle_lbrace_lexer(
|
|
@@ -145,6 +159,9 @@ pub fn handle_content_lexing(content: String) -> Result<Vec<Token>, String> {
|
|
|
145
159
|
&mut column
|
|
146
160
|
);
|
|
147
161
|
}
|
|
162
|
+
'[' => { tokens.push(Token { kind: TokenKind::LBracket, lexeme: "[".to_string(), line, column, indent: current_indent }); }
|
|
163
|
+
']' => { tokens.push(Token { kind: TokenKind::RBracket, lexeme: "]".to_string(), line, column, indent: current_indent }); }
|
|
164
|
+
',' => { tokens.push(Token { kind: TokenKind::Comma, lexeme: ",".to_string(), line, column, indent: current_indent }); }
|
|
148
165
|
'(' => {
|
|
149
166
|
handle_lparen_lexer(
|
|
150
167
|
ch,
|
|
@@ -189,37 +206,49 @@ pub fn handle_content_lexing(content: String) -> Result<Vec<Token>, String> {
|
|
|
189
206
|
&mut column
|
|
190
207
|
);
|
|
191
208
|
}
|
|
192
|
-
'
|
|
193
|
-
|
|
209
|
+
'$' => {
|
|
210
|
+
// Treat `$` as start of a special identifier like `$env` or `$math`
|
|
211
|
+
let mut ident = String::from("$");
|
|
212
|
+
while let Some(&c) = chars.peek() {
|
|
213
|
+
if c.is_ascii_alphanumeric() || c == '_' {
|
|
214
|
+
ident.push(c);
|
|
215
|
+
chars.next();
|
|
216
|
+
column += 1;
|
|
217
|
+
} else { break; }
|
|
218
|
+
}
|
|
219
|
+
tokens.push(Token { kind: TokenKind::Identifier, lexeme: ident, line, column, indent: current_indent });
|
|
220
|
+
}
|
|
221
|
+
'0'..='9' => {
|
|
222
|
+
handle_number_lexer(
|
|
194
223
|
ch,
|
|
195
224
|
&mut chars,
|
|
196
225
|
&mut current_indent,
|
|
197
226
|
&mut indent_stack,
|
|
198
227
|
&mut tokens,
|
|
199
228
|
&mut line,
|
|
200
|
-
&mut column
|
|
229
|
+
&mut column,
|
|
201
230
|
);
|
|
202
231
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
232
|
+
'a'..='z' | 'A'..='Z' | '_' => {
|
|
233
|
+
handle_identifier_lexer(
|
|
234
|
+
ch,
|
|
206
235
|
&mut chars,
|
|
207
236
|
&mut current_indent,
|
|
208
237
|
&mut indent_stack,
|
|
209
238
|
&mut tokens,
|
|
210
239
|
&mut line,
|
|
211
|
-
&mut column
|
|
240
|
+
&mut column,
|
|
212
241
|
);
|
|
213
242
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
243
|
+
'"' | '\'' => {
|
|
244
|
+
handle_string_lexer(
|
|
245
|
+
ch,
|
|
217
246
|
&mut chars,
|
|
218
247
|
&mut current_indent,
|
|
219
248
|
&mut indent_stack,
|
|
220
249
|
&mut tokens,
|
|
221
250
|
&mut line,
|
|
222
|
-
&mut column
|
|
251
|
+
&mut column,
|
|
223
252
|
);
|
|
224
253
|
}
|
|
225
254
|
_ => {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
use crate::core::lexer::token::{ Token, TokenKind };
|
|
2
2
|
|
|
3
3
|
pub fn handle_identifier_lexer(
|
|
4
|
-
|
|
4
|
+
ch: char,
|
|
5
5
|
chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
6
6
|
current_indent: &mut usize,
|
|
7
|
-
|
|
7
|
+
_indent_stack: &mut Vec<usize>,
|
|
8
8
|
tokens: &mut Vec<Token>,
|
|
9
9
|
line: &mut usize,
|
|
10
10
|
column: &mut usize
|
|
11
11
|
) {
|
|
12
|
-
let mut ident =
|
|
12
|
+
let mut ident = ch.to_string();
|
|
13
13
|
|
|
14
14
|
while let Some(&c) = chars.peek() {
|
|
15
15
|
if c.is_ascii_alphanumeric() || c == '_' {
|
|
@@ -26,7 +26,8 @@ pub fn handle_identifier_lexer(
|
|
|
26
26
|
"else" => TokenKind::Else,
|
|
27
27
|
"bank" => TokenKind::Bank,
|
|
28
28
|
"bpm" => TokenKind::Tempo,
|
|
29
|
-
|
|
29
|
+
"loop" => TokenKind::Loop,
|
|
30
|
+
"for" => TokenKind::Loop,
|
|
30
31
|
"synth" => TokenKind::Synth,
|
|
31
32
|
"fn" => TokenKind::Function,
|
|
32
33
|
_ => TokenKind::Identifier,
|
|
@@ -2,7 +2,7 @@ use crate::core::lexer::token::{ Token, TokenKind };
|
|
|
2
2
|
|
|
3
3
|
pub fn handle_newline_lexer(
|
|
4
4
|
ch: char,
|
|
5
|
-
|
|
5
|
+
_chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
6
6
|
tokens: &mut Vec<Token>,
|
|
7
7
|
line: &mut usize,
|
|
8
8
|
column: &mut usize,
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
use crate::core::lexer::token::{ Token, TokenKind };
|
|
2
2
|
|
|
3
3
|
pub fn handle_number_lexer(
|
|
4
|
-
|
|
4
|
+
ch: char,
|
|
5
5
|
chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
6
6
|
current_indent: &mut usize,
|
|
7
|
-
|
|
7
|
+
_indent_stack: &mut Vec<usize>,
|
|
8
8
|
tokens: &mut Vec<Token>,
|
|
9
9
|
line: &mut usize,
|
|
10
10
|
column: &mut usize
|
|
11
11
|
) {
|
|
12
|
-
let mut number =
|
|
12
|
+
let mut number = ch.to_string();
|
|
13
13
|
|
|
14
14
|
while let Some(&c) = chars.peek() {
|
|
15
15
|
if c.is_ascii_digit() {
|
|
@@ -4,7 +4,7 @@ pub fn handle_operator_lexer(
|
|
|
4
4
|
ch: char,
|
|
5
5
|
chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
6
6
|
current_indent: &mut usize,
|
|
7
|
-
|
|
7
|
+
_indent_stack: &mut Vec<usize>,
|
|
8
8
|
tokens: &mut Vec<Token>,
|
|
9
9
|
line: &mut usize,
|
|
10
10
|
column: &mut usize
|
|
@@ -16,6 +16,8 @@ pub fn handle_operator_lexer(
|
|
|
16
16
|
('!', Some('=')) => (TokenKind::NotEquals, 2),
|
|
17
17
|
('>', Some('=')) => (TokenKind::GreaterEqual, 2),
|
|
18
18
|
('<', Some('=')) => (TokenKind::LessEqual, 2),
|
|
19
|
+
('+', _) => (TokenKind::Plus, 1),
|
|
20
|
+
('*', _) => (TokenKind::Asterisk, 1),
|
|
19
21
|
('=', _) => (TokenKind::Equals, 1),
|
|
20
22
|
('>', _) => (TokenKind::Greater, 1),
|
|
21
23
|
('<', _) => (TokenKind::Less, 1),
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
use crate::core::lexer::token::{ Token, TokenKind };
|
|
2
2
|
|
|
3
3
|
pub fn handle_rparen_lexer(
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
ch: char,
|
|
5
|
+
_chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
6
6
|
current_indent: &mut usize,
|
|
7
|
-
|
|
7
|
+
_indent_stack: &mut Vec<usize>,
|
|
8
8
|
tokens: &mut Vec<Token>,
|
|
9
9
|
line: &mut usize,
|
|
10
10
|
column: &mut usize
|
|
11
11
|
) {
|
|
12
12
|
tokens.push(Token {
|
|
13
13
|
kind: TokenKind::RParen,
|
|
14
|
-
lexeme:
|
|
14
|
+
lexeme: ch.to_string(),
|
|
15
15
|
line: *line,
|
|
16
16
|
column: *column,
|
|
17
17
|
indent: *current_indent,
|
|
@@ -21,17 +21,17 @@ pub fn handle_rparen_lexer(
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
pub fn handle_lparen_lexer(
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
ch: char,
|
|
25
|
+
_chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
26
26
|
current_indent: &mut usize,
|
|
27
|
-
|
|
27
|
+
_indent_stack: &mut Vec<usize>,
|
|
28
28
|
tokens: &mut Vec<Token>,
|
|
29
29
|
line: &mut usize,
|
|
30
30
|
column: &mut usize
|
|
31
31
|
) {
|
|
32
32
|
tokens.push(Token {
|
|
33
33
|
kind: TokenKind::LParen,
|
|
34
|
-
lexeme:
|
|
34
|
+
lexeme: ch.to_string(),
|
|
35
35
|
line: *line,
|
|
36
36
|
column: *column,
|
|
37
37
|
indent: *current_indent,
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
use crate::core::lexer::token::{ Token, TokenKind };
|
|
2
2
|
|
|
3
3
|
pub fn handle_slash_lexer(
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
ch: char,
|
|
5
|
+
_chars: &mut std::iter::Peekable<std::str::Chars>,
|
|
6
6
|
current_indent: &mut usize,
|
|
7
|
-
|
|
7
|
+
_indent_stack: &mut Vec<usize>,
|
|
8
8
|
tokens: &mut Vec<Token>,
|
|
9
9
|
line: &mut usize,
|
|
10
10
|
column: &mut usize
|
|
11
11
|
) {
|
|
12
|
-
let
|
|
12
|
+
let slash = ch.to_string();
|
|
13
13
|
|
|
14
14
|
tokens.push(Token {
|
|
15
15
|
kind: TokenKind::Slash,
|
|
16
16
|
lexeme: slash,
|
|
17
17
|
line: *line,
|
|
18
18
|
column: *column,
|
|
19
|
-
|
|
19
|
+
indent: *current_indent,
|
|
20
20
|
});
|
|
21
21
|
}
|
package/rust/core/lexer/mod.rs
CHANGED