@devaloop/devalang 0.0.1-alpha.7 → 0.0.1-alpha.8
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 +29 -10
- package/docs/CHANGELOG.md +22 -2
- package/docs/ROADMAP.md +2 -2
- package/docs/SYNTAX.md +41 -7
- package/docs/TODO.md +3 -3
- package/examples/condition.deva +24 -0
- package/examples/index.deva +4 -5
- package/examples/variables.deva +1 -1
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +1 -1
- package/project-version.json +3 -3
- package/rust/cli/build.rs +6 -1
- package/rust/core/audio/evaluator.rs +31 -0
- package/rust/core/audio/interpreter/call.rs +42 -0
- package/rust/core/audio/interpreter/condition.rs +65 -0
- package/rust/core/audio/interpreter/driver.rs +204 -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 +59 -0
- package/rust/core/audio/interpreter/mod.rs +11 -0
- package/rust/core/audio/interpreter/sleep.rs +36 -0
- package/rust/core/audio/interpreter/spawn.rs +65 -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/{render.rs → renderer.rs} +6 -2
- 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/driver.rs +215 -0
- package/rust/core/lexer/handler/identifier.rs +2 -0
- package/rust/core/lexer/handler/mod.rs +3 -227
- package/rust/core/lexer/handler/operator.rs +44 -0
- package/rust/core/lexer/mod.rs +1 -1
- package/rust/core/lexer/token.rs +36 -9
- package/rust/core/parser/driver.rs +312 -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.rs +38 -36
- package/rust/core/parser/handler/loop_.rs +1 -1
- package/rust/core/parser/handler/mod.rs +2 -1
- package/rust/core/parser/handler/tempo.rs +1 -1
- package/rust/core/parser/mod.rs +3 -237
- package/rust/core/parser/statement.rs +29 -36
- package/rust/core/preprocessor/loader.rs +7 -6
- package/rust/core/preprocessor/processor.rs +1 -1
- package/rust/core/preprocessor/resolver/call.rs +53 -0
- package/rust/core/preprocessor/resolver/condition.rs +66 -0
- package/rust/core/preprocessor/resolver/driver.rs +182 -0
- package/rust/core/preprocessor/resolver/group.rs +89 -84
- package/rust/core/preprocessor/resolver/mod.rs +5 -153
- package/rust/core/preprocessor/resolver/spawn.rs +53 -0
- package/rust/core/audio/interpreter.rs +0 -317
- package/rust/core/lexer/handler/equal.rs +0 -32
package/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "devalang"
|
|
3
|
-
version = "0.0.1-alpha.
|
|
3
|
+
version = "0.0.1-alpha.8"
|
|
4
4
|
authors = ["Devaloop <contact@devaloop.com>"]
|
|
5
5
|
description = "Write music like code. Devalang is a domain-specific language (DSL) for sound designers and music hackers. Compose, automate, and control sound — in plain text."
|
|
6
6
|
license = "MIT"
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
## 🎼 Devalang, by **Devaloop Labs**
|
|
20
20
|
|
|
21
|
-
🎶 Compose music with code —
|
|
21
|
+
🎶 Compose music with code — structured, expressive, and fast.
|
|
22
22
|
|
|
23
23
|
Devalang is a tiny domain-specific language (DSL) for music makers, sound designers, and audio hackers.
|
|
24
24
|
Compose loops, control samples, render and play audio — all in clean, readable text.
|
|
@@ -27,11 +27,13 @@ Compose loops, control samples, render and play audio — all in clean, readable
|
|
|
27
27
|
|
|
28
28
|
From studio sketches to live sets, Devalang gives you rhythmic control — with the elegance of code.
|
|
29
29
|
|
|
30
|
-
> 🚧 **v0.0.1-alpha.
|
|
30
|
+
> 🚧 **v0.0.1-alpha.8 Notice** 🚧
|
|
31
31
|
>
|
|
32
32
|
> NEW: Devalang VSCode extension is now available !
|
|
33
33
|
> [Get it here](https://marketplace.visualstudio.com/items?itemName=devaloop.devalang-vscode).
|
|
34
34
|
>
|
|
35
|
+
> NEW: Devalang supports conditional statements (`if`, `else`, `else if`) for more dynamic compositions !
|
|
36
|
+
>
|
|
35
37
|
> Currently, Devalang CLI is only available for **Windows**.
|
|
36
38
|
> Linux and macOS binaries will be added in future releases via cross-platform builds.
|
|
37
39
|
|
|
@@ -45,6 +47,15 @@ From studio sketches to live sets, Devalang gives you rhythmic control — with
|
|
|
45
47
|
- [🎨 Prettier Plugin](https://www.npmjs.com/package/@devaloop/prettier-plugin-devalang)
|
|
46
48
|
- [🌐 Project Website](https://devalang.com)
|
|
47
49
|
|
|
50
|
+
## ❓ Why Devalang?
|
|
51
|
+
|
|
52
|
+
- 🎹 Prototype audio ideas without opening a DAW
|
|
53
|
+
- 💻 Integrate sound into code-based workflows
|
|
54
|
+
- 🎛️ Control audio parameters through readable syntax
|
|
55
|
+
- 🧪 Build musical logic with variables and conditions
|
|
56
|
+
|
|
57
|
+
> Producer, coder, or both — Devalang gives you musical structure, instantly.
|
|
58
|
+
|
|
48
59
|
## 🚀 Features
|
|
49
60
|
|
|
50
61
|
- 🎵 **Audio Engine**: Integrated audio playback and rendering
|
|
@@ -53,6 +64,8 @@ From studio sketches to live sets, Devalang gives you rhythmic control — with
|
|
|
53
64
|
- 🔢 **Basic data types**: strings, numbers, booleans, maps, arrays
|
|
54
65
|
- 👁️ **Watch mode** for `build`, `check` and `play` commands
|
|
55
66
|
- 📂 **Project templates** for quick setup
|
|
67
|
+
- 🎛️ **Custom samples**: easily load and trigger your own audio files
|
|
68
|
+
- 🔄 **Looping and grouping**: create complex patterns with ease
|
|
56
69
|
|
|
57
70
|
## 📆 Installation
|
|
58
71
|
|
|
@@ -63,13 +76,13 @@ From studio sketches to live sets, Devalang gives you rhythmic control — with
|
|
|
63
76
|
Install the package globally (NPM)
|
|
64
77
|
|
|
65
78
|
```bash
|
|
66
|
-
npm install -g @devaloop/devalang
|
|
79
|
+
npm install -g @devaloop/devalang@latest
|
|
67
80
|
```
|
|
68
81
|
|
|
69
82
|
Usage without install (NPX)
|
|
70
83
|
|
|
71
84
|
```bash
|
|
72
|
-
npx @devaloop/devalang
|
|
85
|
+
npx @devaloop/devalang@latest
|
|
73
86
|
```
|
|
74
87
|
|
|
75
88
|
### For contributors
|
|
@@ -78,10 +91,11 @@ npx @devaloop/devalang <command>
|
|
|
78
91
|
> - ⚠️ Requires [Rust 1.70+](https://www.rust-lang.org/learn/get-started#installing-rust)
|
|
79
92
|
|
|
80
93
|
```bash
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
94
|
+
git clone https://github.com/devaloop-labs/devalang.git
|
|
95
|
+
|
|
96
|
+
cd devalang
|
|
97
|
+
|
|
98
|
+
npm install
|
|
85
99
|
```
|
|
86
100
|
|
|
87
101
|
Development usage (you can customize arguments in package.json)
|
|
@@ -89,6 +103,7 @@ Development usage (you can customize arguments in package.json)
|
|
|
89
103
|
```bash
|
|
90
104
|
# For syntax checking test
|
|
91
105
|
npm run rust:dev:check
|
|
106
|
+
|
|
92
107
|
# For building test
|
|
93
108
|
npm run rust:dev:build
|
|
94
109
|
```
|
|
@@ -182,9 +197,12 @@ group myGroup:
|
|
|
182
197
|
call myGroup
|
|
183
198
|
|
|
184
199
|
# Will be executed in parallel (concurrently)
|
|
200
|
+
# ⚠️ Note: `spawn` runs the entire group in parallel, but the group’s internal logic remains sequential unless it uses `spawn` internally.
|
|
185
201
|
# spawn myGroup
|
|
186
202
|
```
|
|
187
203
|
|
|
204
|
+
> 🧠 Note: `call` and `spawn` only work with `group` blocks. They do not apply to individual samples or other statements.
|
|
205
|
+
|
|
188
206
|
```deva
|
|
189
207
|
# variables.deva
|
|
190
208
|
|
|
@@ -197,14 +215,15 @@ let kickDuration = 500
|
|
|
197
215
|
|
|
198
216
|
## 🧯 Known issues
|
|
199
217
|
|
|
200
|
-
- No
|
|
218
|
+
- No smart modules yet, all groups, variables, and samples must be explicitly imported where used
|
|
219
|
+
- No support yet for `pattern`, `function`, ... statements
|
|
201
220
|
- No support yet for cross-platform builds (Linux, macOS)
|
|
202
221
|
|
|
203
222
|
## 🧪 Roadmap Highlights
|
|
204
223
|
|
|
205
224
|
For more info, see [docs/ROADMAP.md](./docs/ROADMAP.md)
|
|
206
225
|
|
|
207
|
-
- ⏳ Other statements (e.g `
|
|
226
|
+
- ⏳ Other statements (e.g `function`, `pattern`, ...)
|
|
208
227
|
- ⏳ Cross-platform support (Linux, macOS)
|
|
209
228
|
- ⏳ More built-in instruments (e.g. snare, hi-hat, etc.)
|
|
210
229
|
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -4,11 +4,31 @@
|
|
|
4
4
|
|
|
5
5
|
# Changelog
|
|
6
6
|
|
|
7
|
-
## Version 0.0.1-alpha.
|
|
7
|
+
## Version 0.0.1-alpha.8 (2025-07-12)
|
|
8
|
+
|
|
9
|
+
### Syntax
|
|
10
|
+
|
|
11
|
+
- Implemented `if` directive to conditionally execute blocks of code.
|
|
12
|
+
- Implemented `else` directive to provide an alternative block of code when the `if` condition is not met.
|
|
13
|
+
- Implemented `else if` directive to provide additional conditions for the `if` directive.
|
|
14
|
+
|
|
15
|
+
### Core Components
|
|
16
|
+
|
|
17
|
+
- Implemented evaluator for audio statements, to execute conditional statements.
|
|
18
|
+
- Fixed `group` resolution and export issues.
|
|
19
|
+
- Implemented `Global Store` debugger to inspect variables by module for build command.
|
|
20
|
+
- Organized `TokenKind` and `StatementKind` enums for better clarity and maintainability.
|
|
21
|
+
- Refactored audio interpreter to handle the new syntax and directives.
|
|
22
|
+
- Refactored lexer to handle new directives and improve tokenization.
|
|
23
|
+
- Refactored parser to handle new directives and improve parsing logic.
|
|
24
|
+
- Added support for `call` and `spawn` execution of imported groups.
|
|
25
|
+
- Enforced scoped resolution of groups in `spawn` and `call` (must be imported in current module).
|
|
26
|
+
|
|
27
|
+
## Version 0.0.1-alpha.7 (2025-07-11)
|
|
8
28
|
|
|
9
29
|
## Examples
|
|
10
30
|
|
|
11
|
-
- Added
|
|
31
|
+
- Added examples in `examples` folder (group, loop, variables, index).
|
|
12
32
|
|
|
13
33
|
## Structure
|
|
14
34
|
|
package/docs/ROADMAP.md
CHANGED
|
@@ -24,8 +24,8 @@ Devalang is a work in progress. Here’s what we’re planning next:
|
|
|
24
24
|
|
|
25
25
|
## Upcoming
|
|
26
26
|
|
|
27
|
+
- ⏳ **Smart modules**: Let Devalang detect and use groups, samples, and variables without needing to import them manually.
|
|
27
28
|
- ⏳ **VSCode extension**: Create a VSCode extension for syntax highlighting and code completion.
|
|
28
|
-
- ⏳ **Other statements**: Implement `
|
|
29
|
-
- ⏳ **Pattern and group statements**: Add support for `@pattern` and `@group` to organize code.
|
|
29
|
+
- ⏳ **Other statements**: Implement `pattern`, `function`, and other control structures.
|
|
30
30
|
- ⏳ **Functions**: Add support for defining and calling functions.
|
|
31
31
|
- ⏳ **Testing**: Expand test coverage for all features.
|
package/docs/SYNTAX.md
CHANGED
|
@@ -58,7 +58,7 @@ let boolean = false
|
|
|
58
58
|
Maps are key-value pairs defined using curly braces. Keys are strings, and values can be of any type (string, number, boolean, map, or array).
|
|
59
59
|
|
|
60
60
|
```deva
|
|
61
|
-
let map = {myKey: 99}
|
|
61
|
+
let map = { myKey: 99 }
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
### Array
|
|
@@ -93,13 +93,17 @@ bank 808
|
|
|
93
93
|
|
|
94
94
|
Modules can be imported and exported to share variables between different files.
|
|
95
95
|
|
|
96
|
+
> ⚠️ The import/export system is still experimental and may change in the future.
|
|
97
|
+
>
|
|
98
|
+
> You must explicitly declare imports and exports in each file — Devalang does not automatically detect or resolve them.
|
|
99
|
+
|
|
96
100
|
Exporting variables from a module :
|
|
97
101
|
|
|
98
102
|
```deva
|
|
99
103
|
# exported.deva
|
|
100
104
|
|
|
101
105
|
let exportedIterator = 10
|
|
102
|
-
let exportedParams = {drive: 50, decay: 30}
|
|
106
|
+
let exportedParams = { drive: 50, decay: 30 }
|
|
103
107
|
|
|
104
108
|
@export { exportedIterator, exportedParams }
|
|
105
109
|
```
|
|
@@ -115,7 +119,7 @@ loop exportedIterator:
|
|
|
115
119
|
.kick auto exportedParams
|
|
116
120
|
```
|
|
117
121
|
|
|
118
|
-
###
|
|
122
|
+
### Loading Samples
|
|
119
123
|
|
|
120
124
|
You can load your own samples and use them in your music.
|
|
121
125
|
|
|
@@ -129,7 +133,7 @@ Trigger usage : `.<name> <duration> <params>`
|
|
|
129
133
|
.mySample auto {reverb: 50, drive: 25}
|
|
130
134
|
```
|
|
131
135
|
|
|
132
|
-
###
|
|
136
|
+
### Variables
|
|
133
137
|
|
|
134
138
|
Variables are defined using the `let` keyword, followed by the variable name and its value. The value can be of any type (string, number, boolean, map, or array).
|
|
135
139
|
|
|
@@ -137,7 +141,7 @@ Variables are defined using the `let` keyword, followed by the variable name and
|
|
|
137
141
|
let number = 0
|
|
138
142
|
let boolean = true
|
|
139
143
|
let string = "string"
|
|
140
|
-
let map = {myKey: 200}
|
|
144
|
+
let map = { myKey: 200 }
|
|
141
145
|
let array = [0, 1, 2]
|
|
142
146
|
```
|
|
143
147
|
|
|
@@ -154,14 +158,40 @@ loop 10:
|
|
|
154
158
|
|
|
155
159
|
Groups are defined using the `group` keyword, followed by the group name. The body of the group is indented.
|
|
156
160
|
|
|
161
|
+
Groups allow you to organize your code into reusable blocks. They can be called or spawned later in the code.
|
|
162
|
+
|
|
157
163
|
```deva
|
|
158
164
|
group myGroup:
|
|
159
165
|
# ...
|
|
160
166
|
```
|
|
161
167
|
|
|
168
|
+
### Conditions
|
|
169
|
+
|
|
170
|
+
Conditions are defined using the `if` keyword, followed by a condition. The body of the condition is indented.
|
|
171
|
+
|
|
172
|
+
```deva
|
|
173
|
+
if myCondition:
|
|
174
|
+
# ...
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
You can also use `else` and `else if` for alternative branches:
|
|
178
|
+
|
|
179
|
+
```deva
|
|
180
|
+
if myCondition:
|
|
181
|
+
# ...
|
|
182
|
+
else if anotherCondition:
|
|
183
|
+
# ...
|
|
184
|
+
else:
|
|
185
|
+
# ...
|
|
186
|
+
```
|
|
187
|
+
|
|
162
188
|
### Calling Groups (Sequential Execution)
|
|
163
189
|
|
|
164
|
-
Groups can be called using the `call` keyword, which executes the group in sequence.
|
|
190
|
+
Groups can be called using the `call` keyword, which executes only the group in sequence.
|
|
191
|
+
|
|
192
|
+
> ⚠️ `call` only works on `group` declarations. It does not apply to other statements.
|
|
193
|
+
|
|
194
|
+
This executes the entire group in the current execution thread, following a sequential order.
|
|
165
195
|
|
|
166
196
|
```deva
|
|
167
197
|
call myGroup
|
|
@@ -169,7 +199,11 @@ call myGroup
|
|
|
169
199
|
|
|
170
200
|
### Spawning Groups (Parallel Execution)
|
|
171
201
|
|
|
172
|
-
Groups can be spawned using the `spawn` keyword, which executes the group in parallel.
|
|
202
|
+
Groups can be spawned using the `spawn` keyword, which executes only the group in parallel.
|
|
203
|
+
|
|
204
|
+
> ⚠️ spawn also only works on group declarations. It does not make the group’s content parallel unless it explicitly uses spawn inside.
|
|
205
|
+
|
|
206
|
+
This runs the entire group in a separate execution thread, allowing it to play alongside other actions.
|
|
173
207
|
|
|
174
208
|
```deva
|
|
175
209
|
spawn myGroup
|
package/docs/TODO.md
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# This file demonstrates the use of conditional blocks in Devalang.
|
|
2
|
+
|
|
3
|
+
@import { myGroup } from "./examples/group.deva"
|
|
4
|
+
|
|
5
|
+
@load "./examples/samples/kick-808.wav" as kickCustom
|
|
6
|
+
@load "./examples/samples/hat-808.wav" as hatCustom
|
|
7
|
+
|
|
8
|
+
group conditionBlock:
|
|
9
|
+
if tempo > 120:
|
|
10
|
+
# Will be executed if the condition is true
|
|
11
|
+
.kickCustom
|
|
12
|
+
else if tempo > 155:
|
|
13
|
+
# Will be executed if the condition is false
|
|
14
|
+
.hatCustom
|
|
15
|
+
else:
|
|
16
|
+
# Following lines will be executed if the condition is neither true or false
|
|
17
|
+
|
|
18
|
+
# This will call myGroup sequentially (in the main thread)
|
|
19
|
+
call myGroup
|
|
20
|
+
|
|
21
|
+
# This will spawn a group in parallel to the main thread (kick + hat simultaneously)
|
|
22
|
+
# spawn myGroup
|
|
23
|
+
|
|
24
|
+
@export { conditionBlock }
|
package/examples/index.deva
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# This file demonstrates the use of main features in Devalang.
|
|
2
2
|
|
|
3
3
|
@import { duration, default_bank, params, loopCount, tempo } from "./examples/variables.deva"
|
|
4
|
+
@import { conditionBlock } from "./examples/condition.deva"
|
|
4
5
|
@import { myGroup } from "./examples/group.deva"
|
|
5
6
|
|
|
7
|
+
|
|
6
8
|
@load "./examples/samples/kick-808.wav" as kickCustom
|
|
7
9
|
@load "./examples/samples/hat-808.wav" as hatCustom
|
|
8
10
|
|
|
@@ -10,8 +12,5 @@ bpm tempo
|
|
|
10
12
|
|
|
11
13
|
bank default_bank
|
|
12
14
|
|
|
13
|
-
#
|
|
14
|
-
call
|
|
15
|
-
|
|
16
|
-
# Will be executed in parallel (concurrently)
|
|
17
|
-
# spawn myGroup
|
|
15
|
+
# This will call the group sequentially (line by line)
|
|
16
|
+
call conditionBlock
|
package/examples/variables.deva
CHANGED
package/out-tsc/bin/devalang.exe
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devaloop/devalang",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.0.1-alpha.
|
|
4
|
+
"version": "0.0.1-alpha.8",
|
|
5
5
|
"description": "Write music like code. Devalang is a domain-specific language (DSL) for sound designers and music hackers. Compose, automate, and control sound — in plain text.",
|
|
6
6
|
"main": "out-tsc/index.js",
|
|
7
7
|
"bin": {
|
package/project-version.json
CHANGED
package/rust/cli/build.rs
CHANGED
|
@@ -2,7 +2,11 @@ use crate::{
|
|
|
2
2
|
config::Config,
|
|
3
3
|
core::{
|
|
4
4
|
builder::Builder,
|
|
5
|
-
debugger::{
|
|
5
|
+
debugger::{
|
|
6
|
+
lexer::write_lexer_log_file,
|
|
7
|
+
preprocessor::write_preprocessor_log_file,
|
|
8
|
+
store::write_store_log_file,
|
|
9
|
+
},
|
|
6
10
|
preprocessor::loader::ModuleLoader,
|
|
7
11
|
store::global::GlobalStore,
|
|
8
12
|
utils::path::{ find_entry_file, normalize_path },
|
|
@@ -113,6 +117,7 @@ fn begin_build(entry: String, output: String) {
|
|
|
113
117
|
"resolved_statements.log",
|
|
114
118
|
modules_statements.clone()
|
|
115
119
|
);
|
|
120
|
+
write_store_log_file(&normalized_output_dir, "global_store.log", global_store.modules.clone());
|
|
116
121
|
|
|
117
122
|
// SECTION Building AST and Audio
|
|
118
123
|
let builder = Builder::new();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
use crate::core::{ shared::value::Value, store::variable::VariableTable };
|
|
2
|
+
|
|
3
|
+
pub fn evaluate_condition_string(expr: &str, vars: &VariableTable) -> bool {
|
|
4
|
+
let tokens: Vec<&str> = expr.split_whitespace().collect();
|
|
5
|
+
if tokens.len() != 3 {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let left = tokens[0];
|
|
10
|
+
let op = tokens[1];
|
|
11
|
+
let right = tokens[2];
|
|
12
|
+
|
|
13
|
+
let left_val = match vars.get(left) {
|
|
14
|
+
Some(Value::Number(n)) => *n,
|
|
15
|
+
_ => {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
let right_val: f32 = right.parse().unwrap_or(0.0);
|
|
21
|
+
|
|
22
|
+
match op {
|
|
23
|
+
">" => left_val > right_val,
|
|
24
|
+
"<" => left_val < right_val,
|
|
25
|
+
">=" => left_val >= right_val,
|
|
26
|
+
"<=" => left_val <= right_val,
|
|
27
|
+
"==" => (left_val - right_val).abs() < f32::EPSILON,
|
|
28
|
+
"!=" => (left_val - right_val).abs() > f32::EPSILON,
|
|
29
|
+
_ => false,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
audio::{ engine::AudioEngine, interpreter::{ driver::execute_audio_block } },
|
|
3
|
+
parser::statement::{ Statement, StatementKind },
|
|
4
|
+
shared::{ duration::Duration, value::Value },
|
|
5
|
+
store::variable::VariableTable,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub fn interprete_call_statement(
|
|
9
|
+
stmt: &Statement,
|
|
10
|
+
audio_engine: AudioEngine,
|
|
11
|
+
variable_table: VariableTable,
|
|
12
|
+
base_bpm: f32,
|
|
13
|
+
base_duration: f32,
|
|
14
|
+
max_end_time: f32,
|
|
15
|
+
cursor_time: f32
|
|
16
|
+
) -> (AudioEngine, f32, f32) {
|
|
17
|
+
if let Value::String(identifier) = &stmt.value {
|
|
18
|
+
if let Some(Value::Map(map)) = variable_table.clone().get(identifier) {
|
|
19
|
+
if let Some(Value::Block(block)) = map.get("body") {
|
|
20
|
+
let (eng, _, end_time) = execute_audio_block(
|
|
21
|
+
audio_engine,
|
|
22
|
+
variable_table,
|
|
23
|
+
block.clone(),
|
|
24
|
+
base_bpm,
|
|
25
|
+
base_duration,
|
|
26
|
+
max_end_time,
|
|
27
|
+
cursor_time
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return (eng, max_end_time.max(end_time), end_time);
|
|
31
|
+
} else {
|
|
32
|
+
eprintln!("❌ 'body' must be a block");
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
eprintln!("❌ Call target '{}' not found or invalid", identifier);
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
eprintln!("❌ Invalid call statement: expected string identifier");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
(audio_engine, max_end_time, cursor_time)
|
|
42
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
audio::{ engine::AudioEngine, evaluator::evaluate_condition_string, interpreter::driver::execute_audio_block },
|
|
3
|
+
parser::statement::Statement,
|
|
4
|
+
shared::value::Value,
|
|
5
|
+
store::variable::VariableTable,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub fn interprete_condition_statement(
|
|
9
|
+
stmt: &Statement,
|
|
10
|
+
audio_engine: AudioEngine,
|
|
11
|
+
variable_table: VariableTable,
|
|
12
|
+
base_bpm: f32,
|
|
13
|
+
base_duration: f32,
|
|
14
|
+
max_end_time: f32,
|
|
15
|
+
cursor_time: f32
|
|
16
|
+
) -> (AudioEngine, f32, f32) {
|
|
17
|
+
let mut engine = audio_engine.clone();
|
|
18
|
+
let mut vars = variable_table.clone();
|
|
19
|
+
let mut cur_time = cursor_time;
|
|
20
|
+
let mut max_time = max_end_time;
|
|
21
|
+
|
|
22
|
+
let mut current = stmt.value.clone();
|
|
23
|
+
|
|
24
|
+
loop {
|
|
25
|
+
let Value::Map(map) = current else {
|
|
26
|
+
break;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let should_execute = match map.get("condition") {
|
|
30
|
+
Some(Value::Boolean(b)) => *b,
|
|
31
|
+
Some(Value::String(expr)) => evaluate_condition_string(expr, &vars),
|
|
32
|
+
Some(_) => false,
|
|
33
|
+
None => true,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
if should_execute {
|
|
37
|
+
if let Some(Value::Block(block)) = map.get("body") {
|
|
38
|
+
let (new_engine, _, new_max) = execute_audio_block(
|
|
39
|
+
engine,
|
|
40
|
+
vars,
|
|
41
|
+
block.clone(),
|
|
42
|
+
base_bpm,
|
|
43
|
+
base_duration,
|
|
44
|
+
max_time,
|
|
45
|
+
cur_time
|
|
46
|
+
);
|
|
47
|
+
return (new_engine, new_max, new_max);
|
|
48
|
+
} else {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Advance to the next condition
|
|
54
|
+
match map.get("next") {
|
|
55
|
+
Some(Value::Map(next_map)) => {
|
|
56
|
+
current = Value::Map(next_map.clone());
|
|
57
|
+
}
|
|
58
|
+
_ => {
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
(audio_engine, max_end_time, cursor_time)
|
|
65
|
+
}
|