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(
108 mut parts: impl Iterator<Item = impl AsRef<str>>,
109 line_number: usize,
110) -> Result<Statement> {
111 let name = parts
112 .next()
113 .ok_or_else(|| anyhow!("group requires a name"))?
114 .as_ref()
115 .trim_end_matches(':')
116 .to_string();
117
118 let next = parts.next();
120 let duration = if next.as_ref().map(|w| w.as_ref()) == Some("during") {
121 let duration_str = parts
123 .map(|p| p.as_ref().to_string())
124 .collect::<Vec<_>>()
125 .join(" ")
126 .trim_end_matches(':')
127 .to_string();
128
129 Some(super::super::duration::parse_duration_token(&duration_str)?)
130 } else {
131 None
132 };
133
134 Ok(Statement::new(
135 StatementKind::Group {
136 name: name.clone(),
137 body: Vec::new(),
138 duration,
139 },
140 Value::Identifier(name),
141 0,
142 line_number,
143 1,
144 ))
145}
146
147pub fn parse_automate(
149 mut parts: impl Iterator<Item = impl AsRef<str>>,
150 line_number: usize,
151) -> Result<Statement> {
152 let target = parts
154 .next()
155 .ok_or_else(|| anyhow!("automate requires a target"))?
156 .as_ref()
157 .trim_end_matches(':')
158 .to_string();
159
160 let mut mode: Option<String> = None;
162 if let Some(word) = parts.next() {
163 if word.as_ref() == "mode" {
164 if let Some(m) = parts.next() {
165 mode = Some(m.as_ref().trim_end_matches(':').to_string());
166 }
167 }
168 }
169
170 let mut map = HashMap::new();
171 if let Some(m) = mode {
172 map.insert("mode".to_string(), Value::String(m));
173 }
174
175 Ok(Statement::new(
176 StatementKind::Automate { target },
177 Value::Map(map),
178 0,
179 line_number,
180 1,
181 ))
182}
183
184pub fn parse_loop(
186 parts: impl Iterator<Item = impl AsRef<str>>,
187 line_number: usize,
188) -> Result<Statement> {
189 let all_parts: Vec<String> = parts.map(|p| p.as_ref().to_string()).collect();
195
196 let count = if all_parts.is_empty() {
197 Value::Null
199 } else {
200 let count_str = all_parts.join(" ").trim_end_matches(':').to_string();
202
203 if let Ok(num) = count_str.parse::<f32>() {
211 Value::Number(num)
212 } else if count_str.contains('(') && count_str.ends_with(')') {
213 if let Some(open_idx) = count_str.find('(') {
215 let name = count_str[..open_idx].to_string();
216 let inside = &count_str[open_idx + 1..count_str.len() - 1];
217
218 if name == "pass" && !inside.trim().is_empty() {
220 if let Ok(duration) =
222 crate::language::syntax::parser::driver::parse_duration_token(inside.trim())
223 {
224 Value::Duration(duration)
225 } else {
226 let args = if inside.trim().is_empty() {
228 Vec::new()
229 } else {
230 crate::language::syntax::parser::driver::parse_function_args(inside)?
231 };
232 Value::Call { name, args }
233 }
234 } else {
235 let args = if inside.trim().is_empty() {
237 Vec::new()
238 } else {
239 crate::language::syntax::parser::driver::parse_function_args(inside)?
240 };
241 Value::Call { name, args }
242 }
243 } else {
244 Value::Identifier(count_str.to_string())
245 }
246 } else {
247 Value::Identifier(count_str)
248 }
249 };
250
251 Ok(Statement::new(
252 StatementKind::Loop {
253 count,
254 body: Vec::new(), },
256 Value::Null,
257 0,
258 line_number,
259 1,
260 ))
261}
262
263pub fn parse_for(
265 parts: impl Iterator<Item = impl AsRef<str>>,
266 line_number: usize,
267) -> Result<Statement> {
268 let parts_vec: Vec<String> = parts.map(|s| s.as_ref().to_string()).collect();
270
271 if parts_vec.is_empty() {
272 return Err(anyhow!("for loop requires a variable name"));
273 }
274
275 let variable = parts_vec[0].clone();
276
277 if parts_vec.len() < 2 || parts_vec[1] != "in" {
279 return Err(anyhow!("Expected 'in' after variable in for loop"));
280 }
281
282 let iterable_str = parts_vec[2..].join(" ");
284 let iterable_str = iterable_str.trim_end_matches(':').trim();
285
286 let iterable = if iterable_str.starts_with('[') && iterable_str.ends_with(']') {
287 parse_array_value(iterable_str)?
289 } else {
290 if let Ok(num) = iterable_str.parse::<f32>() {
292 Value::Number(num)
293 } else {
294 Value::Identifier(iterable_str.to_string())
295 }
296 };
297
298 Ok(Statement::new(
299 StatementKind::For {
300 variable,
301 iterable,
302 body: Vec::new(), },
304 Value::Null,
305 0,
306 line_number,
307 1,
308 ))
309}
310
311pub fn parse_function(line: &str, line_number: usize) -> Result<Statement> {
313 let after_kw = line
315 .trim()
316 .strip_prefix("function")
317 .ok_or_else(|| anyhow!("function parsing error"))?
318 .trim();
319
320 if let Some(paren_idx) = after_kw.find('(') {
322 let name = after_kw[..paren_idx].trim().to_string();
323 if let Some(close_idx) = after_kw.rfind(')') {
324 let args_str = &after_kw[paren_idx + 1..close_idx];
325 let params: Vec<String> = if args_str.trim().is_empty() {
327 Vec::new()
328 } else {
329 args_str
330 .split(',')
331 .map(|s| s.trim().trim_end_matches(':').to_string())
332 .filter(|s| !s.is_empty())
333 .collect()
334 };
335
336 return Ok(Statement::new(
337 StatementKind::Function {
338 name: name.clone(),
339 parameters: params,
340 body: Vec::new(),
341 },
342 Value::Identifier(name),
343 0,
344 line_number,
345 1,
346 ));
347 }
348 }
349
350 Err(anyhow!("Invalid function declaration"))
351}
352
353pub fn parse_if(
355 parts: impl Iterator<Item = impl AsRef<str>>,
356 line_number: usize,
357) -> Result<Statement> {
358 let condition_str = parts
360 .map(|s| s.as_ref().to_string())
361 .collect::<Vec<_>>()
362 .join(" ");
363 let condition_str = condition_str.trim_end_matches(':').trim();
364
365 let condition = parse_condition(condition_str)?;
367
368 Ok(Statement::new(
369 StatementKind::If {
370 condition,
371 body: Vec::new(), else_body: None,
373 },
374 Value::Null,
375 0,
376 line_number,
377 1,
378 ))
379}
380
381pub fn parse_while(
384 parts: impl Iterator<Item = impl AsRef<str>>,
385 line_number: usize,
386) -> Result<Statement> {
387 let condition_str = parts
389 .map(|s| s.as_ref().to_string())
390 .collect::<Vec<_>>()
391 .join(" ");
392 let condition_str = condition_str.trim_end_matches(':').trim();
393
394 let condition = parse_condition(condition_str)?;
396
397 Ok(Statement::new(
398 StatementKind::While {
399 condition,
400 body: Vec::new(), },
402 Value::Null,
403 0,
404 line_number,
405 1,
406 ))
407}
408
409pub fn parse_else(line: &str, line_number: usize) -> Result<Statement> {
411 if line.trim().starts_with("else if") {
413 let condition_str = line.trim().strip_prefix("else if").unwrap().trim();
415 let condition_str = condition_str.trim_end_matches(':').trim();
416 let condition = parse_condition(condition_str)?;
417
418 Ok(Statement::new(
421 StatementKind::If {
422 condition,
423 body: Vec::new(), else_body: None,
425 },
426 Value::String("else-if".to_string()),
427 0,
428 line_number,
429 1,
430 ))
431 } else {
432 Ok(Statement::new(
437 StatementKind::Comment, Value::String("else".to_string()),
439 0,
440 line_number,
441 1,
442 ))
443 }
444}
445
446pub fn parse_call(
451 line: &str,
452 mut parts: impl Iterator<Item = impl AsRef<str>>,
453 line_number: usize,
454) -> Result<Statement> {
455 let first_token = parts
459 .next()
460 .ok_or_else(|| anyhow!("call requires a target"))?
461 .as_ref()
462 .to_string();
463 let mut call_name = first_token.clone();
464 if line.find('(').is_some() {
465 if let Some(after_call) = line.trim().strip_prefix("call") {
467 let snippet = after_call.trim();
468 if let Some(pidx) = snippet.find('(') {
469 call_name = snippet[..pidx].trim().to_string();
470 }
471 }
472 }
473
474 if line.contains('=') {
476 let eq_parts: Vec<&str> = line.splitn(2, '=').collect();
478 if eq_parts.len() == 2 {
479 let target = eq_parts[0]
481 .trim()
482 .strip_prefix("call")
483 .unwrap_or(eq_parts[0])
484 .trim()
485 .to_string();
486 let pattern = eq_parts[1].trim().trim_matches('"').to_string();
487
488 let mut map = HashMap::new();
491 map.insert("inline_pattern".to_string(), Value::Boolean(true));
492 map.insert("target".to_string(), Value::String(target.clone()));
493 map.insert("pattern".to_string(), Value::String(pattern));
494
495 return Ok(Statement::new(
496 StatementKind::Call {
497 name: target,
498 args: Vec::new(),
499 },
500 Value::Map(map),
501 0,
502 line_number,
503 1,
504 ));
505 }
506 }
507
508 if let Some(paren_idx) = line.find('(') {
511 if let Some(close_idx) = line.rfind(')') {
512 let args_str = &line[paren_idx + 1..close_idx];
513 let args = if args_str.trim().is_empty() {
514 Vec::new()
515 } else {
516 crate::language::syntax::parser::driver::parse_function_args(args_str)?
518 };
519
520 return Ok(Statement::new(
521 StatementKind::Call {
522 name: call_name,
523 args,
524 },
525 Value::Null,
526 0,
527 line_number,
528 1,
529 ));
530 }
531 }
532
533 Ok(Statement::new(
534 StatementKind::Call {
535 name: call_name,
536 args: Vec::new(),
537 },
538 Value::Null,
539 0,
540 line_number,
541 1,
542 ))
543}
544
545pub fn parse_break(
547 _parts: impl Iterator<Item = impl AsRef<str>>,
548 line_number: usize,
549) -> Result<Statement> {
550 Ok(Statement::new(
551 StatementKind::Break,
552 Value::Null,
553 0,
554 line_number,
555 1,
556 ))
557}
558
559pub fn parse_spawn(
561 mut parts: impl Iterator<Item = impl AsRef<str>>,
562 line_number: usize,
563) -> Result<Statement> {
564 let name = parts
565 .next()
566 .ok_or_else(|| anyhow!("spawn requires a target"))?
567 .as_ref()
568 .to_string();
569
570 Ok(Statement::new(
571 StatementKind::Spawn {
572 name,
573 args: Vec::new(),
574 },
575 Value::Null,
576 0,
577 line_number,
578 1,
579 ))
580}
581
582pub fn parse_on(
584 mut parts: impl Iterator<Item = impl AsRef<str>>,
585 line_number: usize,
586) -> Result<Statement> {
587 let raw = parts
590 .next()
591 .ok_or_else(|| anyhow!("on statement requires an event name"))?
592 .as_ref()
593 .to_string();
594
595 let mut event_name = raw.clone();
597 let mut args_vec: Vec<Value> = Vec::new();
598 if let Some(open_idx) = raw.find('(') {
599 if raw.ends_with(')') {
600 let base = raw[..open_idx].to_string();
601 let inside = &raw[open_idx + 1..raw.len() - 1];
602 if let Ok(n) = inside.trim().parse::<f32>() {
603 args_vec.push(Value::Number(n));
604 }
605 event_name = base;
606 }
607 }
608
609 let next_word = parts.next();
611 let once = next_word.map(|w| w.as_ref() == "once").unwrap_or(false);
612 if once {
613 args_vec.push(Value::String("once".to_string()));
614 }
615
616 let args = if !args_vec.is_empty() {
617 Some(args_vec)
618 } else {
619 None
620 };
621
622 Ok(Statement::new(
623 StatementKind::On {
624 event: event_name,
625 args,
626 body: Vec::new(), },
628 Value::Null,
629 0,
630 line_number,
631 1,
632 ))
633}
634
635pub fn parse_emit(
637 line: &str,
638 mut parts: impl Iterator<Item = impl AsRef<str>>,
639 line_number: usize,
640) -> Result<Statement> {
641 let event_name = parts
643 .next()
644 .ok_or_else(|| anyhow!("emit statement requires an event name"))?
645 .as_ref()
646 .to_string();
647
648 let remainder = line.splitn(2, &event_name).nth(1).unwrap_or("").trim();
650
651 let payload = if remainder.starts_with('{') && remainder.ends_with('}') {
652 let inner = &remainder[1..remainder.len() - 1];
654 let mut map = HashMap::new();
655
656 for pair in inner.split(',') {
658 let parts: Vec<&str> = pair.split(':').collect();
659 if parts.len() == 2 {
660 let key = parts[0].trim().to_string();
661 let value_str = parts[1].trim();
662
663 let value = if value_str.starts_with('"') && value_str.ends_with('"') {
665 Value::String(value_str.trim_matches('"').to_string())
666 } else if let Ok(num) = value_str.parse::<f32>() {
667 Value::Number(num)
668 } else {
669 Value::Identifier(value_str.to_string())
670 };
671
672 map.insert(key, value);
673 }
674 }
675
676 Some(Value::Map(map))
677 } else {
678 None
679 };
680
681 Ok(Statement::new(
682 StatementKind::Emit {
683 event: event_name,
684 payload,
685 },
686 Value::Null,
687 0,
688 line_number,
689 1,
690 ))
691}