1use serde::{Deserialize, Serialize};
4use std::fmt;
5
6use crate::debug_access::Statement::Comment;
7
8#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
10pub enum DebugAccessParseError {
11 MissingAttribute(String),
13 UnknownStatement(String),
15}
16
17impl Default for DebugAccessParseError {
18 fn default() -> Self {
19 Self::UnknownStatement(String::default())
20 }
21}
22
23impl From<DebugAccessParseError> for crate::Error {
24 fn from(value: DebugAccessParseError) -> Self {
25 Self::Debug(value)
26 }
27}
28
29impl std::fmt::Display for DebugAccessParseError {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 match self {
32 Self::MissingAttribute(msg) => write!(f, "missing attribute: {msg}"),
33 Self::UnknownStatement(name) => write!(f, "unknown statement: {name}"),
34 }
35 }
36}
37
38impl std::error::Error for DebugAccessParseError {}
39
40#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
41pub enum Statement {
44 Expression(Expression),
46
47 Assignment(Assignment),
49
50 Definition(Assignment),
52
53 Comment(String),
55}
56
57impl Default for Statement {
58 fn default() -> Self {
59 Comment(String::default())
60 }
61}
62
63impl TryFrom<String> for Statement {
64 type Error = crate::Error;
65
66 fn try_from(value: String) -> Result<Self, Self::Error> {
67 let input = value.trim().to_string();
69
70 if input.starts_with("//") {
72 return Ok(Self::Comment(input));
73 }
74
75 let input = input.strip_suffix(";").unwrap_or(&input).to_string();
77
78 let split: Option<(&str, &str)> = input.split_once('=');
80 let result: Self = match split {
81 None => {
82 let expression: Expression = input.try_into()?;
84 Self::Expression(expression)
85 }
86 Some((variable, expression)) => {
87 let variable = variable.trim();
88 let expression = expression.trim();
89 variable.strip_prefix("__var").map_or_else(
90 || {
91 let expression: Expression = expression.try_into()?;
92 Ok::<Self, Self::Error>(Self::Assignment(Assignment {
93 variable: variable.to_string(),
94 expression,
95 }))
96 },
97 |variable| {
98 let variable = variable.trim();
99 let expression: Expression = expression.try_into()?;
100 Ok::<Self, Self::Error>(Self::Definition(Assignment {
101 variable: variable.to_string(),
102 expression,
103 }))
104 },
105 )?
106 }
107 };
108
109 Ok(result)
110 }
111}
112
113#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
114pub struct Assignment {
116 pub variable: String,
117 pub expression: Expression,
118}
119
120#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
121pub enum Expression {
123 Normal(String),
125
126 Conditional(Box<Conditional>),
133
134 FunctionCall(Box<DebugFunction>),
137}
138
139impl Default for Expression {
140 fn default() -> Self {
141 Self::Normal(String::default())
142 }
143}
144
145impl TryFrom<String> for Expression {
146 type Error = crate::Error;
147
148 fn try_from(value: String) -> Result<Self, Self::Error> {
149 Ok(Self::try_from(value.as_str())?)
150 }
151}
152
153impl TryFrom<&str> for Expression {
154 type Error = DebugAccessParseError;
155
156 fn try_from(value: &str) -> Result<Self, Self::Error> {
157 if let Ok(condition) = Conditional::try_from(value) {
158 return Ok(Self::Conditional(Box::new(condition)));
159 }
160
161 if let Some((name, args_str)) = detect_function_call(value) {
162 let args: Vec<Self> = split_args(args_str)
163 .into_iter()
164 .map(Self::try_from)
165 .collect::<Result<Vec<_>, _>>()?;
166 let func = DebugFunction::try_from((name.to_string(), args))?;
167
168 return Ok(Self::FunctionCall(Box::new(func)));
169 }
170
171 Ok(Self::Normal(value.to_string()))
172 }
173}
174
175impl fmt::Display for Expression {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 match self {
178 Self::Normal(value) => f.write_str(value),
179 Self::Conditional(condition) => condition.fmt(f),
180 Self::FunctionCall(function) => function.fmt(f),
181 }
182 }
183}
184
185#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
186pub struct Conditional {
193 pub condition: Expression,
195 pub true_value: Expression,
197 pub false_value: Expression,
199}
200
201impl TryFrom<String> for Conditional {
202 type Error = DebugAccessParseError;
203
204 fn try_from(value: String) -> Result<Self, Self::Error> {
212 Self::try_from(value.as_str())
213 }
214}
215
216impl TryFrom<&str> for Conditional {
217 type Error = DebugAccessParseError;
218
219 fn try_from(value: &str) -> Result<Self, Self::Error> {
227 #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
229 enum WalkerProgress {
230 #[default]
231 None,
232 ParenOpen,
233 ParenClose,
234 Question,
235 Colon,
236 }
237
238 let mut condition_str: String = String::new();
240 let mut truthy_str: String = String::new();
241 let mut falsey_str: String = String::new();
242
243 let mut progress = WalkerProgress::None;
245 for c in value.chars() {
246 match progress {
247 WalkerProgress::None => {
248 if c == '(' {
249 progress = WalkerProgress::ParenOpen;
250 }
251 }
252 WalkerProgress::ParenOpen => {
253 if c == ')' {
254 progress = WalkerProgress::ParenClose;
255 } else {
256 condition_str.push(c);
257 }
258 }
259 WalkerProgress::ParenClose => {
260 if c == '?' {
261 progress = WalkerProgress::Question;
262 }
263 }
264 WalkerProgress::Question => {
265 if c == ':' {
266 progress = WalkerProgress::Colon;
267 } else {
268 truthy_str.push(c);
269 }
270 }
271 WalkerProgress::Colon => {
272 if c == ';' {
273 break;
274 }
275 falsey_str.push(c);
276 }
277 }
278 }
279
280 let walk_ok = progress == WalkerProgress::Colon && !falsey_str.is_empty();
281
282 if walk_ok {
283 let condition: Expression = condition_str.trim().try_into()?;
284 let true_value: Expression = truthy_str.trim().try_into()?;
285 let false_value: Expression = falsey_str.trim().try_into()?;
286
287 Ok(Self {
288 condition,
289 true_value,
290 false_value,
291 })
292 } else {
293 Err(DebugAccessParseError::MissingAttribute(
294 "conditional syntax: expected '(condition) ? truthy : falsy'".to_string(),
295 ))
296 }
297 }
298}
299
300impl fmt::Display for Conditional {
301 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302 write!(
303 f,
304 "({}) ? {} : {}",
305 self.condition, self.true_value, self.false_value
306 )
307 }
308}
309
310#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
311pub enum DebugFunction {
315 Read8 { addr: Expression },
318 Read16 { addr: Expression },
320 Read32 { addr: Expression },
322 Read64 { addr: Expression },
324 Write8 { addr: Expression, val: Expression },
326 Write16 { addr: Expression, val: Expression },
328 Write32 { addr: Expression, val: Expression },
330 Write64 { addr: Expression, val: Expression },
332
333 ReadAP { addr: Expression },
336 WriteAP { addr: Expression, val: Expression },
338 ReadDP { addr: Expression },
340 WriteDP { addr: Expression, val: Expression },
342 ReadAccessAP { addr: Expression },
344 WriteAccessAP { addr: Expression, val: Expression },
346
347 DapDelay { delay: Expression },
350 DapWriteAbort { value: Expression },
352 DapSwjPins {
354 pinout: Expression,
355 pinselect: Expression,
356 pinwait: Expression,
357 },
358 DapSwjClock { val: Expression },
360 DapSwjSequence { cnt: Expression, val: Expression },
362 DapJtagSequence {
364 cnt: Expression,
365 tms: Expression,
366 tdi: Expression,
367 },
368
369 Sequence { name: Expression },
372 Query {
374 query_type: Expression,
375 message: Expression,
376 default: Expression,
377 },
378 QueryValue {
380 message: Expression,
381 default: Expression,
382 },
383 Message {
385 msg_type: Expression,
386 format: Expression,
387 args: Vec<Expression>,
388 },
389
390 FlashWriteBuffer {
393 addr: Expression,
394 offs: Expression,
395 len: Expression,
396 mode: Expression,
397 },
398 FlashLoadAlgorithm {
400 algo_path: Expression,
401 ram_start: Expression,
402 ram_size: Expression,
403 },
404
405 BufferSet {
408 buff_id: Expression,
409 buff_offset: Expression,
410 count: Expression,
411 size: Expression,
412 value: Expression,
413 },
414 BufferGet {
416 buff_id: Expression,
417 buff_offset: Expression,
418 size: Expression,
419 },
420 BufferSize { buff_id: Expression },
422 BufferRead {
424 buff_id: Expression,
425 buff_offset: Expression,
426 addr: Expression,
427 length: Expression,
428 mode: Expression,
429 },
430 BufferWrite {
432 buff_id: Expression,
433 buff_offset: Expression,
434 addr: Expression,
435 length: Expression,
436 mode: Expression,
437 },
438
439 BufferStreamIn {
442 buff_id: Expression,
443 buff_offset: Expression,
444 length: Expression,
445 path: Expression,
446 mode: Expression,
447 timeout: Expression,
448 },
449 BufferStreamOut {
451 buff_id: Expression,
452 buff_offset: Expression,
453 length: Expression,
454 dest_path: Expression,
455 dest_mode: Expression,
456 timeout: Expression,
457 },
458 RunApplication {
460 app_path: Expression,
461 arguments: Expression,
462 work_directory: Expression,
463 timeout: Expression,
464 },
465 RunPythonScript {
467 script_path: Expression,
468 arguments: Expression,
469 work_directory: Expression,
470 timeout: Expression,
471 },
472 FilePathExists {
474 path: Expression,
475 timeout: Expression,
476 },
477 LoadDebugInfo { file: Expression },
479}
480
481fn fmt_debug_function(f: &mut fmt::Formatter<'_>, name: &str, args: &[&Expression]) -> fmt::Result {
482 write!(f, "{name}(")?;
483 for (index, arg) in args.iter().enumerate() {
484 if index != 0 {
485 f.write_str(", ")?;
486 }
487 fmt::Display::fmt(*arg, f)?;
488 }
489 f.write_str(")")
490}
491
492impl fmt::Display for DebugFunction {
493 #[allow(clippy::too_many_lines)]
494 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
495 match self {
496 Self::Read8 { addr } => fmt_debug_function(f, "Read8", &[addr]),
497 Self::Read16 { addr } => fmt_debug_function(f, "Read16", &[addr]),
498 Self::Read32 { addr } => fmt_debug_function(f, "Read32", &[addr]),
499 Self::Read64 { addr } => fmt_debug_function(f, "Read64", &[addr]),
500 Self::Write8 { addr, val } => fmt_debug_function(f, "Write8", &[addr, val]),
501 Self::Write16 { addr, val } => fmt_debug_function(f, "Write16", &[addr, val]),
502 Self::Write32 { addr, val } => fmt_debug_function(f, "Write32", &[addr, val]),
503 Self::Write64 { addr, val } => fmt_debug_function(f, "Write64", &[addr, val]),
504 Self::ReadAP { addr } => fmt_debug_function(f, "ReadAP", &[addr]),
505 Self::WriteAP { addr, val } => fmt_debug_function(f, "WriteAP", &[addr, val]),
506 Self::ReadDP { addr } => fmt_debug_function(f, "ReadDP", &[addr]),
507 Self::WriteDP { addr, val } => fmt_debug_function(f, "WriteDP", &[addr, val]),
508 Self::ReadAccessAP { addr } => fmt_debug_function(f, "ReadAccessAP", &[addr]),
509 Self::WriteAccessAP { addr, val } => {
510 fmt_debug_function(f, "WriteAccessAP", &[addr, val])
511 }
512 Self::DapDelay { delay } => fmt_debug_function(f, "DAP_Delay", &[delay]),
513 Self::DapWriteAbort { value } => fmt_debug_function(f, "DAP_WriteABORT", &[value]),
514 Self::DapSwjPins {
515 pinout,
516 pinselect,
517 pinwait,
518 } => fmt_debug_function(f, "DAP_SWJ_Pins", &[pinout, pinselect, pinwait]),
519 Self::DapSwjClock { val } => fmt_debug_function(f, "DAP_SWJ_Clock", &[val]),
520 Self::DapSwjSequence { cnt, val } => {
521 fmt_debug_function(f, "DAP_SWJ_Sequence", &[cnt, val])
522 }
523 Self::DapJtagSequence { cnt, tms, tdi } => {
524 fmt_debug_function(f, "DAP_JTAG_Sequence", &[cnt, tms, tdi])
525 }
526 Self::Sequence { name } => fmt_debug_function(f, "Sequence", &[name]),
527 Self::Query {
528 query_type,
529 message,
530 default,
531 } => fmt_debug_function(f, "Query", &[query_type, message, default]),
532 Self::QueryValue { message, default } => {
533 fmt_debug_function(f, "QueryValue", &[message, default])
534 }
535 Self::Message {
536 msg_type,
537 format,
538 args,
539 } => {
540 let mut all_args = Vec::with_capacity(args.len().saturating_add(2));
541 all_args.push(msg_type);
542 all_args.push(format);
543 all_args.extend(args);
544 fmt_debug_function(f, "Message", &all_args)
545 }
546 Self::FlashWriteBuffer {
547 addr,
548 offs,
549 len,
550 mode,
551 } => fmt_debug_function(f, "FlashWriteBuffer", &[addr, offs, len, mode]),
552 Self::FlashLoadAlgorithm {
553 algo_path,
554 ram_start,
555 ram_size,
556 } => fmt_debug_function(f, "FlashLoadAlgorithm", &[algo_path, ram_start, ram_size]),
557 Self::BufferSet {
558 buff_id,
559 buff_offset,
560 count,
561 size,
562 value,
563 } => fmt_debug_function(f, "BufferSet", &[buff_id, buff_offset, count, size, value]),
564 Self::BufferGet {
565 buff_id,
566 buff_offset,
567 size,
568 } => fmt_debug_function(f, "BufferGet", &[buff_id, buff_offset, size]),
569 Self::BufferSize { buff_id } => fmt_debug_function(f, "BufferSize", &[buff_id]),
570 Self::BufferRead {
571 buff_id,
572 buff_offset,
573 addr,
574 length,
575 mode,
576 } => fmt_debug_function(f, "BufferRead", &[buff_id, buff_offset, addr, length, mode]),
577 Self::BufferWrite {
578 buff_id,
579 buff_offset,
580 addr,
581 length,
582 mode,
583 } => fmt_debug_function(
584 f,
585 "BufferWrite",
586 &[buff_id, buff_offset, addr, length, mode],
587 ),
588 Self::BufferStreamIn {
589 buff_id,
590 buff_offset,
591 length,
592 path,
593 mode,
594 timeout,
595 } => fmt_debug_function(
596 f,
597 "BufferStreamIn",
598 &[buff_id, buff_offset, length, path, mode, timeout],
599 ),
600 Self::BufferStreamOut {
601 buff_id,
602 buff_offset,
603 length,
604 dest_path,
605 dest_mode,
606 timeout,
607 } => fmt_debug_function(
608 f,
609 "BufferStreamOut",
610 &[buff_id, buff_offset, length, dest_path, dest_mode, timeout],
611 ),
612 Self::RunApplication {
613 app_path,
614 arguments,
615 work_directory,
616 timeout,
617 } => fmt_debug_function(
618 f,
619 "RunApplication",
620 &[app_path, arguments, work_directory, timeout],
621 ),
622 Self::RunPythonScript {
623 script_path,
624 arguments,
625 work_directory,
626 timeout,
627 } => fmt_debug_function(
628 f,
629 "RunPythonScript",
630 &[script_path, arguments, work_directory, timeout],
631 ),
632 Self::FilePathExists { path, timeout } => {
633 fmt_debug_function(f, "FilePathExists", &[path, timeout])
634 }
635 Self::LoadDebugInfo { file } => fmt_debug_function(f, "LoadDebugInfo", &[file]),
636 }
637 }
638}
639
640impl Default for DebugFunction {
641 fn default() -> Self {
642 Self::DapDelay {
643 delay: Expression::Normal("0".to_string()),
644 }
645 }
646}
647
648impl TryFrom<(String, Vec<Expression>)> for DebugFunction {
649 type Error = DebugAccessParseError;
650
651 #[allow(clippy::too_many_lines)]
655 fn try_from((name, args): (String, Vec<Expression>)) -> Result<Self, Self::Error> {
656 match name.as_str() {
657 "Read8" => match <[Expression; 1]>::try_from(args) {
659 Ok([addr]) => Ok(Self::Read8 { addr }),
660 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
661 "Read8 expects 1 argument, got {}",
662 v.len()
663 ))),
664 },
665 "Read16" => match <[Expression; 1]>::try_from(args) {
666 Ok([addr]) => Ok(Self::Read16 { addr }),
667 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
668 "Read16 expects 1 argument, got {}",
669 v.len()
670 ))),
671 },
672 "Read32" => match <[Expression; 1]>::try_from(args) {
673 Ok([addr]) => Ok(Self::Read32 { addr }),
674 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
675 "Read32 expects 1 argument, got {}",
676 v.len()
677 ))),
678 },
679 "Read64" => match <[Expression; 1]>::try_from(args) {
680 Ok([addr]) => Ok(Self::Read64 { addr }),
681 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
682 "Read64 expects 1 argument, got {}",
683 v.len()
684 ))),
685 },
686 "Write8" => match <[Expression; 2]>::try_from(args) {
688 Ok([addr, val]) => Ok(Self::Write8 { addr, val }),
689 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
690 "Write8 expects 2 arguments, got {}",
691 v.len()
692 ))),
693 },
694 "Write16" => match <[Expression; 2]>::try_from(args) {
695 Ok([addr, val]) => Ok(Self::Write16 { addr, val }),
696 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
697 "Write16 expects 2 arguments, got {}",
698 v.len()
699 ))),
700 },
701 "Write32" => match <[Expression; 2]>::try_from(args) {
702 Ok([addr, val]) => Ok(Self::Write32 { addr, val }),
703 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
704 "Write32 expects 2 arguments, got {}",
705 v.len()
706 ))),
707 },
708 "Write64" => match <[Expression; 2]>::try_from(args) {
709 Ok([addr, val]) => Ok(Self::Write64 { addr, val }),
710 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
711 "Write64 expects 2 arguments, got {}",
712 v.len()
713 ))),
714 },
715 "ReadAP" => match <[Expression; 1]>::try_from(args) {
717 Ok([addr]) => Ok(Self::ReadAP { addr }),
718 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
719 "ReadAP expects 1 argument, got {}",
720 v.len()
721 ))),
722 },
723 "ReadDP" => match <[Expression; 1]>::try_from(args) {
724 Ok([addr]) => Ok(Self::ReadDP { addr }),
725 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
726 "ReadDP expects 1 argument, got {}",
727 v.len()
728 ))),
729 },
730 "ReadAccessAP" => match <[Expression; 1]>::try_from(args) {
731 Ok([addr]) => Ok(Self::ReadAccessAP { addr }),
732 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
733 "ReadAccessAP expects 1 argument, got {}",
734 v.len()
735 ))),
736 },
737 "WriteAP" => match <[Expression; 2]>::try_from(args) {
739 Ok([addr, val]) => Ok(Self::WriteAP { addr, val }),
740 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
741 "WriteAP expects 2 arguments, got {}",
742 v.len()
743 ))),
744 },
745 "WriteDP" => match <[Expression; 2]>::try_from(args) {
746 Ok([addr, val]) => Ok(Self::WriteDP { addr, val }),
747 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
748 "WriteDP expects 2 arguments, got {}",
749 v.len()
750 ))),
751 },
752 "WriteAccessAP" => match <[Expression; 2]>::try_from(args) {
753 Ok([addr, val]) => Ok(Self::WriteAccessAP { addr, val }),
754 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
755 "WriteAccessAP expects 2 arguments, got {}",
756 v.len()
757 ))),
758 },
759 "DAP_Delay" => match <[Expression; 1]>::try_from(args) {
761 Ok([delay]) => Ok(Self::DapDelay { delay }),
762 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
763 "DAP_Delay expects 1 argument, got {}",
764 v.len()
765 ))),
766 },
767 "DAP_WriteABORT" => match <[Expression; 1]>::try_from(args) {
768 Ok([value]) => Ok(Self::DapWriteAbort { value }),
769 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
770 "DAP_WriteABORT expects 1 argument, got {}",
771 v.len()
772 ))),
773 },
774 "DAP_SWJ_Clock" => match <[Expression; 1]>::try_from(args) {
775 Ok([val]) => Ok(Self::DapSwjClock { val }),
776 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
777 "DAP_SWJ_Clock expects 1 argument, got {}",
778 v.len()
779 ))),
780 },
781 "DAP_SWJ_Sequence" => match <[Expression; 2]>::try_from(args) {
783 Ok([cnt, val]) => Ok(Self::DapSwjSequence { cnt, val }),
784 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
785 "DAP_SWJ_Sequence expects 2 arguments, got {}",
786 v.len()
787 ))),
788 },
789 "DAP_SWJ_Pins" => match <[Expression; 3]>::try_from(args) {
791 Ok([pinout, pinselect, pinwait]) => Ok(Self::DapSwjPins {
792 pinout,
793 pinselect,
794 pinwait,
795 }),
796 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
797 "DAP_SWJ_Pins expects 3 arguments, got {}",
798 v.len()
799 ))),
800 },
801 "DAP_JTAG_Sequence" => match <[Expression; 3]>::try_from(args) {
802 Ok([cnt, tms, tdi]) => Ok(Self::DapJtagSequence { cnt, tms, tdi }),
803 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
804 "DAP_JTAG_Sequence expects 3 arguments, got {}",
805 v.len()
806 ))),
807 },
808 "Sequence" => match <[Expression; 1]>::try_from(args) {
810 Ok([name]) => Ok(Self::Sequence { name }),
811 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
812 "Sequence expects 1 argument, got {}",
813 v.len()
814 ))),
815 },
816 "QueryValue" => match <[Expression; 2]>::try_from(args) {
818 Ok([message, default]) => Ok(Self::QueryValue { message, default }),
819 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
820 "QueryValue expects 2 arguments, got {}",
821 v.len()
822 ))),
823 },
824 "Query" => match <[Expression; 3]>::try_from(args) {
826 Ok([query_type, message, default]) => Ok(Self::Query {
827 query_type,
828 message,
829 default,
830 }),
831 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
832 "Query expects 3 arguments, got {}",
833 v.len()
834 ))),
835 },
836 "Message" => {
838 let mut it = args.into_iter();
839 let msg_type = it.next().ok_or_else(|| {
840 DebugAccessParseError::MissingAttribute(
841 "Message expects at least 2 arguments, got 0".to_string(),
842 )
843 })?;
844 let format_expr = it.next().ok_or_else(|| {
845 DebugAccessParseError::MissingAttribute(
846 "Message expects at least 2 arguments, got 1".to_string(),
847 )
848 })?;
849 Ok(Self::Message {
850 msg_type,
851 format: format_expr,
852 args: it.collect(),
853 })
854 }
855 "FlashLoadAlgorithm" => match <[Expression; 3]>::try_from(args) {
857 Ok([algo_path, ram_start, ram_size]) => Ok(Self::FlashLoadAlgorithm {
858 algo_path,
859 ram_start,
860 ram_size,
861 }),
862 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
863 "FlashLoadAlgorithm expects 3 arguments, got {}",
864 v.len()
865 ))),
866 },
867 "FlashWriteBuffer" => match <[Expression; 4]>::try_from(args) {
869 Ok([addr, offs, len, mode]) => Ok(Self::FlashWriteBuffer {
870 addr,
871 offs,
872 len,
873 mode,
874 }),
875 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
876 "FlashWriteBuffer expects 4 arguments, got {}",
877 v.len()
878 ))),
879 },
880 "BufferSize" => match <[Expression; 1]>::try_from(args) {
882 Ok([buff_id]) => Ok(Self::BufferSize { buff_id }),
883 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
884 "BufferSize expects 1 argument, got {}",
885 v.len()
886 ))),
887 },
888 "BufferGet" => match <[Expression; 3]>::try_from(args) {
890 Ok([buff_id, buff_offset, size]) => Ok(Self::BufferGet {
891 buff_id,
892 buff_offset,
893 size,
894 }),
895 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
896 "BufferGet expects 3 arguments, got {}",
897 v.len()
898 ))),
899 },
900 "BufferSet" => match <[Expression; 5]>::try_from(args) {
902 Ok([buff_id, buff_offset, count, size, value]) => Ok(Self::BufferSet {
903 buff_id,
904 buff_offset,
905 count,
906 size,
907 value,
908 }),
909 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
910 "BufferSet expects 5 arguments, got {}",
911 v.len()
912 ))),
913 },
914 "BufferRead" => match <[Expression; 5]>::try_from(args) {
915 Ok([buff_id, buff_offset, addr, length, mode]) => Ok(Self::BufferRead {
916 buff_id,
917 buff_offset,
918 addr,
919 length,
920 mode,
921 }),
922 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
923 "BufferRead expects 5 arguments, got {}",
924 v.len()
925 ))),
926 },
927 "BufferWrite" => match <[Expression; 5]>::try_from(args) {
928 Ok([buff_id, buff_offset, addr, length, mode]) => Ok(Self::BufferWrite {
929 buff_id,
930 buff_offset,
931 addr,
932 length,
933 mode,
934 }),
935 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
936 "BufferWrite expects 5 arguments, got {}",
937 v.len()
938 ))),
939 },
940 "LoadDebugInfo" => match <[Expression; 1]>::try_from(args) {
942 Ok([file]) => Ok(Self::LoadDebugInfo { file }),
943 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
944 "LoadDebugInfo expects 1 argument, got {}",
945 v.len()
946 ))),
947 },
948 "FilePathExists" => match <[Expression; 2]>::try_from(args) {
950 Ok([path, timeout]) => Ok(Self::FilePathExists { path, timeout }),
951 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
952 "FilePathExists expects 2 arguments, got {}",
953 v.len()
954 ))),
955 },
956 "RunApplication" => match <[Expression; 4]>::try_from(args) {
958 Ok([app_path, arguments, work_directory, timeout]) => Ok(Self::RunApplication {
959 app_path,
960 arguments,
961 work_directory,
962 timeout,
963 }),
964 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
965 "RunApplication expects 4 arguments, got {}",
966 v.len()
967 ))),
968 },
969 "RunPythonScript" => match <[Expression; 4]>::try_from(args) {
970 Ok([script_path, arguments, work_directory, timeout]) => {
971 Ok(Self::RunPythonScript {
972 script_path,
973 arguments,
974 work_directory,
975 timeout,
976 })
977 }
978 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
979 "RunPythonScript expects 4 arguments, got {}",
980 v.len()
981 ))),
982 },
983 "BufferStreamIn" => match <[Expression; 6]>::try_from(args) {
985 Ok([buff_id, buff_offset, length, path, mode, timeout]) => {
986 Ok(Self::BufferStreamIn {
987 buff_id,
988 buff_offset,
989 length,
990 path,
991 mode,
992 timeout,
993 })
994 }
995 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
996 "BufferStreamIn expects 6 arguments, got {}",
997 v.len()
998 ))),
999 },
1000 "BufferStreamOut" => match <[Expression; 6]>::try_from(args) {
1001 Ok([buff_id, buff_offset, length, dest_path, dest_mode, timeout]) => {
1002 Ok(Self::BufferStreamOut {
1003 buff_id,
1004 buff_offset,
1005 length,
1006 dest_path,
1007 dest_mode,
1008 timeout,
1009 })
1010 }
1011 Err(v) => Err(DebugAccessParseError::MissingAttribute(format!(
1012 "BufferStreamOut expects 6 arguments, got {}",
1013 v.len()
1014 ))),
1015 },
1016 _ => Err(DebugAccessParseError::UnknownStatement(name)),
1017 }
1018 }
1019}
1020
1021fn detect_function_call(s: &str) -> Option<(&str, &str)> {
1025 if !s.ends_with(')') {
1026 return None;
1027 }
1028
1029 let paren_pos = s.find('(')?;
1030 #[allow(clippy::string_slice)]
1031 let name = &s[..paren_pos];
1035
1036 let mut name_chars = name.chars();
1038 let first = name_chars.next()?;
1039 if !first.is_alphabetic() && first != '_' {
1040 return None;
1041 }
1042 if !name_chars.all(|c| c.is_alphanumeric() || c == '_') {
1043 return None;
1044 }
1045
1046 #[allow(clippy::arithmetic_side_effects)]
1047 #[allow(clippy::string_slice)]
1052 let args_str = &s[paren_pos + 1..s.len() - 1];
1057 Some((name, args_str))
1058}
1059
1060fn split_args(args_str: &str) -> Vec<&str> {
1064 if args_str.trim().is_empty() {
1065 return Vec::new();
1066 }
1067
1068 let mut result = Vec::new();
1069 let mut depth: u32 = 0u32;
1070 let mut start: usize = 0;
1071
1072 #[allow(clippy::arithmetic_side_effects)]
1073 for (i, c) in args_str.char_indices() {
1076 match c {
1077 '(' => depth += 1,
1078 ')' => depth -= 1,
1079 ',' if depth == 0 => {
1080 #[allow(clippy::string_slice)]
1081 result.push(args_str[start..i].trim());
1086 start = i + 1;
1087 }
1088 _ => {}
1089 }
1090 }
1091
1092 #[allow(clippy::string_slice)]
1093 let last = args_str[start..].trim();
1096 if !last.is_empty() {
1097 result.push(last);
1098 }
1099
1100 result
1101}
1102
1103#[cfg(test)]
1104mod tests {
1105 use crate::debug_access::{
1106 Assignment, Conditional, DebugAccessParseError, DebugFunction, Expression, Statement,
1107 };
1108
1109 #[test]
1110 fn parse_comment() {
1111 let line = "// This is a comment!".to_string();
1112
1113 let statement: Statement = line.try_into().unwrap();
1114
1115 assert_eq!(
1116 statement,
1117 Statement::Comment("// This is a comment!".to_string())
1118 );
1119 }
1120
1121 #[test]
1122 fn semicolon_handling() {
1123 let line1 = "Read32(0x10)".to_string();
1124 let line2 = "Read32(0x10);".to_string();
1125
1126 let statement1: Statement = line1.try_into().unwrap();
1127 let statement2: Statement = line2.try_into().unwrap();
1128
1129 assert_eq!(statement1, statement2);
1130 }
1131
1132 #[test]
1133 fn parse_expression_normal() {
1134 let line = "addr + offset;".to_string();
1135
1136 let statement: Statement = line.try_into().unwrap();
1137
1138 assert_eq!(
1139 statement,
1140 Statement::Expression(Expression::Normal("addr + offset".to_string()))
1141 );
1142 }
1143
1144 #[test]
1145 fn parse_expression_normal_variable() {
1146 let line = "doIfBlock".to_string();
1147
1148 let statement: Statement = line.try_into().unwrap();
1149
1150 assert_eq!(
1151 statement,
1152 Statement::Expression(Expression::Normal("doIfBlock".to_string()))
1153 );
1154 }
1155
1156 #[test]
1157 fn parse_expression_conditional() {
1158 let line = "(x < y) ? a : b".to_string();
1159
1160 let statement: Statement = line.try_into().unwrap();
1161
1162 assert_eq!(
1163 statement,
1164 Statement::Expression(Expression::Conditional(Box::new(Conditional {
1165 condition: Expression::Normal("x < y".to_string()),
1166 true_value: Expression::Normal("a".to_string()),
1167 false_value: Expression::Normal("b".to_string())
1168 })))
1169 );
1170 }
1171
1172 #[test]
1173 fn parse_assignment_comparison() {
1174 let line = "thisValue = (readTheCoolRegister(0x248) == 5);".to_string();
1175
1176 let statement: Statement = line.try_into().unwrap();
1177
1178 assert_eq!(
1179 statement,
1180 Statement::Assignment(Assignment {
1181 variable: "thisValue".to_string(),
1182 expression: Expression::Normal("(readTheCoolRegister(0x248) == 5)".to_string())
1183 })
1184 );
1185 }
1186
1187 #[test]
1188 fn parse_assignment() {
1189 let line = "variable = expression;".to_string();
1190
1191 let statement: Statement = line.try_into().unwrap();
1192
1193 assert_eq!(
1194 statement,
1195 Statement::Assignment(Assignment {
1196 expression: Expression::Normal("expression".to_string()),
1197 variable: "variable".to_string(),
1198 })
1199 )
1200 }
1201
1202 #[test]
1203 fn parse_definition() {
1204 let line = "__var variable = 0;".to_string();
1205
1206 let statement: Statement = line.try_into().unwrap();
1207
1208 assert_eq!(
1209 statement,
1210 Statement::Definition(Assignment {
1211 expression: Expression::Normal("0".to_string()),
1212 variable: "variable".to_string(),
1213 })
1214 )
1215 }
1216
1217 #[test]
1218 fn parse_function_call_single_arg() {
1219 let line = "Read32(0x40000000);".to_string();
1220
1221 let statement: Statement = line.try_into().unwrap();
1222
1223 assert_eq!(
1224 statement,
1225 Statement::Expression(Expression::FunctionCall(Box::new(DebugFunction::Read32 {
1226 addr: Expression::Normal("0x40000000".to_string())
1227 })))
1228 );
1229 }
1230
1231 #[test]
1232 fn parse_function_call_two_args() {
1233 let line = "Write32(addr, val);".to_string();
1234
1235 let statement: Statement = line.try_into().unwrap();
1236
1237 assert_eq!(
1238 statement,
1239 Statement::Expression(Expression::FunctionCall(Box::new(DebugFunction::Write32 {
1240 addr: Expression::Normal("addr".to_string()),
1241 val: Expression::Normal("val".to_string()),
1242 })))
1243 );
1244 }
1245
1246 #[test]
1247 fn parse_function_call_string_arg() {
1248 let line = "Sequence(\"ResetAndHalt\");".to_string();
1249
1250 let statement: Statement = line.try_into().unwrap();
1251
1252 assert_eq!(
1253 statement,
1254 Statement::Expression(Expression::FunctionCall(Box::new(
1255 DebugFunction::Sequence {
1256 name: Expression::Normal("\"ResetAndHalt\"".to_string())
1257 }
1258 )))
1259 );
1260 }
1261
1262 #[test]
1263 fn parse_function_call_three_args() {
1264 let line = "DAP_SWJ_Pins(pinout, pinselect, pinwait);".to_string();
1265
1266 let statement: Statement = line.try_into().unwrap();
1267
1268 assert_eq!(
1269 statement,
1270 Statement::Expression(Expression::FunctionCall(Box::new(
1271 DebugFunction::DapSwjPins {
1272 pinout: Expression::Normal("pinout".to_string()),
1273 pinselect: Expression::Normal("pinselect".to_string()),
1274 pinwait: Expression::Normal("pinwait".to_string()),
1275 }
1276 )))
1277 );
1278 }
1279
1280 #[test]
1281 fn parse_function_call_variadic() {
1282 let line = "Message(1, \"debug message\");".to_string();
1283
1284 let statement: Statement = line.try_into().unwrap();
1285
1286 assert_eq!(
1287 statement,
1288 Statement::Expression(Expression::FunctionCall(Box::new(DebugFunction::Message {
1289 msg_type: Expression::Normal("1".to_string()),
1290 format: Expression::Normal("\"debug message\"".to_string()),
1291 args: vec![],
1292 })))
1293 );
1294 }
1295
1296 #[test]
1297 fn parse_function_call_nested_arg() {
1298 let line = "Write32(addr, Read32(base));".to_string();
1300
1301 let statement: Statement = line.try_into().unwrap();
1302
1303 assert_eq!(
1304 statement,
1305 Statement::Expression(Expression::FunctionCall(Box::new(DebugFunction::Write32 {
1306 addr: Expression::Normal("addr".to_string()),
1307 val: Expression::FunctionCall(Box::new(DebugFunction::Read32 {
1308 addr: Expression::Normal("base".to_string()),
1309 })),
1310 })))
1311 );
1312 }
1313
1314 #[test]
1315 #[should_panic(expected = "unknown statement: GetBase")]
1316 fn unknown_function_panics() {
1317 if let Err(e) = Expression::try_from("GetBase()") {
1318 panic!("{e}");
1319 }
1320 }
1321
1322 #[test]
1323 fn conditional_missing_syntax() {
1324 let result = Conditional::try_from("no parentheses here");
1325 assert!(matches!(
1326 result,
1327 Err(DebugAccessParseError::MissingAttribute(_))
1328 ));
1329 }
1330
1331 #[test]
1332 fn unknown_function_returns_unknown_statement() {
1333 let result = DebugFunction::try_from(("GetBase".to_string(), vec![]));
1334 assert_eq!(
1335 result.unwrap_err(),
1336 DebugAccessParseError::UnknownStatement("GetBase".to_string())
1337 );
1338 }
1339
1340 fn normal(value: &str) -> Expression {
1341 Expression::Normal(value.to_string())
1342 }
1343
1344 #[test]
1345 fn format_debug_functions_exhaustively() {
1346 let cases = vec![
1347 (DebugFunction::Read8 { addr: normal("a") }, "Read8(a)"),
1348 (DebugFunction::Read16 { addr: normal("a") }, "Read16(a)"),
1349 (DebugFunction::Read32 { addr: normal("a") }, "Read32(a)"),
1350 (DebugFunction::Read64 { addr: normal("a") }, "Read64(a)"),
1351 (
1352 DebugFunction::Write8 {
1353 addr: normal("a"),
1354 val: normal("v"),
1355 },
1356 "Write8(a, v)",
1357 ),
1358 (
1359 DebugFunction::Write16 {
1360 addr: normal("a"),
1361 val: normal("v"),
1362 },
1363 "Write16(a, v)",
1364 ),
1365 (
1366 DebugFunction::Write32 {
1367 addr: normal("a"),
1368 val: normal("v"),
1369 },
1370 "Write32(a, v)",
1371 ),
1372 (
1373 DebugFunction::Write64 {
1374 addr: normal("a"),
1375 val: normal("v"),
1376 },
1377 "Write64(a, v)",
1378 ),
1379 (DebugFunction::ReadAP { addr: normal("a") }, "ReadAP(a)"),
1380 (
1381 DebugFunction::WriteAP {
1382 addr: normal("a"),
1383 val: normal("v"),
1384 },
1385 "WriteAP(a, v)",
1386 ),
1387 (DebugFunction::ReadDP { addr: normal("a") }, "ReadDP(a)"),
1388 (
1389 DebugFunction::WriteDP {
1390 addr: normal("a"),
1391 val: normal("v"),
1392 },
1393 "WriteDP(a, v)",
1394 ),
1395 (
1396 DebugFunction::ReadAccessAP { addr: normal("a") },
1397 "ReadAccessAP(a)",
1398 ),
1399 (
1400 DebugFunction::WriteAccessAP {
1401 addr: normal("a"),
1402 val: normal("v"),
1403 },
1404 "WriteAccessAP(a, v)",
1405 ),
1406 (
1407 DebugFunction::DapDelay {
1408 delay: normal("delay"),
1409 },
1410 "DAP_Delay(delay)",
1411 ),
1412 (
1413 DebugFunction::DapWriteAbort {
1414 value: normal("value"),
1415 },
1416 "DAP_WriteABORT(value)",
1417 ),
1418 (
1419 DebugFunction::DapSwjPins {
1420 pinout: normal("pinout"),
1421 pinselect: normal("pinselect"),
1422 pinwait: normal("pinwait"),
1423 },
1424 "DAP_SWJ_Pins(pinout, pinselect, pinwait)",
1425 ),
1426 (
1427 DebugFunction::DapSwjClock { val: normal("val") },
1428 "DAP_SWJ_Clock(val)",
1429 ),
1430 (
1431 DebugFunction::DapSwjSequence {
1432 cnt: normal("cnt"),
1433 val: normal("val"),
1434 },
1435 "DAP_SWJ_Sequence(cnt, val)",
1436 ),
1437 (
1438 DebugFunction::DapJtagSequence {
1439 cnt: normal("cnt"),
1440 tms: normal("tms"),
1441 tdi: normal("tdi"),
1442 },
1443 "DAP_JTAG_Sequence(cnt, tms, tdi)",
1444 ),
1445 (
1446 DebugFunction::Sequence {
1447 name: normal("name"),
1448 },
1449 "Sequence(name)",
1450 ),
1451 (
1452 DebugFunction::Query {
1453 query_type: normal("query_type"),
1454 message: normal("message"),
1455 default: normal("default"),
1456 },
1457 "Query(query_type, message, default)",
1458 ),
1459 (
1460 DebugFunction::QueryValue {
1461 message: normal("message"),
1462 default: normal("default"),
1463 },
1464 "QueryValue(message, default)",
1465 ),
1466 (
1467 DebugFunction::Message {
1468 msg_type: normal("msg_type"),
1469 format: normal("format"),
1470 args: vec![],
1471 },
1472 "Message(msg_type, format)",
1473 ),
1474 (
1475 DebugFunction::FlashWriteBuffer {
1476 addr: normal("addr"),
1477 offs: normal("offs"),
1478 len: normal("len"),
1479 mode: normal("mode"),
1480 },
1481 "FlashWriteBuffer(addr, offs, len, mode)",
1482 ),
1483 (
1484 DebugFunction::FlashLoadAlgorithm {
1485 algo_path: normal("algo_path"),
1486 ram_start: normal("ram_start"),
1487 ram_size: normal("ram_size"),
1488 },
1489 "FlashLoadAlgorithm(algo_path, ram_start, ram_size)",
1490 ),
1491 (
1492 DebugFunction::BufferSet {
1493 buff_id: normal("buff_id"),
1494 buff_offset: normal("buff_offset"),
1495 count: normal("count"),
1496 size: normal("size"),
1497 value: normal("value"),
1498 },
1499 "BufferSet(buff_id, buff_offset, count, size, value)",
1500 ),
1501 (
1502 DebugFunction::BufferGet {
1503 buff_id: normal("buff_id"),
1504 buff_offset: normal("buff_offset"),
1505 size: normal("size"),
1506 },
1507 "BufferGet(buff_id, buff_offset, size)",
1508 ),
1509 (
1510 DebugFunction::BufferSize {
1511 buff_id: normal("buff_id"),
1512 },
1513 "BufferSize(buff_id)",
1514 ),
1515 (
1516 DebugFunction::BufferRead {
1517 buff_id: normal("buff_id"),
1518 buff_offset: normal("buff_offset"),
1519 addr: normal("addr"),
1520 length: normal("length"),
1521 mode: normal("mode"),
1522 },
1523 "BufferRead(buff_id, buff_offset, addr, length, mode)",
1524 ),
1525 (
1526 DebugFunction::BufferWrite {
1527 buff_id: normal("buff_id"),
1528 buff_offset: normal("buff_offset"),
1529 addr: normal("addr"),
1530 length: normal("length"),
1531 mode: normal("mode"),
1532 },
1533 "BufferWrite(buff_id, buff_offset, addr, length, mode)",
1534 ),
1535 (
1536 DebugFunction::BufferStreamIn {
1537 buff_id: normal("buff_id"),
1538 buff_offset: normal("buff_offset"),
1539 length: normal("length"),
1540 path: normal("path"),
1541 mode: normal("mode"),
1542 timeout: normal("timeout"),
1543 },
1544 "BufferStreamIn(buff_id, buff_offset, length, path, mode, timeout)",
1545 ),
1546 (
1547 DebugFunction::BufferStreamOut {
1548 buff_id: normal("buff_id"),
1549 buff_offset: normal("buff_offset"),
1550 length: normal("length"),
1551 dest_path: normal("dest_path"),
1552 dest_mode: normal("dest_mode"),
1553 timeout: normal("timeout"),
1554 },
1555 "BufferStreamOut(buff_id, buff_offset, length, dest_path, dest_mode, timeout)",
1556 ),
1557 (
1558 DebugFunction::RunApplication {
1559 app_path: normal("app_path"),
1560 arguments: normal("arguments"),
1561 work_directory: normal("work_directory"),
1562 timeout: normal("timeout"),
1563 },
1564 "RunApplication(app_path, arguments, work_directory, timeout)",
1565 ),
1566 (
1567 DebugFunction::RunPythonScript {
1568 script_path: normal("script_path"),
1569 arguments: normal("arguments"),
1570 work_directory: normal("work_directory"),
1571 timeout: normal("timeout"),
1572 },
1573 "RunPythonScript(script_path, arguments, work_directory, timeout)",
1574 ),
1575 (
1576 DebugFunction::FilePathExists {
1577 path: normal("path"),
1578 timeout: normal("timeout"),
1579 },
1580 "FilePathExists(path, timeout)",
1581 ),
1582 (
1583 DebugFunction::LoadDebugInfo {
1584 file: normal("file"),
1585 },
1586 "LoadDebugInfo(file)",
1587 ),
1588 ];
1589
1590 for (function, expected) in cases {
1591 let actual = function.to_string();
1592 assert_eq!(actual, expected);
1593 assert!(!actual.contains(';'));
1594 assert!(!actual.contains(", "));
1595 }
1596
1597 assert_eq!(
1598 DebugFunction::Read8 {
1599 addr: normal("0x64FF")
1600 }
1601 .to_string(),
1602 "Read8(0x64FF)"
1603 );
1604 }
1605
1606 #[test]
1607 fn format_expression_variants_recursively() {
1608 assert_eq!(
1609 normal("arbitrary text, unchanged").to_string(),
1610 "arbitrary text, unchanged"
1611 );
1612 assert_eq!(
1613 Expression::Conditional(Box::new(Conditional {
1614 condition: normal("x < y"),
1615 true_value: normal("a"),
1616 false_value: normal("b"),
1617 }))
1618 .to_string(),
1619 "(x < y) ? a : b"
1620 );
1621
1622 let nested = Expression::Conditional(Box::new(Conditional {
1623 condition: Expression::FunctionCall(Box::new(DebugFunction::Read8 {
1624 addr: normal("condition_addr"),
1625 })),
1626 true_value: Expression::FunctionCall(Box::new(DebugFunction::Read16 {
1627 addr: normal("true_addr"),
1628 })),
1629 false_value: Expression::FunctionCall(Box::new(DebugFunction::Read32 {
1630 addr: normal("false_addr"),
1631 })),
1632 }));
1633 assert_eq!(
1634 nested.to_string(),
1635 "(Read8(condition_addr)) ? Read16(true_addr) : Read32(false_addr)"
1636 );
1637 }
1638
1639 #[test]
1640 fn format_message_variadic_arguments() {
1641 let message = |args| DebugFunction::Message {
1642 msg_type: normal("1"),
1643 format: normal("\"message\""),
1644 args,
1645 };
1646
1647 assert_eq!(message(vec![]).to_string(), "Message(1, \"message\")");
1648 assert_eq!(
1649 message(vec![normal("arg1")]).to_string(),
1650 "Message(1, \"message\", arg1)"
1651 );
1652 assert_eq!(
1653 message(vec![normal("arg1"), normal("arg2"), normal("arg3")]).to_string(),
1654 "Message(1, \"message\", arg1, arg2, arg3)"
1655 );
1656 }
1657
1658 #[test]
1659 fn format_canonical_debug_names() {
1660 let cases = [
1661 ("DAP_Delay", DebugFunction::DapDelay { delay: normal("1") }),
1662 (
1663 "DAP_WriteABORT",
1664 DebugFunction::DapWriteAbort { value: normal("2") },
1665 ),
1666 (
1667 "DAP_SWJ_Pins",
1668 DebugFunction::DapSwjPins {
1669 pinout: normal("3"),
1670 pinselect: normal("4"),
1671 pinwait: normal("5"),
1672 },
1673 ),
1674 (
1675 "DAP_SWJ_Clock",
1676 DebugFunction::DapSwjClock { val: normal("6") },
1677 ),
1678 (
1679 "DAP_SWJ_Sequence",
1680 DebugFunction::DapSwjSequence {
1681 cnt: normal("7"),
1682 val: normal("8"),
1683 },
1684 ),
1685 (
1686 "DAP_JTAG_Sequence",
1687 DebugFunction::DapJtagSequence {
1688 cnt: normal("9"),
1689 tms: normal("10"),
1690 tdi: normal("11"),
1691 },
1692 ),
1693 ];
1694
1695 for (name, function) in cases {
1696 assert!(function.to_string().starts_with(name));
1697 }
1698 }
1699
1700 #[test]
1701 fn format_parsed_expressions_round_trip() {
1702 let cases = [
1703 ("Read8(0x64FF)", "Read8(0x64FF)"),
1704 ("Write32(addr, Read32(base))", "Write32(addr, Read32(base))"),
1705 (
1706 "(condition) ? Write8(addr, 1) : Read16(addr)",
1707 "(condition) ? Write8(addr, 1) : Read16(addr)",
1708 ),
1709 ("Sequence(\"ResetAndHalt\")", "Sequence(\"ResetAndHalt\")"),
1710 (
1711 "Message(1, \"value\", Read32(addr), extra)",
1712 "Message(1, \"value\", Read32(addr), extra)",
1713 ),
1714 ];
1715
1716 for (source, expected) in cases {
1717 let expression = Expression::try_from(source).unwrap();
1718 assert_eq!(expression.to_string(), expected);
1719 }
1720 }
1721}