@devaloop/devalang 0.0.1-alpha.17 → 0.0.1-alpha.18
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 +5 -1
- package/Cargo.toml +4 -4
- package/README.md +9 -6
- package/docs/CHANGELOG.md +46 -1
- package/docs/TODO.md +1 -1
- package/examples/index.deva +9 -6
- package/examples/pattern.deva +5 -5
- package/out-tsc/pkg/devalang_core.d.ts +1 -1
- package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +7 -7
- package/package.json +1 -1
- package/project-version.json +3 -3
- package/rust/cli/build/commands.rs +10 -0
- package/rust/cli/install/addon.rs +84 -38
- package/rust/cli/telemetry/event_creator.rs +17 -17
- package/rust/core/audio/engine/helpers.rs +21 -9
- package/rust/core/audio/engine/sample.rs +68 -7
- package/rust/core/audio/engine/synth.rs +19 -4
- package/rust/core/audio/evaluator.rs +64 -26
- package/rust/core/audio/interpreter/arrow_call.rs +21 -16
- package/rust/core/audio/interpreter/call.rs +156 -1
- package/rust/core/audio/interpreter/spawn.rs +145 -1
- package/rust/core/audio/special/math.rs +22 -2
- package/rust/core/lexer/driver.rs +61 -0
- package/rust/core/lexer/handler/identifier.rs +3 -2
- package/rust/core/lexer/mod.rs +1 -62
- package/rust/core/lexer/token.rs +1 -0
- package/rust/core/parser/driver.rs +12 -9
- package/rust/core/parser/handler/dot.rs +3 -2
- package/rust/core/parser/handler/loop_.rs +2 -2
- package/rust/core/parser/handler/mod.rs +1 -0
- package/rust/core/parser/handler/pattern.rs +74 -0
- package/rust/core/preprocessor/loader.rs +87 -127
- package/rust/core/preprocessor/processor.rs +7 -7
- package/rust/core/preprocessor/resolver/call.rs +28 -0
- package/rust/core/preprocessor/resolver/driver.rs +15 -13
- package/rust/core/preprocessor/resolver/mod.rs +1 -0
- package/rust/core/preprocessor/resolver/pattern.rs +75 -0
- package/rust/core/preprocessor/resolver/spawn.rs +27 -0
- package/rust/core/store/variable.rs +15 -1
- package/rust/main.rs +4 -1
- package/rust/types/Cargo.toml +3 -0
- package/rust/types/src/ast.rs +4 -0
- package/rust/utils/Cargo.toml +4 -1
- package/rust/web/api.rs +2 -2
package/rust/core/lexer/mod.rs
CHANGED
|
@@ -1,64 +1,3 @@
|
|
|
1
1
|
pub mod handler;
|
|
2
2
|
pub mod token;
|
|
3
|
-
|
|
4
|
-
use crate::core::{
|
|
5
|
-
lexer::{handler::driver::handle_content_lexing, token::Token},
|
|
6
|
-
utils::path::normalize_path,
|
|
7
|
-
};
|
|
8
|
-
use std::fs;
|
|
9
|
-
use std::path::Path;
|
|
10
|
-
|
|
11
|
-
pub struct Lexer {}
|
|
12
|
-
|
|
13
|
-
impl Default for Lexer {
|
|
14
|
-
fn default() -> Self {
|
|
15
|
-
Self::new()
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
impl Lexer {
|
|
20
|
-
pub fn new() -> Self {
|
|
21
|
-
Lexer {}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
pub fn lex_from_source(&self, source: &str) -> Result<Vec<Token>, String> {
|
|
25
|
-
handle_content_lexing(source.to_string())
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
pub fn lex_tokens(&self, entrypoint: &str) -> Result<Vec<Token>, String> {
|
|
29
|
-
let path = normalize_path(entrypoint);
|
|
30
|
-
let resolved_path = Self::resolve_entry_path(&path)?;
|
|
31
|
-
|
|
32
|
-
let file_content = fs::read_to_string(&resolved_path).map_err(|e| {
|
|
33
|
-
format!(
|
|
34
|
-
"Failed to read the entrypoint file '{}': {}",
|
|
35
|
-
resolved_path, e
|
|
36
|
-
)
|
|
37
|
-
})?;
|
|
38
|
-
|
|
39
|
-
handle_content_lexing(file_content).map_err(|e| format!("Failed to lex the content: {}", e))
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
fn resolve_entry_path(path: &str) -> Result<String, String> {
|
|
43
|
-
let candidate = Path::new(path);
|
|
44
|
-
|
|
45
|
-
if candidate.is_dir() {
|
|
46
|
-
let index_path = candidate.join("index.deva");
|
|
47
|
-
if index_path.exists() {
|
|
48
|
-
Ok(index_path.to_string_lossy().replace("\\", "/"))
|
|
49
|
-
} else {
|
|
50
|
-
Err(format!(
|
|
51
|
-
"Expected 'index.deva' in directory '{}', but it was not found",
|
|
52
|
-
path
|
|
53
|
-
))
|
|
54
|
-
}
|
|
55
|
-
} else if candidate.is_file() {
|
|
56
|
-
return Ok(path.to_string());
|
|
57
|
-
} else {
|
|
58
|
-
return Err(format!(
|
|
59
|
-
"Provided entrypoint '{}' is not a valid file or directory",
|
|
60
|
-
path
|
|
61
|
-
));
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
3
|
+
pub mod driver;
|
package/rust/core/lexer/token.rs
CHANGED
|
@@ -9,6 +9,7 @@ use crate::core::{
|
|
|
9
9
|
bank::parse_bank_token,
|
|
10
10
|
condition::parse_condition_token,
|
|
11
11
|
dot::parse_dot_token,
|
|
12
|
+
pattern::parse_pattern_token,
|
|
12
13
|
identifier::{
|
|
13
14
|
emit::parse_emit_token, function::parse_function_token, on::parse_on_token,
|
|
14
15
|
parse_identifier_token,
|
|
@@ -156,6 +157,7 @@ impl Parser {
|
|
|
156
157
|
TokenKind::Dot => parse_dot_token(self, global_store),
|
|
157
158
|
TokenKind::Tempo => parse_tempo_token(self, global_store),
|
|
158
159
|
TokenKind::Bank => parse_bank_token(self, global_store),
|
|
160
|
+
TokenKind::Pattern => parse_pattern_token(self, global_store),
|
|
159
161
|
TokenKind::Loop => parse_loop_token(self, global_store),
|
|
160
162
|
TokenKind::If => parse_condition_token(self, global_store),
|
|
161
163
|
TokenKind::Function => parse_function_token(self, global_store),
|
|
@@ -202,6 +204,8 @@ impl Parser {
|
|
|
202
204
|
}
|
|
203
205
|
|
|
204
206
|
pub fn parse_map_value(&mut self) -> Option<Value> {
|
|
207
|
+
let logger = devalang_utils::logger::Logger::new();
|
|
208
|
+
use devalang_utils::logger::LogLevel;
|
|
205
209
|
if !self.match_token(TokenKind::LBrace) {
|
|
206
210
|
return None;
|
|
207
211
|
}
|
|
@@ -244,7 +248,7 @@ impl Parser {
|
|
|
244
248
|
}
|
|
245
249
|
|
|
246
250
|
if !self.match_token(TokenKind::Colon) {
|
|
247
|
-
|
|
251
|
+
logger.log_message(LogLevel::Error, &format!("Expected ':' after map key '{}'", key));
|
|
248
252
|
break;
|
|
249
253
|
}
|
|
250
254
|
|
|
@@ -278,14 +282,11 @@ impl Parser {
|
|
|
278
282
|
number_str.push('.');
|
|
279
283
|
number_str.push_str(&decimal_token.lexeme);
|
|
280
284
|
} else {
|
|
281
|
-
|
|
282
|
-
"Expected number after dot, got {:?}",
|
|
283
|
-
decimal_token
|
|
284
|
-
);
|
|
285
|
+
logger.log_message(LogLevel::Error, &format!("Expected number after dot, got {:?}", decimal_token));
|
|
285
286
|
return Some(Value::Null);
|
|
286
287
|
}
|
|
287
288
|
} else {
|
|
288
|
-
|
|
289
|
+
logger.log_message(LogLevel::Error, "Expected number after dot, but reached EOF");
|
|
289
290
|
return Some(Value::Null);
|
|
290
291
|
}
|
|
291
292
|
}
|
|
@@ -325,7 +326,7 @@ impl Parser {
|
|
|
325
326
|
Value::Identifier(parts.join("."))
|
|
326
327
|
}
|
|
327
328
|
_ => {
|
|
328
|
-
|
|
329
|
+
logger.log_message(LogLevel::Error, &format!("Unexpected token in map value: {:?}", token));
|
|
329
330
|
Value::Null
|
|
330
331
|
}
|
|
331
332
|
}
|
|
@@ -345,7 +346,7 @@ impl Parser {
|
|
|
345
346
|
}
|
|
346
347
|
|
|
347
348
|
if !self.match_token(TokenKind::RBrace) {
|
|
348
|
-
|
|
349
|
+
logger.log_message(LogLevel::Error, "Expected '}' at end of map");
|
|
349
350
|
}
|
|
350
351
|
|
|
351
352
|
Some(Value::Map(map))
|
|
@@ -353,6 +354,8 @@ impl Parser {
|
|
|
353
354
|
|
|
354
355
|
// Parse an array value like [1, 2, 3] or ["a", b]
|
|
355
356
|
pub fn parse_array_value(&mut self) -> Option<Value> {
|
|
357
|
+
let logger = devalang_utils::logger::Logger::new();
|
|
358
|
+
use devalang_utils::logger::LogLevel;
|
|
356
359
|
if !self.match_token(TokenKind::LBracket) {
|
|
357
360
|
return None;
|
|
358
361
|
}
|
|
@@ -436,7 +439,7 @@ impl Parser {
|
|
|
436
439
|
}
|
|
437
440
|
|
|
438
441
|
if !self.match_token(TokenKind::RBracket) {
|
|
439
|
-
|
|
442
|
+
logger.log_message(LogLevel::Error, "Expected ']' at end of array");
|
|
440
443
|
}
|
|
441
444
|
|
|
442
445
|
Some(Value::Array(arr))
|
|
@@ -13,7 +13,8 @@ pub fn parse_dot_token(
|
|
|
13
13
|
_global_store: &mut crate::core::store::global::GlobalStore,
|
|
14
14
|
) -> Statement {
|
|
15
15
|
parser.advance(); // consume '.'
|
|
16
|
-
|
|
16
|
+
let logger = devalang_utils::logger::Logger::new();
|
|
17
|
+
use devalang_utils::logger::LogLevel;
|
|
17
18
|
let Some(dot_token) = parser.previous_clone() else {
|
|
18
19
|
return Statement::unknown();
|
|
19
20
|
};
|
|
@@ -56,7 +57,7 @@ pub fn parse_dot_token(
|
|
|
56
57
|
let entity = if !parts.is_empty() {
|
|
57
58
|
parts.join(".") // only join within the same line
|
|
58
59
|
} else {
|
|
59
|
-
|
|
60
|
+
logger.log_message(LogLevel::Warning, &format!("Empty entity after '.' at line {}", dot_token.line));
|
|
60
61
|
String::new()
|
|
61
62
|
};
|
|
62
63
|
|
|
@@ -100,7 +100,7 @@ pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) ->
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
let tokens =
|
|
103
|
-
parser.collect_until(|t|
|
|
103
|
+
parser.collect_until(|t| t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF);
|
|
104
104
|
let loop_body = parser.parse_block(tokens.clone(), global_store);
|
|
105
105
|
if let Some(token) = parser.peek() {
|
|
106
106
|
if token.kind == TokenKind::Dedent {
|
|
@@ -173,7 +173,7 @@ pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) ->
|
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
let tokens =
|
|
176
|
-
parser.collect_until(|t|
|
|
176
|
+
parser.collect_until(|t| t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF);
|
|
177
177
|
let loop_body = parser.parse_block(tokens.clone(), global_store);
|
|
178
178
|
if let Some(token) = parser.peek() {
|
|
179
179
|
if token.kind == TokenKind::Dedent {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
use devalang_types::Value;
|
|
2
|
+
|
|
3
|
+
use crate::core::{
|
|
4
|
+
lexer::token::TokenKind,
|
|
5
|
+
parser::{
|
|
6
|
+
driver::Parser,
|
|
7
|
+
statement::{Statement, StatementKind},
|
|
8
|
+
},
|
|
9
|
+
store::global::GlobalStore,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
pub fn parse_pattern_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
|
|
13
|
+
// consume 'pattern'
|
|
14
|
+
parser.advance();
|
|
15
|
+
|
|
16
|
+
let Some(tok) = parser.previous_clone() else {
|
|
17
|
+
return Statement::unknown();
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Parse pattern name
|
|
21
|
+
let mut name = String::new();
|
|
22
|
+
if let Some(next) = parser.peek_clone() {
|
|
23
|
+
if next.kind == TokenKind::Identifier {
|
|
24
|
+
parser.advance();
|
|
25
|
+
name = next.lexeme.clone();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// optional 'with <target>' sequence
|
|
30
|
+
let mut target: Option<String> = None;
|
|
31
|
+
if parser.peek_is("with") {
|
|
32
|
+
parser.advance(); // consume 'with'
|
|
33
|
+
if let Some(tok2) = parser.peek_clone() {
|
|
34
|
+
// target can be identifier or dotted identifier
|
|
35
|
+
if tok2.kind == TokenKind::Identifier {
|
|
36
|
+
parser.advance();
|
|
37
|
+
let mut base = tok2.lexeme.clone();
|
|
38
|
+
if let Some(dot) = parser.peek_clone() {
|
|
39
|
+
if dot.kind == TokenKind::Dot {
|
|
40
|
+
parser.advance();
|
|
41
|
+
if let Some(suf) = parser.peek_clone() {
|
|
42
|
+
if suf.kind == TokenKind::Identifier || suf.kind == TokenKind::Number {
|
|
43
|
+
parser.advance();
|
|
44
|
+
base.push('.');
|
|
45
|
+
base.push_str(&suf.lexeme);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
target = Some(base);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// optional '=' and pattern string
|
|
56
|
+
let mut value: Value = Value::Null;
|
|
57
|
+
if parser.peek_is("=") {
|
|
58
|
+
parser.advance();
|
|
59
|
+
if let Some(tok3) = parser.peek_clone() {
|
|
60
|
+
if tok3.kind == TokenKind::String {
|
|
61
|
+
parser.advance();
|
|
62
|
+
value = Value::String(tok3.lexeme.clone());
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
Statement {
|
|
68
|
+
kind: StatementKind::Pattern { name, target },
|
|
69
|
+
value,
|
|
70
|
+
indent: tok.indent,
|
|
71
|
+
line: tok.line,
|
|
72
|
+
column: tok.column,
|
|
73
|
+
}
|
|
74
|
+
}
|