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)*)?) => { ... };
}
Expand description
A macro to easily create a Language
.
define_language
derives Debug
, PartialEq
, Eq
, PartialOrd
, Ord
,
Hash
, and Clone
on the given enum
so it can implement Language
.
The macro also implements Display
and FromOp
for the enum
based on either the data of variants or the provided strings.
The final variant must have a trailing comma; this is due to limitations in macro parsing.
The language discriminant will use the cases of the enum (the enum discriminant).
See LanguageChildren
for acceptable types of children Id
s.
Note that you can always implement Language
yourself by just not using this
macro.
Presently, the macro does not support data variant with children, but that may be added later.
§Example
The following macro invocation shows the the accepted forms of variants:
define_language! {
enum SimpleLanguage {
// string variant with no children
"pi" = Pi,
// string variants with an array of child `Id`s (any static size)
// any type that implements LanguageChildren may be used here
"+" = Add([Id; 2]),
"-" = Sub([Id; 2]),
"*" = Mul([Id; 2]),
// can also do a variable number of children in a boxed slice
// this will only match if the lengths are the same
"list" = List(Box<[Id]>),
// string variants with a single child `Id`
// note that this is distinct from `Sub`, even though it has the same
// string, because it has a different number of children
"-" = Neg(Id),
// data variants with a single field
// this field must implement `FromStr` and `Display`
Num(i32),
// language items are parsed in order, and we want symbol to
// be a fallback, so we put it last
Symbol(Symbol),
// This is the ultimate fallback, it will parse any operator (as a string)
// and any number of children.
// Note that if there were 0 children, the previous branch would have succeeded
Other(Symbol, Vec<Id>),
}
}
It is also possible to define languages that are generic over some bounded type.
You must use the where
-like syntax below to specify the bounds on the type, they cannot go in the enum
definition.
You need at least the following bounds, since they are required by the Language
trait.
§Example
use egg::*;
use std::{
fmt::{Debug, Display},
hash::Hash,
str::FromStr,
};
define_language! {
enum GenericLang<S, T> {
String(S),
Number(T),
"+" = Add([Id; 2]),
"-" = Sub([Id; 2]),
"/" = Div([Id; 2]),
"*" = Mult([Id; 2]),
}
where
S: Hash + Debug + Display + Clone + Eq + Ord + Hash + FromStr,
T: Hash + Debug + Display + Clone + Eq + Ord + Hash + FromStr,
// also required by the macro impl that parses S, T
<S as FromStr>::Err: Debug,
<T as FromStr>::Err: Debug,
}