devalang_wasm/language/syntax/parser/driver/
helpers.rs

1use crate::language::syntax::ast::Value;
2/// Helper functions for parsing values, arguments, and complex structures
3use anyhow::Result;
4use std::collections::HashMap;
5
6/// Parse function arguments from string
7/// Supports: numbers, strings, identifiers, arrays, maps
8pub fn parse_function_args(args_str: &str) -> Result<Vec<Value>> {
9    let mut args = Vec::new();
10    let mut current_arg = String::new();
11    let mut depth = 0; // Track nested structures
12    let mut in_string = false;
13
14    for ch in args_str.chars() {
15        match ch {
16            '"' => {
17                in_string = !in_string;
18                current_arg.push(ch);
19            }
20            '[' | '{' if !in_string => {
21                depth += 1;
22                current_arg.push(ch);
23            }
24            ']' | '}' if !in_string => {
25                depth -= 1;
26                current_arg.push(ch);
27            }
28            ',' if depth == 0 && !in_string => {
29                // End of argument
30                if !current_arg.trim().is_empty() {
31                    args.push(parse_single_arg(current_arg.trim())?);
32                    current_arg.clear();
33                }
34            }
35            _ => {
36                current_arg.push(ch);
37            }
38        }
39    }
40
41    // Last argument
42    if !current_arg.trim().is_empty() {
43        args.push(parse_single_arg(current_arg.trim())?);
44    }
45
46    Ok(args)
47}
48
49/// Parse a single argument value
50pub fn parse_single_arg(arg: &str) -> Result<Value> {
51    let arg = arg.trim();
52
53    // String literal
54    if arg.starts_with('"') && arg.ends_with('"') {
55        return Ok(Value::String(arg[1..arg.len() - 1].to_string()));
56    }
57
58    // Array
59    if arg.starts_with('[') && arg.ends_with(']') {
60        let inner = &arg[1..arg.len() - 1];
61        let items = parse_function_args(inner)?;
62        return Ok(Value::Array(items));
63    }
64
65    // Map/Object
66    if arg.starts_with('{') && arg.ends_with('}') {
67        let inner = &arg[1..arg.len() - 1];
68        let mut map = HashMap::new();
69
70        // Parse key: value pairs
71        for pair in inner.split(',') {
72            if let Some(colon_idx) = pair.find(':') {
73                let key = pair[..colon_idx].trim().trim_matches('"');
74                let value = parse_single_arg(pair[colon_idx + 1..].trim())?;
75                map.insert(key.to_string(), value);
76            }
77        }
78
79        return Ok(Value::Map(map));
80    }
81
82    // Number
83    if let Ok(num) = arg.parse::<f32>() {
84        return Ok(Value::Number(num));
85    }
86
87    // Boolean
88    match arg.to_lowercase().as_str() {
89        "true" => return Ok(Value::Boolean(true)),
90        "false" => return Ok(Value::Boolean(false)),
91        _ => {}
92    }
93
94    // Default to identifier
95    Ok(Value::Identifier(arg.to_string()))
96}
97
98/// Parse synth definition: synth waveform { params }
99/// Returns a Map with type="synth", waveform, and ADSR parameters
100pub fn parse_synth_definition(input: &str) -> Result<Value> {
101    // Remove "synth " prefix
102    let input = input.trim_start_matches("synth ").trim();
103
104    // Extract waveform (everything before '{')
105    let (waveform, params_str) = if let Some(brace_idx) = input.find('{') {
106        let waveform = input[..brace_idx].trim();
107        let params = &input[brace_idx..];
108        (waveform, params)
109    } else {
110        // No parameters, just waveform
111        return Ok(Value::Map({
112            let mut map = HashMap::new();
113            map.insert("type".to_string(), Value::String("synth".to_string()));
114            map.insert("waveform".to_string(), Value::String(input.to_string()));
115            map
116        }));
117    };
118
119    // Parse parameters from { key: value, ... }
120    let params_str = params_str.trim_matches(|c| c == '{' || c == '}').trim();
121    let mut params_map = HashMap::new();
122
123    // Add type and waveform
124    params_map.insert("type".to_string(), Value::String("synth".to_string()));
125    params_map.insert("waveform".to_string(), Value::String(waveform.to_string()));
126
127    // Parse key: value pairs (support newlines by replacing them with commas)
128    if !params_str.is_empty() {
129        // First, remove inline comments (everything after //)
130        let mut cleaned_lines = Vec::new();
131        for line in params_str.lines() {
132            if let Some(comment_pos) = line.find("//") {
133                let clean_line = &line[..comment_pos];
134                if !clean_line.trim().is_empty() {
135                    cleaned_lines.push(clean_line);
136                }
137            } else if !line.trim().is_empty() {
138                cleaned_lines.push(line);
139            }
140        }
141
142        // Now join lines and split by comma and newline
143        let cleaned = cleaned_lines.join("\n");
144        let normalized = cleaned.replace('\n', ",").replace('\r', "");
145
146        for pair in normalized.split(',') {
147            let pair = pair.trim();
148            if pair.is_empty() {
149                continue;
150            }
151
152            let parts: Vec<&str> = pair.split(':').collect();
153            if parts.len() >= 2 {
154                let key = parts[0].trim().to_string();
155                // Join back in case value contains ':'
156                let value_part = parts[1..].join(":");
157                let value_str = value_part.trim().trim_matches(',');
158
159                // Parse arrays (for filters)
160                if value_str.starts_with('[') {
161                    if let Ok(array_val) = parse_array_value(value_str) {
162                        params_map.insert(key, array_val);
163                        continue;
164                    }
165                }
166
167                // Try to parse as number
168                if let Ok(num) = value_str.parse::<f32>() {
169                    params_map.insert(key, Value::Number(num));
170                } else {
171                    // Store as string
172                    params_map.insert(key, Value::String(value_str.to_string()));
173                }
174            }
175        }
176    }
177
178    Ok(Value::Map(params_map))
179}
180
181/// Parse array value like [{ key: val }, ...]
182pub fn parse_array_value(input: &str) -> Result<Value> {
183    let input = input.trim().trim_matches(|c| c == '[' || c == ']').trim();
184    if input.is_empty() {
185        return Ok(Value::Array(Vec::new()));
186    }
187
188    // Check for range pattern: "start..end"
189    if input.contains("..") {
190        let parts: Vec<&str> = input.split("..").collect();
191        if parts.len() == 2 {
192            let start_str = parts[0].trim();
193            let end_str = parts[1].trim();
194
195            // Try to parse as numbers
196            if let (Ok(start), Ok(end)) = (start_str.parse::<f32>(), end_str.parse::<f32>()) {
197                return Ok(Value::Range {
198                    start: Box::new(Value::Number(start)),
199                    end: Box::new(Value::Number(end)),
200                });
201            }
202        }
203    }
204
205    let mut items = Vec::new();
206    let mut depth = 0;
207    let mut current = String::new();
208
209    for ch in input.chars() {
210        match ch {
211            '{' => {
212                depth += 1;
213                current.push(ch);
214            }
215            '}' => {
216                depth -= 1;
217                current.push(ch);
218
219                if depth == 0 && !current.trim().is_empty() {
220                    // Parse this object
221                    if let Ok(obj) = parse_map_value(&current) {
222                        items.push(obj);
223                    }
224                    current.clear();
225                }
226            }
227            ',' if depth == 0 => {
228                // Skip commas at array level
229                continue;
230            }
231            _ => {
232                current.push(ch);
233            }
234        }
235    }
236
237    Ok(Value::Array(items))
238}
239
240/// Parse map value like { key: val, key2: val2 }
241pub fn parse_map_value(input: &str) -> Result<Value> {
242    let input = input.trim().trim_matches(|c| c == '{' || c == '}').trim();
243    let mut map = HashMap::new();
244
245    for pair in input.split(',') {
246        let pair = pair.trim();
247        if pair.is_empty() {
248            continue;
249        }
250
251        let parts: Vec<&str> = pair.split(':').collect();
252        if parts.len() >= 2 {
253            let key = parts[0].trim().to_string();
254            // Join back in case value contains ':' (shouldn't happen but just in case)
255            let value_part = parts[1..].join(":");
256
257            // Remove inline comments (everything after //)
258            let value_clean = if let Some(comment_pos) = value_part.find("//") {
259                &value_part[..comment_pos]
260            } else {
261                &value_part
262            };
263
264            let value_str = value_clean.trim().trim_matches('"').trim_matches('\'');
265
266            // Try to parse as number
267            if let Ok(num) = value_str.parse::<f32>() {
268                map.insert(key, Value::Number(num));
269            } else {
270                map.insert(key, Value::String(value_str.to_string()));
271            }
272        }
273    }
274
275    Ok(Value::Map(map))
276}
277
278/// Parse a condition string into a Value (for if statements)
279/// Supports: var > value, var < value, var == value, var != value, var >= value, var <= value
280pub fn parse_condition(condition_str: &str) -> Result<Value> {
281    // Find the operator
282    let operators = vec![">=", "<=", "==", "!=", ">", "<"];
283    for op in operators {
284        if let Some(idx) = condition_str.find(op) {
285            let left = condition_str[..idx].trim();
286            let right = condition_str[idx + op.len()..].trim();
287
288            // Create a map representing the condition
289            let mut map = HashMap::new();
290            map.insert("operator".to_string(), Value::String(op.to_string()));
291            map.insert(
292                "left".to_string(),
293                if let Ok(num) = left.parse::<f32>() {
294                    Value::Number(num)
295                } else {
296                    Value::Identifier(left.to_string())
297                },
298            );
299            map.insert(
300                "right".to_string(),
301                if let Ok(num) = right.parse::<f32>() {
302                    Value::Number(num)
303                } else {
304                    Value::Identifier(right.to_string())
305                },
306            );
307
308            return Ok(Value::Map(map));
309        }
310    }
311
312    // No operator found, treat as boolean identifier
313    Ok(Value::Identifier(condition_str.to_string()))
314}