devalang_wasm/language/syntax/parser/driver/statements/
core.rs

1use super::super::duration::parse_duration_token;
2use super::super::helpers::{parse_array_value, parse_synth_definition};
3use crate::language::syntax::ast::{Statement, StatementKind, Value};
4/// Core statement parsing: tempo, print, let, var, const, sleep, bank
5use anyhow::{Result, anyhow};
6use std::iter::Iterator;
7
8/// Parse tempo/bpm statement
9pub fn parse_tempo(
10    mut parts: impl Iterator<Item = impl AsRef<str>>,
11    line_number: usize,
12) -> Result<Statement> {
13    let value = parts
14        .next()
15        .ok_or_else(|| anyhow!("tempo declaration requires a value"))?;
16    let bpm: f32 = value
17        .as_ref()
18        .parse()
19        .map_err(|_| anyhow!("invalid tempo value: '{}'", value.as_ref()))?;
20    Ok(Statement::tempo(bpm, line_number, 1))
21}
22
23/// Parse print statement
24pub fn parse_print(line: &str, line_number: usize) -> Result<Statement> {
25    let message = line.strip_prefix("print").unwrap().trim();
26    let cleaned = message.trim_matches('"').to_string();
27    Ok(Statement::print(cleaned, line_number, 1))
28}
29
30/// Parse sleep statement
31pub fn parse_sleep(
32    mut parts: impl Iterator<Item = impl AsRef<str>>,
33    line_number: usize,
34) -> Result<Statement> {
35    let value = parts
36        .next()
37        .ok_or_else(|| anyhow!("sleep instruction requires a duration"))?;
38    let duration = parse_duration_token(value.as_ref())?;
39    Ok(Statement::new(
40        StatementKind::Sleep,
41        Value::Duration(duration),
42        0,
43        line_number,
44        1,
45    ))
46}
47
48/// Parse bank statement
49pub fn parse_bank(
50    mut parts: impl Iterator<Item = impl AsRef<str>>,
51    line_number: usize,
52) -> Result<Statement> {
53    let name = parts
54        .next()
55        .ok_or_else(|| anyhow!("bank declaration requires a name"))?
56        .as_ref()
57        .to_string();
58
59    let alias = if let Some(word) = parts.next() {
60        if word.as_ref() == "as" {
61            parts.next().map(|v| v.as_ref().to_string())
62        } else {
63            None
64        }
65    } else {
66        None
67    };
68
69    Ok(Statement::new(
70        StatementKind::Bank { name, alias },
71        Value::Null,
72        0,
73        line_number,
74        1,
75    ))
76}
77
78/// Parse let statement
79pub fn parse_let(
80    line: &str,
81    mut parts: impl Iterator<Item = impl AsRef<str>>,
82    line_number: usize,
83) -> Result<Statement> {
84    let name = parts
85        .next()
86        .ok_or_else(|| anyhow!("let statement requires a name"))?
87        .as_ref()
88        .to_string();
89
90    let remainder = line
91        .splitn(2, '=')
92        .nth(1)
93        .map(|r| r.trim().to_string())
94        .unwrap_or_default();
95
96    let value = if remainder.is_empty() {
97        None
98    } else if remainder.starts_with("synth ") {
99        // Parse synth definition: synth waveform { params }
100        Some(parse_synth_definition(&remainder)?)
101    } else if remainder.starts_with('[') && remainder.ends_with(']') {
102        // Parse as array: ["C4", "E4", "G4"] or [1..10]
103        Some(parse_array_value(&remainder)?)
104    } else {
105        // Try to parse as number first
106        if let Ok(num) = remainder.parse::<f32>() {
107            Some(Value::Number(num))
108        } else {
109            // Otherwise treat as identifier
110            Some(Value::Identifier(remainder))
111        }
112    };
113
114    Ok(Statement::new(
115        StatementKind::Let { name, value },
116        Value::Null,
117        0,
118        line_number,
119        1,
120    ))
121}
122
123/// Parse var statement
124pub fn parse_var(
125    line: &str,
126    mut parts: impl Iterator<Item = impl AsRef<str>>,
127    line_number: usize,
128) -> Result<Statement> {
129    let name = parts
130        .next()
131        .ok_or_else(|| anyhow!("var statement requires a name"))?
132        .as_ref()
133        .to_string();
134
135    let remainder = line
136        .splitn(2, '=')
137        .nth(1)
138        .map(|r| r.trim().to_string())
139        .unwrap_or_default();
140
141    let value = if remainder.is_empty() {
142        None
143    } else {
144        // Try to parse as number first
145        if let Ok(num) = remainder.parse::<f32>() {
146            Some(Value::Number(num))
147        } else {
148            // Otherwise treat as identifier
149            Some(Value::Identifier(remainder))
150        }
151    };
152
153    Ok(Statement::new(
154        StatementKind::Var { name, value },
155        Value::Null,
156        0,
157        line_number,
158        1,
159    ))
160}
161
162/// Parse const statement
163pub fn parse_const(
164    line: &str,
165    mut parts: impl Iterator<Item = impl AsRef<str>>,
166    line_number: usize,
167) -> Result<Statement> {
168    let name = parts
169        .next()
170        .ok_or_else(|| anyhow!("const statement requires a name"))?
171        .as_ref()
172        .to_string();
173
174    let remainder = line
175        .splitn(2, '=')
176        .nth(1)
177        .map(|r| r.trim().to_string())
178        .unwrap_or_default();
179
180    if remainder.is_empty() {
181        return Err(anyhow!("const declaration requires initialization"));
182    }
183
184    // Try to parse as number first
185    let value = if let Ok(num) = remainder.parse::<f32>() {
186        Some(Value::Number(num))
187    } else {
188        // Otherwise treat as identifier
189        Some(Value::Identifier(remainder))
190    };
191
192    Ok(Statement::new(
193        StatementKind::Const { name, value },
194        Value::Null,
195        0,
196        line_number,
197        1,
198    ))
199}