1use std::path::Path;
2
3use crate::{ast, utils::ParserUtils as _};
4
5mod inner;
6pub(crate) use inner::{Parser as InnerParser, Rule};
7
8pub struct Parser;
9
10impl Parser {
11 pub fn parse<P: AsRef<Path>>(path: &P) -> ast::Ast {
12 let ast_raw = Self::preprocess(path).unwrap();
13 ast::Ast::complete(ast_raw)
14 }
15}
16
17#[cfg(test)]
18mod tests {
19 use crate::ast::{HasName, TopDecl};
20 use crate::*;
21 use std::io::Write;
22
23 #[test]
24 fn test_parse_custom_union_id() {
25 let mut schema_file = tempfile::NamedTempFile::new().unwrap();
26 schema_file
27 .write_all(
28 b"
29array a0 [byte;1];
30array a1 [byte;1];
31array a2 [byte;1];
32array a3 [byte;1];
33array a4 [byte;1];
34
35union UnionWithoutCustomId {
36 a0,
37 a1,
38 a2,
39 a3,
40}
41
42union UninoWithFullContinuousCustomIdFrom0 {
43 a0 : 0,
44 a1 : 1,
45 a2 : 2,
46 a3 : 3,
47}
48
49union UninoWithFullContinuousCustomIdFrom5 {
50 a0 : 5,
51 a1 : 6,
52 a2 : 7,
53 a3 : 8,
54}
55
56union UninoWithFullDiscontinuousCustomId {
57 a0 : 2,
58 a1 : 3,
59 a2 : 7,
60 a3 : 8,
61}
62
63union UninoWithPartialCustomId_0 {
64 a0 : 3,
65 a1,
66 a2,
67 a3,
68}
69
70union UninoWithPartialCustomId_1 {
71 a0,
72 a1 : 3,
73 a2,
74 a3,
75}
76
77union UninoWithPartialCustomId_2 {
78 a0 : 3,
79 a1,
80 a2 : 5,
81 a3,
82}
83
84union UninoWithPartialCustomId_Reverse {
85 a0 : 5,
86 a1,
87 a2 : 3,
88 a3,
89}
90
91
92",
93 )
94 .unwrap();
95 schema_file.flush().unwrap();
96
97 let ast = Parser::parse(&schema_file.into_temp_path());
98 ast.decls().iter().for_each(|decl| {
99 if let TopDecl::Union(union) = decl.as_ref() {
100 match union.name() {
101 "UnionWithoutCustomId" => {
102 assert_eq!(union.items().len(), 4);
103 for union_item_decl in union.items() {
104 match union_item_decl.typ().name() {
105 "a0" => assert_eq!(union_item_decl.id(), 0),
106 "a1" => assert_eq!(union_item_decl.id(), 1),
107 "a2" => assert_eq!(union_item_decl.id(), 2),
108 "a3" => assert_eq!(union_item_decl.id(), 3),
109 _ => unreachable!(),
110 }
111 }
112 }
113 "UninoWithFullContinuousCustomIdFrom0" => {
114 assert_eq!(union.items().len(), 4);
115 for union_item_decl in union.items() {
116 match union_item_decl.typ().name() {
117 "a0" => assert_eq!(union_item_decl.id(), 0),
118 "a1" => assert_eq!(union_item_decl.id(), 1),
119 "a2" => assert_eq!(union_item_decl.id(), 2),
120 "a3" => assert_eq!(union_item_decl.id(), 3),
121 _ => unreachable!(),
122 }
123 }
124 }
125 "UninoWithFullContinuousCustomIdFrom5" => {
126 assert_eq!(union.items().len(), 4);
127 for union_item_decl in union.items() {
128 match union_item_decl.typ().name() {
129 "a0" => assert_eq!(union_item_decl.id(), 5),
130 "a1" => assert_eq!(union_item_decl.id(), 6),
131 "a2" => assert_eq!(union_item_decl.id(), 7),
132 "a3" => assert_eq!(union_item_decl.id(), 8),
133 _ => unreachable!(),
134 }
135 }
136 }
137 "UninoWithFullDiscontinuousCustomId" => {
138 assert_eq!(union.items().len(), 4);
139 for union_item_decl in union.items() {
140 match union_item_decl.typ().name() {
141 "a0" => assert_eq!(union_item_decl.id(), 2),
142 "a1" => assert_eq!(union_item_decl.id(), 3),
143 "a2" => assert_eq!(union_item_decl.id(), 7),
144 "a3" => assert_eq!(union_item_decl.id(), 8),
145 _ => unreachable!(),
146 }
147 }
148 }
149 "UninoWithPartialCustomId_0" => {
150 assert_eq!(union.items().len(), 4);
151 for union_item_decl in union.items() {
152 match union_item_decl.typ().name() {
153 "a0" => assert_eq!(union_item_decl.id(), 3),
154 "a1" => assert_eq!(union_item_decl.id(), 4),
155 "a2" => assert_eq!(union_item_decl.id(), 5),
156 "a3" => assert_eq!(union_item_decl.id(), 6),
157 _ => unreachable!(),
158 }
159 }
160 }
161 "UninoWithPartialCustomId_1" => {
162 assert_eq!(union.items().len(), 4);
163 for union_item_decl in union.items() {
164 match union_item_decl.typ().name() {
165 "a0" => assert_eq!(union_item_decl.id(), 0),
166 "a1" => assert_eq!(union_item_decl.id(), 3),
167 "a2" => assert_eq!(union_item_decl.id(), 4),
168 "a3" => assert_eq!(union_item_decl.id(), 5),
169 _ => unreachable!(),
170 }
171 }
172 }
173 "UninoWithPartialCustomId_2" => {
174 assert_eq!(union.items().len(), 4);
175 for union_item_decl in union.items() {
176 match union_item_decl.typ().name() {
177 "a0" => assert_eq!(union_item_decl.id(), 3),
178 "a1" => assert_eq!(union_item_decl.id(), 4),
179 "a2" => assert_eq!(union_item_decl.id(), 5),
180 "a3" => assert_eq!(union_item_decl.id(), 6),
181 _ => unreachable!(),
182 }
183 }
184 }
185 "UninoWithPartialCustomId_Reverse" => {
186 assert_eq!(union.items().len(), 4);
187 for union_item_decl in union.items() {
188 match union_item_decl.typ().name() {
189 "a0" => assert_eq!(union_item_decl.id(), 5),
190 "a1" => assert_eq!(union_item_decl.id(), 6),
191 "a2" => assert_eq!(union_item_decl.id(), 3),
192 "a3" => assert_eq!(union_item_decl.id(), 4),
193 _ => unreachable!(),
194 }
195 }
196 }
197
198 _ => unreachable!(),
199 }
200 }
201 });
202 }
203
204 #[test]
205 fn test_union_items_should_ordered_by_custom_id() {
206 let mut schema_file0 = tempfile::NamedTempFile::new().unwrap();
207 schema_file0
208 .write_all(
209 b"
210array a0 [byte;1];
211array a1 [byte;2];
212array a2 [byte;3];
213array a3 [byte;4];
214union Foo {
215 a0 : 1,
216 a1,
217 a2 : 10,
218 a3,
219}
220",
221 )
222 .unwrap();
223
224 schema_file0.flush().unwrap();
225
226 let mut schema_file1 = tempfile::NamedTempFile::new().unwrap();
227 schema_file1
228 .write_all(
229 b"
230array a0 [byte;1];
231array a1 [byte;2];
232array a2 [byte;3];
233array a3 [byte;4];
234union Foo {
235 a2 : 10,
236 a3,
237 a0 : 1,
238 a1,
239}
240",
241 )
242 .unwrap();
243
244 schema_file1.flush().unwrap();
245
246 let ast0 = Parser::parse(&schema_file0.into_temp_path());
247 let ast1 = Parser::parse(&schema_file1.into_temp_path());
248
249 for ast in [ast0, ast1] {
250 if let TopDecl::Union(union) = ast
252 .decls()
253 .iter()
254 .find(|decl| decl.name() == "Foo")
255 .unwrap()
256 .as_ref()
257 {
258 let custom_ids: Vec<usize> = union.items().iter().map(|item| item.id()).collect();
259 assert_eq!(custom_ids, vec![1, 2, 10, 11]);
260 }
261 }
262 }
263
264 #[should_panic]
265 #[test]
266 fn test_bad_explicit_duplicate_union_schema() {
267 let mut schema_file = tempfile::NamedTempFile::new().unwrap();
268 schema_file
269 .write_all(
270 b"
271array a0 [byte;1];
272array a1 [byte;1];
273array a2 [byte;1];
274array a3 [byte;1];
275union UninoWithPartialDuplicateCustomId {
276 a0,
277 a1 : 3,
278 a2 : 3,
279 a3,
280}
281",
282 )
283 .unwrap();
284
285 schema_file.flush().unwrap();
286
287 let _should_panic = Parser::parse(&schema_file.into_temp_path());
288 }
289
290 #[should_panic]
291 #[test]
292 fn test_bad_implicit_duplicate_union_schema() {
293 let mut schema_file = tempfile::NamedTempFile::new().unwrap();
294 schema_file
295 .write_all(
296 b"
297array a0 [byte;1];
298array a1 [byte;1];
299array a2 [byte;1];
300array a3 [byte;1];
301array a4 [byte;1];
302
303union UninoWithPartialDuplicateCustomIdInReverseOrder {
304 a0 : 10,
305 a1,
306 a2,
307 a3 : 11,
308 a4,
309}
310",
311 )
312 .unwrap();
313
314 schema_file.flush().unwrap();
315
316 let _should_panic = Parser::parse(&schema_file.into_temp_path());
317 }
318}