conjure_cp_essence_macros/lib.rs
1use proc_macro::TokenStream;
2use proc_macro2::{Delimiter, Group, TokenStream as TokenStream2, TokenTree};
3
4mod expand;
5
6use expand::{expand_expr, expand_expr_vec};
7
8/// Parses an Essence expression into its corresponding Conjure AST at compile time.
9///
10/// ## Input
11/// The input can be one of the following:
12/// - The raw Essence tokens (`essence_expr!(2 + 2)`)
13/// - A string literal (`essence_expr!("2 + 2")`)
14///
15/// The macro may reference variables in the current scope (called "metavars")
16/// using the syntax `&<name>`. For example:
17/// ```rust
18/// use conjure_cp_essence_macros::essence_expr;
19/// let x = 42;
20/// let expr = essence_expr!(2 + &x);
21/// ```
22///
23///
24/// ## Expansion
25/// If the input is valid Essence, expands to a valid AST constructor
26///
27/// ## Note
28/// Some characters (e.g. `\`) are valid Essence tokens, but not Rust tokens.
29/// If you encounter an error similar to:
30///
31/// > rustc: unknown start of token: \
32///
33/// The workaround is to wrap the Essence code in a string literal (e.g. `r"a /\ b"`).
34///
35/// ## Example
36///
37/// ```rust
38/// use conjure_cp::ast::{Atom, Expression, Moo, Metadata};
39/// use conjure_cp::matrix_expr;
40/// use conjure_cp_essence_macros::essence_expr;
41/// let x = 42;
42/// let expr = essence_expr!(2 + &x);
43/// assert_eq!(
44/// expr,
45/// Expression::Sum(Metadata::new(), Moo::new(matrix_expr![
46/// Expression::Atomic(Metadata::new(), 2.into()),
47/// Expression::Atomic(Metadata::new(), 42.into())
48/// ]))
49/// );
50/// ```
51#[proc_macro]
52pub fn essence_expr(args: TokenStream) -> TokenStream {
53 let ts = TokenStream2::from(args);
54 let tt = TokenTree::Group(Group::new(Delimiter::None, ts));
55 match expand_expr(&tt) {
56 Ok(tokens) => tokens.into(),
57 Err(err) => err.to_compile_error().into(),
58 }
59}
60
61/// Parses a sequence of Essence expressions into a vector of Conjure AST instances
62///
63/// ## Example
64/// ```rust
65/// use conjure_cp::ast::{Atom, Expression, Moo, Metadata};
66/// use conjure_cp::matrix_expr;
67/// use conjure_cp_essence_macros::essence_vec;
68///
69/// let exprs = essence_vec!(2 + 2, false = true);
70/// println!("{:?}", exprs);
71/// assert_eq!(exprs.len(), 2);
72/// assert_eq!(
73/// exprs[0],
74/// Expression::Sum(Metadata::new(), Moo::new(matrix_expr![
75/// Expression::Atomic(Metadata::new(), 2.into()),
76/// Expression::Atomic(Metadata::new(), 2.into())
77/// ]))
78/// );
79/// assert_eq!(
80/// exprs[1],
81/// Expression::Eq(Metadata::new(),
82/// Moo::new(Expression::Atomic(Metadata::new(), false.into())),
83/// Moo::new(Expression::Atomic(Metadata::new(), true.into()))
84/// )
85/// );
86/// ```
87#[proc_macro]
88pub fn essence_vec(args: TokenStream) -> TokenStream {
89 let ts = TokenStream2::from(args);
90 let tt = TokenTree::Group(Group::new(Delimiter::None, ts));
91 match expand_expr_vec(&tt) {
92 Ok(tokens) => tokens.into(),
93 Err(err) => err.to_compile_error().into(),
94 }
95}