openpql-range-parser 0.1.0

Poker Range Notation Parser
Documentation
use super::{
    ast::{RangeCard, Expr, List, CardRank, Span, CardSuit, Term, TermElem},
    Error,
};

grammar(is_shortdeck: bool);

extern {
    type Error = Error;
}

pub Expr: Box<Expr> = {
    <l: Expr > "," <r: ExprAnd> => Box::new(Expr::Or(l, r)),
    ExprAnd => <>,
}

ExprAnd: Box<Expr> = {
    <l: ExprAnd> ":" <r: ExprNot> => Box::new(Expr::And(l, r)),
    ExprNot => <>,
}

ExprNot: Box<Expr> = {
    <l: ExprNot> "!" <r: ExprLeaf> => Box::new(Expr::Not(l, r)),
    ExprLeaf => <>,
}

ExprLeaf: Box<Expr> = {
    "(" <e: Expr> ")" => e,
    ExprTerm,
}

ExprTerm: Box<Expr> = {
    <l: @R> <t: Term> <r: @R> => Box::new((t, (l, r)).into()),
}

pub(crate) Term: Term = {
    TermElem+ => <>.into(),
    RawSpan => <>.into(),
}

pub(crate) RawSpan: Span = {
    <l: @R> <cs: TermElem+> "-" <r: @R> =>? Span::spandown(<>),
    <l: @R> <cs: TermElem+> "+" <r: @R> =>? Span::spanup(<>),
    <l: @R> <top: TermElem+> "-" <btm: TermElem+> <r: @R> =>? Span::spanto(<>),
}

TermElem: TermElem = {
    RangeCard => TermElem::Card(<>),
    List => TermElem::List(<>),
    "[" <s: Span> "]" => TermElem::Span(s),
}

pub(crate) Span: Span = {
    <l: @R> <cs: RangeCard+> "-" <r: @R> =>? Span::spandown(<>),
    <l: @R> <cs: RangeCard+> "+" <r: @R> =>? Span::spanup(<>),
    <l: @R> <top: RangeCard+> "-" <btm: RangeCard+> <r: @R> =>? Span::spanto(<>),
}

pub(crate) List: List = <l: @R> "[" <v: CommaSep<RangeCard>> "]" <r: @R> =>? (<>).try_into();

pub(crate) RangeCard: RangeCard = {
    CardRank => (<>).into(),
    CardSuit => (<>).into(),
    RankSuit => (<>).into(),
}

CardRank: CardRank = <l: @R> <s: "Rank"> <r: @R> =>?
    CardRank::from_token(is_shortdeck, s.chars().next().unwrap(), (l, r));

CardSuit: CardSuit = <l: @R> <s: "Suit"> <r: @R> =>?
    CardSuit::from_token(s.chars().next().unwrap(), (l, r));

RankSuit: (CardRank, CardSuit) = { <l: @R> <s: "RankSuit"> <r: @R> =>? Ok(( 
    CardRank::from_token(is_shortdeck, s.chars().nth(0).unwrap(), (l, r-1))?,
    CardSuit::from_token(s.chars().nth(1).unwrap(), (l+1, r))?,
)) }

CommaSep<T>: Vec<T> = {
    <head: T> <rest: ("," <T>)*> ","? =>
      [head].into_iter().chain(rest.into_iter()).collect::<Vec<_>>()
};

match {
  r"(?i)[23456789TJQKABEFGILMNOPRUV\*][shdcwxyz]" => "RankSuit",
} else {
  r"(?i)[23456789TJQKABEFGILMNOPRUV\*]" => "Rank",
  r"(?i)[shdcwxyz]" => "Suit",
  _
}