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
26        .strip_prefix("print")
27        .ok_or_else(|| {
28            anyhow!(
29                "Invalid print statement: expected 'print' keyword at line {}",
30                line_number
31            )
32        })?
33        .trim();
34
35    // If message is a quoted string, keep it as String; if it's a number, parse as Number;
36    // otherwise treat as an identifier (variable name) so it can be resolved at runtime.
37    if message.starts_with('"') && message.ends_with('"') && message.len() >= 2 {
38        let cleaned = message[1..message.len() - 1].to_string();
39        Ok(Statement::new(
40            StatementKind::Print,
41            Value::String(cleaned),
42            0,
43            line_number,
44            1,
45        ))
46    } else if let Ok(num) = message.parse::<f32>() {
47        Ok(Statement::new(
48            StatementKind::Print,
49            Value::Number(num),
50            0,
51            line_number,
52            1,
53        ))
54    } else {
55        // treat as identifier
56        Ok(Statement::new(
57            StatementKind::Print,
58            Value::Identifier(message.to_string()),
59            0,
60            line_number,
61            1,
62        ))
63    }
64}
65
66/// Parse sleep statement
67pub fn parse_sleep(
68    mut parts: impl Iterator<Item = impl AsRef<str>>,
69    line_number: usize,
70) -> Result<Statement> {
71    let value = parts
72        .next()
73        .ok_or_else(|| anyhow!("sleep instruction requires a duration"))?;
74    let duration = parse_duration_token(value.as_ref())?;
75    Ok(Statement::new(
76        StatementKind::Sleep,
77        Value::Duration(duration),
78        0,
79        line_number,
80        1,
81    ))
82}
83
84/// Parse bank statement
85pub fn parse_bank(
86    mut parts: impl Iterator<Item = impl AsRef<str>>,
87    line_number: usize,
88) -> Result<Statement> {
89    let name = parts
90        .next()
91        .ok_or_else(|| anyhow!("bank declaration requires a name"))?
92        .as_ref()
93        .to_string();
94
95    let alias = if let Some(word) = parts.next() {
96        if word.as_ref() == "as" {
97            parts.next().map(|v| v.as_ref().to_string())
98        } else {
99            None
100        }
101    } else {
102        None
103    };
104
105    Ok(Statement::new(
106        StatementKind::Bank { name, alias },
107        Value::Null,
108        0,
109        line_number,
110        1,
111    ))
112}
113
114/// Parse let statement
115pub fn parse_let(
116    line: &str,
117    mut parts: impl Iterator<Item = impl AsRef<str>>,
118    line_number: usize,
119) -> Result<Statement> {
120    let name = parts
121        .next()
122        .ok_or_else(|| anyhow!("let statement requires a name"))?
123        .as_ref()
124        .to_string();
125
126    let remainder = line
127        .splitn(2, '=')
128        .nth(1)
129        .map(|r| r.trim().to_string())
130        .unwrap_or_default();
131
132    let value = if remainder.is_empty() {
133        None
134    } else if remainder.starts_with("synth ") {
135        // Parse synth definition: synth waveform { params }
136        Some(parse_synth_definition(&remainder)?)
137    } else if remainder.starts_with('[') && remainder.ends_with(']') {
138        // Parse as array: ["C4", "E4", "G4"] or [1..10]
139        Some(parse_array_value(&remainder)?)
140    } else {
141        // Try to parse as number first
142        if let Ok(num) = remainder.parse::<f32>() {
143            Some(Value::Number(num))
144        } else {
145            // Otherwise treat as identifier
146            Some(Value::Identifier(remainder))
147        }
148    };
149
150    Ok(Statement::new(
151        StatementKind::Let { name, value },
152        Value::Null,
153        0,
154        line_number,
155        1,
156    ))
157}
158
159/// Parse var statement
160pub fn parse_var(
161    line: &str,
162    mut parts: impl Iterator<Item = impl AsRef<str>>,
163    line_number: usize,
164) -> Result<Statement> {
165    let name = parts
166        .next()
167        .ok_or_else(|| anyhow!("var statement requires a name"))?
168        .as_ref()
169        .to_string();
170
171    let remainder = line
172        .splitn(2, '=')
173        .nth(1)
174        .map(|r| r.trim().to_string())
175        .unwrap_or_default();
176
177    let value = if remainder.is_empty() {
178        None
179    } else {
180        // Try to parse as number first
181        if let Ok(num) = remainder.parse::<f32>() {
182            Some(Value::Number(num))
183        } else {
184            // Otherwise treat as identifier
185            Some(Value::Identifier(remainder))
186        }
187    };
188
189    Ok(Statement::new(
190        StatementKind::Var { name, value },
191        Value::Null,
192        0,
193        line_number,
194        1,
195    ))
196}
197
198/// Parse const statement
199pub fn parse_const(
200    line: &str,
201    mut parts: impl Iterator<Item = impl AsRef<str>>,
202    line_number: usize,
203) -> Result<Statement> {
204    let name = parts
205        .next()
206        .ok_or_else(|| anyhow!("const statement requires a name"))?
207        .as_ref()
208        .to_string();
209
210    let remainder = line
211        .splitn(2, '=')
212        .nth(1)
213        .map(|r| r.trim().to_string())
214        .unwrap_or_default();
215
216    if remainder.is_empty() {
217        return Err(anyhow!("const declaration requires initialization"));
218    }
219
220    // Try to parse as number first
221    let value = if let Ok(num) = remainder.parse::<f32>() {
222        Some(Value::Number(num))
223    } else {
224        // Otherwise treat as identifier
225        Some(Value::Identifier(remainder))
226    };
227
228    Ok(Statement::new(
229        StatementKind::Const { name, value },
230        Value::Null,
231        0,
232        line_number,
233        1,
234    ))
235}