1pub mod tokens;
2
3#[cfg(test)]
4mod tests {
5 use crate::tokens::{ error::TokenError, prelude::*, tokenizer::Tokenizer };
6
7 type TestResult<T = ()> = Result<T, TokenError>;
8
9 fn main_query(input: &str) -> Result<Token, TokenError> {
10 let tokens = Tokenizer::from_input(input)
11 .map_err(pretty)?
12 .collect::<Result<Vec<Token>, _>>()?;
13 let token = tokens.into_iter().last().unwrap();
14 Ok(token)
15 }
16
17 fn first_main_filter(input: &str) -> Result<Filter, TokenError> {
18 let tokens = Tokenizer::from_input(input)
19 .map_err(pretty)?
20 .collect::<Result<Vec<Token>, _>>()?;
21
22 let filter: Filter = tokens
23 .into_iter()
24 .find_map(|t| {
25 if let Token::Main(query) = t {
26 query.into_iter().find_map(|qt| {
27 match qt {
28 QueryToken::Filter(filter) => { Some(filter) }
29 QueryToken::Molecule(mol) =>
30 mol.into_iter().find_map(|qt| {
31 if let QueryToken::Filter(filter) = qt {
32 Some(filter)
33 } else {
34 None
35 }
36 }),
37 _ => { None }
38 }
39 })
40 } else {
41 None
42 }
43 })
44 .expect("No filter found in main query");
45
46 Ok(filter)
47 }
48
49 macro_rules! assert_main_query_eq {
50 ($input:expr, $expected:expr, debug) => {
51 let actual = main_query($input)?;
52 println!("Resolved Type: {:#?}", actual);
53 println!("Input: {}", $input);
54 println!("Resolved: {actual}");
55 assert_eq!(actual, $expected);
56 };
57 ($input:expr, $expected:expr) => {
58 let actual = main_query($input)?;
59 assert_eq!(actual, $expected);
60 };
61 }
62
63 macro_rules! assert_first_main_filter_eq {
64 ($input:expr, $expected:expr) => {
65 let actual = first_main_filter($input)?;
66 assert_eq!(actual, $expected);
67 };
68 }
69
70 #[inline]
71 pub(crate) fn pretty<D: std::fmt::Display>(err: D) -> D {
72 eprintln!("{err}");
73 err
74 }
75
76 #[test]
95 fn string() -> TestResult {
96 let input = "\"a\"";
97 let expected = Token::Main(vec![QueryToken::Atom(Atom::String(input.to_string()))].into());
98 assert_main_query_eq!(input, expected);
99
100 let input = r#""a.b.c""#;
101 let expected = Token::Main(vec![QueryToken::Atom(Atom::String(input.to_string()))].into());
102 assert_main_query_eq!(input, expected);
103 Ok(())
104 }
105
106 #[test]
107 fn index() -> TestResult {
108 let input = "0";
109 let expected = Token::Main(vec![QueryToken::Atom(Atom::Index(0))].into());
110 assert_main_query_eq!(input, expected);
111
112 let input = "a.0";
113 let expected = Token::Main(
114 vec![
115 QueryToken::Molecule(
116 vec![
117 QueryToken::Atom(Atom::Ident("a".to_string())),
118 QueryToken::Atom(Atom::Index(0))
119 ].into()
120 )
121 ].into()
122 );
123 assert_main_query_eq!(input, expected);
124 Ok(())
125 }
126
127 #[test]
128 fn float_basic() -> TestResult {
129 let expected = Filter(
130 vec![
131 FilterToken::Operation(
132 FilterOperation(vec![FilterOperationToken::Atom(Atom::Float(Float::F64(3.14)))])
133 )
134 ]
135 );
136 let input = "*~{ '3.14_f64 }";
137 assert_first_main_filter_eq!(input, expected);
138 let input = "*~{ '3.14f64 }";
139 assert_first_main_filter_eq!(input, expected);
140
141 let expected = Filter(
142 vec![
143 FilterToken::Operation(
144 FilterOperation(vec![FilterOperationToken::Atom(Atom::Float(Float::F32(3.14)))])
145 )
146 ]
147 );
148 let input = "*~{ '3.14_f32 }";
149 assert_first_main_filter_eq!(input, expected);
150 let input = "*~{ '3.14f32 }";
151 assert_first_main_filter_eq!(input, expected);
152
153 let expected = Filter(
154 vec![
155 FilterToken::Operation(
156 FilterOperation(vec![FilterOperationToken::Atom(Atom::Float(Float::F32(3.0)))])
157 )
158 ]
159 );
160 let input = "*~{ '3.0f32 }";
161 assert_first_main_filter_eq!(input, expected);
162 let input = "*~{ '3.0_f32 }";
163 assert_first_main_filter_eq!(input, expected);
164 Ok(())
165 }
166
167 #[test]
168 fn float_scientific() -> TestResult {
169 let input = "'3.14e2_f64";
171 let expected = Token::Main(
172 vec![
173 QueryToken::Molecule(
174 Molecule(
175 vec![
176 QueryToken::Atom(
177 Atom::Call(LensCall {
178 lens: "3".to_string().into(),
179 args: vec![],
180 })
181 ),
182 QueryToken::Atom(Atom::Ident("14e2_f64".to_string()))
183 ]
184 )
185 )
186 ].into()
187 );
188 assert_main_query_eq!(input, expected);
189
190 let input = "*~{ '3.14e2_f64 }";
192 let expected = Token::Main(
193 vec![
194 QueryToken::Molecule(
195 vec![
196 QueryToken::Atom(Atom::Wild(Wild::Once)),
197 QueryToken::Filter(
198 Filter(
199 vec![
200 FilterToken::Operation(
201 FilterOperation(
202 vec![
203 FilterOperationToken::Atom(
204 Atom::Float(Float::F64(3.14e2))
205 )
206 ]
207 )
208 )
209 ]
210 )
211 )
212 ].into()
213 )
214 ].into()
215 );
216 assert_main_query_eq!(input, expected);
217
218 let input = "*~{ '3.14e+2f64 }";
220 let expected = Token::Main(
221 vec![
222 QueryToken::Molecule(
223 vec![
224 QueryToken::Atom(Atom::Wild(Wild::Once)),
225 QueryToken::Filter(
226 Filter(
227 vec![
228 FilterToken::Operation(
229 FilterOperation(
230 vec![
231 FilterOperationToken::Atom(
232 Atom::Float(Float::F64(3.14e2))
233 )
234 ]
235 )
236 )
237 ]
238 )
239 )
240 ].into()
241 )
242 ].into()
243 );
244 assert_main_query_eq!(input, expected);
245
246 Ok(())
247 }
248
249 #[test]
250 fn float_negated() -> TestResult {
251 let input = "a~{ -'2_f64 }";
252 let expected = Token::Main(
253 vec![
254 QueryToken::Molecule(
255 vec![
256 QueryToken::Atom(Atom::Ident("a".to_string())),
257 QueryToken::Filter(
258 Filter(
259 vec![
260 FilterToken::Operation(
261 FilterOperation(
262 vec![
263 FilterOperationToken::Prefix(FilterPrefix::Neg),
264 FilterOperationToken::Atom(
265 Atom::Float(Float::F64(2.0))
266 )
267 ]
268 )
269 )
270 ]
271 )
272 )
273 ].into()
274 )
275 ].into()
276 );
277
278 assert_main_query_eq!(input, expected);
279
280 Ok(())
281 }
282
283 #[test]
284 fn fail_query_postfix() -> TestResult {
285 let input = "a??";
286 let err = Tokenizer::from_input(input).expect_err("illegal double postfix");
287 assert!(matches!(err, TokenError::Parsing(_)));
288
289 let input = "a?!";
290 let err = Tokenizer::from_input(input).expect_err("illegal double postfix");
291 assert!(matches!(err, TokenError::Parsing(_)));
292
293 let input = "a!a";
294 let err = Tokenizer::from_input(input).expect_err("postfix between atoms, not a query");
295 assert!(matches!(err, TokenError::Parsing(_)));
296
297 let input = "a~{ b }?!";
298 let err = Tokenizer::from_input(input).expect_err("illegal triple postfix combination");
299 assert!(matches!(err, TokenError::Parsing(_)));
300
301 let input = "a~{ b }!?";
302 let err = Tokenizer::from_input(input).expect_err("illegal triple postfix combination");
303 assert!(matches!(err, TokenError::Parsing(_)));
304 Ok(())
305 }
306
307 #[test]
308 fn pass_query_postfix() -> TestResult {
309 let input = "a?.b!.c~{ d }!";
310 let expected = Token::Main(
311 vec![
312 QueryToken::Molecule(
313 vec![
314 QueryToken::Atom(Atom::Ident("a".to_string())),
315 QueryToken::Postfix(Postfix::Optional),
316 QueryToken::Atom(Atom::Ident("b".to_string())),
317 QueryToken::Postfix(Postfix::Assertion),
318 QueryToken::Atom(Atom::Ident("c".to_string())),
319 QueryToken::Filter(
320 vec![
321 FilterToken::Operation(
322 FilterOperation(
323 vec![
324 FilterOperationToken::Atom(Atom::Ident("d".to_string()))
325 ]
326 )
327 )
328 ].into()
329 ),
330 QueryToken::Postfix(Postfix::Assertion)
331 ].into()
332 )
333 ].into()
334 );
335
336 assert_main_query_eq!(input, expected);
337
338 Ok(())
339 }
340
341 #[test]
342 fn query_infix() -> TestResult {
343 let input = "0 | a | b & c | d & (e & f | g) `and` h `and` `foo` 1";
344 let expected = Token::Main(
345 vec![
346 QueryToken::Atom(Atom::Index(0)),
347 QueryToken::Infix(Infix::Union),
348 QueryToken::Atom(Atom::Ident("a".to_string())),
349 QueryToken::Infix(Infix::Union),
350
351 QueryToken::Atom(Atom::Ident("b".to_string())),
352 QueryToken::Infix(Infix::Intersection),
353 QueryToken::Atom(Atom::Ident("c".to_string())),
354
355 QueryToken::Infix(Infix::Union),
356
357 QueryToken::Atom(Atom::Ident("d".to_string())),
358 QueryToken::Infix(Infix::Intersection),
359 QueryToken::Group(
360 vec![
361 QueryToken::Atom(Atom::Ident("e".to_string())),
362 QueryToken::Infix(Infix::Intersection),
363 QueryToken::Atom(Atom::Ident("f".to_string())),
364 QueryToken::Infix(Infix::Union),
365 QueryToken::Atom(Atom::Ident("g".to_string()))
366 ].into()
367 ),
368 QueryToken::Infix(Infix::Lens("and".to_string().into())),
369 QueryToken::Atom(Atom::Ident("h".to_string())),
370 QueryToken::Infix(Infix::Lens("and".to_string().into())),
371 QueryToken::Infix(Infix::Lens("foo".to_string().into())),
372 QueryToken::Atom(Atom::Index(1))
373 ].into()
374 );
375
376 assert_main_query_eq!(input, expected);
377 Ok(())
378 }
379
380 #[test]
381 fn query_def() -> TestResult {
382 let input = "'foo('a, 'b): 'b?.'a(1) | 'b~{ 'c }; 'foo(a, b)";
383 let expected = Token::Definition(Definition {
384 lens: LensIdent("foo".into()),
385 params: vec![LensParam(LensIdent("a".into())), LensParam(LensIdent("b".into()))],
386 body: LensBody::Query(
387 vec![
388 QueryToken::Molecule(
389 vec![
390 QueryToken::Atom(
391 Atom::Call(LensCall {
392 lens: LensIdent("b".to_string()),
393 args: vec![],
394 })
395 ),
396 QueryToken::Postfix(Postfix::Optional),
397 QueryToken::Atom(
398 Atom::Call(LensCall {
399 lens: LensIdent("a".to_string()),
400 args: vec![Token::QueryToken(QueryToken::Atom(Atom::Index(1)))],
401 })
402 )
403 ].into()
404 ),
405 QueryToken::Infix(Infix::Union),
406 QueryToken::Molecule(
407 vec![
408 QueryToken::Atom(
409 Atom::Call(LensCall {
410 lens: LensIdent("b".to_string()),
411 args: vec![],
412 })
413 ),
414 QueryToken::Filter(
415 Filter(
416 vec![
417 FilterToken::Operation(
418 FilterOperation(
419 vec![
420 FilterOperationToken::Implicit(
421 ImplicitFilterToken::Call(LensCall {
422 lens: LensIdent("c".into()),
423 args: vec![],
424 })
425 )
426 ]
427 )
428 )
429 ]
430 )
431 )
432 ].into()
433 )
434 ].into()
435 ),
436 });
437
438 let tok = Tokenizer::from_input(input).map_err(pretty)?;
439 let tokens = tok.collect::<Result<Vec<Token>, _>>()?;
440
441 let def = tokens.into_iter().next().unwrap();
442 assert_eq!(def, expected);
443
444 Ok(())
445 }
446}