devalang_wasm/language/syntax/parser/driver/statements/
structure.rs1use super::super::helpers::{parse_array_value, parse_condition};
2use crate::language::syntax::ast::{Statement, StatementKind, Value};
3use anyhow::{Result, anyhow};
5use std::collections::HashMap;
6use std::iter::Iterator;
7
8pub fn parse_pattern(
13 mut parts: impl Iterator<Item = impl AsRef<str>>,
14 line_number: usize,
15) -> Result<Statement> {
16 let name = parts
17 .next()
18 .ok_or_else(|| anyhow!("pattern requires a name"))?
19 .as_ref()
20 .to_string();
21
22 let mut target = None;
23 let mut pattern_str = None;
24 let mut options: HashMap<String, Value> = HashMap::new();
25
26 if let Some(word) = parts.next() {
28 if word.as_ref() == "with" {
29 target = parts.next().map(|v| v.as_ref().to_string());
31
32 let rest: Vec<String> = parts.map(|s| s.as_ref().to_string()).collect();
34 let joined = rest.join(" ");
35
36 if let Some(brace_start) = joined.find('{') {
38 if let Some(brace_end) = joined.rfind('}') {
39 let options_str = &joined[brace_start + 1..brace_end];
41
42 for pair in options_str.split(',') {
44 let parts: Vec<&str> = pair.split(':').collect();
45 if parts.len() == 2 {
46 let key = parts[0].trim().to_string();
47 let value_str = parts[1].trim();
48
49 let value = if let Ok(num) = value_str.parse::<f32>() {
51 Value::Number(num)
52 } else if value_str == "true" {
53 Value::Boolean(true)
54 } else if value_str == "false" {
55 Value::Boolean(false)
56 } else if value_str.starts_with('"') && value_str.ends_with('"') {
57 Value::String(value_str.trim_matches('"').to_string())
58 } else {
59 Value::Identifier(value_str.to_string())
60 };
61
62 options.insert(key, value);
63 }
64 }
65
66 let after_brace = joined[brace_end + 1..].trim();
68 if let Some(eq_pos) = after_brace.find('=') {
69 let pattern_part = after_brace[eq_pos + 1..].trim();
70 pattern_str = Some(pattern_part.trim_matches('"').to_string());
71 }
72 } else {
73 return Err(anyhow!("Unclosed brace in pattern options"));
74 }
75 } else {
76 if let Some(eq_pos) = joined.find('=') {
78 let pattern_part = joined[eq_pos + 1..].trim();
79 pattern_str = Some(pattern_part.trim_matches('"').to_string());
80 }
81 }
82 }
83 }
84
85 let value = if !options.is_empty() {
87 let mut map = options;
88 if let Some(pat) = pattern_str {
89 map.insert("pattern".to_string(), Value::String(pat));
90 }
91 Value::Map(map)
92 } else {
93 pattern_str.map(Value::String).unwrap_or(Value::Null)
94 };
95
96 Ok(Statement::new(
97 StatementKind::Pattern { name, target },
98 value,
99 0,
100 line_number,
101 1,
102 ))
103}
104
105pub fn parse_group(
107 mut parts: impl Iterator<Item = impl AsRef<str>>,
108 line_number: usize,
109) -> Result<Statement> {
110 let name = parts
111 .next()
112 .ok_or_else(|| anyhow!("group requires a name"))?
113 .as_ref()
114 .trim_end_matches(':')
115 .to_string();
116
117 Ok(Statement::new(
118 StatementKind::Group {
119 name: name.clone(),
120 body: Vec::new(),
121 },
122 Value::Identifier(name),
123 0,
124 line_number,
125 1,
126 ))
127}
128
129pub fn parse_automate(
131 mut parts: impl Iterator<Item = impl AsRef<str>>,
132 line_number: usize,
133) -> Result<Statement> {
134 let target = parts
136 .next()
137 .ok_or_else(|| anyhow!("automate requires a target"))?
138 .as_ref()
139 .trim_end_matches(':')
140 .to_string();
141
142 let mut mode: Option<String> = None;
144 if let Some(word) = parts.next() {
145 if word.as_ref() == "mode" {
146 if let Some(m) = parts.next() {
147 mode = Some(m.as_ref().trim_end_matches(':').to_string());
148 }
149 }
150 }
151
152 let mut map = HashMap::new();
153 if let Some(m) = mode {
154 map.insert("mode".to_string(), Value::String(m));
155 }
156
157 Ok(Statement::new(
158 StatementKind::Automate { target },
159 Value::Map(map),
160 0,
161 line_number,
162 1,
163 ))
164}
165
166pub fn parse_loop(
168 mut parts: impl Iterator<Item = impl AsRef<str>>,
169 line_number: usize,
170) -> Result<Statement> {
171 let count_str_ref = parts
173 .next()
174 .ok_or_else(|| anyhow!("loop requires a count"))?;
175 let count_str = count_str_ref.as_ref().trim_end_matches(':');
176
177 let count = if let Ok(num) = count_str.parse::<f32>() {
178 Value::Number(num)
179 } else {
180 Value::Identifier(count_str.to_string())
181 };
182
183 Ok(Statement::new(
184 StatementKind::Loop {
185 count,
186 body: Vec::new(), },
188 Value::Null,
189 0,
190 line_number,
191 1,
192 ))
193}
194
195pub fn parse_for(
197 parts: impl Iterator<Item = impl AsRef<str>>,
198 line_number: usize,
199) -> Result<Statement> {
200 let parts_vec: Vec<String> = parts.map(|s| s.as_ref().to_string()).collect();
202
203 if parts_vec.is_empty() {
204 return Err(anyhow!("for loop requires a variable name"));
205 }
206
207 let variable = parts_vec[0].clone();
208
209 if parts_vec.len() < 2 || parts_vec[1] != "in" {
211 return Err(anyhow!("Expected 'in' after variable in for loop"));
212 }
213
214 let iterable_str = parts_vec[2..].join(" ");
216 let iterable_str = iterable_str.trim_end_matches(':').trim();
217
218 let iterable = if iterable_str.starts_with('[') && iterable_str.ends_with(']') {
219 parse_array_value(iterable_str)?
221 } else {
222 if let Ok(num) = iterable_str.parse::<f32>() {
224 Value::Number(num)
225 } else {
226 Value::Identifier(iterable_str.to_string())
227 }
228 };
229
230 Ok(Statement::new(
231 StatementKind::For {
232 variable,
233 iterable,
234 body: Vec::new(), },
236 Value::Null,
237 0,
238 line_number,
239 1,
240 ))
241}
242
243pub fn parse_if(
245 parts: impl Iterator<Item = impl AsRef<str>>,
246 line_number: usize,
247) -> Result<Statement> {
248 let condition_str = parts
250 .map(|s| s.as_ref().to_string())
251 .collect::<Vec<_>>()
252 .join(" ");
253 let condition_str = condition_str.trim_end_matches(':').trim();
254
255 let condition = parse_condition(condition_str)?;
257
258 Ok(Statement::new(
259 StatementKind::If {
260 condition,
261 body: Vec::new(), else_body: None,
263 },
264 Value::Null,
265 0,
266 line_number,
267 1,
268 ))
269}
270
271pub fn parse_else(line: &str, line_number: usize) -> Result<Statement> {
273 if line.trim().starts_with("else if") {
275 let condition_str = line.trim().strip_prefix("else if").unwrap().trim();
277 let condition_str = condition_str.trim_end_matches(':').trim();
278 let condition = parse_condition(condition_str)?;
279
280 Ok(Statement::new(
281 StatementKind::If {
282 condition,
283 body: Vec::new(), else_body: None,
285 },
286 Value::Null,
287 0,
288 line_number,
289 1,
290 ))
291 } else {
292 Ok(Statement::new(
294 StatementKind::Comment, Value::String("else".to_string()),
296 0,
297 line_number,
298 1,
299 ))
300 }
301}
302
303pub fn parse_call(
308 line: &str,
309 mut parts: impl Iterator<Item = impl AsRef<str>>,
310 line_number: usize,
311) -> Result<Statement> {
312 let first = parts
313 .next()
314 .ok_or_else(|| anyhow!("call requires a target"))?
315 .as_ref()
316 .to_string();
317
318 if line.contains('=') {
320 let eq_parts: Vec<&str> = line.splitn(2, '=').collect();
322 if eq_parts.len() == 2 {
323 let target = eq_parts[0]
325 .trim()
326 .strip_prefix("call")
327 .unwrap_or(eq_parts[0])
328 .trim()
329 .to_string();
330 let pattern = eq_parts[1].trim().trim_matches('"').to_string();
331
332 let mut map = HashMap::new();
335 map.insert("inline_pattern".to_string(), Value::Boolean(true));
336 map.insert("target".to_string(), Value::String(target.clone()));
337 map.insert("pattern".to_string(), Value::String(pattern));
338
339 return Ok(Statement::new(
340 StatementKind::Call {
341 name: target,
342 args: Vec::new(),
343 },
344 Value::Map(map),
345 0,
346 line_number,
347 1,
348 ));
349 }
350 }
351
352 Ok(Statement::new(
354 StatementKind::Call {
355 name: first,
356 args: Vec::new(),
357 },
358 Value::Null,
359 0,
360 line_number,
361 1,
362 ))
363}
364
365pub fn parse_spawn(
367 mut parts: impl Iterator<Item = impl AsRef<str>>,
368 line_number: usize,
369) -> Result<Statement> {
370 let name = parts
371 .next()
372 .ok_or_else(|| anyhow!("spawn requires a target"))?
373 .as_ref()
374 .to_string();
375
376 Ok(Statement::new(
377 StatementKind::Spawn {
378 name,
379 args: Vec::new(),
380 },
381 Value::Null,
382 0,
383 line_number,
384 1,
385 ))
386}
387
388pub fn parse_on(
390 mut parts: impl Iterator<Item = impl AsRef<str>>,
391 line_number: usize,
392) -> Result<Statement> {
393 let event_name = parts
395 .next()
396 .ok_or_else(|| anyhow!("on statement requires an event name"))?
397 .as_ref()
398 .to_string();
399
400 let next_word = parts.next();
402 let once = next_word.map(|w| w.as_ref() == "once").unwrap_or(false);
403
404 let args = if once {
406 Some(vec![Value::String("once".to_string())])
407 } else {
408 None
409 };
410
411 Ok(Statement::new(
412 StatementKind::On {
413 event: event_name,
414 args,
415 body: Vec::new(), },
417 Value::Null,
418 0,
419 line_number,
420 1,
421 ))
422}
423
424pub fn parse_emit(
426 line: &str,
427 mut parts: impl Iterator<Item = impl AsRef<str>>,
428 line_number: usize,
429) -> Result<Statement> {
430 let event_name = parts
432 .next()
433 .ok_or_else(|| anyhow!("emit statement requires an event name"))?
434 .as_ref()
435 .to_string();
436
437 let remainder = line.splitn(2, &event_name).nth(1).unwrap_or("").trim();
439
440 let payload = if remainder.starts_with('{') && remainder.ends_with('}') {
441 let inner = &remainder[1..remainder.len() - 1];
443 let mut map = HashMap::new();
444
445 for pair in inner.split(',') {
447 let parts: Vec<&str> = pair.split(':').collect();
448 if parts.len() == 2 {
449 let key = parts[0].trim().to_string();
450 let value_str = parts[1].trim();
451
452 let value = if value_str.starts_with('"') && value_str.ends_with('"') {
454 Value::String(value_str.trim_matches('"').to_string())
455 } else if let Ok(num) = value_str.parse::<f32>() {
456 Value::Number(num)
457 } else {
458 Value::Identifier(value_str.to_string())
459 };
460
461 map.insert(key, value);
462 }
463 }
464
465 Some(Value::Map(map))
466 } else {
467 None
468 };
469
470 Ok(Statement::new(
471 StatementKind::Emit {
472 event: event_name,
473 payload,
474 },
475 Value::Null,
476 0,
477 line_number,
478 1,
479 ))
480}