1use itertools::Itertools;
2use std::{
3 collections::HashMap,
4 fmt::Display,
5 fs::File,
6 io::Read,
7 num::{NonZero, TryFromIntError},
8 path::PathBuf,
9};
10use thiserror::Error;
11
12type Word = u32;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
16pub struct FileId(Word);
17
18impl Display for FileId {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 self.0.fmt(f)
21 }
22}
23
24impl FileId {
25 pub fn new(id: Word) -> Self {
26 Self(id)
27 }
28}
29
30impl From<Word> for FileId {
31 fn from(value: Word) -> Self {
32 Self(value)
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
38pub struct PositionId(Word);
39
40impl PositionId {
41 pub fn new(id: Word) -> Self {
42 Self(id)
43 }
44
45 pub fn value(&self) -> Word {
46 self.0
47 }
48}
49
50impl From<Word> for PositionId {
51 fn from(value: Word) -> Self {
52 Self(value)
53 }
54}
55
56impl TryFrom<u64> for PositionId {
57 type Error = TryFromIntError;
58
59 fn try_from(value: u64) -> Result<Self, Self::Error> {
60 let v: u32 = value.try_into()?;
61 Ok(Self(v))
62 }
63}
64
65impl Display for PositionId {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 self.0.fmt(f)
68 }
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub struct LineNum(NonZero<Word>);
74
75impl LineNum {
76 pub fn new(line: Word) -> Self {
77 Self(NonZero::new(line).expect("Line number must be non-zero"))
78 }
79 pub fn as_usize(&self) -> usize {
80 self.0.get() as usize
81 }
82}
83
84impl Display for LineNum {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 self.0.fmt(f)
87 }
88}
89
90#[derive(Error)]
91#[error("Line number cannot be zero")]
92pub struct LineNumCreationError;
93
94impl std::fmt::Debug for LineNumCreationError {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 std::fmt::Display::fmt(self, f)
97 }
98}
99
100impl TryFrom<Word> for LineNum {
101 type Error = LineNumCreationError;
102
103 fn try_from(value: Word) -> Result<Self, Self::Error> {
104 if value != 0 {
105 Ok(Self(NonZero::new(value).unwrap()))
106 } else {
107 Err(LineNumCreationError)
108 }
109 }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
114pub struct MemoryLocationId(Word);
115
116impl TryFrom<u64> for MemoryLocationId {
117 type Error = TryFromIntError;
118
119 fn try_from(value: u64) -> Result<Self, Self::Error> {
120 let v: u32 = value.try_into()?;
121 Ok(Self(v))
122 }
123}
124
125impl From<Word> for MemoryLocationId {
126 fn from(value: Word) -> Self {
127 Self(value)
128 }
129}
130
131impl Display for MemoryLocationId {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> core::fmt::Result {
133 <Word as Display>::fmt(&self.0, f)
134 }
135}
136
137#[derive(Debug, Clone, PartialEq, Eq)]
138pub struct MemoryLocation {
139 pub cell: String,
140 pub address: Vec<usize>,
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
145pub struct VariableAssignmentId(Word);
146
147impl Display for VariableAssignmentId {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> core::fmt::Result {
149 <Word as Display>::fmt(&self.0, f)
150 }
151}
152impl TryFrom<u64> for VariableAssignmentId {
153 type Error = TryFromIntError;
154
155 fn try_from(value: u64) -> Result<Self, Self::Error> {
156 let v: u32 = value.try_into()?;
157 Ok(Self(v))
158 }
159}
160#[derive(Debug, Clone, PartialEq, Eq, Default)]
161pub struct SourceInfoTable {
162 file_map: HashMap<FileId, PathBuf>,
164 position_map: HashMap<PositionId, SourceLocation>,
166 mem_location_map: HashMap<MemoryLocationId, MemoryLocation>,
168 variable_assignment_map:
170 HashMap<VariableAssignmentId, HashMap<String, MemoryLocationId>>,
171 position_state_map: HashMap<PositionId, VariableAssignmentId>,
174}
175
176impl SourceInfoTable {
177 const HEADER: &str = "sourceinfo";
178
179 pub fn lookup_file_path(&self, file: FileId) -> &PathBuf {
184 &self.file_map[&file]
185 }
186
187 pub fn lookup_position(&self, pos: PositionId) -> &SourceLocation {
192 &self.position_map[&pos]
193 }
194
195 pub fn get_position(&self, pos: PositionId) -> Option<&SourceLocation> {
198 self.position_map.get(&pos)
199 }
200
201 pub fn iter_file_map(&self) -> impl Iterator<Item = (&FileId, &PathBuf)> {
204 self.file_map.iter()
205 }
206
207 pub fn iter_file_paths(&self) -> impl Iterator<Item = &PathBuf> {
209 self.file_map.values()
210 }
211
212 pub fn iter_file_ids(&self) -> impl Iterator<Item = FileId> + '_ {
214 self.file_map.keys().copied()
215 }
216
217 pub fn iter_position_map(
220 &self,
221 ) -> impl Iterator<Item = (&PositionId, &SourceLocation)> {
222 self.position_map.iter()
223 }
224
225 pub fn iter_positions(&self) -> impl Iterator<Item = PositionId> + '_ {
227 self.position_map.keys().copied()
228 }
229
230 pub fn iter_source_locations(
232 &self,
233 ) -> impl Iterator<Item = &SourceLocation> {
234 self.position_map.values()
235 }
236
237 pub fn add_file(&mut self, file: FileId, path: PathBuf) {
239 self.file_map.insert(file, path);
240 }
241
242 pub fn push_file(&mut self, path: PathBuf) -> FileId {
246 let max = self.iter_file_ids().max().unwrap_or(0.into());
248 let new = FileId(max.0 + 1);
249
250 self.add_file(new, path);
251 new
252 }
253 pub fn add_position(
254 &mut self,
255 pos: PositionId,
256 file: FileId,
257 line: LineNum,
258 ) {
259 self.position_map
260 .insert(pos, SourceLocation::new(file, line));
261 }
262
263 pub fn push_position(&mut self, file: FileId, line: LineNum) -> PositionId {
267 let max = self.iter_positions().max().unwrap_or(0.into());
269 let new = PositionId(max.0 + 1);
270
271 self.add_position(new, file, line);
272 new
273 }
274
275 pub fn add_location(&mut self, id: MemoryLocationId, info: MemoryLocation) {
276 self.mem_location_map.insert(id, info);
277 }
278
279 pub fn new_empty() -> Self {
281 Self {
282 file_map: HashMap::new(),
283 position_map: HashMap::new(),
284 mem_location_map: HashMap::new(),
285 variable_assignment_map: HashMap::new(),
286 position_state_map: HashMap::new(),
287 }
288 }
289
290 pub fn new_minimal(
293 files: impl IntoIterator<Item = (FileId, PathBuf)>,
294 positions: impl IntoIterator<Item = (PositionId, FileId, LineNum)>,
295 ) -> SourceInfoResult<Self> {
296 let loc: Vec<(MemoryLocationId, MemoryLocation)> = vec![];
299 let states: Vec<(PositionId, VariableAssignmentId)> = vec![];
300 let variable_assigns: Vec<(
301 VariableAssignmentId,
302 Vec<(String, MemoryLocationId)>,
303 )> = vec![];
304
305 Self::new(files, positions, loc, variable_assigns, states)
306 }
307
308 pub fn new(
310 files: impl IntoIterator<Item = (FileId, PathBuf)>,
311 positions: impl IntoIterator<Item = (PositionId, FileId, LineNum)>,
312 locations: impl IntoIterator<Item = (MemoryLocationId, MemoryLocation)>,
313 variable_assigns: impl IntoIterator<
314 Item = (
315 VariableAssignmentId,
316 impl IntoIterator<Item = (String, MemoryLocationId)>,
317 ),
318 >,
319 states: impl IntoIterator<Item = (PositionId, VariableAssignmentId)>,
320 ) -> SourceInfoResult<Self> {
321 let files = files.into_iter();
322 let positions = positions.into_iter();
323 let locations = locations.into_iter();
324 let vars = variable_assigns.into_iter();
325 let states = states.into_iter();
326
327 let mut file_map = HashMap::with_capacity(
328 files.size_hint().1.unwrap_or(files.size_hint().0),
329 );
330 let mut position_map = HashMap::with_capacity(
331 positions.size_hint().1.unwrap_or(positions.size_hint().0),
332 );
333
334 let mut memory_location_map: HashMap<MemoryLocationId, MemoryLocation> =
335 HashMap::new();
336
337 let mut variable_map = HashMap::new();
338 let mut state_map = HashMap::new();
339
340 for (file, path) in files {
341 if let Some(first_path) = file_map.insert(file, path) {
342 let inserted_path = &file_map[&file];
343 if &first_path != inserted_path {
344 return Err(SourceInfoTableError::DuplicateFiles {
345 id1: file,
346 path1: first_path,
347 path2: inserted_path.clone(),
348 });
349 }
350 }
351 }
352
353 for (pos, file, line) in positions {
354 let source = SourceLocation::new(file, line);
355 if let Some(first_pos) = position_map.insert(pos, source) {
356 let inserted_position = &position_map[&pos];
357 if inserted_position != &first_pos {
358 return Err(SourceInfoTableError::DuplicatePositions {
359 pos,
360 s1: first_pos,
361 s2: position_map[&pos].clone(),
362 });
363 }
364 }
365 }
366
367 for (id, loc) in locations {
368 if memory_location_map.insert(id, loc).is_some() {
369 return Err(SourceInfoTableError::DuplicateMemoryIdentifiers {
371 id,
372 });
373 }
374 }
375
376 for (assign_label, assigns) in vars {
377 let mut mapping = HashMap::new();
378 for (name, location) in assigns {
379 #[allow(clippy::map_entry)]
386 if !memory_location_map.contains_key(&location) {
387 return Err(SourceInfoTableError::UnknownMemoryId {
389 id: location,
390 });
391 } else if mapping.contains_key(&name) {
392 return Err(
394 SourceInfoTableError::DuplicateVariableAssignments {
395 id: assign_label,
396 var: name,
397 },
398 );
399 } else {
400 mapping.insert(name, location);
401 }
402 }
403 if variable_map.insert(assign_label, mapping).is_some() {
404 return Err(SourceInfoTableError::DuplicateVariableMappings {
406 id: assign_label,
407 });
408 };
409 }
410
411 for (pos_id, var_id) in states {
412 if !variable_map.contains_key(&var_id) {
413 return Err(SourceInfoTableError::UnknownVariableMapping {
415 id: var_id,
416 });
417 }
418 if state_map.insert(pos_id, var_id).is_some() {
419 return Err(SourceInfoTableError::DuplicatePosStateMappings {
421 id: pos_id,
422 });
423 }
424 }
425
426 Ok(SourceInfoTable {
427 file_map,
428 position_map,
429 mem_location_map: memory_location_map,
430 variable_assignment_map: variable_map,
431 position_state_map: state_map,
432 })
433 }
434
435 pub fn serialize<W: std::io::Write>(
436 &self,
437 mut f: W,
438 ) -> Result<(), std::io::Error> {
439 writeln!(f, "{} #{{", Self::HEADER)?;
440
441 writeln!(f, "FILES")?;
443 for (file, path) in self.file_map.iter().sorted_by_key(|(k, _)| **k) {
444 writeln!(f, " {file}: {}", path.display())?;
445 }
446
447 writeln!(f, "POSITIONS")?;
449 for (position, SourceLocation { line, file }) in
450 self.position_map.iter().sorted_by_key(|(k, _)| **k)
451 {
452 writeln!(f, " {position}: {file} {line}")?;
453 }
454
455 writeln!(f, "}}#")
456 }
457
458 pub fn get_position_string(
462 &self,
463 pos: PositionId,
464 ) -> Result<String, SourceLookupError> {
465 let Some(src_loc) = self.get_position(pos) else {
466 return Err(SourceLookupError::MissingPosition(pos));
467 };
468 let file_path = self.lookup_file_path(src_loc.file);
471
472 let Ok(mut file) = File::open(file_path) else {
473 return Err(SourceLookupError::MissingFile(file_path));
474 };
475
476 let mut file_contents = String::new();
477
478 match file.read_to_string(&mut file_contents) {
479 Ok(_) => {}
480 Err(_) => {
481 return Err(SourceLookupError::MissingFile(file_path));
482 }
483 }
484
485 let Some(line) = file_contents.lines().nth(src_loc.line.as_usize() - 1)
486 else {
487 return Err(SourceLookupError::MissingLine {
488 file: file_path,
489 line: src_loc.line.as_usize(),
490 });
491 };
492
493 Ok(String::from(line))
494 }
495}
496
497#[derive(Debug, Clone, PartialEq, Eq)]
498pub struct SourceLocation {
499 pub file: FileId,
500 pub line: LineNum,
501}
502
503impl SourceLocation {
504 pub fn new(file: FileId, line: LineNum) -> Self {
505 Self { line, file }
506 }
507}
508#[derive(Error)]
509pub enum SourceInfoTableError {
510 #[error("Duplicate positions found in the metadata table. Position {pos} is defined multiple times:
511 1. file {}, line {}
512 2. file {}, line {}\n", s1.file, s1.line, s2.file, s2.line)]
513 DuplicatePositions {
514 pos: PositionId,
515 s1: SourceLocation,
516 s2: SourceLocation,
517 },
518
519 #[error("Duplicate files found in the metadata table. File id {id1} is defined multiple times:
520 1. {path1}
521 2. {path2}\n")]
522 DuplicateFiles {
523 id1: FileId,
524 path1: PathBuf,
525 path2: PathBuf,
526 },
527
528 #[error("Duplicate definitions for memory location {id}")]
529 DuplicateMemoryIdentifiers { id: MemoryLocationId },
530
531 #[error("Memory location {id} is referenced but never defined")]
532 UnknownMemoryId { id: MemoryLocationId },
533
534 #[error("Variable mapping {id} is referenced but never defined")]
535 UnknownVariableMapping { id: VariableAssignmentId },
536
537 #[error("Duplicate definitions for variable mapping {id}")]
538 DuplicateVariableMappings { id: VariableAssignmentId },
539
540 #[error(
541 "Duplicate definitions for variable mapping associated with position {id}"
542 )]
543 DuplicatePosStateMappings { id: PositionId },
544
545 #[error(
546 "In variable mapping {id} the variable '{var}' has multiple definitions"
547 )]
548 DuplicateVariableAssignments {
549 id: VariableAssignmentId,
550 var: String,
551 },
552}
553
554#[derive(Error, Debug)]
557pub enum SourceLookupError<'a> {
558 #[error("unable to open file {0}")]
559 MissingFile(&'a PathBuf),
560 #[error("file {file} does not have a line {line}")]
561 MissingLine { file: &'a PathBuf, line: usize },
562 #[error("position id {0} does not exist")]
563 MissingPosition(PositionId),
564}
565
566impl std::fmt::Debug for SourceInfoTableError {
567 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
568 std::fmt::Display::fmt(&self, f)
569 }
570}
571
572pub type SourceInfoResult<T> = Result<T, SourceInfoTableError>;
573
574#[cfg(test)]
575mod tests {
576 use std::path::PathBuf;
577
578 use crate::{
579 parser::CalyxParser,
580 source_info::{
581 FileId, LineNum, MemoryLocationId, PositionId,
582 SourceInfoTableError, VariableAssignmentId,
583 },
584 };
585
586 use super::SourceInfoTable;
587
588 #[test]
589 fn test_parse_metadata() {
590 let input_str = r#"sourceinfo #{
591 FILES
592 0: test.calyx
593 1: test2.calyx
594 2: test3.calyx
595 POSITIONS
596 0: 0 5
597 1: 0 1
598 2: 0 2
599 MEMORY_LOCATIONS
600 0: main.reg1
601 1: main.reg2
602 2: main.mem1 [1,4]
603 VARIABLE_ASSIGNMENTS
604 0: {
605 x: 0
606 y: 1
607 z: 2
608 }
609 1: {
610 q: 0
611 }
612 POSITION_STATE_MAP
613 0: 0
614 2: 1
615}#"#;
616
617 let metadata = CalyxParser::parse_source_info_table(input_str)
618 .unwrap()
619 .unwrap();
620 let file = metadata.lookup_file_path(1.into());
621 assert_eq!(file, &PathBuf::from("test2.calyx"));
622
623 let pos = metadata.lookup_position(1.into());
624 assert_eq!(pos.file, 0.into());
625 assert_eq!(pos.line, LineNum::new(1));
626 }
627
628 #[test]
629 fn test_undefined_mem_loc() {
630 let input_str = r#"sourceinfo #{
631 FILES
632 0: test.calyx
633 POSITIONS
634 0: 0 5
635 1: 0 1
636 2: 0 2
637 MEMORY_LOCATIONS
638 0: main.reg1
639 2: main.mem1 [1,4]
640 VARIABLE_ASSIGNMENTS
641 0: {
642 x: 0
643 y: 1
644 z: 2
645 }
646 1: {
647 q: 0
648 }
649 POSITION_STATE_MAP
650 0: 0
651 2: 1
652}#"#;
653 let metadata = CalyxParser::parse_source_info_table(input_str).unwrap();
654 assert!(metadata.is_err());
655 let err = metadata.unwrap_err();
656 assert!(matches!(
657 &err,
658 SourceInfoTableError::UnknownMemoryId {
659 id: MemoryLocationId(1)
660 }
661 ));
662 }
663
664 #[test]
665 fn test_undefined_variable() {
666 let input_str = r#"sourceinfo #{
667 FILES
668 0: test.calyx
669 POSITIONS
670 0: 0 5
671 1: 0 1
672 2: 0 2
673 MEMORY_LOCATIONS
674 0: main.reg1
675 1: main.reg2
676 2: main.mem1 [1,4]
677 VARIABLE_ASSIGNMENTS
678 0: {
679 x: 0
680 y: 1
681 z: 2
682 }
683 1: {
684 q: 0
685 }
686 POSITION_STATE_MAP
687 0: 0
688 2: 2
689}#"#;
690 let metadata = CalyxParser::parse_source_info_table(input_str).unwrap();
691 assert!(metadata.is_err());
692 let err = metadata.unwrap_err();
693
694 assert!(matches!(
695 &err,
696 SourceInfoTableError::UnknownVariableMapping {
697 id: VariableAssignmentId(2)
698 }
699 ));
700 }
701
702 #[test]
703 fn test_duplicate_variable_maps() {
704 let input_str = r#"sourceinfo #{
705 FILES
706 0: test.calyx
707 POSITIONS
708 0: 0 5
709 1: 0 1
710 2: 0 2
711 MEMORY_LOCATIONS
712 0: main.reg1
713 1: main.reg2
714 2: main.mem1 [1,4]
715 VARIABLE_ASSIGNMENTS
716 0: {
717 x: 0
718 y: 1
719 z: 2
720 }
721 1: {
722 q: 0
723 }
724 1: {
725 a: 0
726 }
727 POSITION_STATE_MAP
728 0: 0
729 2: 1
730}#"#;
731 let metadata = CalyxParser::parse_source_info_table(input_str).unwrap();
732 assert!(metadata.is_err());
733 let err = metadata.unwrap_err();
734 assert!(matches!(
735 &err,
736 SourceInfoTableError::DuplicateVariableMappings {
737 id: VariableAssignmentId(1)
738 }
739 ));
740 }
741
742 #[test]
743 fn test_duplicate_variable_assignment() {
744 let input_str = r#"sourceinfo #{
745 FILES
746 0: test.calyx
747 POSITIONS
748 0: 0 5
749 1: 0 1
750 2: 0 2
751 MEMORY_LOCATIONS
752 0: main.reg1
753 1: main.reg2
754 2: main.mem1 [1,4]
755 VARIABLE_ASSIGNMENTS
756 0: {
757 x: 0
758 y: 1
759 z: 2
760 }
761 1: {
762 q: 0
763 q: 1
764 }
765 POSITION_STATE_MAP
766 0: 0
767 2: 1
768}#"#;
769 let metadata = CalyxParser::parse_source_info_table(input_str).unwrap();
770 assert!(metadata.is_err());
771 let err = metadata.unwrap_err();
772 assert!(matches!(
773 &err,
774 SourceInfoTableError::DuplicateVariableAssignments {
775 id: VariableAssignmentId(1),
776 var
777 } if var == "q"
778 ));
779 }
780
781 #[test]
782 fn test_duplicate_mem_def() {
783 let input_str = r#"sourceinfo #{
784 FILES
785 0: test.calyx
786 POSITIONS
787 0: 0 5
788 1: 0 1
789 2: 0 2
790 MEMORY_LOCATIONS
791 0: main.reg1
792 1: main.reg2
793 1: main.mem1 [1,4]
794 VARIABLE_ASSIGNMENTS
795 0: {
796 x: 0
797 y: 1
798 z: 2
799 }
800 1: {
801 q: 0
802 }
803 POSITION_STATE_MAP
804 0: 0
805 2: 1
806}#"#;
807 let metadata = CalyxParser::parse_source_info_table(input_str).unwrap();
808 assert!(metadata.is_err());
809 let err = metadata.unwrap_err();
810 assert!(matches!(
811 &err,
812 SourceInfoTableError::DuplicateMemoryIdentifiers {
813 id: MemoryLocationId(1)
814 }
815 ));
816 }
817
818 #[test]
819 fn test_duplicate_pos_state() {
820 let input_str = r#"sourceinfo #{
821 FILES
822 0: test.calyx
823 POSITIONS
824 0: 0 5
825 1: 0 1
826 2: 0 2
827 MEMORY_LOCATIONS
828 0: main.reg1
829 1: main.reg2
830 2: main.mem1 [1,4]
831 VARIABLE_ASSIGNMENTS
832 0: {
833 x: 0
834 y: 1
835 z: 2
836 }
837 1: {
838 q: 0
839 }
840 POSITION_STATE_MAP
841 0: 0
842 0: 1
843}#"#;
844 let metadata = CalyxParser::parse_source_info_table(input_str).unwrap();
845 assert!(metadata.is_err());
846 let err = metadata.unwrap_err();
847 assert!(matches!(
848 &err,
849 SourceInfoTableError::DuplicatePosStateMappings {
850 id: PositionId(0)
851 }
852 ));
853 }
854
855 #[test]
856 fn test_duplicate_file_parse() {
857 let input_str = r#"sourceinfo #{
858 FILES
859 0: test.calyx
860 0: test2.calyx
861 2: test3.calyx
862 POSITIONS
863 0: 0 5
864 1: 0 1
865 2: 0 2
866 }#"#;
867 let metadata = CalyxParser::parse_source_info_table(input_str).unwrap();
868
869 assert!(metadata.is_err());
870 let err = metadata.unwrap_err();
871 assert!(matches!(&err, SourceInfoTableError::DuplicateFiles { .. }));
872 if let SourceInfoTableError::DuplicateFiles { id1, .. } = &err {
873 assert_eq!(id1, &FileId::new(0))
874 } else {
875 unreachable!()
876 }
877 }
878
879 #[test]
880 fn test_duplicate_position_parse() {
881 let input_str = r#"sourceinfo #{
882 FILES
883 0: test.calyx
884 1: test2.calyx
885 2: test3.calyx
886 POSITIONS
887 0: 0 5
888 0: 0 1
889 2: 0 2
890 }#"#;
891 let metadata = CalyxParser::parse_source_info_table(input_str).unwrap();
892
893 assert!(metadata.is_err());
894 let err = metadata.unwrap_err();
895 assert!(matches!(
896 &err,
897 SourceInfoTableError::DuplicatePositions { .. }
898 ));
899 if let SourceInfoTableError::DuplicatePositions { pos, .. } = err {
900 assert_eq!(pos, PositionId::new(0))
901 } else {
902 unreachable!()
903 }
904 }
905
906 #[test]
907 fn test_serialize() {
908 let mut metadata = SourceInfoTable::new_empty();
909 metadata.add_file(0.into(), "test.calyx".into());
910 metadata.add_file(1.into(), "test2.calyx".into());
911 metadata.add_file(2.into(), "test3.calyx".into());
912
913 metadata.add_position(0.into(), 0.into(), LineNum::new(1));
914 metadata.add_position(1.into(), 1.into(), LineNum::new(2));
915 metadata.add_position(150.into(), 2.into(), LineNum::new(148));
916
917 let mut serialized_str = vec![];
918 metadata.serialize(&mut serialized_str).unwrap();
919 let serialized_str = String::from_utf8(serialized_str).unwrap();
920
921 let parsed_metadata =
922 CalyxParser::parse_source_info_table(&serialized_str)
923 .unwrap()
924 .unwrap();
925
926 assert_eq!(metadata, parsed_metadata)
927 }
928}