@devaloop/devalang 0.0.1-alpha.8 → 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 +4 -8
- package/docs/CHANGELOG.md +27 -0
- package/examples/condition.deva +8 -12
- package/examples/group.deva +3 -3
- package/examples/index.deva +10 -8
- package/examples/loop.deva +10 -8
- package/examples/synth.deva +14 -0
- package/examples/variables.deva +1 -1
- 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/core/audio/engine.rs +89 -12
- package/rust/core/audio/interpreter/arrow_call.rs +129 -0
- package/rust/core/audio/interpreter/call.rs +29 -7
- package/rust/core/audio/interpreter/condition.rs +5 -1
- package/rust/core/audio/interpreter/driver.rs +41 -29
- package/rust/core/audio/interpreter/loop_.rs +11 -3
- package/rust/core/audio/interpreter/mod.rs +1 -0
- package/rust/core/audio/interpreter/spawn.rs +43 -42
- package/rust/core/audio/interpreter/trigger.rs +1 -1
- package/rust/core/audio/renderer.rs +15 -18
- package/rust/core/lexer/handler/arrow.rs +31 -0
- package/rust/core/lexer/handler/driver.rs +12 -1
- package/rust/core/lexer/handler/identifier.rs +1 -0
- package/rust/core/lexer/handler/mod.rs +1 -0
- package/rust/core/lexer/mod.rs +24 -3
- package/rust/core/lexer/token.rs +4 -0
- package/rust/core/parser/driver.rs +23 -4
- package/rust/core/parser/handler/arrow_call.rs +126 -0
- 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 +24 -18
- package/rust/core/parser/handler/mod.rs +2 -1
- package/rust/core/parser/statement.rs +8 -0
- package/rust/core/preprocessor/loader.rs +57 -43
- package/rust/core/preprocessor/module.rs +3 -6
- package/rust/core/preprocessor/processor.rs +13 -4
- package/rust/core/preprocessor/resolver/call.rs +99 -29
- package/rust/core/preprocessor/resolver/condition.rs +38 -12
- package/rust/core/preprocessor/resolver/driver.rs +74 -29
- package/rust/core/preprocessor/resolver/group.rs +24 -81
- 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 +5 -1
- package/rust/core/preprocessor/resolver/spawn.rs +41 -36
- 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/parser/handler/identifier.rs +0 -262
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
|
|
3
|
+
use crate::{
|
|
4
|
+
core::{
|
|
5
|
+
parser::statement::{ Statement, StatementKind },
|
|
6
|
+
preprocessor::{ module::Module, resolver::driver::resolve_statement },
|
|
7
|
+
shared::value::Value,
|
|
8
|
+
store::{ global::GlobalStore, variable::VariableTable },
|
|
9
|
+
},
|
|
10
|
+
utils::logger::{ LogLevel, Logger },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
fn find_export_value(name: &str, global_store: &GlobalStore) -> Option<Value> {
|
|
14
|
+
for (_path, module) in &global_store.modules {
|
|
15
|
+
if let Some(val) = module.export_table.get_export(name) {
|
|
16
|
+
return Some(val.clone());
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
None
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pub fn resolve_value(value: &Value, module: &Module, global_store: &mut GlobalStore) -> Value {
|
|
23
|
+
match value {
|
|
24
|
+
Value::Identifier(name) => {
|
|
25
|
+
if let Some(original_val) = module.variable_table.get(name) {
|
|
26
|
+
return resolve_value(original_val, module, global_store);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if let Some(export_val) = find_export_value(name, global_store) {
|
|
30
|
+
return resolve_value(&export_val, module, global_store);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
eprintln!("⚠️ Unresolved identifier '{}'", name);
|
|
34
|
+
Value::Null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
Value::Map(map) => {
|
|
38
|
+
if let Some(Value::Identifier(entity)) = map.get("entity") {
|
|
39
|
+
// SECTION Synth
|
|
40
|
+
if entity == "synth" {
|
|
41
|
+
if let Some(Value::Map(synth_data)) = map.get("value") {
|
|
42
|
+
let resolved_waveform = synth_data
|
|
43
|
+
.get("waveform")
|
|
44
|
+
.map(|wf| resolve_value(wf, module, global_store))
|
|
45
|
+
.unwrap_or(Value::Null);
|
|
46
|
+
|
|
47
|
+
let resolved_params = synth_data
|
|
48
|
+
.get("parameters")
|
|
49
|
+
.map(|p| resolve_value(p, module, global_store))
|
|
50
|
+
.unwrap_or(Value::Map(HashMap::new()));
|
|
51
|
+
|
|
52
|
+
let mut result = HashMap::new();
|
|
53
|
+
result.insert("waveform".to_string(), resolved_waveform);
|
|
54
|
+
result.insert("parameters".to_string(), resolved_params);
|
|
55
|
+
|
|
56
|
+
return Value::Map(result);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let mut resolved = HashMap::new();
|
|
62
|
+
for (k, v) in map {
|
|
63
|
+
resolved.insert(k.clone(), resolve_value(v, module, global_store));
|
|
64
|
+
}
|
|
65
|
+
Value::Map(resolved)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Value::Block(stmts) => {
|
|
69
|
+
let resolved_stmts = stmts
|
|
70
|
+
.iter()
|
|
71
|
+
.map(|stmt| resolve_statement(stmt, module, &module.path, global_store))
|
|
72
|
+
.collect();
|
|
73
|
+
Value::Block(resolved_stmts)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
other => other.clone(),
|
|
77
|
+
}
|
|
78
|
+
}
|
package/rust/core/utils/path.rs
CHANGED
|
@@ -1,46 +1,31 @@
|
|
|
1
|
-
use std::path::{ Component, Path };
|
|
1
|
+
use std::path::{ Component, Path, PathBuf };
|
|
2
2
|
|
|
3
|
-
pub fn find_entry_file(
|
|
4
|
-
let path = Path::new(
|
|
3
|
+
pub fn find_entry_file(entry: &str) -> Option<String> {
|
|
4
|
+
let path = Path::new(entry);
|
|
5
5
|
|
|
6
|
-
// Check if the path is a file
|
|
7
6
|
if path.is_file() {
|
|
8
|
-
return Some(
|
|
7
|
+
return Some(normalize_path(entry));
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
// Check if the path is a directory
|
|
12
10
|
if path.is_dir() {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return Some(index_path.to_string_lossy().to_string());
|
|
11
|
+
let candidate = path.join("index.deva");
|
|
12
|
+
if candidate.exists() {
|
|
13
|
+
return Some(normalize_path(&candidate));
|
|
17
14
|
}
|
|
18
15
|
}
|
|
19
16
|
|
|
20
17
|
None
|
|
21
18
|
}
|
|
22
19
|
|
|
23
|
-
pub fn normalize_path(path:
|
|
24
|
-
let
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
for comp in Path::new(path).components() {
|
|
28
|
-
match comp {
|
|
29
|
-
Component::CurDir => {
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
Component::Normal(c) => components.push(c),
|
|
33
|
-
Component::RootDir => components.clear(),
|
|
34
|
-
_ => {}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Join the components into a normalized path
|
|
39
|
-
let normalized = components
|
|
40
|
-
.iter()
|
|
41
|
-
.map(|c| c.to_string_lossy())
|
|
42
|
-
.collect::<Vec<_>>()
|
|
43
|
-
.join("/");
|
|
20
|
+
pub fn normalize_path<P: AsRef<Path>>(path: P) -> String {
|
|
21
|
+
let path_buf = PathBuf::from(path.as_ref());
|
|
22
|
+
path_buf.components().collect::<PathBuf>().to_string_lossy().replace('\\', "/")
|
|
23
|
+
}
|
|
44
24
|
|
|
45
|
-
|
|
25
|
+
pub fn resolve_relative_path(base: &str, import: &str) -> String {
|
|
26
|
+
let base_path = Path::new(base)
|
|
27
|
+
.parent()
|
|
28
|
+
.unwrap_or_else(|| Path::new(""));
|
|
29
|
+
let full_path = base_path.join(import);
|
|
30
|
+
full_path.components().collect::<PathBuf>().to_string_lossy().replace("\\", "/")
|
|
46
31
|
}
|
|
@@ -1,35 +1,37 @@
|
|
|
1
1
|
use crate::core::{ preprocessor::module::Module, shared::value::Value, store::global::GlobalStore };
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
let built_ins = ["kick", "snare", "hat", "clap"];
|
|
3
|
+
// NOTE: Deprecated functions, kept for reference
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
5
|
+
// pub fn is_valid_entity(entity: &str, module: &Module, global_store: &GlobalStore) -> bool {
|
|
6
|
+
// let built_ins = ["kick", "snare", "hat", "clap"];
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
_ => false,
|
|
14
|
-
}
|
|
15
|
-
} else {
|
|
16
|
-
false
|
|
17
|
-
}
|
|
18
|
-
}
|
|
8
|
+
// if built_ins.contains(&entity) {
|
|
9
|
+
// return true;
|
|
10
|
+
// }
|
|
19
11
|
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
// if let Some(val) = module.variable_table.get(entity) {
|
|
13
|
+
// match val {
|
|
14
|
+
// Value::Sample(_) => true,
|
|
15
|
+
// _ => false,
|
|
16
|
+
// }
|
|
17
|
+
// } else {
|
|
18
|
+
// false
|
|
19
|
+
// }
|
|
20
|
+
// }
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
22
|
+
// pub fn is_valid_identifier(ident: &str, module: &Module) -> bool {
|
|
23
|
+
// let built_ins = ["auto"];
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
25
|
+
// if built_ins.contains(&ident) {
|
|
26
|
+
// return true;
|
|
27
|
+
// }
|
|
28
|
+
|
|
29
|
+
// if let Some(val) = module.variable_table.get(ident) {
|
|
30
|
+
// match val {
|
|
31
|
+
// Value::Identifier(_) => true,
|
|
32
|
+
// _ => false,
|
|
33
|
+
// }
|
|
34
|
+
// } else {
|
|
35
|
+
// false
|
|
36
|
+
// }
|
|
37
|
+
// }
|
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
2
|
import { execSync } from "child_process";
|
|
4
3
|
|
|
5
4
|
export const fetchVersion = async (projectVersionPath: string) => {
|
|
6
|
-
// Lire le fichier
|
|
7
5
|
const data = JSON.parse(fs.readFileSync(projectVersionPath, "utf-8"));
|
|
8
6
|
|
|
9
|
-
// Incrémenter le numéro de build
|
|
10
7
|
data.build = (data.build || 0) + 1;
|
|
11
8
|
|
|
12
|
-
// Récupérer le dernier hash git
|
|
13
9
|
try {
|
|
14
10
|
const commit = execSync("git rev-parse HEAD").toString().trim();
|
|
15
11
|
data.lastCommit = commit;
|
|
16
12
|
} catch (err) {
|
|
17
|
-
console.warn("⚠️
|
|
13
|
+
console.warn("⚠️ Unable to fetch git commit hash. Ensure you are in a git repository.");
|
|
18
14
|
}
|
|
19
15
|
|
|
20
|
-
// Écrire la mise à jour
|
|
21
16
|
fs.writeFileSync(projectVersionPath, JSON.stringify(data, null, 2));
|
|
22
17
|
}
|
|
23
18
|
|
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
use crate::core::{
|
|
2
|
-
lexer::token::{ Token, TokenKind },
|
|
3
|
-
parser::{ statement::{ Statement, StatementKind }, driver::Parser },
|
|
4
|
-
shared::value::Value,
|
|
5
|
-
store::global::GlobalStore,
|
|
6
|
-
};
|
|
7
|
-
use std::collections::HashMap;
|
|
8
|
-
|
|
9
|
-
pub fn parse_identifier_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
|
|
10
|
-
let Some(current_token) = parser.peek_clone() else {
|
|
11
|
-
return Statement::unknown();
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
if current_token.lexeme == "let" {
|
|
15
|
-
parser.advance(); // consume "let"
|
|
16
|
-
|
|
17
|
-
let identifier = if let Some(token) = parser.peek_clone() {
|
|
18
|
-
if token.kind == TokenKind::Identifier {
|
|
19
|
-
parser.advance();
|
|
20
|
-
token.lexeme.clone()
|
|
21
|
-
} else {
|
|
22
|
-
return Statement::error(token, "Expected identifier after 'let'".to_string());
|
|
23
|
-
}
|
|
24
|
-
} else {
|
|
25
|
-
return Statement::error(current_token, "Expected identifier after 'let'".to_string());
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
if !parser.match_token(TokenKind::Equals) {
|
|
29
|
-
return Statement::error(current_token, "Expected '=' after identifier".to_string());
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
let value = match parser.peek_clone() {
|
|
33
|
-
Some(token) if token.kind == TokenKind::Identifier => {
|
|
34
|
-
parser.advance();
|
|
35
|
-
Value::Identifier(token.lexeme.clone())
|
|
36
|
-
}
|
|
37
|
-
Some(token) if token.kind == TokenKind::String => {
|
|
38
|
-
parser.advance();
|
|
39
|
-
Value::String(token.lexeme.clone())
|
|
40
|
-
}
|
|
41
|
-
Some(token) if token.kind == TokenKind::Number => {
|
|
42
|
-
parser.advance();
|
|
43
|
-
Value::Number(token.lexeme.parse().unwrap_or(0.0))
|
|
44
|
-
}
|
|
45
|
-
Some(token) if token.kind == TokenKind::LBrace => {
|
|
46
|
-
parser.advance(); // consume '{'
|
|
47
|
-
let mut map = HashMap::new();
|
|
48
|
-
|
|
49
|
-
while let Some(key_token) = parser.peek_clone() {
|
|
50
|
-
if key_token.kind == TokenKind::RBrace {
|
|
51
|
-
parser.advance(); // consume '}'
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if key_token.kind != TokenKind::Identifier {
|
|
56
|
-
return Statement::error(
|
|
57
|
-
token,
|
|
58
|
-
"Expected key identifier in map".to_string()
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
parser.advance();
|
|
62
|
-
let key = key_token.lexeme.clone();
|
|
63
|
-
|
|
64
|
-
if !parser.match_token(TokenKind::Colon) {
|
|
65
|
-
let message = format!("Expected ':' after key '{}'", key);
|
|
66
|
-
return Statement::error(token, message);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
let val = match parser.peek_clone() {
|
|
70
|
-
Some(t) if t.kind == TokenKind::Number => {
|
|
71
|
-
parser.advance();
|
|
72
|
-
Value::Number(t.lexeme.parse().unwrap_or(0.0))
|
|
73
|
-
}
|
|
74
|
-
Some(t) if t.kind == TokenKind::String => {
|
|
75
|
-
parser.advance();
|
|
76
|
-
Value::String(t.lexeme.clone())
|
|
77
|
-
}
|
|
78
|
-
Some(t) if t.kind == TokenKind::Identifier => {
|
|
79
|
-
parser.advance();
|
|
80
|
-
Value::Identifier(t.lexeme.clone())
|
|
81
|
-
}
|
|
82
|
-
_ => { Value::Null }
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
if val == Value::Null {
|
|
86
|
-
let message = format!("Invalid value for key '{}'", key);
|
|
87
|
-
return Statement::error(token, message);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
map.insert(key, val);
|
|
91
|
-
|
|
92
|
-
if let Some(t) = parser.peek() {
|
|
93
|
-
if t.kind == TokenKind::Comma {
|
|
94
|
-
parser.advance(); // skip comma
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
Value::Map(map)
|
|
100
|
-
}
|
|
101
|
-
other => {
|
|
102
|
-
let message = format!("Unexpected value token in let: {:?}", other);
|
|
103
|
-
return Statement::error(current_token, message);
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
return Statement {
|
|
108
|
-
kind: StatementKind::Let { name: identifier },
|
|
109
|
-
value,
|
|
110
|
-
indent: current_token.indent,
|
|
111
|
-
line: current_token.line,
|
|
112
|
-
column: current_token.column,
|
|
113
|
-
};
|
|
114
|
-
} else if current_token.lexeme == "group" {
|
|
115
|
-
parser.advance(); // consume "group"
|
|
116
|
-
|
|
117
|
-
let Some(identifier_token) = parser.peek_clone() else {
|
|
118
|
-
return Statement::error(current_token, "Expected identifier after 'group'".to_string());
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
if
|
|
122
|
-
identifier_token.kind != TokenKind::Identifier &&
|
|
123
|
-
identifier_token.kind != TokenKind::String
|
|
124
|
-
{
|
|
125
|
-
return Statement::error(identifier_token, "Expected valid identifier".to_string());
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
parser.advance(); // consume identifier
|
|
129
|
-
|
|
130
|
-
let Some(colon_token) = parser.peek_clone() else {
|
|
131
|
-
return Statement::error(
|
|
132
|
-
identifier_token,
|
|
133
|
-
"Expected ':' after group identifier".to_string()
|
|
134
|
-
);
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
if colon_token.kind != TokenKind::Colon {
|
|
138
|
-
return Statement::error(
|
|
139
|
-
colon_token.clone(),
|
|
140
|
-
"Expected ':' after group identifier".to_string()
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
parser.advance(); // consume ':'
|
|
145
|
-
|
|
146
|
-
let base_indent = current_token.indent;
|
|
147
|
-
|
|
148
|
-
// Clone without consuming tokens
|
|
149
|
-
let mut index = parser.token_index;
|
|
150
|
-
let mut tokens_inside_group = Vec::new();
|
|
151
|
-
|
|
152
|
-
while index < parser.tokens.len() {
|
|
153
|
-
let token = parser.tokens[index].clone();
|
|
154
|
-
|
|
155
|
-
if token.indent <= base_indent && token.kind != TokenKind::Newline {
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
tokens_inside_group.push(token);
|
|
160
|
-
index += 1;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Advance index once to skip the processed tokens
|
|
164
|
-
parser.token_index = index;
|
|
165
|
-
|
|
166
|
-
let body = parser.parse_block(tokens_inside_group, global_store);
|
|
167
|
-
|
|
168
|
-
let mut value_map = HashMap::new();
|
|
169
|
-
value_map.insert("identifier".to_string(), Value::String(identifier_token.lexeme.clone()));
|
|
170
|
-
value_map.insert("body".to_string(), Value::Block(body));
|
|
171
|
-
|
|
172
|
-
return Statement {
|
|
173
|
-
kind: StatementKind::Group,
|
|
174
|
-
value: Value::Map(value_map),
|
|
175
|
-
indent: current_token.indent,
|
|
176
|
-
line: current_token.line,
|
|
177
|
-
column: current_token.column,
|
|
178
|
-
};
|
|
179
|
-
} else if current_token.lexeme == "call" {
|
|
180
|
-
parser.advance(); // consume "call"
|
|
181
|
-
|
|
182
|
-
let identifier = if let Some(token) = parser.peek_clone() {
|
|
183
|
-
if token.kind == TokenKind::Identifier {
|
|
184
|
-
parser.advance();
|
|
185
|
-
token.lexeme.clone()
|
|
186
|
-
} else {
|
|
187
|
-
return Statement::error(token, "Expected identifier after 'call'".to_string());
|
|
188
|
-
}
|
|
189
|
-
} else {
|
|
190
|
-
return Statement::error(current_token, "Expected identifier after 'call'".to_string());
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
return Statement {
|
|
194
|
-
kind: StatementKind::Call,
|
|
195
|
-
value: Value::String(identifier),
|
|
196
|
-
indent: current_token.indent,
|
|
197
|
-
line: current_token.line,
|
|
198
|
-
column: current_token.column,
|
|
199
|
-
};
|
|
200
|
-
} else if current_token.lexeme == "spawn" {
|
|
201
|
-
parser.advance(); // consume "spawn"
|
|
202
|
-
|
|
203
|
-
let identifier = if let Some(token) = parser.peek_clone() {
|
|
204
|
-
if token.kind == TokenKind::Identifier {
|
|
205
|
-
parser.advance();
|
|
206
|
-
token.lexeme.clone()
|
|
207
|
-
} else {
|
|
208
|
-
return Statement::error(token, "Expected identifier after 'spawn'".to_string());
|
|
209
|
-
}
|
|
210
|
-
} else {
|
|
211
|
-
return Statement::error(current_token, "Expected identifier after 'spawn'".to_string());
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
return Statement {
|
|
215
|
-
kind: StatementKind::Spawn,
|
|
216
|
-
value: Value::String(identifier),
|
|
217
|
-
indent: current_token.indent,
|
|
218
|
-
line: current_token.line,
|
|
219
|
-
column: current_token.column,
|
|
220
|
-
};
|
|
221
|
-
} else if current_token.lexeme == "sleep" {
|
|
222
|
-
parser.advance(); // consume "sleep"
|
|
223
|
-
|
|
224
|
-
let duration = if let Some(token) = parser.peek_clone() {
|
|
225
|
-
if token.kind == TokenKind::Number {
|
|
226
|
-
parser.advance();
|
|
227
|
-
token.lexeme.parse().unwrap_or(0.0)
|
|
228
|
-
} else {
|
|
229
|
-
return Statement::error(token, "Expected number after 'sleep'".to_string());
|
|
230
|
-
}
|
|
231
|
-
} else {
|
|
232
|
-
return Statement::error(current_token, "Expected number after 'sleep'".to_string());
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
return Statement {
|
|
236
|
-
kind: StatementKind::Sleep,
|
|
237
|
-
value: Value::Number(duration),
|
|
238
|
-
indent: current_token.indent,
|
|
239
|
-
line: current_token.line,
|
|
240
|
-
column: current_token.column,
|
|
241
|
-
};
|
|
242
|
-
} else {
|
|
243
|
-
// Unknown identifier handling
|
|
244
|
-
Statement {
|
|
245
|
-
kind: StatementKind::Unknown,
|
|
246
|
-
value: Value::String(current_token.lexeme.clone()),
|
|
247
|
-
indent: current_token.indent,
|
|
248
|
-
line: current_token.line,
|
|
249
|
-
column: current_token.column,
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
parser.advance(); // unknown identifier fallback
|
|
254
|
-
|
|
255
|
-
Statement {
|
|
256
|
-
kind: StatementKind::Unknown,
|
|
257
|
-
value: Value::String(current_token.lexeme.clone()),
|
|
258
|
-
indent: current_token.indent,
|
|
259
|
-
line: current_token.line,
|
|
260
|
-
column: current_token.column,
|
|
261
|
-
}
|
|
262
|
-
}
|