#[macro_export]
macro_rules! grammar {
( @assoc < $( $ts:tt )* ) => (true);
( @assoc $( $ts:tt )* ) => (false);
( @conv $atomic:tt $slf:ident [ < $( $ts:tt )* ] [] [] ) => {
grammar!(@conv $atomic $slf [ $( $ts )* ] [] [])
};
( @conv_prec $pos:ident ($_prec:expr) $_atomic:tt $slf:ident [] [] [] ) => {
{
$slf.input_mut().set_pos($pos);
None
}
};
( @conv_prec $pos:ident ($prec:expr) $atomic:tt $slf:ident
[ $name:ident = { $( $head:tt )* } $( $tail:tt )* ] [] [] ) => {
{
if grammar!(@conv $atomic $slf [ $( $head )* ] [] []) {
return Some((Some(Rule::$name), $prec, grammar!(@assoc $( $head )*)))
} else {
grammar!(@conv_prec $pos ($prec + 1) $atomic $slf [ $( $tail )* ] [] [])
}
}
};
( @conv_prec $pos:ident ($prec:expr) $atomic:tt $slf:ident
[ $name:ident = @{ $( $head:tt )* } $( $tail:tt )* ] [] [] ) => {
{
if grammar!(@conv true $slf [ $( $head )* ] [] []) {
return Some((Some(Rule::$name), $prec, grammar!(@assoc $( $head )*)))
} else {
grammar!(@conv_prec $pos ($prec + 1) $atomic $slf [ $( $tail )* ] [] [])
}
}
};
( @conv_prec $pos:ident ($prec:expr) $atomic:tt $slf:ident
[ $name:ident = _{ $( $head:tt )* } $( $tail:tt )* ] [] [] ) => {
{
if grammar!(@conv $atomic $slf [ $( $head )* ] [] []) {
return Some((None, $prec, grammar!(@assoc $( $head )*)))
} else {
grammar!(@conv_prec $pos ($prec + 1) $atomic $slf [ $( $tail )* ] [] [])
}
}
};
( @conv $atomic:tt $slf:ident [ { $( $primary:tt )* } $( $ts:tt )* ] [] [] ) => {
{
let mut primary = |slf: &mut Self| {
let pos = slf.input().pos();
grammar!(@skip $atomic slf);
let result = grammar!(@conv $atomic slf [ $( $primary )* ] [] []);
if !result {
slf.input_mut().set_pos(pos);
}
result
};
let mut climb = |slf: &mut Self| {
let pos = slf.input().pos();
grammar!(@skip $atomic slf);
grammar!(@conv_prec pos (0u8) $atomic slf [ $( $ts )* ] [] [])
};
let mut pos = $slf.input().pos();
let queue_pos = $slf.queue().len();
let result = primary($slf);
if let Some(token) = $slf.queue().get(queue_pos) {
pos = token.start;
}
if result {
$slf.prec_climb(queue_pos, pos, 0, None, &mut primary, &mut climb);
}
result
}
};
( @conv $atomic:tt $slf:ident [ ( $( $head:tt )* ) $( $tail:tt )* ] [ $( $optail:tt )* ]
[ $( $output:tt )* ] ) => {
grammar!(@conv $atomic $slf [ $( $head )* _rp $( $tail )* ] [ _lp $( $optail )* ]
[ $( $output )* ])
};
( @conv $atomic:tt $slf:ident [ _rp $( $tail:tt )* ] [ _lp $( $optail:tt )* ]
[ $( $output:tt )* ] ) => {
grammar!(@conv $atomic $slf [ $( $tail )* ] [ $( $optail )* ] [ $( $output )* ])
};
( @conv $atomic:tt $slf:ident [ _rp $( $tail:tt )* ] [ $op:tt $( $optail:tt )* ]
[ $( $output:tt )* ] ) => {
grammar!(@conv $atomic $slf [ _rp $( $tail )* ] [ $( $optail )* ] [ $( $output )* $op ])
};
( @conv $atomic:tt $slf:ident [ & $head:tt $( $tail:tt )* ] [ $( $optail:tt )* ]
$output:tt ) => {
grammar!(@conv $atomic $slf [ $head _pres $( $tail )* ] [ $( $optail )* ] $output)
};
( @conv $atomic:tt $slf:ident [ ! $head:tt $( $tail:tt )* ] [ $( $optail:tt )* ]
$output:tt ) => {
grammar!(@conv $atomic $slf [ $head _abs $( $tail )* ] [ $( $optail )* ] $output)
};
( @conv $atomic:tt $slf:ident [ ~ $( $tail:tt )* ] [ ~ $( $optail:tt )* ]
[ $( $output:tt )* ] ) => {
grammar!(@conv $atomic $slf [ ~ $( $tail )* ] [ $( $optail )* ] [ $( $output )* ~ ])
};
( @conv $atomic:tt $slf:ident [ ~ $( $tail:tt )* ] [ $( $optail:tt )* ] $output:tt) => {
grammar!(@conv $atomic $slf [ $( $tail )* ] [ ~ $( $optail )* ] $output)
};
( @conv $atomic:tt $slf:ident [ | $( $tail:tt )* ] [ ~ $( $optail:tt )* ]
[ $( $output:tt )* ] ) => {
grammar!(@conv $atomic $slf [ | $( $tail )* ] [ $( $optail )* ] [ $( $output )* ~ ])
};
( @conv $atomic:tt $slf:ident [ | $( $tail:tt )* ] [ | $( $optail:tt )* ]
[ $( $output:tt )* ] ) => {
grammar!(@conv $atomic $slf [ | $( $tail )* ] [ $( $optail )* ] [ $( $output )* | ])
};
( @conv $atomic:tt $slf:ident [ | $( $tail:tt )* ] [ $( $optail:tt )* ] $output:tt) => {
grammar!(@conv $atomic $slf [ $( $tail )* ] [ | $( $optail )* ] $output)
};
( @conv $atomic:tt $slf:ident [ $head:tt $( $tail:tt )* ] $ops:tt [ $( $output:tt )* ] ) => {
grammar!(@conv $atomic $slf [ $( $tail )* ] $ops [ $( $output )* $head ])
};
( @conv $atomic:tt $slf:ident [] [] [ $( $output:tt )* ] ) => {
grammar!(@process $atomic $slf [] [ $( $output )* ])
};
( @conv $atomic:tt $slf:ident [] [ $op:tt $( $optail:tt )* ] [ $( $output:tt )* ] ) => {
grammar!(@conv $atomic $slf [] [ $( $optail )* ] [ $( $output )* $op ])
};
( @mtc $slf:ident (( $exp:expr )) ) => (($exp));
( @mtc $slf:ident [ $left:tt .. $right:tt ]) => (grammar!(@mtc $slf [$left, $right]));
( @mtc $slf:ident [ $left:expr, $right:expr ]) => {
$slf.input_mut().match_range($left, $right)
};
( @mtc $slf:ident [ $str:expr ]) => ($slf.input_mut().match_string($str));
( @mtc $slf:ident $rule:ident) => ($slf.$rule());
( @process $_atomic:tt $_slf:ident [( $result:expr )] [] ) => ($result);
( @process false $slf:ident [ $b:tt $a:tt $( $tail:tt )* ] [ ~ $( $optail:tt )* ] ) => {
{
grammar!(@process false $slf [(( $slf.try(false, |$slf| {
if grammar!(@mtc $slf $a) {
let original = $slf.input().pos();
$slf.skip_ws();
let pos = $slf.input().pos();
let len = $slf.queue().len();
let result = grammar!(@mtc $slf $b);
if $slf.input().pos() == pos && !$slf.eoi_matched() {
$slf.input_mut().set_pos(original);
}
result
} else {
false
}
}) )) $( $tail )* ] [ $( $optail )* ])
}
};
( @process true $slf:ident [ $b:tt $a:tt $( $tail:tt )* ] [ ~ $( $optail:tt )* ] ) => {
{
grammar!(@process true $slf [(( $slf.try(false, |$slf| {
grammar!(@mtc $slf $a) && grammar!(@mtc $slf $b)
}) )) $( $tail )* ] [ $( $optail )* ])
}
};
( @process $atomic:tt $slf:ident [ $b:tt $a:tt $( $tail:tt )* ] [ | $( $optail:tt )* ] ) => {
{
grammar!(@process $atomic $slf [((
grammar!(@mtc $slf $a) || grammar!(@mtc $slf $b)
)) $( $tail )* ] [ $( $optail )* ])
}
};
( @process false $slf:ident [ $a:tt $( $tail:tt )* ] [ * $( $optail:tt )* ] ) => {
{
grammar!(@process false $slf [(( {
let mut pos = $slf.input().pos();
loop {
if !grammar!(@mtc $slf $a) {
$slf.input_mut().set_pos(pos);
break
}
pos = $slf.input().pos();
$slf.skip_ws();
}
true
} )) $( $tail )* ] [ $( $optail )* ])
}
};
( @process true $slf:ident [ $a:tt $( $tail:tt )* ] [ * $( $optail:tt )* ] ) => {
{
grammar!(@process true $slf [(( {
loop {
if !grammar!(@mtc $slf $a) {
break
}
}
true
} )) $( $tail )* ] [ $( $optail )* ])
}
};
( @process false $slf:ident [ $a:tt $( $tail:tt )* ] [ + $( $optail:tt )* ] ) => {
{
grammar!(@process false $slf [(( if grammar!(@mtc $slf $a) {
loop {
let pos = $slf.input().pos();
$slf.skip_ws();
if !grammar!(@mtc $slf $a) {
$slf.input_mut().set_pos(pos);
break
}
}
true
} else {
false
} )) $( $tail )* ] [ $( $optail )* ])
}
};
( @process true $slf:ident [ $a:tt $( $tail:tt )* ] [ + $( $optail:tt )* ] ) => {
{
grammar!(@process true $slf [(( if grammar!(@mtc $slf $a) {
loop {
if !grammar!(@mtc $slf $a) {
break
}
}
true
} else {
false
} )) $( $tail )* ] [ $( $optail )* ])
}
};
( @process $atomic:tt $slf:ident [ $a:tt $( $tail:tt )* ] [ ? $( $optail:tt )* ] ) => {
{
grammar!(@process $atomic $slf [(( {
grammar!(@mtc $slf $a);
true
} )) $( $tail )* ] [ $( $optail )* ])
}
};
( @process $atomic:tt $slf:ident [ $a:tt $( $tail:tt )* ] [ _pres $( $optail:tt )* ] ) => {
{
grammar!(@process $atomic $slf [(( $slf.try(true, |$slf| {
grammar!(@mtc $slf $a)
}) )) $( $tail )* ] [ $( $optail )* ])
}
};
( @process $atomic:tt $slf:ident [ $a:tt $( $tail:tt )* ] [ _abs $( $optail:tt )* ] ) => {
{
grammar!(@process $atomic $slf [(( $slf.try(true, |$slf| {
!grammar!(@mtc $slf $a)
}) )) $( $tail )* ] [ $( $optail )* ])
}
};
( @process $_atomic:tt $slf:ident [] [ $single:tt ] ) => {
grammar!(@mtc $slf $single)
};
( @process $atomic:tt $slf:ident [ $( $optail:tt )* ] [ $head:tt $( $tail:tt )* ] ) => {
grammar!(@process $atomic $slf [ $head $( $optail )* ] [ $( $tail )* ])
};
( @skip whitespace $_slf:ident ) => ();
( @skip comment $slf:ident ) => ($slf.skip_ws());
( @skip $_name:ident $slf:ident ) => {
{
$slf.skip_com();
$slf.skip_ws();
}
};
( @skip false $slf:ident ) => ();
( @skip true $slf:ident ) => {
{
$slf.skip_com();
$slf.skip_ws();
}
};
( @atomic whitespace $_atomic:tt $slf:ident $rules:tt ) => {
grammar!(@conv true $slf $rules [] [])
};
( @atomic comment $_atomic:tt $slf:ident $rules:tt ) => {
grammar!(@conv true $slf $rules [] [])
};
( @atomic $_name:ident $atomic:tt $slf:ident $rules:tt ) => {
grammar!(@conv $atomic $slf $rules [] [])
};
() => ();
( $name:ident = { $( $ts:tt )* } $( $tail:tt )* ) => {
#[allow(unused_parens, unused_variables)]
#[inline]
pub fn $name(&mut self) -> bool {
let slf = self;
grammar!(@skip $name slf);
let pos = slf.input().pos();
let len = slf.queue().len();
let tracked_len = slf.tracked_len();
let result = grammar!(@atomic $name false slf [ $( $ts )* ]);
if result {
let new_pos = slf.input().pos();
let token = Token {
rule: Rule::$name,
start: pos,
end: new_pos
};
slf.queue_mut().insert(len, token);
} else {
slf.queue_mut().truncate(len);
if slf.tracked_len() == tracked_len {
slf.track(Rule::$name, pos);
}
}
result
}
grammar!($( $tail )*);
};
( $name:ident = @{ $( $ts:tt )* } $( $tail:tt )* ) => {
#[allow(unused_parens, unused_variables)]
#[inline]
pub fn $name(&mut self) -> bool {
let slf = self;
grammar!(@skip $name slf);
let pos = slf.input().pos();
let len = slf.queue().len();
let toggled = slf.is_atomic();
if !toggled {
slf.set_atomic(true);
}
let result = grammar!(@conv true slf [ $( $ts )* ] [] []);
if !toggled {
slf.set_atomic(false);
}
if result {
let new_pos = slf.input().pos();
let token = Token {
rule: Rule::$name,
start: pos,
end: new_pos
};
slf.queue_mut().insert(len, token);
} else {
slf.queue_mut().truncate(len);
slf.track(Rule::$name, pos);
}
result
}
grammar!($( $tail )*);
};
( $name:ident = _{ $( $ts:tt )* } $( $tail:tt )* ) => {
#[allow(unused_parens, unused_variables)]
#[inline]
pub fn $name(&mut self) -> bool {
let slf = self;
grammar!(@skip $name slf);
let pos = slf.input().pos();
let result = grammar!(@atomic $name false slf [ $( $ts )* ]);
result
}
grammar!($( $tail )*);
};
}