#![expect(clippy::result_large_err, reason="unsynn!{..} causes it so i have no idea how i would fix it.")]
pub(crate) mod rust_analyzer_friendly;
use unsynn::*;
use std::{fmt::Display, result::Result};
unsynn!{
pub(crate) keyword KBefore = "before";
pub(crate) keyword KAfter = "after";
keyword KIn = "in";
keyword KBiased = "biased";
keyword KUnbiased = "unbiased";
keyword KFootgun= "footgun";
keyword KIf = "if";
pub(crate) enum Block {
Braced {
tokens: BraceGroupContaining<Delimited<Vec<TokenTree>, Semicolon>>,
_seperator: Option<Comma>,
},
Expr {
_except: Except<Either<Comma, EndOfStream>>,
tokens: LazyVec<TokenTree, Either<Comma, EndOfStream>>,
},
}
pub(crate) struct Arm<P: Parser> {
pattern: P,
_arrow: FatArrow,
block: Block,
}
pub(crate) struct FuturePattern {
_except_1: Except<KIn>,
pattern: LazyVec<TokenTree, KIn>,
condition: Option<Cons<KIf, Except<Comma>, LazyVec<TokenTree, Comma>>>,
footgun: Option<KFootgun>,
_except_2: Except<FatArrow>,
expr: LazyVecUntil<TokenTree, FatArrow>,
}
pub(crate) enum AnyArm {
Before(Arm<KBefore>),
After(Arm<KAfter>),
Future(Arm<FuturePattern>),
}
pub(crate) struct UnvalidatedAst {
biasedness: Either<KBiased, KUnbiased>,
arms: Vec<AnyArm>,
}
}
impl<P: Parse> Arm<P> {
pub(crate) fn pattern(&self) -> &P {
&self.pattern
}
pub(crate) fn block(&self) -> TokenStream {
match &self.block {
Block::Braced { tokens, _seperator } => tokens.content.to_token_stream(),
Block::Expr { _except, tokens } => tokens.vec.to_token_stream(),
}
}
}
impl FuturePattern {
pub(crate) fn pattern(&self) -> &[TokenTree] {
&self.pattern.vec
}
pub(crate) fn expr(&self) -> &[TokenTree] {
&self.expr.vec
}
pub(crate) fn footgun_allowed(&self) -> bool {
self.footgun.is_some()
}
pub(crate) fn condition(&self) -> Option<&[TokenTree]> {
self.condition.as_ref().map(|condition| condition.third.vec.as_slice())
}
}
#[derive(Debug)]
pub(crate) enum ValidationError {
TooManyBeforeAfter {
before_count: usize,
after_count: usize,
}
}
impl Display for ValidationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::TooManyBeforeAfter { before_count, after_count } => {
write!(f, "expected 0..=1 before and after, found {before_count} befores and {after_count} afters")
}
}
}
}
impl UnvalidatedAst {
pub(crate) fn validated(self) -> Result<Ast, ValidationError> {
let mut before = None;
let mut before_count = 0;
let mut after = None;
let mut after_count = 0;
let mut arms = Vec::new();
for arm in self.arms {
match arm {
AnyArm::Before(arm) => {
before = Some(arm);
before_count += 1;
}
AnyArm::After(arm) => {
after = Some(arm);
after_count += 1;
}
AnyArm::Future(arm) => {
arms.push(arm);
}
}
};
if before_count > 1 || after_count > 1 {
return Err(ValidationError::TooManyBeforeAfter {
before_count,
after_count,
});
}
Ok(Ast {
biased: matches!(self.biasedness, Either::First(_)),
before,
after,
arms,
})
}
}
pub(crate) struct Ast {
pub(crate) biased: bool,
pub(crate) before: Option<Arm<KBefore>>,
pub(crate) after: Option<Arm<KAfter>>,
pub(crate) arms: Vec<Arm<FuturePattern>>,
}
#[cfg(test)]
mod test {
use unsynn::{Parse, ToTokens};
use crate::parser::UnvalidatedAst;
fn test_parse(tokens: &str) {
match UnvalidatedAst::parse_all(&mut tokens.to_token_iter()) {
Ok(ast) => match ast.validated() {
Ok(_) => {}
Err(err) => panic!("{err}"),
}
Err(err) => panic!("{err}"),
}
}
#[test]
fn parse_empty() {
test_parse("unbiased");
}
#[test]
fn parse_arm() {
test_parse(r#"
biased
bar in baz => {
todo!();
},
foo in Timer::new() => println!("bar")
"#);
}
#[test]
fn parse_all() {
test_parse("
unbiased
before => eprintln!(await),
(foo1, Ok(foo2)) in for In(foo) in { break 42 } => match foo {
Some(x) => x,
None => panic!(),
}
after => (),
bar in Timer::new(30s) => {
x;
y;
for i in 0..10 {
foobar();
}
},
");
}
}