1use num_traits::{AsPrimitive, Num};
2use std::fmt::Display;
3use thiserror::Error;
4
5use crate::a2ml::A2mlTypeSpec;
6use crate::tokenizer::{A2lToken, A2lTokenType, TokenResult};
7use crate::{A2lError, Filename, ParseableA2lObject};
8
9const MAX_IDENT: usize = 1024;
10
11struct TokenIter<'a> {
12 tokens: &'a [A2lToken],
13 pos: usize,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
17pub enum A2lVersion {
18 V1_5_0,
19 V1_5_1,
20 V1_6_0,
21 V1_6_1,
22 V1_7_0,
23 V1_7_1,
24}
25
26pub struct ParserState<'a> {
27 token_cursor: TokenIter<'a>,
28 filedata: &'a [String],
29 pub(crate) filenames: &'a [Filename],
30 pub(crate) last_token_position: u32,
31 sequential_id: u32,
32 pub(crate) log_msgs: &'a mut Vec<A2lError>,
33 strict: bool,
34 file_ver: A2lVersion,
35 pub(crate) a2mlspec: Vec<A2mlTypeSpec>,
36}
37
38#[derive(Debug, Clone)]
40pub struct ParseContext {
41 pub element: String,
42 pub fileid: usize,
43 pub line: u32,
44}
45#[derive(Debug, Error)]
46#[non_exhaustive]
47pub enum ParserError {
48 #[error(
49 "{filename}:{error_line}: expected token of type {expected_ttype:?}, got {actual_ttype:?} (\"{actual_text}\") inside block {element} starting on line {block_line}"
50 )]
51 UnexpectedTokenType {
52 filename: String,
53 error_line: u32,
54 block_line: u32,
55 element: String,
56 actual_ttype: A2lTokenType,
57 actual_text: String,
58 expected_ttype: A2lTokenType,
59 },
60
61 #[error("{filename}:{error_line}: string \"{numstr}\" could not be interpreted as a number")]
62 MalformedNumber {
63 filename: String,
64 error_line: u32,
65 numstr: String,
66 },
67
68 #[error(
69 "{filename}:{error_line}: expected an enum value, but \"{enumtxt}\" is not part of the enum (located inside block {block} starting on line {block_line})"
70 )]
71 InvalidEnumValue {
72 filename: String,
73 error_line: u32,
74 enumtxt: String,
75 block: String,
76 block_line: u32,
77 },
78
79 #[error(
80 "{filename}:{error_line}: optional element {tag} occurs too often within block {block} starting on line {block_line}"
81 )]
82 InvalidMultiplicityTooMany {
83 filename: String,
84 error_line: u32,
85 tag: String,
86 block: String,
87 block_line: u32,
88 },
89
90 #[error(
91 "{filename}:{error_line}: element {tag} is missing in block {block} starting on line {block_line}"
92 )]
93 InvalidMultiplicityNotPresent {
94 filename: String,
95 error_line: u32,
96 tag: String,
97 block: String,
98 block_line: u32,
99 },
100
101 #[error(
102 "{filename}:{error_line}: element {tag} in block {block} starting on line {block_line} must be enclosed in /begin and /end"
103 )]
104 IncorrectBlockError {
105 filename: String,
106 error_line: u32,
107 tag: String,
108 block: String,
109 block_line: u32,
110 },
111
112 #[error(
113 "{filename}:{error_line}: element {tag} in block {block} starting on line {block_line} may not be enclosed in /begin and /end"
114 )]
115 IncorrectKeywordError {
116 filename: String,
117 error_line: u32,
118 tag: String,
119 block: String,
120 block_line: u32,
121 },
122
123 #[error(
124 "{filename}:{error_line}: Wrong end tag {tag} found at the end of block {block} starting on line {block_line}"
125 )]
126 IncorrectEndTag {
127 filename: String,
128 error_line: u32,
129 tag: String,
130 block: String,
131 block_line: u32,
132 },
133
134 #[error(
135 "{filename}:{error_line}: Unknown sub-block {tag} found inside block {block} starting on line {block_line}"
136 )]
137 UnknownSubBlock {
138 filename: String,
139 error_line: u32,
140 tag: String,
141 block: String,
142 block_line: u32,
143 },
144
145 #[error(
146 "{filename}:{error_line}: encountered end of file while not done parsing block {block} starting on line {block_line}"
147 )]
148 UnexpectedEOF {
149 filename: String,
150 error_line: u32,
151 block: String,
152 block_line: u32,
153 },
154
155 #[error(
156 "{filename}:{error_line}: String \"{text}\" in block {block} is {length} bytes long, but the maximum allowed length is {max_length}"
157 )]
158 StringTooLong {
159 filename: String,
160 error_line: u32,
161 block: String,
162 text: String,
163 length: usize,
164 max_length: usize,
165 },
166
167 #[error(
168 "{filename}:{error_line}: Sub-block \"{tag}\" in block {block} is deprecated after version {limit_ver:.2}, but the file declares version {file_ver:.2}"
169 )]
170 BlockRefDeprecated {
171 filename: String,
172 error_line: u32,
173 block: String,
174 tag: String,
175 limit_ver: A2lVersion,
176 file_ver: A2lVersion,
177 },
178
179 #[error(
180 "{filename}:{error_line}: Sub-block \"{tag}\" in block {block} is available from version {limit_ver:.2}, but the file declares version {file_ver:.2}"
181 )]
182 BlockRefTooNew {
183 filename: String,
184 error_line: u32,
185 block: String,
186 tag: String,
187 limit_ver: A2lVersion,
188 file_ver: A2lVersion,
189 },
190
191 #[error(
192 "{filename}:{error_line}: Enum item \"{tag}\" in block {block} is deprecated after version {limit_ver:.2}, but the file declares version {file_ver:.2}"
193 )]
194 EnumRefDeprecated {
195 filename: String,
196 error_line: u32,
197 block: String,
198 tag: String,
199 limit_ver: A2lVersion,
200 file_ver: A2lVersion,
201 },
202
203 #[error(
204 "{filename}:{error_line}: Enum item \"{tag}\" in block {block} is available from version {limit_ver:.2}, but the file declares version {file_ver:.2}"
205 )]
206 EnumRefTooNew {
207 filename: String,
208 error_line: u32,
209 block: String,
210 tag: String,
211 limit_ver: A2lVersion,
212 file_ver: A2lVersion,
213 },
214
215 #[error("{filename}:{error_line}: /begin in block {block} is not followed by a valid tag")]
216 InvalidBegin {
217 filename: String,
218 error_line: u32,
219 block: String,
220 },
221
222 #[error(
223 "{filename}:{error_line}: The string '{ident}' in block {block} is not a valid identifier"
224 )]
225 InvalidIdentifier {
226 filename: String,
227 error_line: u32,
228 ident: String,
229 block: String,
230 },
231
232 #[error("{filename}:{error_line}: A2ML parser reports {errmsg}")]
233 A2mlError {
234 filename: String,
235 error_line: u32,
236 errmsg: String,
237 },
238
239 #[error(
241 "{filename}:{error_line}: unexpected additional data \"{text}...\" after parsed a2l file content"
242 )]
243 AdditionalTokensError {
244 filename: String,
245 error_line: u32,
246 text: String,
247 },
248
249 #[error("File is not recognized as an a2l file. Mandatory version information is missing.")]
251 MissingVersionInfo,
252
253 #[error("File has invalid version {major} {minor}")]
255 InvalidVersion { major: u16, minor: u16 },
256}
257
258pub(crate) enum BlockContent<'a> {
259 Block(&'a A2lToken, bool, u32),
260 Comment(&'a A2lToken, u32),
261 None,
262}
263
264impl<'a> TokenIter<'a> {
266 fn next(&mut self) -> Option<&'a A2lToken> {
267 if self.pos < self.tokens.len() {
268 let item = &self.tokens[self.pos];
269 self.pos += 1;
270 Some(item)
271 } else {
272 None
273 }
274 }
275
276 fn peek(&mut self) -> Option<&'a A2lToken> {
277 if self.pos < self.tokens.len() {
278 let item = &self.tokens[self.pos];
279 Some(item)
280 } else {
281 None
282 }
283 }
284
285 fn back(&mut self) {
286 self.pos -= 1;
287 }
288}
289
290impl<'a> ParserState<'a> {
291 pub(crate) fn new<'b>(
292 tokenresult: &'b TokenResult,
293 log_msgs: &'b mut Vec<A2lError>,
294 strict: bool,
295 ) -> ParserState<'b> {
296 Self::new_internal(
297 &tokenresult.tokens,
298 &tokenresult.filedata,
299 &tokenresult.filenames,
300 log_msgs,
301 strict,
302 )
303 }
304
305 pub(crate) fn new_internal<'b>(
306 tokens: &'b [A2lToken],
307 filedata: &'b [String],
308 filenames: &'b [Filename],
309 log_msgs: &'b mut Vec<A2lError>,
310 strict: bool,
311 ) -> ParserState<'b> {
312 ParserState {
313 token_cursor: TokenIter { tokens, pos: 0 },
314 filedata,
315 filenames,
316 last_token_position: 0,
317 sequential_id: 0,
318 log_msgs,
319 strict,
320 file_ver: A2lVersion::V1_7_1,
321 a2mlspec: Vec::new(),
322 }
323 }
324
325 pub(crate) fn parse_file(&mut self) -> Result<crate::A2lFile, ParserError> {
326 let firstline = self.token_cursor.tokens.first().map_or(1, |tok| tok.line);
327 let context = ParseContext {
329 element: "A2L_FILE".to_string(),
330 fileid: 0,
331 line: firstline,
332 };
333
334 self.file_ver = self.parse_version(&context)?;
337
338 let a2l_file = crate::A2lFile::parse(self, &context, 0)?;
340
341 if let Some(token) = self.peek_token() {
343 self.error_or_log(ParserError::AdditionalTokensError {
344 filename: self.filenames[token.fileid].to_string(),
345 error_line: self.last_token_position,
346 text: self.get_token_text(token).to_owned(),
347 })?;
348 }
349
350 Ok(a2l_file)
351 }
352
353 fn parse_version(&mut self, context: &ParseContext) -> Result<crate::A2lVersion, ParserError> {
354 if let Some(token) = self.peek_token() {
355 let ident = self.get_identifier(context);
356 let ver_context = ParseContext::from_token("", token);
357 if let Ok("ASAP2_VERSION") = ident.as_deref() {
358 let version_result = crate::Asap2Version::parse(self, &ver_context, 0)
359 .map_err(|_| ParserError::MissingVersionInfo)
360 .and_then(|ver| A2lVersion::new(ver.version_no, ver.upgrade_no));
361
362 self.set_tokenpos(0);
364
365 return match version_result {
366 Ok(ver) => Ok(ver),
367 Err(err) => {
368 self.error_or_log(err)?;
370 Ok(A2lVersion::V1_7_1)
371 }
372 };
373 }
374 }
375 self.set_tokenpos(0);
377 self.error_or_log(ParserError::MissingVersionInfo)?;
378 Ok(A2lVersion::V1_5_1)
379 }
380 pub(crate) fn get_token(
383 &mut self,
384 context: &ParseContext,
385 ) -> Result<&'a A2lToken, ParserError> {
386 if let Some(token) = self.token_cursor.next() {
387 self.last_token_position = token.line;
388 Ok(token)
389 } else {
390 Err(ParserError::unexpected_eof(self, context))
391 }
392 }
393
394 pub(crate) fn undo_get_token(&mut self) {
395 self.token_cursor.back();
396 }
397
398 pub(crate) fn peek_token(&mut self) -> Option<&'a A2lToken> {
399 self.token_cursor.peek()
400 }
401
402 pub(crate) fn log_warning(&mut self, parse_error: ParserError) {
403 self.log_msgs.push(A2lError::ParserError {
404 parser_error: parse_error,
405 });
406 }
407
408 pub(crate) fn error_or_log(&mut self, err: ParserError) -> Result<(), ParserError> {
409 if self.strict {
410 Err(err)
411 } else {
412 self.log_warning(err);
413 Ok(())
414 }
415 }
416
417 pub(crate) fn get_tokenpos(&self) -> usize {
418 self.token_cursor.pos
419 }
420
421 pub(crate) fn set_tokenpos(&mut self, newpos: usize) {
422 self.token_cursor.pos = newpos;
423 }
424
425 pub(crate) fn get_token_text(&self, token: &'a A2lToken) -> &'a str {
426 let data = &self.filedata[token.fileid];
427 &data[token.startpos..token.endpos]
428 }
429
430 pub(crate) fn get_line_offset(&self) -> u32 {
436 if self.token_cursor.pos > 1 && self.token_cursor.pos < self.token_cursor.tokens.len() {
437 let prev_line = self.token_cursor.tokens[self.token_cursor.pos - 2].line;
438 let prev_fileid = self.token_cursor.tokens[self.token_cursor.pos - 2].fileid;
439 let cur_line = self.token_cursor.tokens[self.token_cursor.pos - 1].line;
440 let cur_fileid = self.token_cursor.tokens[self.token_cursor.pos - 1].fileid;
441
442 if prev_fileid == cur_fileid {
444 cur_line - prev_line
445 } else {
446 2
449 }
450 } else {
451 self.token_cursor.tokens[0].line - 1
452 }
453 }
454
455 pub(crate) fn get_next_id(&mut self) -> u32 {
456 self.sequential_id += 1;
457 self.sequential_id
458 }
459
460 pub(crate) fn get_incfilename(&self, fileid: usize) -> Option<String> {
461 if fileid == 0 || fileid >= self.filenames.len() {
462 None
463 } else {
464 Some(self.filenames[fileid].to_string())
465 }
466 }
467
468 pub(crate) fn set_file_version(&mut self, version: A2lVersion) {
469 self.file_ver = version;
470 }
471
472 pub(crate) fn check_block_version_lower(
473 &mut self,
474 context: &ParseContext,
475 tag: &str,
476 min_ver: A2lVersion,
477 ) -> Result<(), ParserError> {
478 if self.file_ver < min_ver {
479 self.error_or_log(ParserError::BlockRefTooNew {
480 filename: self.filenames[context.fileid].to_string(),
481 error_line: self.last_token_position,
482 block: context.element.clone(),
483 tag: tag.to_string(),
484 limit_ver: min_ver,
485 file_ver: self.file_ver,
486 })?;
487 }
488 Ok(())
489 }
490
491 pub(crate) fn check_block_version_upper(
492 &mut self,
493 context: &ParseContext,
494 tag: &str,
495 max_ver: A2lVersion,
496 ) {
497 if self.file_ver > max_ver {
498 self.log_warning(ParserError::BlockRefDeprecated {
499 filename: self.filenames[context.fileid].to_string(),
500 error_line: self.last_token_position,
501 block: context.element.clone(),
502 tag: tag.to_string(),
503 limit_ver: max_ver,
504 file_ver: self.file_ver,
505 });
506 }
507 }
508
509 pub(crate) fn check_enumitem_version_lower(
510 &mut self,
511 context: &ParseContext,
512 tag: &str,
513 min_ver: A2lVersion,
514 ) -> Result<(), ParserError> {
515 if self.file_ver < min_ver {
516 self.error_or_log(ParserError::EnumRefTooNew {
517 filename: self.filenames[context.fileid].to_string(),
518 error_line: self.last_token_position,
519 block: context.element.clone(),
520 tag: tag.to_string(),
521 limit_ver: min_ver,
522 file_ver: self.file_ver,
523 })?;
524 }
525 Ok(())
526 }
527
528 pub(crate) fn check_enumitem_version_upper(
529 &mut self,
530 context: &ParseContext,
531 tag: &str,
532 max_ver: A2lVersion,
533 ) {
534 if self.file_ver > max_ver {
535 self.log_warning(ParserError::EnumRefDeprecated {
536 filename: self.filenames[context.fileid].to_string(),
537 error_line: self.last_token_position,
538 block: context.element.clone(),
539 tag: tag.to_string(),
540 limit_ver: max_ver,
541 file_ver: self.file_ver,
542 });
543 }
544 }
545
546 pub(crate) fn expect_token(
549 &mut self,
550 context: &ParseContext,
551 token_type: A2lTokenType,
552 ) -> Result<&'a A2lToken, ParserError> {
553 let mut token = self.get_token(context)?;
554 while token.ttype == A2lTokenType::Comment {
556 token = self.get_token(context)?;
557 }
558
559 if token.ttype != token_type {
560 return Err(ParserError::unexpected_token_type(
561 self, context, token, token_type,
562 ));
563 }
564
565 Ok(token)
566 }
567
568 pub(crate) fn get_string(&mut self, context: &ParseContext) -> Result<String, ParserError> {
571 let text = if let Some(
572 token @ A2lToken {
573 ttype: A2lTokenType::Identifier,
574 ..
575 },
576 ) = self.peek_token()
577 {
578 let text = self.get_identifier(context)?;
580 self.error_or_log(ParserError::unexpected_token_type(
581 self,
582 context,
583 token,
584 A2lTokenType::String,
585 ))?;
586 text
587 } else {
588 let token = self.expect_token(context, A2lTokenType::String)?;
589 let mut text = self.get_token_text(token);
590
591 if text.starts_with('\"') {
592 text = &text[1..text.len() - 1];
593 }
594
595 unescape_string(text)
596 };
597
598 Ok(text)
599 }
600
601 pub(crate) fn get_string_maxlen(
604 &mut self,
605 context: &ParseContext,
606 maxlen: usize,
607 ) -> Result<String, ParserError> {
608 let text = self.get_string(context)?;
609 if text.len() > maxlen {
610 self.error_or_log(ParserError::StringTooLong {
611 filename: self.filenames[context.fileid].to_string(),
612 error_line: self.last_token_position,
613 block: context.element.clone(),
614 text: text.clone(),
615 length: text.len(),
616 max_length: maxlen,
617 })?;
618 }
619 Ok(text)
620 }
621
622 pub(crate) fn get_identifier(&mut self, context: &ParseContext) -> Result<String, ParserError> {
625 let token = self.expect_token(context, A2lTokenType::Identifier)?;
626 let text = self.get_token_text(token);
627 if text.as_bytes()[0].is_ascii_digit() || text.len() > MAX_IDENT {
628 self.error_or_log(ParserError::InvalidIdentifier {
629 filename: self.filenames[context.fileid].to_string(),
630 error_line: self.last_token_position,
631 block: context.element.clone(),
632 ident: text.to_owned(),
633 })?;
634 }
635 Ok(String::from(text))
636 }
637
638 pub(crate) fn get_float(&mut self, context: &ParseContext) -> Result<f32, ParserError> {
642 let token = self.expect_token(context, A2lTokenType::Number)?;
643 let text = self.get_token_text(token);
644 if text.starts_with("0x") || text.starts_with("0X") {
648 match u64::from_str_radix(&text[2..], 16) {
649 Ok(num) => Ok(num as f32),
650 Err(_) => Err(ParserError::malformed_number(self, context, text)),
651 }
652 } else {
653 match text.parse::<f32>() {
654 Ok(num) => Ok(num),
655 Err(_) => Err(ParserError::malformed_number(self, context, text)),
656 }
657 }
658 }
659
660 pub(crate) fn get_double(&mut self, context: &ParseContext) -> Result<f64, ParserError> {
664 let token = self.expect_token(context, A2lTokenType::Number)?;
665 let text = self.get_token_text(token);
666 if text.starts_with("0x") || text.starts_with("0X") {
670 match u64::from_str_radix(&text[2..], 16) {
671 Ok(num) => Ok(num as f64),
672 Err(_) => Err(ParserError::malformed_number(self, context, text)),
673 }
674 } else {
675 match text.parse::<f64>() {
676 Ok(num) => Ok(num),
677 Err(_) => Err(ParserError::malformed_number(self, context, text)),
678 }
679 }
680 }
681
682 pub(crate) fn get_integer<T>(
683 &mut self,
684 context: &ParseContext,
685 ) -> Result<(T, bool), ParserError>
686 where
687 T: Num + std::str::FromStr + Copy + 'static,
688 u64: AsPrimitive<T>,
689 {
690 let token = self.expect_token(context, A2lTokenType::Number)?;
691 let text = self.get_token_text(token);
692 if text.len() > 2 && (text.starts_with("0x") || text.starts_with("0X")) {
693 match u64::from_str_radix(&text[2..], 16) {
694 Ok(num_u64) => Ok((num_u64.as_(), true)),
695 Err(_) => Err(ParserError::malformed_number(self, context, text)),
696 }
697 } else {
698 match text.parse() {
699 Ok(num) => Ok((num, false)),
700 Err(_) => Err(ParserError::malformed_number(self, context, text)),
701 }
702 }
703 }
704
705 pub(crate) fn get_next_tag_or_comment(
713 &mut self,
714 context: &ParseContext,
715 ) -> Result<BlockContent<'a>, ParserError> {
716 let mut is_block = false;
717 let tokenpos = self.get_tokenpos();
718
719 if let Some(
720 tokenval @ A2lToken {
721 ttype: A2lTokenType::Comment,
722 ..
723 },
724 ) = self.token_cursor.peek()
725 {
726 self.token_cursor.next(); let start_offset = self.get_line_offset();
728 Ok(BlockContent::Comment(tokenval, start_offset))
729 } else {
730 let (token_result, start_offset) = if let Some(A2lToken {
732 ttype: A2lTokenType::Begin,
733 ..
734 }) = self.token_cursor.peek()
735 {
736 is_block = true;
737 self.get_token(context)?;
738 let start_offset = self.get_line_offset();
741 (
742 self.expect_token(context, A2lTokenType::Identifier),
743 start_offset,
744 )
745 } else {
746 (
748 self.expect_token(context, A2lTokenType::Identifier),
749 self.get_line_offset(),
750 )
751 };
752
753 match token_result {
755 Ok(token) => Ok(BlockContent::Block(token, is_block, start_offset)),
756 Err(error) => {
757 self.set_tokenpos(tokenpos);
758 if is_block {
759 Err(error)
760 } else {
761 Ok(BlockContent::None)
763 }
764 }
765 }
766 }
767 }
768
769 pub(crate) fn handle_unknown_taggedstruct_tag(
772 &mut self,
773 context: &ParseContext,
774 item_tag: &str,
775 item_is_block: bool,
776 stoplist: &[&str],
777 ) -> Result<(), ParserError> {
778 self.error_or_log(ParserError::unknown_sub_block(self, context, item_tag))?;
779 let _ = self.get_token(context)?;
781 self.undo_get_token();
782 let startpos = self.get_tokenpos();
783 let text = self.get_token_text(&self.token_cursor.tokens[startpos]);
784 let errcontext = ParseContext::from_token(text, &self.token_cursor.tokens[startpos]);
785
786 let mut balance = 0;
787 if item_is_block {
788 balance = 1;
789 }
790
791 loop {
792 let token = self.get_token(context)?;
793 let text = self.get_token_text(token);
794 match token.ttype {
795 A2lTokenType::Begin => balance += 1,
796 A2lTokenType::End => {
797 balance -= 1;
798 if balance == -1 {
799 self.token_cursor.back();
800 break;
801 }
802 }
803 A2lTokenType::Identifier => {
804 if item_is_block {
805 if balance == 0 {
808 if text == item_tag {
809 break;
810 } else {
811 return Err(ParserError::incorrect_end_tag(
812 self,
813 &errcontext,
814 text,
815 ));
816 }
817 }
818 } else {
819 if balance == 0 || balance == 1 {
824 let found = stoplist.iter().find(|entry| **entry == text);
825 if found.is_some() {
826 self.token_cursor.back();
828 if balance == 1 {
829 self.token_cursor.back();
830 }
831 break;
832 }
833 }
834 }
835 }
836 _ => {
837 if item_is_block && balance == 0 {
839 return Err(ParserError::incorrect_end_tag(self, &errcontext, text));
840 }
841 }
843 }
844 }
845
846 Ok(())
847 }
848
849 pub(crate) fn handle_multiplicity_error(
850 &mut self,
851 context: &ParseContext,
852 tag: &str,
853 is_error: bool,
854 ) -> Result<(), ParserError> {
855 if is_error {
856 self.error_or_log(ParserError::invalid_multiplicity_too_many(
857 self, context, tag,
858 ))?;
859 }
860 Ok(())
861 }
862
863 pub(crate) fn require_block(
864 &self,
865 tag: &str,
866 is_block: bool,
867 context: &ParseContext,
868 ) -> Result<(), ParserError> {
869 if !is_block {
870 Err(ParserError::IncorrectBlockError {
871 filename: self.filenames[context.fileid].to_string(),
872 error_line: self.last_token_position,
873 tag: tag.to_string(),
874 block: context.element.clone(),
875 block_line: context.line,
876 })
877 } else {
878 Ok(())
879 }
880 }
881
882 pub(crate) fn require_keyword(
883 &self,
884 tag: &str,
885 is_block: bool,
886 context: &ParseContext,
887 ) -> Result<(), ParserError> {
888 if is_block {
889 Err(ParserError::IncorrectKeywordError {
890 filename: self.filenames[context.fileid].to_string(),
891 error_line: self.last_token_position,
892 tag: tag.to_string(),
893 block: context.element.clone(),
894 block_line: context.line,
895 })
896 } else {
897 Ok(())
898 }
899 }
900}
901
902impl ParseContext {
903 pub(crate) fn from_token(text: &str, token: &A2lToken) -> ParseContext {
904 ParseContext {
905 element: text.to_string(),
906 fileid: token.fileid,
907 line: token.line,
908 }
909 }
910}
911
912impl ParserError {
913 pub(crate) fn unexpected_token_type(
914 parser: &ParserState,
915 context: &ParseContext,
916 token: &A2lToken,
917 expected_ttype: A2lTokenType,
918 ) -> Self {
919 Self::UnexpectedTokenType {
920 filename: parser.filenames[context.fileid].to_string(),
921 error_line: parser.last_token_position,
922 block_line: context.line,
923 element: context.element.clone(),
924 actual_ttype: token.ttype.clone(),
925 actual_text: parser.get_token_text(token).to_owned(),
926 expected_ttype,
927 }
928 }
929
930 pub(crate) fn malformed_number(
931 parser: &ParserState,
932 context: &ParseContext,
933 numstr: &str,
934 ) -> Self {
935 Self::MalformedNumber {
936 filename: parser.filenames[context.fileid].to_string(),
937 error_line: parser.last_token_position,
938 numstr: numstr.to_owned(),
939 }
940 }
941
942 pub(crate) fn invalid_enum_value(
943 parser: &ParserState,
944 context: &ParseContext,
945 enumitem: &str,
946 ) -> Self {
947 Self::InvalidEnumValue {
948 filename: parser.filenames[context.fileid].to_string(),
949 error_line: parser.last_token_position,
950 enumtxt: enumitem.to_owned(),
951 block: context.element.clone(),
952 block_line: context.line,
953 }
954 }
955
956 pub(crate) fn invalid_multiplicity_too_many(
957 parser: &ParserState,
958 context: &ParseContext,
959 tag: &str,
960 ) -> Self {
961 Self::InvalidMultiplicityTooMany {
962 filename: parser.filenames[context.fileid].to_string(),
963 error_line: parser.last_token_position,
964 tag: tag.to_string(),
965 block: context.element.clone(),
966 block_line: context.line,
967 }
968 }
969
970 pub(crate) fn incorrect_end_tag(
971 parser: &ParserState,
972 context: &ParseContext,
973 tag: &str,
974 ) -> Self {
975 Self::IncorrectEndTag {
976 filename: parser.filenames[context.fileid].to_string(),
977 error_line: parser.last_token_position,
978 tag: tag.to_owned(),
979 block: context.element.clone(),
980 block_line: context.line,
981 }
982 }
983
984 pub(crate) fn unknown_sub_block(
985 parser: &ParserState,
986 context: &ParseContext,
987 tag: &str,
988 ) -> Self {
989 Self::UnknownSubBlock {
990 filename: parser.filenames[context.fileid].to_string(),
991 error_line: parser.last_token_position,
992 tag: tag.to_owned(),
993 block: context.element.clone(),
994 block_line: context.line,
995 }
996 }
997
998 pub(crate) fn unexpected_eof(parser: &ParserState, context: &ParseContext) -> Self {
999 Self::UnexpectedEOF {
1000 filename: parser.filenames[context.fileid].to_string(),
1001 error_line: parser.last_token_position,
1002 block: context.element.clone(),
1003 block_line: context.line,
1004 }
1005 }
1006}
1007
1008fn unescape_string(text: &str) -> String {
1009 if text.chars().any(|c| c == '\\' || c == '"') {
1011 let input_chars: Vec<char> = text.chars().collect();
1012 let mut output_chars = Vec::<char>::new();
1013
1014 let mut idx = 1;
1015 while idx < input_chars.len() {
1016 if (input_chars[idx - 1] == '\\' || input_chars[idx - 1] == '"')
1017 && input_chars[idx] == '"'
1018 {
1019 output_chars.push('"');
1020 idx += 1;
1021 } else if input_chars[idx - 1] == '\\' && input_chars[idx] == '\'' {
1022 output_chars.push('\'');
1023 idx += 1;
1024 } else if input_chars[idx - 1] == '\\' && input_chars[idx] == '\\' {
1025 output_chars.push('\\');
1026 idx += 1;
1027 } else if input_chars[idx - 1] == '\\' && input_chars[idx] == 'n' {
1028 output_chars.push('\n');
1029 idx += 1;
1030 } else if input_chars[idx - 1] == '\\' && input_chars[idx] == 'r' {
1031 output_chars.push('\r');
1032 idx += 1;
1033 } else if input_chars[idx - 1] == '\\' && input_chars[idx] == 't' {
1034 output_chars.push('\t');
1035 idx += 1;
1036 } else {
1037 output_chars.push(input_chars[idx - 1]);
1038 }
1039
1040 idx += 1;
1041 }
1042 if idx == input_chars.len() {
1043 output_chars.push(input_chars[idx - 1]);
1044 }
1045
1046 output_chars.iter().collect()
1047 } else {
1048 text.to_owned()
1049 }
1050}
1051
1052impl A2lVersion {
1053 pub fn new(major: u16, minor: u16) -> Result<Self, ParserError> {
1054 match (major, minor) {
1055 (1, 50) => Ok(A2lVersion::V1_5_0),
1056 (1, 51) => Ok(A2lVersion::V1_5_1),
1057 (1, 60) => Ok(A2lVersion::V1_6_0),
1058 (1, 61) => Ok(A2lVersion::V1_6_1),
1059 (1, 70) => Ok(A2lVersion::V1_7_0),
1060 (1, 71) => Ok(A2lVersion::V1_7_1),
1061 _ => Err(ParserError::InvalidVersion { major, minor }),
1062 }
1063 }
1064}
1065
1066impl Display for A2lVersion {
1067 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1068 match self {
1069 A2lVersion::V1_5_0 => f.write_str("1.5.0"),
1070 A2lVersion::V1_5_1 => f.write_str("1.5.1"),
1071 A2lVersion::V1_6_0 => f.write_str("1.6.0"),
1072 A2lVersion::V1_6_1 => f.write_str("1.6.1"),
1073 A2lVersion::V1_7_0 => f.write_str("1.7.0"),
1074 A2lVersion::V1_7_1 => f.write_str("1.7.1"),
1075 }
1076 }
1077}
1078
1079#[cfg(test)]
1080mod tests {
1081 use super::*;
1082 use crate::{Filename, load_from_string, tokenizer};
1083
1084 #[test]
1085 fn parsing_numbers_test() {
1086 let input_text = r##"0 0x1 1.0e+2 1000 0 0.1 0x11 1.0e+2 0X1f 0X2F 2F F"##;
1087 let tokenresult = tokenizer::tokenize(&Filename::from("test_input"), 0, input_text);
1088 assert!(tokenresult.is_ok());
1089
1090 let tokenresult = tokenresult.unwrap();
1091 let mut log_msgs = Vec::<A2lError>::new();
1092 let mut parser = ParserState::new(&tokenresult, &mut log_msgs, true);
1093 let context = ParseContext {
1094 element: "TEST".to_string(),
1095 fileid: 0,
1096 line: 0,
1097 };
1098
1099 let res = parser.get_integer::<u8>(&context);
1101 assert!(res.is_ok());
1102 let val = res.unwrap();
1103 assert_eq!(val, (0, false));
1104
1105 let res = parser.get_integer::<u8>(&context);
1107 assert!(res.is_ok());
1108 let val = res.unwrap();
1109 assert_eq!(val, (1, true));
1110
1111 let res = parser.get_integer::<u8>(&context);
1113 assert!(res.is_err());
1114
1115 let res = parser.get_integer::<u8>(&context);
1117 assert!(res.is_err());
1118
1119 let res = parser.get_float(&context);
1121 assert!(res.is_ok());
1122 let val = res.unwrap();
1123 assert_eq!(val, 0f32);
1124
1125 let res = parser.get_float(&context);
1127 assert!(res.is_ok());
1128 let val = res.unwrap();
1129 assert_eq!(val, 0.1f32);
1130
1131 let res = parser.get_float(&context);
1133 assert!(res.is_ok());
1134 let val = res.unwrap();
1135 assert_eq!(val, 17f32);
1136
1137 let res = parser.get_float(&context);
1139 assert!(res.is_ok());
1140 let val = res.unwrap();
1141 assert_eq!(val, 100f32);
1142
1143 let res = parser.get_float(&context);
1145 assert!(res.is_ok());
1146 let val = res.unwrap();
1147 assert_eq!(val, 31f32);
1148
1149 let res = parser.get_float(&context);
1151 assert!(res.is_ok());
1152 let val = res.unwrap();
1153 assert_eq!(val, 47f32);
1154
1155 let res = parser.get_float(&context);
1157 assert!(res.is_err());
1158
1159 let res = parser.get_float(&context);
1161 assert!(res.is_err());
1162 }
1163
1164 #[test]
1165 fn test_unescape_string() {
1166 let result = unescape_string(" ");
1168 assert_eq!(result, " ");
1169 let result = unescape_string(r#""""#);
1171 assert_eq!(result, r#"""#);
1172 let result = unescape_string(r#"\""#);
1174 assert_eq!(result, r#"""#);
1175 let result = unescape_string(r#"\'"#);
1177 assert_eq!(result, r#"'"#);
1178 let result = unescape_string(r#"\\"#);
1180 assert_eq!(result, r#"\"#);
1181 let result = unescape_string(r#"\n"#);
1183 assert_eq!(result, "\n");
1184 let result = unescape_string(r#"\r"#);
1186 assert_eq!(result, "\r");
1187 let result = unescape_string(r#"\txx"#);
1189 assert_eq!(result, "\txx");
1190 }
1191
1192 #[test]
1193 fn parsing_identifiers_test() {
1194 let input_text = r##"ident 0ident 123"##;
1195 let tokenresult = tokenizer::tokenize(&Filename::from("test_input"), 0, input_text);
1196 assert!(tokenresult.is_ok());
1197
1198 let tokenresult = tokenresult.unwrap();
1199 let mut log_msgs = Vec::<A2lError>::new();
1200 let mut parser = ParserState::new(&tokenresult, &mut log_msgs, true);
1201 let context = ParseContext {
1202 element: "TEST".to_string(),
1203 fileid: 0,
1204 line: 0,
1205 };
1206
1207 let res = parser.get_identifier(&context);
1209 assert!(res.is_ok());
1210 let val = res.unwrap();
1211 assert_eq!(val, "ident");
1212
1213 let res = parser.get_identifier(&context);
1215 assert!(matches!(res, Err(ParserError::InvalidIdentifier { .. })));
1216
1217 let res = parser.get_identifier(&context);
1219 assert!(res.is_err());
1220 }
1221
1222 #[test]
1223 fn test_check_version() {
1224 let tokenresult = tokenizer::tokenize(&Filename::from("test_input"), 0, "").unwrap();
1225 let mut log_msgs = Vec::<A2lError>::new();
1226 let mut parser = ParserState::new(&tokenresult, &mut log_msgs, true);
1227 let context = ParseContext {
1228 element: "TEST".to_string(),
1229 fileid: 0,
1230 line: 0,
1231 };
1232 parser.file_ver = A2lVersion::V1_6_0;
1233
1234 let result = parser.check_block_version_lower(&context, "CHECK", A2lVersion::V1_5_0);
1236 assert!(result.is_ok());
1237
1238 let result = parser.check_block_version_lower(&context, "CHECK", A2lVersion::V1_7_0);
1240 assert!(result.is_err());
1241
1242 let num_warnings = parser.log_msgs.len();
1244 parser.check_block_version_upper(&context, "CHECK", A2lVersion::V1_5_0);
1245 assert_ne!(num_warnings, parser.log_msgs.len());
1246
1247 let num_warnings = parser.log_msgs.len();
1249 parser.check_block_version_upper(&context, "CHECK", A2lVersion::V1_7_0);
1250 assert_eq!(num_warnings, parser.log_msgs.len());
1251
1252 let result: Result<(), ParserError> =
1254 parser.check_enumitem_version_lower(&context, "CHECK", A2lVersion::V1_5_0);
1255 assert!(result.is_ok());
1256
1257 let result = parser.check_enumitem_version_lower(&context, "CHECK", A2lVersion::V1_7_0);
1259 assert!(result.is_err());
1260
1261 let num_warnings = parser.log_msgs.len();
1263 parser.check_enumitem_version_upper(&context, "CHECK", A2lVersion::V1_5_0);
1264 assert_ne!(num_warnings, parser.log_msgs.len());
1265
1266 let num_warnings = parser.log_msgs.len();
1268 parser.check_enumitem_version_upper(&context, "CHECK", A2lVersion::V1_7_0);
1269 assert_eq!(num_warnings, parser.log_msgs.len());
1270 }
1271
1272 #[test]
1273 fn a2lversion() {
1274 let v150 = A2lVersion::new(1, 50).unwrap();
1275 let v151 = A2lVersion::new(1, 51).unwrap();
1276 let v160 = A2lVersion::new(1, 60).unwrap();
1277 let v161 = A2lVersion::new(1, 61).unwrap();
1278 let v170 = A2lVersion::new(1, 70).unwrap();
1279 let v171 = A2lVersion::new(1, 71).unwrap();
1280
1281 assert!(v150 < v151);
1282 assert!(v151 < v160);
1283 assert!(v160 < v161);
1284 assert!(v161 < v170);
1285 assert!(v170 < v171);
1286
1287 let bad_version = A2lVersion::new(1, 80);
1288 assert!(bad_version.is_err());
1289
1290 let cpy = v171;
1291 assert_eq!(cpy, v171);
1292
1293 assert_eq!(format!("{v150}"), "1.5.0");
1294 assert_eq!(format!("{v151}"), "1.5.1");
1295 assert_eq!(format!("{v160}"), "1.6.0");
1296 assert_eq!(format!("{v161}"), "1.6.1");
1297 assert_eq!(format!("{v170}"), "1.7.0");
1298 assert_eq!(format!("{v171}"), "1.7.1");
1299 }
1300
1301 #[test]
1302 fn error_missing_version() {
1303 static DATA: &str = r#"/begin PROJECT p "" /end PROJECT"#;
1304 let a2l_file = load_from_string(DATA, None, true);
1305 assert!(a2l_file.is_err());
1306 assert!(matches!(
1307 a2l_file,
1308 Err(A2lError::ParserError {
1309 parser_error: ParserError::MissingVersionInfo
1310 })
1311 ));
1312 }
1313
1314 #[test]
1315 fn error_invalid_mult_not_present() {
1316 static DATA: &str = r#"ASAP2_VERSION 1 71"#;
1317 let a2l_file = load_from_string(DATA, None, true);
1318 assert!(matches!(
1319 a2l_file,
1320 Err(A2lError::ParserError {
1321 parser_error: ParserError::InvalidMultiplicityNotPresent { .. }
1322 })
1323 ));
1324 }
1325
1326 #[test]
1327 fn error_invalid_mult_too_many() {
1328 static DATA: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT /begin PROJECT p2 "" /begin MODULE m "" /end MODULE /end PROJECT"#;
1329 let a2l_file = load_from_string(DATA, None, true);
1330 assert!(matches!(
1331 a2l_file,
1332 Err(A2lError::ParserError {
1333 parser_error: ParserError::InvalidMultiplicityTooMany { .. }
1334 })
1335 ));
1336 }
1337
1338 #[test]
1339 fn error_unknown_subblock() {
1340 static DATA: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" ABCDEF /end MODULE /end PROJECT"#;
1341 let a2l_file = load_from_string(DATA, None, true);
1342 assert!(matches!(
1343 a2l_file,
1344 Err(A2lError::ParserError {
1345 parser_error: ParserError::UnknownSubBlock { .. }
1346 })
1347 ));
1348 }
1349
1350 #[test]
1351 fn error_incorrect_end_tag() {
1352 static DATA: &str =
1353 r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" /end MMMMM /end PROJECT"#;
1354 let a2l_file = load_from_string(DATA, None, true);
1355 assert!(matches!(
1356 a2l_file,
1357 Err(A2lError::ParserError {
1358 parser_error: ParserError::IncorrectEndTag { .. }
1359 })
1360 ));
1361 }
1362
1363 #[test]
1364 fn error_unexpected_eof() {
1365 static DATA: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT"#;
1366 let a2l_file = load_from_string(DATA, None, true);
1367 assert!(matches!(
1368 a2l_file,
1369 Err(A2lError::ParserError {
1370 parser_error: ParserError::UnexpectedEOF { .. }
1371 })
1372 ));
1373 }
1374
1375 #[test]
1376 fn error_invalid_identifier() {
1377 let data = format!(
1378 r#"ASAP2_VERSION 1 71 /begin PROJECT {} "" /begin MODULE m "" /end MODULE /end PROJECT"#,
1379 ['a'; 1025].iter().collect::<String>()
1380 );
1381 let a2l_file = load_from_string(&data, None, true);
1382 println!("a2l_file: {:#?}", a2l_file);
1383 assert!(a2l_file.is_err());
1384 assert!(matches!(
1385 a2l_file,
1386 Err(A2lError::ParserError {
1387 parser_error: ParserError::InvalidIdentifier { .. }
1388 })
1389 ));
1390 }
1391
1392 #[test]
1393 fn test_handle_unknown_taggedstruct_tag() {
1394 static DATA: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m ""
1396 /begin UNKNOWN_TAG abc def /begin ghi /end ghi /end UNKNOWN_TAG
1397 /end MODULE /end PROJECT"#;
1398 let result = load_from_string(DATA, None, false);
1399 assert!(result.is_ok());
1400 let (a2l_file, _) = result.unwrap();
1401 assert_eq!(a2l_file.project.module.len(), 1);
1402
1403 static DATA2: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m ""
1405 /begin UNKNOWN_TAG abc def
1406 /end MODULE /end PROJECT"#;
1407 let result = load_from_string(DATA2, None, false);
1408 assert!(matches!(
1409 result,
1410 Err(A2lError::ParserError {
1411 parser_error: ParserError::IncorrectEndTag { .. }
1412 })
1413 ));
1414
1415 static DATA3: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m ""
1417 UNKNOWN_TAG abc def /begin ghi /end ghi
1418 /begin GROUP group_name "" ROOT
1419 /end GROUP
1420 /end MODULE /end PROJECT"#;
1421 let result = load_from_string(DATA3, None, false);
1422 assert!(result.is_ok());
1423 let (a2l_file, _) = result.unwrap();
1424 assert_eq!(a2l_file.project.module.len(), 1);
1425 assert_eq!(a2l_file.project.module[0].group.len(), 1);
1426 }
1427
1428 #[test]
1429 fn parse_with_comments() {
1430 static DATA: &str = r#"
1431 /begin TRANSFORMER Transformer
1432 "1.0.0" // Version info
1433 "TransformerDll32.dll" // Name of the 32bit DLL
1434 "" // Name of the 64bit DLL
1435 1500 // timeout in [ms]
1436 ON_CHANGE
1437 InverseTransformer
1438 /end TRANSFORMER"#;
1439 let module = crate::load_fragment(DATA, None).unwrap();
1440 let bi = &module.transformer[0].__block_info;
1441 assert_eq!(bi.item_location.1, 1); assert_eq!(bi.item_location.2, 1); assert_eq!(bi.item_location.3, 1); assert_eq!(bi.item_location.4, (1, false)); assert_eq!(bi.item_location.5, 1); assert_eq!(bi.item_location.6, 1); assert_eq!(bi.end_offset, 1); }
1449}