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