1use crate::a2ml::{A2mlTaggedTypeSpec, A2mlTypeSpec, GenericIfData, GenericIfDataTaggedItem};
2use crate::parser::{BlockContent, ParseContext, ParserError, ParserState};
3use crate::tokenizer::{A2lToken, A2lTokenType};
4use std::collections::HashMap;
5
6#[cfg(feature = "ifdata_cleanup")]
7use crate::specification::{A2lFile, IfData};
8
9pub(crate) fn parse_ifdata(
16 parser: &mut ParserState,
17 context: &ParseContext,
18) -> Result<(Option<GenericIfData>, bool), ParserError> {
19 let mut result = None;
20 let mut valid = false;
21 if let Some(token) = parser.peek_token()
23 && token.ttype != A2lTokenType::End
24 {
25 let spec_list = std::mem::take(&mut parser.a2mlspec);
27 for a2mlspec in &spec_list {
28 if let Some(ifdata_items) = parse_ifdata_from_spec(parser, context, a2mlspec) {
29 result = Some(ifdata_items);
30 valid = true;
31 break;
32 }
33 }
34 parser.a2mlspec = spec_list;
35
36 if result.is_none() {
37 result = Some(parse_unknown_ifdata_start(parser, context)?);
40 valid = false;
41 }
42 }
43
44 Ok((result, valid))
45}
46
47fn parse_ifdata_from_spec(
51 parser: &mut ParserState,
52 context: &ParseContext,
53 spec: &A2mlTypeSpec,
54) -> Option<GenericIfData> {
55 let pos = parser.get_tokenpos();
56 if let Ok(ifdata) = parse_ifdata_item(parser, context, spec) {
57 if let Some(A2lToken {
58 ttype: A2lTokenType::End,
59 ..
60 }) = parser.peek_token()
61 {
62 Some(parse_ifdata_make_block(
63 ifdata,
64 parser.get_incfilename(context.fileid),
65 context.line,
66 ))
67 } else {
68 parser.set_tokenpos(pos);
71 None
72 }
73 } else {
74 parser.set_tokenpos(pos);
76 None
77 }
78}
79
80fn parse_ifdata_item(
83 parser: &mut ParserState,
84 context: &ParseContext,
85 spec: &A2mlTypeSpec,
86) -> Result<GenericIfData, ParserError> {
87 Ok(match spec {
88 A2mlTypeSpec::None => GenericIfData::None,
89 A2mlTypeSpec::Char => {
90 let value = parser.get_integer(context)?;
91 GenericIfData::Char(parser.get_line_offset(), value)
92 }
93 A2mlTypeSpec::Int => {
94 let value = parser.get_integer(context)?;
95 GenericIfData::Int(parser.get_line_offset(), value)
96 }
97 A2mlTypeSpec::Long => {
98 let value = parser.get_integer(context)?;
99 GenericIfData::Long(parser.get_line_offset(), value)
100 }
101 A2mlTypeSpec::Int64 => {
102 let value = parser.get_integer(context)?;
103 GenericIfData::Int64(parser.get_line_offset(), value)
104 }
105 A2mlTypeSpec::UChar => {
106 let value = parser.get_integer(context)?;
107 GenericIfData::UChar(parser.get_line_offset(), value)
108 }
109 A2mlTypeSpec::UInt => {
110 let value = parser.get_integer(context)?;
111 GenericIfData::UInt(parser.get_line_offset(), value)
112 }
113 A2mlTypeSpec::ULong => {
114 let value = parser.get_integer(context)?;
115 GenericIfData::ULong(parser.get_line_offset(), value)
116 }
117 A2mlTypeSpec::UInt64 => {
118 let value = parser.get_integer(context)?;
119 GenericIfData::UInt64(parser.get_line_offset(), value)
120 }
121 A2mlTypeSpec::Float => {
122 let value = parser.get_float(context)?;
123 GenericIfData::Float(parser.get_line_offset(), value)
124 }
125 A2mlTypeSpec::Double => {
126 let value = parser.get_double(context)?;
127 GenericIfData::Double(parser.get_line_offset(), value)
128 }
129 A2mlTypeSpec::Array(arraytype, dim) => {
130 if **arraytype == A2mlTypeSpec::Char {
131 let value = parser.get_string_maxlen(context, *dim)?;
132 GenericIfData::String(parser.get_line_offset(), value)
133 } else {
134 let mut arrayitems = Vec::new();
135 for _ in 0..*dim {
136 arrayitems.push(parse_ifdata_item(parser, context, arraytype)?);
137 }
138 GenericIfData::Array(arrayitems)
139 }
140 }
141 A2mlTypeSpec::Enum(enumspec) => {
142 let enumitem = parser.get_identifier(context)?;
143 let pos = parser.get_line_offset();
144 if enumspec.get(&enumitem).is_some() {
145 GenericIfData::EnumItem(pos, enumitem)
146 } else {
147 return Err(ParserError::invalid_enum_value(parser, context, &enumitem));
148 }
149 }
150 A2mlTypeSpec::Struct(structspec) => {
151 let mut structitems = Vec::with_capacity(structspec.len());
152 for itemspec in structspec {
153 structitems.push(parse_ifdata_item(parser, context, itemspec)?);
154 }
155 GenericIfData::Struct(parser.get_incfilename(context.fileid), 0, structitems)
156 }
157 A2mlTypeSpec::Sequence(seqspec) => {
158 let mut seqitems = Vec::new();
159 let mut checkpoint = parser.get_tokenpos();
160 while let Ok(item) = parse_ifdata_item(parser, context, seqspec) {
161 seqitems.push(item);
162 checkpoint = parser.get_tokenpos();
163 }
164 parser.set_tokenpos(checkpoint);
165 GenericIfData::Sequence(seqitems)
166 }
167 A2mlTypeSpec::TaggedStruct(tsspec) => {
168 GenericIfData::TaggedStruct(parse_ifdata_taggedstruct(parser, context, tsspec)?)
169 }
170 A2mlTypeSpec::TaggedUnion(tuspec) => {
171 let mut result = HashMap::new();
172 if let Some(taggeditem) = parse_ifdata_taggeditem(parser, context, tuspec)? {
173 result.insert(taggeditem.tag.clone(), vec![taggeditem]);
174 }
175 GenericIfData::TaggedUnion(result)
176 }
177 })
178}
179
180fn parse_ifdata_taggedstruct(
183 parser: &mut ParserState,
184 context: &ParseContext,
185 tsspec: &HashMap<String, A2mlTaggedTypeSpec>,
186) -> Result<HashMap<String, Vec<GenericIfDataTaggedItem>>, ParserError> {
187 let mut result = HashMap::<String, Vec<GenericIfDataTaggedItem>>::new();
188 while let Some(taggeditem) = parse_ifdata_taggeditem(parser, context, tsspec)? {
189 if let Some(itemvec) = result.get_mut(&taggeditem.tag) {
190 itemvec.push(taggeditem);
191 } else {
192 result.insert(taggeditem.tag.clone(), vec![taggeditem]);
193 }
194 }
195
196 Ok(result)
197}
198
199fn parse_ifdata_taggeditem(
207 parser: &mut ParserState,
208 context: &ParseContext,
209 spec: &HashMap<String, A2mlTaggedTypeSpec>,
210) -> Result<Option<GenericIfDataTaggedItem>, ParserError> {
211 let checkpoint = parser.get_tokenpos();
212
213 while let Some(A2lToken {
215 ttype: A2lTokenType::Comment,
216 ..
217 }) = parser.peek_token()
218 {
219 parser.get_token(context)?;
221 }
222
223 if let Ok(BlockContent::Block(token, is_block, start_offset)) =
225 parser.get_next_tag_or_comment(context)
226 {
227 let tag = parser.get_token_text(token);
228
229 if let Some(taggedspec) = spec.get(tag) {
231 if taggedspec.is_block != is_block {
232 parser.set_tokenpos(checkpoint);
233 return Ok(None);
234 }
235 let uid = parser.get_next_id();
236
237 let newcontext = ParseContext::from_token(tag, token);
239 let data = parse_ifdata_item(parser, &newcontext, &taggedspec.item)?;
240 let parsed_item = parse_ifdata_make_block(
241 data,
242 parser.get_incfilename(newcontext.fileid),
243 newcontext.line,
244 );
245
246 let end_offset = if is_block {
248 parser.expect_token(&newcontext, A2lTokenType::End)?;
249 let end_offset = parser.get_line_offset();
250 let endident = parser.expect_token(&newcontext, A2lTokenType::Identifier)?;
251 let endtag = parser.get_token_text(endident);
252 if endtag != tag {
253 return Err(ParserError::IncorrectEndTag {
254 filename: parser.filenames[context.fileid].to_string(),
255 error_line: parser.last_token_position,
256 tag: endtag.to_owned(),
257 block: newcontext.element.clone(),
258 block_line: newcontext.line,
259 });
260 }
261 end_offset
262 } else {
263 0
265 };
266
267 Ok(Some(GenericIfDataTaggedItem {
268 incfile: parser.get_incfilename(newcontext.fileid),
269 line: newcontext.line,
270 uid,
271 start_offset,
272 end_offset,
273 tag: tag.to_string(),
274 data: parsed_item,
275 is_block,
276 }))
277 } else {
278 parser.set_tokenpos(checkpoint);
279 Ok(None)
280 }
281 } else {
282 parser.set_tokenpos(checkpoint);
283 Ok(None)
284 }
285}
286
287fn parse_ifdata_make_block(
290 data: GenericIfData,
291 incfile: Option<String>,
292 line: u32,
293) -> GenericIfData {
294 match data {
295 GenericIfData::Struct(_, _, structitems) => GenericIfData::Block {
296 incfile,
297 line,
298 items: structitems,
299 },
300 _ => GenericIfData::Block {
301 incfile,
302 line,
303 items: vec![data],
304 },
305 }
306}
307
308pub(crate) fn parse_unknown_ifdata_start(
309 parser: &mut ParserState,
310 context: &ParseContext,
311) -> Result<GenericIfData, ParserError> {
312 let token_peek = parser.peek_token();
313 if let Some(A2lToken {
315 ttype: A2lTokenType::Identifier,
316 ..
317 }) = token_peek
318 {
319 let token = parser.get_token(context)?;
321 let start_offset = parser.get_line_offset();
322 let tag = parser.get_token_text(token);
323 let uid = parser.get_next_id();
324 let newcontext = ParseContext::from_token(tag, token);
325 let result = parse_unknown_ifdata(parser, &newcontext, true)?;
326 parser.undo_get_token();
328 let end_offset = parser.get_line_offset();
329 let _ = parser.get_token(context);
330 let taggeditem = GenericIfDataTaggedItem {
331 incfile: parser.get_incfilename(newcontext.fileid),
332 line: newcontext.line,
333 uid,
334 start_offset,
335 end_offset,
336 tag: tag.to_string(),
337 data: result,
338 is_block: false,
339 };
340 let mut tuitem = HashMap::new();
341 tuitem.insert(tag.to_string(), vec![taggeditem]);
342
343 let items: Vec<GenericIfData> = vec![GenericIfData::TaggedUnion(tuitem)];
344
345 Ok(GenericIfData::Block {
346 incfile: parser.get_incfilename(context.fileid),
347 line: start_offset,
348 items,
349 })
350 } else {
351 parse_unknown_ifdata(parser, context, true)
353 }
354}
355
356pub(crate) fn parse_unknown_ifdata(
363 parser: &mut ParserState,
364 context: &ParseContext,
365 is_block: bool,
366) -> Result<GenericIfData, ParserError> {
367 let mut items: Vec<GenericIfData> = Vec::new();
368
369 loop {
370 let token_peek = parser.peek_token();
371 if token_peek.is_none() {
372 return Err(ParserError::unexpected_eof(parser, context));
373 }
374 let token = token_peek.unwrap();
375
376 match token.ttype {
377 A2lTokenType::Identifier => {
378 let value = parser.get_identifier(context)?;
380 items.push(GenericIfData::EnumItem(parser.get_line_offset(), value));
381 }
382 A2lTokenType::String => {
383 let value = parser.get_string(context)?;
384 items.push(GenericIfData::String(parser.get_line_offset(), value));
385 }
386 A2lTokenType::Number => {
387 if let Ok(num) = parser.get_integer::<i32>(context) {
388 let line_offset = parser.get_line_offset();
389 items.push(GenericIfData::Long(line_offset, num));
390 } else {
391 parser.undo_get_token();
393 let floatnum = parser.get_float(context)?; let line_offset = parser.get_line_offset();
395 items.push(GenericIfData::Float(line_offset, floatnum));
396 }
397 }
398 A2lTokenType::Begin => {
399 if is_block {
402 items.push(parse_unknown_taggedstruct(parser, context)?);
403 } else {
404 break;
405 }
406 }
407 A2lTokenType::End => {
408 break;
410 }
411 A2lTokenType::Include => { }
412 A2lTokenType::Comment => {
413 parser.get_token(context)?;
415 }
416 }
417 }
418
419 Ok(GenericIfData::Struct(
420 parser.get_incfilename(context.fileid),
421 0,
422 items,
423 ))
424}
425
426fn parse_unknown_taggedstruct(
430 parser: &mut ParserState,
431 context: &ParseContext,
432) -> Result<GenericIfData, ParserError> {
433 let mut tsitems: HashMap<String, Vec<GenericIfDataTaggedItem>> = HashMap::new();
434
435 while let Some(A2lToken {
436 ttype: A2lTokenType::Comment,
437 ..
438 }) = parser.peek_token()
439 {
440 parser.get_token(context)?;
442 }
443
444 loop {
445 let (token, is_block, start_offset) = match parser.get_next_tag_or_comment(context) {
446 Ok(BlockContent::Block(token, is_block, start_offset)) => {
447 (token, is_block, start_offset)
448 }
449 Ok(BlockContent::Comment(..)) => continue,
450 Ok(BlockContent::None) | Err(_) => break,
451 };
452
453 let uid = parser.get_next_id();
454 let tag = parser.get_token_text(token);
455 let newcontext = ParseContext::from_token(tag, token);
456 let result = parse_unknown_ifdata(parser, &newcontext, is_block)?;
457
458 let end_offset = if is_block {
459 parser.expect_token(&newcontext, A2lTokenType::End)?;
460 let end_offset = parser.get_line_offset();
461 let endident = parser.expect_token(&newcontext, A2lTokenType::Identifier)?;
462 let endtag = parser.get_token_text(endident);
463 if endtag != tag {
464 return Err(ParserError::IncorrectEndTag {
465 filename: parser.filenames[newcontext.fileid].to_string(),
466 error_line: parser.last_token_position,
467 tag: endtag.to_owned(),
468 block: newcontext.element.clone(),
469 block_line: newcontext.line,
470 });
471 }
472 end_offset
473 } else {
474 0
476 };
477
478 let taggeditem = GenericIfDataTaggedItem {
479 incfile: parser.get_incfilename(newcontext.fileid),
480 line: newcontext.line,
481 uid,
482 start_offset,
483 end_offset,
484 tag: tag.to_string(),
485 data: result,
486 is_block,
487 };
488
489 if !tsitems.contains_key(tag) {
490 tsitems.insert(tag.to_string(), vec![]);
491 }
492 tsitems.get_mut(tag).unwrap().push(taggeditem);
493 }
494
495 if let Some(A2lToken {
497 ttype: A2lTokenType::Begin,
498 ..
499 }) = parser.peek_token()
500 {
501 return Err(ParserError::InvalidBegin {
502 filename: parser.filenames[context.fileid].to_string(),
503 error_line: parser.last_token_position,
504 block: context.element.clone(),
505 });
506 }
507
508 Ok(GenericIfData::TaggedStruct(tsitems))
509}
510
511#[cfg(feature = "ifdata_cleanup")]
512pub(crate) fn remove_unknown_ifdata(a2l_file: &mut A2lFile) {
513 for module in &mut a2l_file.project.module {
514 remove_unknown_ifdata_from_list(&mut module.if_data);
515
516 if let Some(mod_par) = &mut module.mod_par {
517 for memory_layout in &mut mod_par.memory_layout {
518 remove_unknown_ifdata_from_list(&mut memory_layout.if_data);
519 }
520
521 for memory_segment in &mut mod_par.memory_segment {
522 remove_unknown_ifdata_from_list(&mut memory_segment.if_data);
523 }
524 }
525
526 for axis_pts in &mut module.axis_pts {
527 remove_unknown_ifdata_from_list(&mut axis_pts.if_data);
528 }
529
530 for blob in &mut module.blob {
531 remove_unknown_ifdata_from_list(&mut blob.if_data);
532 }
533
534 for characteristic in &mut module.characteristic {
535 remove_unknown_ifdata_from_list(&mut characteristic.if_data);
536 }
537
538 for frame in &mut module.frame {
539 remove_unknown_ifdata_from_list(&mut frame.if_data);
540 }
541
542 for function in &mut module.function {
543 remove_unknown_ifdata_from_list(&mut function.if_data);
544 }
545
546 for group in &mut module.group {
547 remove_unknown_ifdata_from_list(&mut group.if_data);
548 }
549
550 for instance in &mut module.instance {
551 remove_unknown_ifdata_from_list(&mut instance.if_data);
552 }
553
554 for measurement in &mut module.measurement {
555 remove_unknown_ifdata_from_list(&mut measurement.if_data);
556 }
557 }
558}
559
560#[cfg(feature = "ifdata_cleanup")]
561fn remove_unknown_ifdata_from_list(ifdata_list: &mut Vec<IfData>) {
562 let mut new_ifdata_list = Vec::new();
563 std::mem::swap(ifdata_list, &mut new_ifdata_list);
564 for if_data in new_ifdata_list {
565 if if_data.ifdata_valid {
566 ifdata_list.push(if_data);
567 }
568 }
569}
570
571#[cfg(test)]
572mod ifdata_test {
573 use super::*;
574 use crate::{self as a2lfile, Filename, IfData};
575
576 crate::a2ml_specification! {
577 <A2mlTest>
578
579 block "IF_DATA" taggedunion if_data {
580 "CHAR" char a;
581 "INT" int b;
582 "LONG" long c;
583 "INT64" int64 d;
584 "UCHAR" uchar e;
585 "UINT" uint64 f;
586 "ULONG" ulong g;
587 "UINT64" uint64 h;
588 "DOUBLE" double i;
589 "FLOAT" float j;
590 "STRUCT" struct structname {
591 char[256];
592 int;
593 };
594 block "BLOCK" taggedstruct tagged_struct {
595 "TAG1" int intval;
596 };
597 "ENUM" enum EnumTest {
598 "ENUMVAL1" = 1,
599 "ENUMVAL2"
600 } named_enum;
601 "ARRAY" uint arr[3];
602 block "SEQUENCE" (char[256] name)*;
603 "NONE";
604 };
605 }
606
607 #[test]
608 fn parse_ifdata() {
609 let result = parse_helper(r##"CHAR 5 /end IFDATA"##);
610 assert!(result.is_ok());
611 let decoded_ifdata = check_and_decode(result);
612 assert!(matches!(decoded_ifdata.char, Some(Char { a: 5, .. })));
613
614 let result = parse_helper(r##"CHAR xyz /end IFDATA"##);
615 assert!(result.is_ok());
616 let (data, valid) = result.unwrap();
617 assert!(data.is_some());
618 assert!(!valid);
619
620 let result = parse_helper(r##"INT 5 /end IFDATA"##);
621 assert!(result.is_ok());
622 let decoded_ifdata = check_and_decode(result);
623 assert!(matches!(decoded_ifdata.int, Some(Int { b: 5, .. })));
624
625 let result = parse_helper(r##"INT xyz /end IFDATA"##);
626 assert!(result.is_ok());
627 let (data, valid) = result.unwrap();
628 assert!(data.is_some());
629 assert!(!valid);
630
631 let result = parse_helper(r##"LONG 5 /end IFDATA"##);
632 assert!(result.is_ok());
633 let decoded_ifdata = check_and_decode(result);
634 assert!(matches!(decoded_ifdata.long, Some(Long { c: 5, .. })));
635
636 let result = parse_helper(r##"LONG xyz /end IFDATA"##);
637 assert!(result.is_ok());
638 let (data, valid) = result.unwrap();
639 assert!(data.is_some());
640 assert!(!valid);
641
642 let result = parse_helper(r##"INT64 5 /end IFDATA"##);
643 assert!(result.is_ok());
644 let decoded_ifdata = check_and_decode(result);
645 assert!(matches!(decoded_ifdata.int64, Some(Int64 { d: 5, .. })));
646
647 let result = parse_helper(r##"INT64 xyz /end IFDATA"##);
648 assert!(result.is_ok());
649 let (data, valid) = result.unwrap();
650 assert!(data.is_some());
651 assert!(!valid);
652
653 let result = parse_helper(r##"UCHAR 5 /end IFDATA"##);
654 assert!(result.is_ok());
655 let decoded_ifdata = check_and_decode(result);
656 assert!(matches!(decoded_ifdata.uchar, Some(Uchar { e: 5, .. })));
657
658 let result = parse_helper(r##"UCHAR xyz /end IFDATA"##);
659 assert!(result.is_ok());
660 let (data, valid) = result.unwrap();
661 assert!(data.is_some());
662 assert!(!valid);
663
664 let result = parse_helper(r##"UINT 5 /end IFDATA"##);
665 assert!(result.is_ok());
666 let decoded_ifdata = check_and_decode(result);
667 assert!(matches!(decoded_ifdata.uint, Some(Uint { f: 5, .. })));
668
669 let result = parse_helper(r##"UINT xyz /end IFDATA"##);
670 assert!(result.is_ok());
671 let (data, valid) = result.unwrap();
672 assert!(data.is_some());
673 assert!(!valid);
674
675 let result = parse_helper(r##"ULONG 5 /end IFDATA"##);
676 assert!(result.is_ok());
677 let decoded_ifdata = check_and_decode(result);
678 assert!(matches!(decoded_ifdata.ulong, Some(Ulong { g: 5, .. })));
679
680 let result = parse_helper(r##"ULONG xyz /end IFDATA"##);
681 assert!(result.is_ok());
682 let (data, valid) = result.unwrap();
683 assert!(data.is_some());
684 assert!(!valid);
685
686 let result = parse_helper(r##"UINT64 5 /end IFDATA"##);
687 assert!(result.is_ok());
688 let decoded_ifdata = check_and_decode(result);
689 assert!(matches!(decoded_ifdata.uint64, Some(Uint64 { h: 5, .. })));
690
691 let result = parse_helper(r##"UINT64 xyz /end IFDATA"##);
692 assert!(result.is_ok());
693 let (data, valid) = result.unwrap();
694 assert!(data.is_some());
695 assert!(!valid);
696
697 let result = parse_helper(r##"DOUBLE 5.5 /end IFDATA"##);
698 assert!(result.is_ok());
699 let decoded_ifdata = check_and_decode(result);
700 assert!(matches!(decoded_ifdata.double, Some(Double { .. })));
701
702 let result = parse_helper(r##"DOUBLE xyz /end IFDATA"##);
703 assert!(result.is_ok());
704 let (data, valid) = result.unwrap();
705 assert!(data.is_some());
706 assert!(!valid);
707
708 let result = parse_helper(r##"FLOAT 5.5 /end IFDATA"##);
709 assert!(result.is_ok());
710 let decoded_ifdata = check_and_decode(result);
711 assert!(matches!(decoded_ifdata.float, Some(Float { .. })));
712
713 let result = parse_helper(r##"FLOAT xyz /end IFDATA"##);
714 assert!(result.is_ok());
715 let (data, valid) = result.unwrap();
716 assert!(data.is_some());
717 assert!(!valid);
718
719 let result = parse_helper(r##"STRUCT "text value" 3 /end IFDATA"##);
720 assert!(result.is_ok());
721 let decoded_ifdata = check_and_decode(result);
722 let var_struct = decoded_ifdata.var_struct.unwrap();
723 assert_eq!(var_struct.item, "text value");
724 assert_eq!(var_struct.item_2, 3);
725
726 let result = parse_helper(r##"STRUCT 5.5 xyz /end IFDATA"##);
727 assert!(result.is_ok());
728 let (data, valid) = result.unwrap();
729 assert!(data.is_some());
730 assert!(!valid);
731
732 let result = parse_helper(r##"/begin BLOCK TAG1 3 /end BLOCK /end IFDATA"##);
733 assert!(result.is_ok());
734 let decoded_ifdata = check_and_decode(result);
735 assert_eq!(decoded_ifdata.block.unwrap().tag1.unwrap().intval, 3);
736
737 let result = parse_helper(r##"ENUM ENUMVAL2 /end IFDATA"##);
738 assert!(result.is_ok());
739 let decoded_ifdata = check_and_decode(result);
740 assert_eq!(
741 decoded_ifdata.var_enum.unwrap().named_enum,
742 EnumTest::Enumval2
743 );
744
745 let result = parse_helper(r##"ENUM NOTVALID /end IFDATA"##);
746 assert!(result.is_ok());
747 let (data, valid) = result.unwrap();
748 assert!(data.is_some());
749 assert!(!valid);
750
751 let result = parse_helper(r##"ARRAY 7 8 9 /end IFDATA"##);
752 assert!(result.is_ok());
753 let decoded_ifdata = check_and_decode(result);
754 assert_eq!(decoded_ifdata.array.unwrap().arr, [7, 8, 9]);
755
756 let result = parse_helper(r##"ARRAY 7 8 "bad" /end IFDATA"##);
757 assert!(result.is_ok());
758 let (data, valid) = result.unwrap();
759 assert!(data.is_some());
760 assert!(!valid);
761
762 let result =
763 parse_helper(r##"/begin SEQUENCE "name 1" "name 2" /end SEQUENCE /end IFDATA"##);
764 assert!(result.is_ok());
765 let decoded_ifdata = check_and_decode(result);
766 assert_eq!(decoded_ifdata.sequence.unwrap().item, ["name 1", "name 2"]);
767
768 let result = parse_helper(r##"NONE /end IFDATA"##);
769 assert!(result.is_ok());
770 let decoded_ifdata = check_and_decode(result);
771 assert!(decoded_ifdata.none.is_some());
772 }
773
774 fn parse_helper(ifdata: &str) -> Result<(Option<GenericIfData>, bool), ParserError> {
775 let token_result =
776 a2lfile::tokenizer::tokenize(&Filename::from("test"), 0, ifdata).unwrap();
777 let mut log_msgs = Vec::new();
778 let ifdatas = [ifdata.to_string()];
779 let filenames = [Filename::from("test")];
780 let mut parser = ParserState::new_internal(
781 &token_result.tokens,
782 &ifdatas,
783 &filenames,
784 &mut log_msgs,
785 false,
786 );
787 parser.a2mlspec.push(
788 a2lfile::a2ml::parse_a2ml(&Filename::from("test"), A2MLTEST_TEXT)
789 .unwrap()
790 .0,
791 );
792 super::parse_ifdata(
793 &mut parser,
794 &a2lfile::ParseContext {
795 fileid: 0,
796 line: 0,
797 element: "IFDATA".to_string(),
798 },
799 )
800 }
801
802 fn check_and_decode(result: Result<(Option<GenericIfData>, bool), ParserError>) -> A2mlTest {
803 let (data, valid) = result.unwrap();
804 assert!(data.is_some());
805 assert!(valid);
806 let mut if_data = IfData::new();
807 if_data.ifdata_items = data;
808 if_data.ifdata_valid = valid;
809 A2mlTest::load_from_ifdata(&if_data).unwrap()
810 }
811
812 #[test]
813 fn test_parse_unknown() {
814 let result = parse_helper(
816 r##"abc def ghi /begin AAA 12 23 34 45 "foo" "bar" /end AAA /end IFDATA"##,
817 );
818 assert!(result.is_ok());
819 let (gen_ifdata, valid) = result.unwrap();
820 assert!(!valid);
821 let gen_ifdata = gen_ifdata.unwrap();
822 let (_, _, items) = gen_ifdata.get_block_items().unwrap();
823
824 assert_eq!(items.len(), 1);
826 let taggedunion = &items[0];
827 let opt_tag_content = taggedunion
828 .get_single_optitem("abc", |data, _, _, _| Ok(data.clone()))
829 .unwrap()
830 .unwrap();
831 let (_, _, items) = opt_tag_content.get_struct_items().unwrap();
833 assert_eq!(items.len(), 3);
834
835 let result =
837 parse_helper(r##"abc def ghi /begin AAA 12 23 34 45 "foo" "bar" /end IFDATA"##);
838 assert!(result.is_err());
839 }
840
841 #[test]
842 fn test_parse_unknown_with_comments() {
843 let result = parse_helper(
847 r##"abc /* block comment */ /begin AAA 12 /end AAA /end IFDATA"##,
848 );
849 assert!(result.is_ok());
850 let (gen_ifdata, valid) = result.unwrap();
851 assert!(!valid);
852 assert!(gen_ifdata.is_some());
853
854 let result = parse_helper(
856 r##"abc // line comment
857 /begin AAA 12 /end AAA /end IFDATA"##,
858 );
859 assert!(result.is_ok());
860 let (gen_ifdata, valid) = result.unwrap();
861 assert!(!valid);
862 assert!(gen_ifdata.is_some());
863
864 let result = parse_helper(
866 r##"abc /* first comment */ /* second comment */ /begin AAA 12 /end AAA /end IFDATA"##,
867 );
868 assert!(result.is_ok());
869 let (gen_ifdata, valid) = result.unwrap();
870 assert!(!valid);
871 assert!(gen_ifdata.is_some());
872 }
873}