feat!(core): Use new AST structure

Derive Expr methods using a derive proc-macro. These macros are present in
the `argus-derive` crate, but the traits are defined in `argus-core`
This commit is contained in:
Anand Balakrishnan 2023-06-06 10:44:45 -04:00
parent 70c5a50d22
commit 1c79847a77
No known key found for this signature in database
22 changed files with 958 additions and 702 deletions

5
argus-derive/src/expr.rs Normal file
View file

@ -0,0 +1,5 @@
mod bool_expr;
mod num_expr;
pub use bool_expr::*;
pub use num_expr::*;

View file

@ -0,0 +1,93 @@
use proc_macro::{self, TokenStream};
use proc_macro2::{Ident, Span};
use quote::{quote, ToTokens};
use syn::DeriveInput;
/// Implement [`IsBoolExpr`](argus_core::expr::traits::IsBoolExpr) and other Boolean
/// operations (`Not`, `BitOr`, and `BitAnd`) for the input identifier.
pub fn bool_expr_impl(input: DeriveInput) -> TokenStream {
let ident = &input.ident;
let marker_impl = quote! {
impl ::argus_core::expr::traits::IsBoolExpr for #ident {}
};
let not_impl = impl_bool_not(&input);
let or_impl = impl_bool_and_or(&input, BoolOp::Or);
let and_impl = impl_bool_and_or(&input, BoolOp::And);
let output = quote! {
#marker_impl
#not_impl
#or_impl
#and_impl
};
output.into()
}
fn impl_bool_not(input: &DeriveInput) -> impl ToTokens {
let ident = &input.ident;
quote! {
impl ::core::ops::Not for #ident {
type Output = ::argus_core::expr::BoolExpr;
#[inline]
fn not(self) -> Self::Output {
(::argus_core::expr::Not { arg: Box::new(self.into()) }).into()
}
}
}
}
#[derive(Debug, Copy, Clone)]
enum BoolOp {
And,
Or,
}
fn impl_bool_and_or(input: &DeriveInput, op: BoolOp) -> impl ToTokens {
let ident = &input.ident;
let (trait_fn, trait_name, enum_id) = match op {
BoolOp::And => {
let trait_name = Ident::new("BitAnd", Span::call_site());
let trait_fn = Ident::new("bitand", Span::call_site());
let enum_id = Ident::new("And", Span::call_site());
(trait_fn, trait_name, enum_id)
}
BoolOp::Or => {
let trait_name = Ident::new("BitOr", Span::call_site());
let trait_fn = Ident::new("bitor", Span::call_site());
let enum_id = Ident::new("Or", Span::call_site());
(trait_fn, trait_name, enum_id)
}
};
quote! {
impl ::core::ops::#trait_name for #ident {
type Output = ::argus_core::expr::BoolExpr;
#[inline]
fn #trait_fn(self, other: Self) -> Self::Output {
use ::argus_core::expr::BoolExpr;
use ::argus_core::expr::#enum_id;
let lhs: BoolExpr = self.into();
let rhs: BoolExpr = other.into();
let expr = match (lhs, rhs) {
(BoolExpr::#enum_id(#enum_id { args: mut left }), BoolExpr::#enum_id(#enum_id { args: mut right })) => {
left.append(&mut right);
#enum_id { args: left }
}
(BoolExpr::#enum_id(#enum_id { mut args }), other) | (other, BoolExpr::#enum_id(#enum_id { mut args })) => {
args.push(other);
#enum_id { args }
}
(left, right) => {
let args = vec![left, right];
#enum_id { args }
}
};
expr.into()
}
}
}
}

View file

@ -0,0 +1,156 @@
use proc_macro::{self, TokenStream};
use proc_macro2::{Ident, Span};
use quote::{quote, ToTokens};
use syn::DeriveInput;
/// Implement [`IsNumExpr`](argus_core::expr::traits::IsNumExpr) and other Numean
/// operations (`Neg`, `Add`, `Mul`, `Sub`, and `Div`) for the input identifier.
pub fn num_expr_impl(input: DeriveInput) -> TokenStream {
let ident = &input.ident;
let marker_impl = quote! {
impl ::argus_core::expr::traits::IsNumExpr for #ident {}
};
let neg_impl = impl_num_neg(&input);
let mul_impl = impl_nary_op(&input, NumOp::Mul);
let add_impl = impl_nary_op(&input, NumOp::Add);
let sub_impl = impl_sub(&input);
let div_impl = impl_div(&input);
let output = quote! {
#marker_impl
#neg_impl
#mul_impl
#add_impl
#sub_impl
#div_impl
};
output.into()
}
fn impl_num_neg(input: &DeriveInput) -> impl ToTokens {
let ident = &input.ident;
quote! {
impl ::core::ops::Neg for #ident {
type Output = ::argus_core::expr::NumExpr;
#[inline]
fn neg(self) -> Self::Output {
(::argus_core::expr::Neg { arg: Box::new(self.into()) }).into()
}
}
}
}
#[derive(Debug, Copy, Clone)]
enum NumOp {
Add,
Mul,
}
impl NumOp {
fn get_trait_name(self) -> Ident {
match self {
NumOp::Add => Ident::new("Add", Span::call_site()),
NumOp::Mul => Ident::new("Mul", Span::call_site()),
}
}
fn get_trait_fn(self) -> Ident {
match self {
NumOp::Add => Ident::new("add", Span::call_site()),
NumOp::Mul => Ident::new("mul", Span::call_site()),
}
}
fn get_expr_name(self) -> Ident {
match self {
NumOp::Add => Ident::new("Add", Span::call_site()),
NumOp::Mul => Ident::new("Mul", Span::call_site()),
}
}
}
fn impl_nary_op(input: &DeriveInput, op: NumOp) -> impl ToTokens {
let ident = &input.ident;
let trait_name = op.get_trait_name();
let trait_fn = op.get_trait_fn();
let node_name = op.get_expr_name();
quote! {
impl<T> ::core::ops::#trait_name<T> for #ident
where
T: ::core::convert::Into<::argus_core::expr::NumExpr>
{
type Output = ::argus_core::expr::NumExpr;
#[inline]
fn #trait_fn(self, other: T) -> Self::Output {
use ::argus_core::expr::NumExpr;
use ::argus_core::expr::#node_name;
let lhs: NumExpr = self.into();
let rhs: NumExpr = other.into();
let expr = match (lhs, rhs) {
(NumExpr::#node_name(#node_name { args: mut left }), NumExpr::#node_name(#node_name { args: mut right })) => {
left.append(&mut right);
#node_name { args: left }
}
(NumExpr::#node_name(#node_name { mut args }), other) | (other, NumExpr::#node_name(#node_name { mut args })) => {
args.push(other);
#node_name { args }
}
(left, right) => {
let args = vec![left, right];
#node_name { args }
}
};
expr.into()
}
}
}
}
fn impl_sub(input: &DeriveInput) -> impl ToTokens {
let ident = &input.ident;
quote! {
impl<T> ::core::ops::Sub<T> for #ident
where
T: ::core::convert::Into<::argus_core::expr::NumExpr>
{
type Output = ::argus_core::expr::NumExpr;
#[inline]
fn sub(self, other: T) -> Self::Output {
use ::argus_core::expr::Sub;
let expr = Sub {
lhs: Box::new(self.into()),
rhs: Box::new(other.into())
};
expr.into()
}
}
}
}
fn impl_div(input: &DeriveInput) -> impl ToTokens {
let ident = &input.ident;
quote! {
impl<T> ::core::ops::Div<T> for #ident
where
T: ::core::convert::Into<::argus_core::expr::NumExpr>
{
type Output = ::argus_core::expr::NumExpr;
#[inline]
fn div(self, other: T) -> Self::Output {
use ::argus_core::expr::Div;
let expr = Div {
dividend: Box::new(self.into()),
divisor: Box::new(other.into())
};
expr.into()
}
}
}
}

18
argus-derive/src/lib.rs Normal file
View file

@ -0,0 +1,18 @@
use proc_macro::{self, TokenStream};
use syn::parse_macro_input;
mod expr;
use expr::{bool_expr_impl, num_expr_impl};
#[proc_macro_derive(BoolExpr)]
pub fn bool_expr(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input);
bool_expr_impl(input)
}
#[proc_macro_derive(NumExpr)]
pub fn num_expr(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input);
num_expr_impl(input)
}