1use vihaco_parser::Ident;
7
8#[derive(Debug, Clone, PartialEq)]
13pub struct ParsedModule<I, Ty, H> {
14 pub header: H,
15 pub functions: Vec<ParsedFunction<I, Ty>>,
16}
17
18#[derive(Debug, Clone, PartialEq)]
19pub struct ParsedFunction<I, Ty> {
20 pub name: Ident,
22 pub params: Vec<Param<Ty>>,
24 pub return_types: Vec<Ty>,
26 pub body: Vec<I>,
27 #[cfg(feature = "typed-declarations")]
29 pub labels: Vec<ParsedLabel>,
30}
31
32#[cfg(feature = "typed-declarations")]
34#[derive(Debug, Clone, PartialEq)]
35pub struct ParsedLabel {
36 pub name: Ident,
37 pub instruction_index: usize,
39}
40
41#[derive(Debug, Clone, PartialEq)]
42pub struct Param<Ty> {
43 pub name: Ident,
44 pub ty: Ty,
45}
46
47impl<I, Ty: PartialEq, H> ParsedModule<I, Ty, H> {
48 pub fn validate_metadata<V, Info>(
51 &self,
52 metadata: &vihaco_module::module::ProgramMetadata<V, Ty, Info>,
53 ) -> eyre::Result<()> {
54 validate_functions(&self.functions, metadata)
55 }
56}
57
58pub(crate) fn validate_functions<I, Ty: PartialEq, V, Info>(
60 functions: &[ParsedFunction<I, Ty>],
61 metadata: &vihaco_module::module::ProgramMetadata<V, Ty, Info>,
62) -> eyre::Result<()> {
63 eyre::ensure!(
64 functions.len() == metadata.functions.len(),
65 "function declarations and metadata must contain the same functions"
66 );
67 let mut seen = std::collections::BTreeSet::new();
68 let mut address = 0usize;
69 for function in functions {
70 let name = function.name.as_str();
71 eyre::ensure!(
72 seen.insert(name),
73 "duplicate function declaration `@{name}`"
74 );
75 let mut matches = metadata.functions.iter().filter(|info| {
76 metadata
77 .strings
78 .get(info.name as usize)
79 .is_some_and(|value| value == name)
80 });
81 let info = matches
82 .next()
83 .ok_or_else(|| eyre::eyre!("function `@{name}` has no metadata"))?;
84 eyre::ensure!(
85 matches.next().is_none(),
86 "function `@{name}` has duplicate metadata"
87 );
88 let end = address
89 .checked_add(function.body.len())
90 .ok_or_else(|| eyre::eyre!("instruction count overflow"))?;
91 eyre::ensure!(
92 info.start_address as usize == address && info.end_address as usize == end,
93 "function `@{name}`: supplied range {}..{} disagrees with body range {address}..{end}",
94 info.start_address,
95 info.end_address
96 );
97 eyre::ensure!(
98 function.params.len() == info.signature.params.len(),
99 "function `@{name}`: parameter count disagrees with metadata"
100 );
101 for (parameter, declared) in function.params.iter().zip(&info.signature.params) {
102 eyre::ensure!(
103 metadata
104 .strings
105 .get(declared.name as usize)
106 .is_some_and(|value| value == parameter.name.as_str())
107 && parameter.ty == declared.ty,
108 "function `@{name}`: parameter `{}` disagrees with metadata",
109 parameter.name
110 );
111 }
112 eyre::ensure!(
113 function.return_types == info.signature.ret,
114 "function `@{name}`: return types disagree with metadata"
115 );
116 #[cfg(feature = "typed-declarations")]
117 {
118 let mut labels = std::collections::BTreeSet::new();
119 for label in &function.labels {
120 eyre::ensure!(
121 labels.insert(label.name.as_str()),
122 "function `@{name}`: duplicate label `@{}`",
123 label.name
124 );
125 eyre::ensure!(
126 label.instruction_index <= function.body.len(),
127 "function `@{name}`: label `@{}` is outside its body",
128 label.name
129 );
130 let label_address = address + label.instruction_index;
131 eyre::ensure!(
132 metadata.labels.iter().any(|info| {
133 metadata
134 .strings
135 .get(info.name as usize)
136 .is_some_and(|value| value == label.name.as_str())
137 && info.address as usize == label_address
138 }),
139 "function `@{name}`: label `@{}` at {label_address} requires matching metadata",
140 label.name
141 );
142 }
143 }
144 address = end;
145 }
146 Ok(())
147}