devalang_wasm/language/syntax/ast/nodes/
mod.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5#[serde(tag = "type", content = "value")]
6pub enum DurationValue {
7    Number(f32),
8    Identifier(String),
9    Beat(String),
10    Beats(f32),
11    Milliseconds(f32),
12    Auto,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
16#[serde(tag = "type", content = "value")]
17pub enum Value {
18    Boolean(bool),
19    Number(f32),
20    Duration(DurationValue),
21    Identifier(String),
22    String(String),
23    Array(Vec<Value>),
24    Map(HashMap<String, Value>),
25    Call { name: String, args: Vec<Value> },
26    Block(Vec<Statement>),
27    Sample(String),
28    Beat(String),
29    Statement(Box<Statement>),
30    StatementKind(Box<StatementKind>),
31    Midi(String),
32    Range { start: Box<Value>, end: Box<Value> },
33    Unknown,
34    Null,
35}
36
37impl Default for Value {
38    fn default() -> Self {
39        Value::Null
40    }
41}
42
43impl Value {
44    pub fn get(&self, key: &str) -> Option<&Value> {
45        if let Value::Map(map) = self {
46            map.get(key)
47        } else {
48            None
49        }
50    }
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
54#[serde(tag = "kind")]
55pub enum StatementKind {
56    Tempo {
57        value: f32,
58        body: Option<Vec<Statement>>,
59    },
60    Print,
61    Pattern {
62        name: String,
63        target: Option<String>,
64    },
65    Trigger {
66        entity: String,
67        duration: DurationValue,
68        effects: Option<Value>,
69    },
70    Sleep,
71    Call {
72        name: String,
73        args: Vec<Value>,
74    },
75    Load {
76        source: String,
77        alias: String,
78    },
79    Use {
80        name: String,
81        alias: Option<String>,
82    },
83    UsePlugin {
84        author: String,
85        name: String,
86        alias: String,
87    },
88    Automate {
89        target: String,
90    },
91    ArrowCall {
92        target: String,
93        method: String,
94        args: Vec<Value>,
95    },
96    Function {
97        name: String,
98        parameters: Vec<String>,
99        body: Vec<Statement>,
100    },
101    Assign {
102        target: String,
103        property: String,
104    },
105    Synth,
106    Bank {
107        name: String,
108        alias: Option<String>,
109    },
110    Let {
111        name: String,
112        value: Option<Value>,
113    },
114    Var {
115        name: String,
116        value: Option<Value>,
117    },
118    Const {
119        name: String,
120        value: Option<Value>,
121    },
122    Group {
123        name: String,
124        body: Vec<Statement>,
125        duration: Option<DurationValue>, // NEW: optional duration for "group name during 4 bars:"
126    },
127    Spawn {
128        name: String,
129        args: Vec<Value>,
130    },
131    Loop {
132        count: Value,
133        body: Vec<Statement>,
134    },
135    For {
136        variable: String,
137        iterable: Value,
138        body: Vec<Statement>,
139    },
140    Routing {
141        body: Vec<Statement>,
142    },
143    RoutingNode {
144        name: String,
145        alias: Option<String>,
146    },
147    RoutingFx {
148        target: String,
149        effects: Value,
150    },
151    RoutingRoute {
152        source: String,
153        destination: String,
154        effects: Option<Value>,
155    },
156    RoutingDuck {
157        source: String,
158        destination: String,
159        effect: Value,
160    },
161    RoutingSidechain {
162        source: String,
163        destination: String,
164        effect: Value,
165    },
166    Bind {
167        source: String,
168        target: String,
169    },
170    FxPipeline {
171        effects: Vec<Value>,
172        subject: String,
173    },
174    Node {
175        name: String,
176    },
177    Sidechain {
178        source: String,
179        effect: Value,
180        target: Option<String>,
181    },
182    Include(String),
183    Export {
184        names: Vec<String>,
185        source: String,
186    },
187    Import {
188        names: Vec<String>,
189        source: String,
190    },
191    On {
192        event: String,
193        args: Option<Vec<Value>>,
194        body: Vec<Statement>,
195    },
196    Emit {
197        event: String,
198        payload: Option<Value>,
199    },
200    Fade {
201        direction: String, // "in" or "out"
202        target: String,    // entity.property
203        duration: DurationValue,
204    },
205    Section {
206        name: String,
207        duration: Option<DurationValue>, // Optional duration, auto-calculated if None
208        body: Vec<Statement>,
209    },
210    Timeline {
211        sections: Vec<(String, Option<DurationValue>)>, // (section_name, optional_duration_override)
212    },
213    Schedule {
214        events: Vec<(DurationValue, Option<DurationValue>, Vec<Statement>)>, // (start_time, optional_duration, body)
215    },
216    Stop {
217        target: Option<String>, // None = "stop all", Some("entity") = "stop entity"
218        after: Option<DurationValue>, // Optional delay before stop
219    },
220    If {
221        condition: Value,
222        body: Vec<Statement>,
223        else_body: Option<Vec<Statement>>, // Can contain statements or another If for else if
224    },
225    While {
226        condition: Value,
227        body: Vec<Statement>,
228    },
229    Return {
230        value: Option<Box<Value>>,
231    },
232    Break,
233    Comment,
234    Indent,
235    Dedent,
236    NewLine,
237    Unknown,
238    Error {
239        message: String,
240    },
241}
242
243impl Default for StatementKind {
244    fn default() -> Self {
245        StatementKind::Unknown
246    }
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
250pub struct Statement {
251    pub kind: StatementKind,
252    #[serde(default)]
253    pub value: Value,
254    pub indent: usize,
255    pub line: usize,
256    pub column: usize,
257}
258
259impl Statement {
260    pub fn new(
261        kind: StatementKind,
262        value: Value,
263        indent: usize,
264        line: usize,
265        column: usize,
266    ) -> Self {
267        Self {
268            kind,
269            value,
270            indent,
271            line,
272            column,
273        }
274    }
275
276    pub fn tempo(value: f32, line: usize, column: usize) -> Self {
277        Self::new(
278            StatementKind::Tempo { value, body: None },
279            Value::Null,
280            0,
281            line,
282            column,
283        )
284    }
285
286    pub fn print(message: impl Into<String>, line: usize, column: usize) -> Self {
287        Self::new(
288            StatementKind::Print,
289            Value::String(message.into()),
290            0,
291            line,
292            column,
293        )
294    }
295
296    pub fn trigger(
297        entity: impl Into<String>,
298        duration: DurationValue,
299        effects: Option<Value>,
300        line: usize,
301        column: usize,
302    ) -> Self {
303        Self::new(
304            StatementKind::Trigger {
305                entity: entity.into(),
306                duration,
307                effects,
308            },
309            Value::Null,
310            0,
311            line,
312            column,
313        )
314    }
315
316    pub fn unknown() -> Self {
317        Self::default()
318    }
319
320    pub fn unknown_with_pos(indent: usize, line: usize, column: usize) -> Self {
321        Self::new(StatementKind::Unknown, Value::Null, indent, line, column)
322    }
323
324    pub fn error_with_pos(indent: usize, line: usize, column: usize, message: String) -> Self {
325        Self::new(
326            StatementKind::Error { message },
327            Value::Null,
328            indent,
329            line,
330            column,
331        )
332    }
333}