#[allow(unused_imports)]
use crate::*;
#[macro_export]
macro_rules! define_language {
($(#[$meta:meta])* $vis:vis enum $name:ident
// annoying parsing hack to parse generic bounds https://stackoverflow.com/a/51580104
// $(<$gen:ident $(, $($gen2:ident),*)?>)?
$(<$($gen:ident),*>)?
{ $($variants:tt)* }
$(where $($where:tt)*)?) => {
$crate::__define_language!(
$(#[$meta])* $vis enum $name [$($($gen),*)?] { $($variants)* }
[$($($where)*)?]
-> {} {} {} {} {} {}
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __define_language {
($(#[$meta:meta])* $vis:vis enum $name:ident [$($gen:ident),*] {}
[$($where:tt)*]
->
$decl:tt {$($matches:tt)*} $children:tt $children_mut:tt
$display:tt {$($from_op:tt)*}
) => {
$(#[$meta])*
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
$vis enum $name <$($gen),*> $decl
impl<$($gen),*> $crate::Language for $name <$($gen),*> where $($where)* {
type Discriminant = std::mem::Discriminant<Self>;
#[inline(always)]
fn discriminant(&self) -> Self::Discriminant {
std::mem::discriminant(self)
}
#[inline(always)]
fn matches(&self, other: &Self) -> bool {
::std::mem::discriminant(self) == ::std::mem::discriminant(other) &&
match (self, other) { $($matches)* _ => false }
}
fn children(&self) -> &[$crate::Id] { match self $children }
fn children_mut(&mut self) -> &mut [$crate::Id] { match self $children_mut }
}
impl<$($gen),*> ::std::fmt::Display for $name <$($gen),*> where $($where)* {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match (self, f) $display
}
}
impl<$($gen),*> $crate::FromOp for $name <$($gen),*> where $($where)* {
type Error = $crate::FromOpError;
fn from_op(op: &str, children: ::std::vec::Vec<$crate::Id>) -> ::std::result::Result<Self, Self::Error> {
match (op, children) {
$($from_op)*
(op, children) => Err($crate::FromOpError::new(op, children)),
}
}
}
};
($(#[$meta:meta])* $vis:vis enum $name:ident [$($gen:ident),*]
{
$string:literal = $variant:ident,
$($variants:tt)*
}
[$($where:tt)*]
->
{ $($decl:tt)* } { $($matches:tt)* } { $($children:tt)* } { $($children_mut:tt)* }
{ $($display:tt)* } { $($from_op:tt)* }
) => {
$crate::__define_language!(
$(#[$meta])* $vis enum $name [$($gen),*]
{ $($variants)* }
[$($where)*]
->
{ $($decl)* $variant, }
{ $($matches)* ($name::$variant, $name::$variant) => true, }
{ $($children)* $name::$variant => &[], }
{ $($children_mut)* $name::$variant => &mut [], }
{ $($display)* ($name::$variant, f) => f.write_str($string), }
{ $($from_op)* ($string, children) if children.is_empty() => Ok($name::$variant), }
);
};
($(#[$meta:meta])* $vis:vis enum $name:ident [$($gen:ident),*]
{
$string:literal = $variant:ident ($ids:ty),
$($variants:tt)*
}
[$($where:tt)*]
->
{ $($decl:tt)* } { $($matches:tt)* } { $($children:tt)* } { $($children_mut:tt)* }
{ $($display:tt)* } { $($from_op:tt)* }
) => {
$crate::__define_language!(
$(#[$meta])* $vis enum $name [$($gen),*]
{ $($variants)* }
[$($where)*]
->
{ $($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)* ($name::$variant(..), f) => f.write_str($string), }
{ $($from_op)* (op, children) if op == $string && <$ids as $crate::LanguageChildren>::can_be_length(children.len()) => {
let children = <$ids as $crate::LanguageChildren>::from_vec(children);
Ok($name::$variant(children))
},
}
);
};
($(#[$meta:meta])* $vis:vis enum $name:ident [$($gen:ident),*]
{
$variant:ident ($data:ty),
$($variants:tt)*
}
[$($where:tt)*]
->
{ $($decl:tt)* } { $($matches:tt)* } { $($children:tt)* } { $($children_mut:tt)* }
{ $($display:tt)* } { $($from_op:tt)* }
) => {
$crate::__define_language!(
$(#[$meta])* $vis enum $name [$($gen),*]
{ $($variants)* }
[$($where)*]
->
{ $($decl)* $variant($data), }
{ $($matches)* ($name::$variant(data1), $name::$variant(data2)) => data1 == data2, }
{ $($children)* $name::$variant(_data) => &[], }
{ $($children_mut)* $name::$variant(_data) => &mut [], }
{ $($display)* ($name::$variant(data), f) => ::std::fmt::Display::fmt(data, f), }
{ $($from_op)* (op, children) if op.parse::<$data>().is_ok() && children.is_empty() => Ok($name::$variant(op.parse().unwrap())), }
);
};
($(#[$meta:meta])* $vis:vis enum $name:ident [$($gen:ident)*]
{
$variant:ident ($data:ty, $ids:ty),
$($variants:tt)*
}
[$($where:tt)*]
->
{ $($decl:tt)* } { $($matches:tt)* } { $($children:tt)* } { $($children_mut:tt)* }
{ $($display:tt)* } { $($from_op:tt)* }
) => {
$crate::__define_language!(
$(#[$meta])* $vis enum $name [$($gen)*]
{ $($variants)* }
[$($where)*]
->
{ $($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)* ($name::$variant(data, _), f) => ::std::fmt::Display::fmt(data, f), }
{ $($from_op)* (op, children) if op.parse::<$data>().is_ok() && <$ids as $crate::LanguageChildren>::can_be_length(children.len()) => {
let data = op.parse::<$data>().unwrap();
let children = <$ids as $crate::LanguageChildren>::from_vec(children);
Ok($name::$variant(data, children))
},
}
);
};
}
#[macro_export]
macro_rules! rewrite {
(
$name:expr;
$lhs:tt => $rhs:tt
$(if $cond:expr)*
) => {{
let searcher = $crate::__rewrite!(@parse Pattern $lhs);
let core_applier = $crate::__rewrite!(@parse Pattern $rhs);
let applier = $crate::__rewrite!(@applier core_applier; $($cond,)*);
$crate::Rewrite::new($name.to_string(), 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)*)
]
}};
}
#[macro_export]
macro_rules! multi_rewrite {
(
$name:expr;
$lhs:tt => $rhs:tt
) => {{
let searcher = $crate::__rewrite!(@parse MultiPattern $lhs);
let applier = $crate::__rewrite!(@parse MultiPattern $rhs);
$crate::Rewrite::new($name.to_string(), searcher, applier).unwrap()
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __rewrite {
(@parse $t:ident $rhs:literal) => {
$rhs.parse::<$crate::$t<_>>().unwrap()
};
(@parse $t:ident $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::new(x.clone(), x)
);
}
}