1use serde::{Deserialize, Serialize};
4
5use crate::debug_access::Statement::Comment;
6
7#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
9pub enum DebugAccessParseError {
10 MissingAttribute(String),
12 UnknownStatement(String),
14}
15
16impl Default for DebugAccessParseError {
17 fn default() -> Self {
18 Self::UnknownStatement(String::default())
19 }
20}
21
22impl From<DebugAccessParseError> for crate::Error {
23 fn from(value: DebugAccessParseError) -> Self {
24 Self::Debug(value)
25 }
26}
27
28impl std::fmt::Display for DebugAccessParseError {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 match self {
31 Self::MissingAttribute(msg) => write!(f, "missing attribute: {msg}"),
32 Self::UnknownStatement(name) => write!(f, "unknown statement: {name}"),
33 }
34 }
35}
36
37impl std::error::Error for DebugAccessParseError {}
38
39#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
40pub enum Statement {
43 Expression(Expression),
45
46 Assignment(Assignment),
48
49 Definition(Assignment),
51
52 Comment(String),
54}
55
56impl Default for Statement {
57 fn default() -> Self {
58 Comment(String::default())
59 }
60}
61
62impl TryFrom<String> for Statement {
63 type Error = crate::Error;
64
65 fn try_from(value: String) -> Result<Self, Self::Error> {
66 let input = value.trim().to_string();
68
69 if input.starts_with("//") {
71 return Ok(Self::Comment(input));
72 }
73
74 let input = input.strip_suffix(";").unwrap_or(&input).to_string();
76
77 let split: Option<(&str, &str)> = input.split_once('=');
79 let result: Self = match split {
80 None => {
81 let expression: Expression = input.try_into()?;
83 Self::Expression(expression)
84 }
85 Some((variable, expression)) => {
86 let variable = variable.trim();
87 let expression = expression.trim();
88 variable.strip_prefix("__var").map_or_else(
89 || {
90 let expression: Expression = expression.try_into()?;
91 Ok::<Self, Self::Error>(Self::Assignment(Assignment {
92 variable: variable.to_string(),
93 expression,
94 }))
95 },
96 |variable| {
97 let variable = variable.trim();
98 let expression: Expression = expression.try_into()?;
99 Ok::<Self, Self::Error>(Self::Definition(Assignment {
100 variable: variable.to_string(),
101 expression,
102 }))
103 },
104 )?
105 }
106 };
107
108 Ok(result)
109 }
110}
111
112#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
113pub struct Assignment {
115 pub variable: String,
116 pub expression: Expression,
117}
118
119#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
120pub enum Expression {
122 Normal(String),
124
125 Conditional(Box<Conditional>),
132
133 FunctionCall(Box<DebugFunction>),
136}
137
138impl Default for Expression {
139 fn default() -> Self {
140 Self::Normal(String::default())
141 }
142}
143
144impl TryFrom<String> for Expression {
145 type Error = crate::Error;
146
147 fn try_from(value: String) -> Result<Self, Self::Error> {
148 Ok(Self::try_from(value.as_str())?)
149 }
150}
151
152impl TryFrom<&str> for Expression {
153 type Error = DebugAccessParseError;
154
155 fn try_from(value: &str) -> Result<Self, Self::Error> {
156 if let Ok(condition) = Conditional::try_from(value) {
157 return Ok(Self::Conditional(Box::new(condition)));
158 }
159
160 if let Some((name, args_str)) = detect_function_call(value) {
161 let args: Vec<Self> = split_args(args_str)
162 .into_iter()
163 .map(Self::try_from)
164 .collect::<Result<Vec<_>, _>>()?;
165 let func = DebugFunction::try_from((name.to_string(), args))?;
166
167 return Ok(Self::FunctionCall(Box::new(func)));
168 }
169
170 Ok(Self::Normal(value.to_string()))
171 }
172}
173
174#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
175pub struct Conditional {
182 pub condition: Expression,
184 pub true_value: Expression,
186 pub false_value: Expression,
188}
189
190impl TryFrom<String> for Conditional {
191 type Error = DebugAccessParseError;
192
193 fn try_from(value: String) -> Result<Self, Self::Error> {
201 Self::try_from(value.as_str())
202 }
203}
204
205impl TryFrom<&str> for Conditional {
206 type Error = DebugAccessParseError;
207
208 fn try_from(value: &str) -> Result<Self, Self::Error> {
216 #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
218 enum WalkerProgress {
219 #[default]
220 None,
221 ParenOpen,
222 ParenClose,
223 Question,
224 Colon,
225 }
226
227 let mut condition_str: String = String::new();
229 let mut truthy_str: String = String::new();
230 let mut falsey_str: String = String::new();
231
232 let mut progress = WalkerProgress::None;
234 for c in value.chars() {
235 match progress {
236 WalkerProgress::None => {
237 if c == '(' {
238 progress = WalkerProgress::ParenOpen;
239 }
240 }
241 WalkerProgress::ParenOpen => {
242 if c == ')' {
243 progress = WalkerProgress::ParenClose;
244 } else {
245 condition_str.push(c);
246 }
247 }
248 WalkerProgress::ParenClose => {
249 if c == '?' {
250 progress = WalkerProgress::Question;
251 }
252 }
253 WalkerProgress::Question => {
254 if c == ':' {
255 progress = WalkerProgress::Colon;
256 } else {
257 truthy_str.push(c);
258 }
259 }
260 WalkerProgress::Colon => {
261 if c == ';' {
262 break;
263 }
264 falsey_str.push(c);
265 }
266 }
267 }
268
269 let walk_ok = progress == WalkerProgress::Colon && !falsey_str.is_empty();
270
271 if walk_ok {
272 let condition: Expression = condition_str.trim().try_into()?;
273 let true_value: Expression = truthy_str.trim().try_into()?;
274 let false_value: Expression = falsey_str.trim().try_into()?;
275
276 Ok(Self {
277 condition,
278 true_value,
279 false_value,
280 })
281 } else {
282 Err(DebugAccessParseError::MissingAttribute(
283 "conditional syntax: expected '(condition) ? truthy : falsy'".to_string(),
284 ))
285 }
286 }
287}
288
289#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
290pub enum DebugFunction {
294 Read8 { addr: Expression },
297 Read16 { addr: Expression },
299 Read32 { addr: Expression },
301 Read64 { addr: Expression },
303 Write8 { addr: Expression, val: Expression },
305 Write16 { addr: Expression, val: Expression },
307 Write32 { addr: Expression, val: Expression },
309 Write64 { addr: Expression, val: Expression },
311
312 ReadAP { addr: Expression },
315 WriteAP { addr: Expression, val: Expression },
317 ReadDP { addr: Expression },
319 WriteDP { addr: Expression, val: Expression },
321 ReadAccessAP { addr: Expression },
323 WriteAccessAP { addr: Expression, val: Expression },
325
326 DapDelay { delay: Expression },
329 DapWriteAbort { value: Expression },
331 DapSwjPins {
333 pinout: Expression,
334 pinselect: Expression,
335 pinwait: Expression,
336 },
337 DapSwjClock { val: Expression },
339 DapSwjSequence { cnt: Expression, val: Expression },
341 DapJtagSequence {
343 cnt: Expression,
344 tms: Expression,
345 tdi: Expression,
346 },
347
348 Sequence { name: Expression },
351 Query {
353 query_type: Expression,
354 message: Expression,
355 default: Expression,
356 },
357 QueryValue {
359 message: Expression,
360 default: Expression,
361 },
362 Message {
364 msg_type: Expression,
365 format: Expression,
366 args: Vec<Expression>,
367 },
368
369 FlashWriteBuffer {
372 addr: Expression,
373 offs: Expression,
374 len: Expression,
375 mode: Expression,
376 },
377 FlashLoadAlgorithm {
379 algo_path: Expression,
380 ram_start: Expression,
381 ram_size: Expression,
382 },
383
384 BufferSet {
387 buff_id: Expression,
388 buff_offset: Expression,
389 count: Expression,
390 size: Expression,
391 value: Expression,
392 },
393 BufferGet {
395 buff_id: Expression,
396 buff_offset: Expression,
397 size: Expression,
398 },
399 BufferSize { buff_id: Expression },
401 BufferRead {
403 buff_id: Expression,
404 buff_offset: Expression,
405 addr: Expression,
406 length: Expression,
407 mode: Expression,
408 },
409 BufferWrite {
411 buff_id: Expression,
412 buff_offset: Expression,
413 addr: Expression,
414 length: Expression,
415 mode: Expression,
416 },
417
418 BufferStreamIn {
421 buff_id: Expression,
422 buff_offset: Expression,
423 length: Expression,
424 path: Expression,
425 mode: Expression,
426 timeout: Expression,
427 },
428 BufferStreamOut {
430 buff_id: Expression,
431 buff_offset: Expression,
432 length: Expression,
433 dest_path: Expression,
434 dest_mode: Expression,
435 timeout: Expression,
436 },
437 RunApplication {
439 app_path: Expression,
440 arguments: Expression,
441 work_directory: Expression,
442 timeout: Expression,
443 },
444 RunPythonScript {
446 script_path: Expression,
447 arguments: Expression,
448 work_directory: Expression,
449 timeout: Expression,
450 },
451 FilePathExists {
453 path: Expression,
454 timeout: Expression,
455 },
456 LoadDebugInfo { file: Expression },
458}
459
460impl Default for DebugFunction {
461 fn default() -> Self {
462 Self::DapDelay {
463 delay: Expression::Normal("0".to_string()),
464 }
465 }
466}
467
468impl TryFrom<(String, Vec<Expression>)> for DebugFunction {
469 type Error = DebugAccessParseError;
470
471 #[allow(clippy::too_many_lines)]
475 fn try_from((name, args): (String, Vec<Expression>)) -> Result<Self, Self::Error> {
476 match name.as_str() {
477 "Read8" => match <[Expression; 1]>::try_from(args) {
479 Ok([addr]) => Ok(Self::Read8 { addr }),
480 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
481 "Read8 expects 1 argument, got {}",
482 v.len()
483 ))),
484 },
485 "Read16" => match <[Expression; 1]>::try_from(args) {
486 Ok([addr]) => Ok(Self::Read16 { addr }),
487 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
488 "Read16 expects 1 argument, got {}",
489 v.len()
490 ))),
491 },
492 "Read32" => match <[Expression; 1]>::try_from(args) {
493 Ok([addr]) => Ok(Self::Read32 { addr }),
494 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
495 "Read32 expects 1 argument, got {}",
496 v.len()
497 ))),
498 },
499 "Read64" => match <[Expression; 1]>::try_from(args) {
500 Ok([addr]) => Ok(Self::Read64 { addr }),
501 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
502 "Read64 expects 1 argument, got {}",
503 v.len()
504 ))),
505 },
506 "Write8" => match <[Expression; 2]>::try_from(args) {
508 Ok([addr, val]) => Ok(Self::Write8 { addr, val }),
509 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
510 "Write8 expects 2 arguments, got {}",
511 v.len()
512 ))),
513 },
514 "Write16" => match <[Expression; 2]>::try_from(args) {
515 Ok([addr, val]) => Ok(Self::Write16 { addr, val }),
516 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
517 "Write16 expects 2 arguments, got {}",
518 v.len()
519 ))),
520 },
521 "Write32" => match <[Expression; 2]>::try_from(args) {
522 Ok([addr, val]) => Ok(Self::Write32 { addr, val }),
523 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
524 "Write32 expects 2 arguments, got {}",
525 v.len()
526 ))),
527 },
528 "Write64" => match <[Expression; 2]>::try_from(args) {
529 Ok([addr, val]) => Ok(Self::Write64 { addr, val }),
530 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
531 "Write64 expects 2 arguments, got {}",
532 v.len()
533 ))),
534 },
535 "ReadAP" => match <[Expression; 1]>::try_from(args) {
537 Ok([addr]) => Ok(Self::ReadAP { addr }),
538 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
539 "ReadAP expects 1 argument, got {}",
540 v.len()
541 ))),
542 },
543 "ReadDP" => match <[Expression; 1]>::try_from(args) {
544 Ok([addr]) => Ok(Self::ReadDP { addr }),
545 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
546 "ReadDP expects 1 argument, got {}",
547 v.len()
548 ))),
549 },
550 "ReadAccessAP" => match <[Expression; 1]>::try_from(args) {
551 Ok([addr]) => Ok(Self::ReadAccessAP { addr }),
552 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
553 "ReadAccessAP expects 1 argument, got {}",
554 v.len()
555 ))),
556 },
557 "WriteAP" => match <[Expression; 2]>::try_from(args) {
559 Ok([addr, val]) => Ok(Self::WriteAP { addr, val }),
560 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
561 "WriteAP expects 2 arguments, got {}",
562 v.len()
563 ))),
564 },
565 "WriteDP" => match <[Expression; 2]>::try_from(args) {
566 Ok([addr, val]) => Ok(Self::WriteDP { addr, val }),
567 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
568 "WriteDP expects 2 arguments, got {}",
569 v.len()
570 ))),
571 },
572 "WriteAccessAP" => match <[Expression; 2]>::try_from(args) {
573 Ok([addr, val]) => Ok(Self::WriteAccessAP { addr, val }),
574 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
575 "WriteAccessAP expects 2 arguments, got {}",
576 v.len()
577 ))),
578 },
579 "DAP_Delay" => match <[Expression; 1]>::try_from(args) {
581 Ok([delay]) => Ok(Self::DapDelay { delay }),
582 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
583 "DAP_Delay expects 1 argument, got {}",
584 v.len()
585 ))),
586 },
587 "DAP_WriteABORT" => match <[Expression; 1]>::try_from(args) {
588 Ok([value]) => Ok(Self::DapWriteAbort { value }),
589 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
590 "DAP_WriteABORT expects 1 argument, got {}",
591 v.len()
592 ))),
593 },
594 "DAP_SWJ_Clock" => match <[Expression; 1]>::try_from(args) {
595 Ok([val]) => Ok(Self::DapSwjClock { val }),
596 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
597 "DAP_SWJ_Clock expects 1 argument, got {}",
598 v.len()
599 ))),
600 },
601 "DAP_SWJ_Sequence" => match <[Expression; 2]>::try_from(args) {
603 Ok([cnt, val]) => Ok(Self::DapSwjSequence { cnt, val }),
604 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
605 "DAP_SWJ_Sequence expects 2 arguments, got {}",
606 v.len()
607 ))),
608 },
609 "DAP_SWJ_Pins" => match <[Expression; 3]>::try_from(args) {
611 Ok([pinout, pinselect, pinwait]) => Ok(Self::DapSwjPins {
612 pinout,
613 pinselect,
614 pinwait,
615 }),
616 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
617 "DAP_SWJ_Pins expects 3 arguments, got {}",
618 v.len()
619 ))),
620 },
621 "DAP_JTAG_Sequence" => match <[Expression; 3]>::try_from(args) {
622 Ok([cnt, tms, tdi]) => Ok(Self::DapJtagSequence { cnt, tms, tdi }),
623 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
624 "DAP_JTAG_Sequence expects 3 arguments, got {}",
625 v.len()
626 ))),
627 },
628 "Sequence" => match <[Expression; 1]>::try_from(args) {
630 Ok([name]) => Ok(Self::Sequence { name }),
631 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
632 "Sequence expects 1 argument, got {}",
633 v.len()
634 ))),
635 },
636 "QueryValue" => match <[Expression; 2]>::try_from(args) {
638 Ok([message, default]) => Ok(Self::QueryValue { message, default }),
639 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
640 "QueryValue expects 2 arguments, got {}",
641 v.len()
642 ))),
643 },
644 "Query" => match <[Expression; 3]>::try_from(args) {
646 Ok([query_type, message, default]) => Ok(Self::Query {
647 query_type,
648 message,
649 default,
650 }),
651 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
652 "Query expects 3 arguments, got {}",
653 v.len()
654 ))),
655 },
656 "Message" => {
658 let mut it = args.into_iter();
659 let msg_type = it.next().ok_or_else(|| {
660 DebugAccessParseError::MissingAttribute(
661 "Message expects at least 2 arguments, got 0".to_string(),
662 )
663 })?;
664 let format_expr = it.next().ok_or_else(|| {
665 DebugAccessParseError::MissingAttribute(
666 "Message expects at least 2 arguments, got 1".to_string(),
667 )
668 })?;
669 Ok(Self::Message {
670 msg_type,
671 format: format_expr,
672 args: it.collect(),
673 })
674 }
675 "FlashLoadAlgorithm" => match <[Expression; 3]>::try_from(args) {
677 Ok([algo_path, ram_start, ram_size]) => Ok(Self::FlashLoadAlgorithm {
678 algo_path,
679 ram_start,
680 ram_size,
681 }),
682 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
683 "FlashLoadAlgorithm expects 3 arguments, got {}",
684 v.len()
685 ))),
686 },
687 "FlashWriteBuffer" => match <[Expression; 4]>::try_from(args) {
689 Ok([addr, offs, len, mode]) => Ok(Self::FlashWriteBuffer {
690 addr,
691 offs,
692 len,
693 mode,
694 }),
695 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
696 "FlashWriteBuffer expects 4 arguments, got {}",
697 v.len()
698 ))),
699 },
700 "BufferSize" => match <[Expression; 1]>::try_from(args) {
702 Ok([buff_id]) => Ok(Self::BufferSize { buff_id }),
703 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
704 "BufferSize expects 1 argument, got {}",
705 v.len()
706 ))),
707 },
708 "BufferGet" => match <[Expression; 3]>::try_from(args) {
710 Ok([buff_id, buff_offset, size]) => Ok(Self::BufferGet {
711 buff_id,
712 buff_offset,
713 size,
714 }),
715 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
716 "BufferGet expects 3 arguments, got {}",
717 v.len()
718 ))),
719 },
720 "BufferSet" => match <[Expression; 5]>::try_from(args) {
722 Ok([buff_id, buff_offset, count, size, value]) => Ok(Self::BufferSet {
723 buff_id,
724 buff_offset,
725 count,
726 size,
727 value,
728 }),
729 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
730 "BufferSet expects 5 arguments, got {}",
731 v.len()
732 ))),
733 },
734 "BufferRead" => match <[Expression; 5]>::try_from(args) {
735 Ok([buff_id, buff_offset, addr, length, mode]) => Ok(Self::BufferRead {
736 buff_id,
737 buff_offset,
738 addr,
739 length,
740 mode,
741 }),
742 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
743 "BufferRead expects 5 arguments, got {}",
744 v.len()
745 ))),
746 },
747 "BufferWrite" => match <[Expression; 5]>::try_from(args) {
748 Ok([buff_id, buff_offset, addr, length, mode]) => Ok(Self::BufferWrite {
749 buff_id,
750 buff_offset,
751 addr,
752 length,
753 mode,
754 }),
755 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
756 "BufferWrite expects 5 arguments, got {}",
757 v.len()
758 ))),
759 },
760 "LoadDebugInfo" => match <[Expression; 1]>::try_from(args) {
762 Ok([file]) => Ok(Self::LoadDebugInfo { file }),
763 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
764 "LoadDebugInfo expects 1 argument, got {}",
765 v.len()
766 ))),
767 },
768 "FilePathExists" => match <[Expression; 2]>::try_from(args) {
770 Ok([path, timeout]) => Ok(Self::FilePathExists { path, timeout }),
771 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
772 "FilePathExists expects 2 arguments, got {}",
773 v.len()
774 ))),
775 },
776 "RunApplication" => match <[Expression; 4]>::try_from(args) {
778 Ok([app_path, arguments, work_directory, timeout]) => Ok(Self::RunApplication {
779 app_path,
780 arguments,
781 work_directory,
782 timeout,
783 }),
784 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
785 "RunApplication expects 4 arguments, got {}",
786 v.len()
787 ))),
788 },
789 "RunPythonScript" => match <[Expression; 4]>::try_from(args) {
790 Ok([script_path, arguments, work_directory, timeout]) => {
791 Ok(Self::RunPythonScript {
792 script_path,
793 arguments,
794 work_directory,
795 timeout,
796 })
797 }
798 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
799 "RunPythonScript expects 4 arguments, got {}",
800 v.len()
801 ))),
802 },
803 "BufferStreamIn" => match <[Expression; 6]>::try_from(args) {
805 Ok([buff_id, buff_offset, length, path, mode, timeout]) => {
806 Ok(Self::BufferStreamIn {
807 buff_id,
808 buff_offset,
809 length,
810 path,
811 mode,
812 timeout,
813 })
814 }
815 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
816 "BufferStreamIn expects 6 arguments, got {}",
817 v.len()
818 ))),
819 },
820 "BufferStreamOut" => match <[Expression; 6]>::try_from(args) {
821 Ok([buff_id, buff_offset, length, dest_path, dest_mode, timeout]) => {
822 Ok(Self::BufferStreamOut {
823 buff_id,
824 buff_offset,
825 length,
826 dest_path,
827 dest_mode,
828 timeout,
829 })
830 }
831 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
832 "BufferStreamOut expects 6 arguments, got {}",
833 v.len()
834 ))),
835 },
836 _ => Err(DebugAccessParseError::UnknownStatement(name)),
837 }
838 }
839}
840
841fn detect_function_call(s: &str) -> Option<(&str, &str)> {
845 if !s.ends_with(')') {
846 return None;
847 }
848
849 let paren_pos = s.find('(')?;
850 #[allow(clippy::string_slice)]
851 let name = &s[..paren_pos];
855
856 let mut name_chars = name.chars();
858 let first = name_chars.next()?;
859 if !first.is_alphabetic() && first != '_' {
860 return None;
861 }
862 if !name_chars.all(|c| c.is_alphanumeric() || c == '_') {
863 return None;
864 }
865
866 #[allow(clippy::arithmetic_side_effects)]
867 #[allow(clippy::string_slice)]
872 let args_str = &s[paren_pos + 1..s.len() - 1];
877 Some((name, args_str))
878}
879
880fn split_args(args_str: &str) -> Vec<&str> {
884 if args_str.trim().is_empty() {
885 return Vec::new();
886 }
887
888 let mut result = Vec::new();
889 let mut depth: u32 = 0u32;
890 let mut start: usize = 0;
891
892 #[allow(clippy::arithmetic_side_effects)]
893 for (i, c) in args_str.char_indices() {
896 match c {
897 '(' => depth += 1,
898 ')' => depth -= 1,
899 ',' if depth == 0 => {
900 #[allow(clippy::string_slice)]
901 result.push(args_str[start..i].trim());
906 start = i + 1;
907 }
908 _ => {}
909 }
910 }
911
912 #[allow(clippy::string_slice)]
913 let last = args_str[start..].trim();
916 if !last.is_empty() {
917 result.push(last);
918 }
919
920 result
921}
922
923#[cfg(test)]
924mod tests {
925 use crate::debug_access::{
926 Assignment, Conditional, DebugAccessParseError, DebugFunction, Expression, Statement,
927 };
928
929 #[test]
930 fn parse_comment() {
931 let line = "// This is a comment!".to_string();
932
933 let statement: Statement = line.try_into().unwrap();
934
935 assert_eq!(
936 statement,
937 Statement::Comment("// This is a comment!".to_string())
938 );
939 }
940
941 #[test]
942 fn semicolon_handling() {
943 let line1 = "Read32(0x10)".to_string();
944 let line2 = "Read32(0x10);".to_string();
945
946 let statement1: Statement = line1.try_into().unwrap();
947 let statement2: Statement = line2.try_into().unwrap();
948
949 assert_eq!(statement1, statement2);
950 }
951
952 #[test]
953 fn parse_expression_normal() {
954 let line = "addr + offset;".to_string();
955
956 let statement: Statement = line.try_into().unwrap();
957
958 assert_eq!(
959 statement,
960 Statement::Expression(Expression::Normal("addr + offset".to_string()))
961 );
962 }
963
964 #[test]
965 fn parse_expression_normal_variable() {
966 let line = "doIfBlock".to_string();
967
968 let statement: Statement = line.try_into().unwrap();
969
970 assert_eq!(
971 statement,
972 Statement::Expression(Expression::Normal("doIfBlock".to_string()))
973 );
974 }
975
976 #[test]
977 fn parse_expression_conditional() {
978 let line = "(x < y) ? a : b".to_string();
979
980 let statement: Statement = line.try_into().unwrap();
981
982 assert_eq!(
983 statement,
984 Statement::Expression(Expression::Conditional(Box::new(Conditional {
985 condition: Expression::Normal("x < y".to_string()),
986 true_value: Expression::Normal("a".to_string()),
987 false_value: Expression::Normal("b".to_string())
988 })))
989 );
990 }
991
992 #[test]
993 fn parse_assignment_comparison() {
994 let line = "thisValue = (readTheCoolRegister(0x248) == 5);".to_string();
995
996 let statement: Statement = line.try_into().unwrap();
997
998 assert_eq!(
999 statement,
1000 Statement::Assignment(Assignment {
1001 variable: "thisValue".to_string(),
1002 expression: Expression::Normal("(readTheCoolRegister(0x248) == 5)".to_string())
1003 })
1004 );
1005 }
1006
1007 #[test]
1008 fn parse_assignment() {
1009 let line = "variable = expression;".to_string();
1010
1011 let statement: Statement = line.try_into().unwrap();
1012
1013 assert_eq!(
1014 statement,
1015 Statement::Assignment(Assignment {
1016 expression: Expression::Normal("expression".to_string()),
1017 variable: "variable".to_string(),
1018 })
1019 )
1020 }
1021
1022 #[test]
1023 fn parse_definition() {
1024 let line = "__var variable = 0;".to_string();
1025
1026 let statement: Statement = line.try_into().unwrap();
1027
1028 assert_eq!(
1029 statement,
1030 Statement::Definition(Assignment {
1031 expression: Expression::Normal("0".to_string()),
1032 variable: "variable".to_string(),
1033 })
1034 )
1035 }
1036
1037 #[test]
1038 fn parse_function_call_single_arg() {
1039 let line = "Read32(0x40000000);".to_string();
1040
1041 let statement: Statement = line.try_into().unwrap();
1042
1043 assert_eq!(
1044 statement,
1045 Statement::Expression(Expression::FunctionCall(Box::new(DebugFunction::Read32 {
1046 addr: Expression::Normal("0x40000000".to_string())
1047 })))
1048 );
1049 }
1050
1051 #[test]
1052 fn parse_function_call_two_args() {
1053 let line = "Write32(addr, val);".to_string();
1054
1055 let statement: Statement = line.try_into().unwrap();
1056
1057 assert_eq!(
1058 statement,
1059 Statement::Expression(Expression::FunctionCall(Box::new(DebugFunction::Write32 {
1060 addr: Expression::Normal("addr".to_string()),
1061 val: Expression::Normal("val".to_string()),
1062 })))
1063 );
1064 }
1065
1066 #[test]
1067 fn parse_function_call_string_arg() {
1068 let line = "Sequence(\"ResetAndHalt\");".to_string();
1069
1070 let statement: Statement = line.try_into().unwrap();
1071
1072 assert_eq!(
1073 statement,
1074 Statement::Expression(Expression::FunctionCall(Box::new(
1075 DebugFunction::Sequence {
1076 name: Expression::Normal("\"ResetAndHalt\"".to_string())
1077 }
1078 )))
1079 );
1080 }
1081
1082 #[test]
1083 fn parse_function_call_three_args() {
1084 let line = "DAP_SWJ_Pins(pinout, pinselect, pinwait);".to_string();
1085
1086 let statement: Statement = line.try_into().unwrap();
1087
1088 assert_eq!(
1089 statement,
1090 Statement::Expression(Expression::FunctionCall(Box::new(
1091 DebugFunction::DapSwjPins {
1092 pinout: Expression::Normal("pinout".to_string()),
1093 pinselect: Expression::Normal("pinselect".to_string()),
1094 pinwait: Expression::Normal("pinwait".to_string()),
1095 }
1096 )))
1097 );
1098 }
1099
1100 #[test]
1101 fn parse_function_call_variadic() {
1102 let line = "Message(1, \"debug message\");".to_string();
1103
1104 let statement: Statement = line.try_into().unwrap();
1105
1106 assert_eq!(
1107 statement,
1108 Statement::Expression(Expression::FunctionCall(Box::new(DebugFunction::Message {
1109 msg_type: Expression::Normal("1".to_string()),
1110 format: Expression::Normal("\"debug message\"".to_string()),
1111 args: vec![],
1112 })))
1113 );
1114 }
1115
1116 #[test]
1117 fn parse_function_call_nested_arg() {
1118 let line = "Write32(addr, Read32(base));".to_string();
1120
1121 let statement: Statement = line.try_into().unwrap();
1122
1123 assert_eq!(
1124 statement,
1125 Statement::Expression(Expression::FunctionCall(Box::new(DebugFunction::Write32 {
1126 addr: Expression::Normal("addr".to_string()),
1127 val: Expression::FunctionCall(Box::new(DebugFunction::Read32 {
1128 addr: Expression::Normal("base".to_string()),
1129 })),
1130 })))
1131 );
1132 }
1133
1134 #[test]
1135 #[should_panic(expected = "unknown statement: GetBase")]
1136 fn unknown_function_panics() {
1137 if let Err(e) = Expression::try_from("GetBase()") {
1138 panic!("{e}");
1139 }
1140 }
1141
1142 #[test]
1143 fn conditional_missing_syntax() {
1144 let result = Conditional::try_from("no parentheses here");
1145 assert!(matches!(
1146 result,
1147 Err(DebugAccessParseError::MissingAttribute(_))
1148 ));
1149 }
1150
1151 #[test]
1152 fn unknown_function_returns_unknown_statement() {
1153 let result = DebugFunction::try_from(("GetBase".to_string(), vec![]));
1154 assert_eq!(
1155 result.unwrap_err(),
1156 DebugAccessParseError::UnknownStatement("GetBase".to_string())
1157 );
1158 }
1159}