Skip to main content

scalar_expr_macro/
lib.rs

1use proc_macro::TokenStream;
2use unsynn::{
3    AndAnd, DelimitedVec, Either, Equal, Ident, LeftAssocExpr, Literal, NotEqual, OrOr, Parse,
4    ToTokenIter, TrailingDelimiter::Forbidden, quote, unsynn,
5};
6use unsynn::{Colon, Cons, Dollar, LiteralString, ToTokens};
7
8unsynn! {
9    keyword Field = "field";
10    keyword Current = "current";
11    struct ComparisonOp(Either<Equal, NotEqual>);
12    type ComparisonExpr = DelimitedVec<Either<Cons<Dollar, Current>, Cons<Field, Colon, LiteralString>, Literal, Ident>, ComparisonOp, Forbidden, 2, 2>;
13
14    struct CompositeOp(Either<AndAnd, OrOr>);
15    type CompositeExpr = LeftAssocExpr<ComparisonExpr, CompositeOp>;
16
17    type Expr = CompositeExpr;
18}
19
20#[proc_macro]
21pub fn expression(token_stream: TokenStream) -> TokenStream {
22    let expr = Expr::parse(&mut unsynn::TokenStream::from(token_stream).to_token_iter())
23        .unwrap()
24        .0;
25
26    expr.iter().rfold(unsynn::TokenStream::new(), |temp, component| {
27        let ts = component_to_token_stream(&component.value);
28        if let Some(delimiter) = &component.delimiter {
29            let operator = match delimiter.0 {
30                Either::First(_) => quote! {And},
31                Either::Second(_) => quote! {Or},
32                _ => unreachable!(),
33            };
34            // if there's a delimiter here, temp already has the right hand side, easy!
35            quote! {::scalar_expr::Expression::#operator {lhs: Box::new(#ts), rhs: Box::new(#temp)}}
36        } else {
37            ts
38        }
39    }).into()
40}
41
42fn component_to_token_stream(component: &ComparisonExpr) -> unsynn::TokenStream {
43    let operator = match component[0]
44        .delimiter
45        .as_ref()
46        .expect("expected an operator")
47        .0
48    {
49        Either::First(_) => quote! {Equals},
50        Either::Second(_) => quote! {NotEquals},
51        _ => unreachable!(),
52    };
53    let lhs = match &component[0].value {
54        Either::First(_) => quote! {::scalar_expr::Value::CurrentField},
55        Either::Second(Cons { third, .. }) => {
56            quote! {::scalar_expr::Value::Ident(#third)}
57        }
58        Either::Third(val) => {
59            quote! {::scalar_expr::Value::Value(::scalar_expr::to_value(#val).unwrap())}
60        }
61        Either::Fourth(val) => {
62            quote! {::scalar_expr::Value::Value(::scalar_expr::to_value(#val).unwrap())}
63        }
64    };
65    let rhs = match &component[1].value {
66        Either::First(_) => quote! {::scalar_expr::Value::CurrentField},
67        Either::Second(Cons { third, .. }) => {
68            quote! {::scalar_expr::Value::Ident(#third)}
69        }
70        Either::Third(val) => {
71            quote! {::scalar_expr::Value::Value(::scalar_expr::to_value(#val).unwrap())}
72        }
73        Either::Fourth(val) => {
74            quote! {::scalar_expr::Value::Value(::scalar_expr::to_value(#val).unwrap())}
75        }
76    };
77    quote! {::scalar_expr::Expression::#operator {lhs: #lhs, rhs: #rhs}}
78}