#[macro_export]
macro_rules! define_language {
($(#[$meta:meta])* $vis:vis enum $name:ident $variants:tt) => {
$crate::__define_language!($(#[$meta])* $vis enum $name $variants -> {} {} {} {} {} {});
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __define_language {
($(#[$meta:meta])* $vis:vis enum $name:ident {} ->
$decl:tt {$($matches:tt)*} $children:tt $children_mut:tt
$display_op:tt {$($from_op_str:tt)*}
) => {
$(#[$meta])*
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
$vis enum $name $decl
impl $crate::Language for $name {
#[inline(always)]
fn matches(&self, other: &Self) -> bool {
::std::mem::discriminant(self) == ::std::mem::discriminant(other) &&
match (self, other) { $($matches)* _ => false }
}
#[inline]
fn children(&self) -> &[Id] {
match self $children
}
#[inline]
fn children_mut(&mut self) -> &mut [Id] {
match self $children_mut
}
fn display_op(&self) -> &dyn ::std::fmt::Display {
match self $display_op
}
fn from_op_str(op_str: &str, children: Vec<$crate::Id>) -> ::std::result::Result<Self, String> {
match (op_str, children) {
$($from_op_str)*
(s, c) => Err(::std::format!("Failed to parse '{}' with children {:?}", s, c)),
}
}
}
};
($(#[$meta:meta])* $vis:vis enum $name:ident
{
$string:literal = $variant:ident,
$($variants:tt)*
} ->
{ $($decl:tt)* } { $($matches:tt)* } { $($children:tt)* } { $($children_mut:tt)* }
{ $($display_op:tt)* } { $($from_op_str:tt)* }
) => {
$crate::__define_language!(
$(#[$meta])* $vis enum $name
{ $($variants)* } ->
{ $($decl)* $variant, }
{ $($matches)* ($name::$variant, $name::$variant) => true, }
{ $($children)* $name::$variant => &[], }
{ $($children_mut)* $name::$variant => &mut [], }
{ $($display_op)* $name::$variant => &$string, }
{ $($from_op_str)* ($string, v) if v.is_empty() => Ok($name::$variant), }
);
};
($(#[$meta:meta])* $vis:vis enum $name:ident
{
$string:literal = $variant:ident ($ids:ty),
$($variants:tt)*
} ->
{ $($decl:tt)* } { $($matches:tt)* } { $($children:tt)* } { $($children_mut:tt)* }
{ $($display_op:tt)* } { $($from_op_str:tt)* }
) => {
$crate::__define_language!(
$(#[$meta])* $vis enum $name
{ $($variants)* } ->
{ $($decl)* $variant($ids), }
{ $($matches)* ($name::$variant(l), $name::$variant(r)) => $crate::LanguageChildren::len(l) == $crate::LanguageChildren::len(r), }
{ $($children)* $name::$variant(ids) => $crate::LanguageChildren::as_slice(ids), }
{ $($children_mut)* $name::$variant(ids) => $crate::LanguageChildren::as_mut_slice(ids), }
{ $($display_op)* $name::$variant(..) => &$string, }
{ $($from_op_str)* (s, v) if s == $string && <$ids as $crate::LanguageChildren>::can_be_length(v.len()) => {
let ids = <$ids as $crate::LanguageChildren>::from_vec(v);
Ok($name::$variant(ids))
}, }
);
};
($(#[$meta:meta])* $vis:vis enum $name:ident
{
$variant:ident ($data:ty),
$($variants:tt)*
} ->
{ $($decl:tt)* } { $($matches:tt)* } { $($children:tt)* } { $($children_mut:tt)* }
{ $($display_op:tt)* } { $($from_op_str:tt)* }
) => {
$crate::__define_language!(
$(#[$meta])* $vis enum $name
{ $($variants)* } ->
{ $($decl)* $variant($data), }
{ $($matches)* ($name::$variant(data1), $name::$variant(data2)) => data1 == data2, }
{ $($children)* $name::$variant(_data) => &[], }
{ $($children_mut)* $name::$variant(_data) => &mut [], }
{ $($display_op)* $name::$variant(data) => data, }
{ $($from_op_str)* (s, v) if s.parse::<$data>().is_ok() && v.is_empty() => Ok($name::$variant(s.parse().unwrap())), }
);
};
($(#[$meta:meta])* $vis:vis enum $name:ident
{
$variant:ident ($data:ty, $ids:ty),
$($variants:tt)*
} ->
{ $($decl:tt)* } { $($matches:tt)* } { $($children:tt)* } { $($children_mut:tt)* }
{ $($display_op:tt)* } { $($from_op_str:tt)* }
) => {
$crate::__define_language!(
$(#[$meta])* $vis enum $name
{ $($variants)* } ->
{ $($decl)* $variant($data, $ids), }
{ $($matches)* ($name::$variant(d1, l), $name::$variant(d2, r)) => d1 == d2 && $crate::LanguageChildren::len(l) == $crate::LanguageChildren::len(r), }
{ $($children)* $name::$variant(_, ids) => $crate::LanguageChildren::as_slice(ids), }
{ $($children_mut)* $name::$variant(_, ids) => $crate::LanguageChildren::as_mut_slice(ids), }
{ $($display_op)* $name::$variant(data, _) => data, }
{ $($from_op_str)* (s, v) if s.parse::<$data>().is_ok() && <$ids as $crate::LanguageChildren>::can_be_length(v.len()) => {
let data = s.parse::<$data>().unwrap();
let ids = <$ids as $crate::LanguageChildren>::from_vec(v);
Ok($name::$variant(data, ids))
}, }
);
};
}
#[macro_export]
macro_rules! rewrite {
(
$name:expr;
$lhs:tt => $rhs:tt
$(if $cond:expr)*
) => {{
let long_name = format!("{} => {}", stringify!($lhs), stringify!($rhs));
let searcher = $crate::__rewrite!(@parse $lhs);
let core_applier = $crate::__rewrite!(@parse $rhs);
let applier = $crate::__rewrite!(@applier core_applier; $($cond,)*);
$crate::Rewrite::new($name, long_name, searcher, applier).unwrap()
}};
(
$name:expr;
$lhs:tt <=> $rhs:tt
$(if $cond:expr)*
) => {{
let name = $name;
let name2 = String::from(name.clone()) + "-rev";
vec![
$crate::rewrite!(name; $lhs => $rhs $(if $cond)*),
$crate::rewrite!(name2; $rhs => $lhs $(if $cond)*)
]
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __rewrite {
(@parse $rhs:literal) => {
$rhs.parse::<$crate::Pattern<_>>().unwrap()
};
(@parse $rhs:expr) => { $rhs };
(@applier $applier:expr;) => { $applier };
(@applier $applier:expr; $cond:expr, $($conds:expr,)*) => {
$crate::ConditionalApplier {
condition: $cond,
applier: $crate::__rewrite!(@applier $applier; $($conds,)*)
}
};
}
#[cfg(test)]
mod tests {
use crate::*;
define_language! {
enum Simple {
"+" = Add([Id; 2]),
"-" = Sub([Id; 2]),
"*" = Mul([Id; 2]),
"-" = Neg(Id),
"list" = List(Box<[Id]>),
"pi" = Pi,
Int(i32),
Var(Symbol),
}
}
#[test]
fn modify_children() {
let mut add = Simple::Add([0.into(), 0.into()]);
add.for_each_mut(|id| *id = 1.into());
assert_eq!(add, Simple::Add([1.into(), 1.into()]));
}
#[test]
fn some_rewrites() {
let mut rws: Vec<Rewrite<Simple, ()>> = vec![
rewrite!("rule"; "cons" => "f"),
rewrite!("rule"; "f" => { "pat".parse::<Pattern<_>>().unwrap() }),
];
rws.extend(rewrite!("two-way"; "foo" <=> "bar"));
}
#[test]
#[should_panic(expected = "refers to unbound var ?x")]
fn rewrite_simple_panic() {
let _: Rewrite<Simple, ()> = rewrite!("bad"; "?a" => "?x");
}
#[test]
#[should_panic(expected = "refers to unbound var ?x")]
fn rewrite_conditional_panic() {
let x: Pattern<Simple> = "?x".parse().unwrap();
let _: Rewrite<Simple, ()> = rewrite!(
"bad"; "?a" => "?a" if ConditionEqual(x.clone(), x)
);
}
}