use tygr::*;
#[derive(Grammar, Debug, PartialEq, Eq)]
pub struct Expr(pub Expr1, pub Vec<(Wrap<Ws, Op1, Ws>, Expr1)>);
#[derive(Grammar, Debug, PartialEq, Eq)]
pub enum Op1 {
Add(StringEq!("+")),
Sub(StringEq!("-")),
}
#[derive(Grammar, Debug, PartialEq, Eq)]
pub struct Expr1(pub Expr2, pub Vec<(Wrap<Ws, Op2, Ws>, Expr2)>);
#[derive(Grammar, Debug, PartialEq, Eq)]
pub enum Op2 {
Mul(StringEq!("*")),
Div(StringEq!("/")),
}
#[derive(Grammar, Debug, PartialEq, Eq)]
pub enum Expr2 {
Paren(Wrap<(StringEq!("("), Ws), Box<Expr>, (Ws, StringEq!(")"))>),
Number(Int),
}
char_class!(pub IsDigit, "digit", |ch| ch.is_ascii_digit());
#[derive(Grammar, Debug, PartialEq, Eq)]
pub struct Int(pub StringOf1<IsDigit>);
char_class!(pub IsSpace, "space", |ch| ch.is_ascii_whitespace());
#[derive(Grammar, Debug, PartialEq, Eq)]
#[grammar(hidden)] pub struct Ws(pub StringOf<IsSpace>);
#[path = "common/mod.rs"]
mod common;
fn main() -> std::process::ExitCode {
match std::env::args().nth(1).as_deref() {
Some("bnf") => {
println!("{}", bnf_rules![Expr, Op1, Expr1, Op2, Expr2, Int]);
std::process::ExitCode::SUCCESS
}
Some("parse") => common::parse::<Expr>(),
Some("test") => common::test::<Expr>(),
_ => common::usage("arith"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trips() {
let e: Expr = Expr::parse("1 + 2 * 3").unwrap();
let ws = || Ws::parse(" ").unwrap();
assert_eq!(
e,
Expr(
Expr1(Expr2::parse("1").unwrap(), vec![]),
vec![(
Wrap::new(ws(), Op1::Add(Default::default()), ws()),
Expr1(
Expr2::parse("2").unwrap(),
vec![(
Wrap::new(ws(), Op2::Mul(Default::default()), ws()),
Expr2::parse("3").unwrap()
)]
)
)]
)
);
assert_eq!(e.print(), "1 + 2 * 3");
}
#[test]
fn generates_bnf() {
let bnf = bnf_rules![Expr, Op1, Expr1, Op2, Expr2, Int].to_string();
assert_eq!(
bnf,
"\
Expr = Expr1 { Op1 Expr1 } ;
Op1 = \"+\" | \"-\" ;
Expr1 = Expr2 { Op2 Expr2 } ;
Op2 = \"*\" | \"/\" ;
Expr2 = \"(\" Expr \")\" | Int ;
Int = 'digit' { 'digit' } ;"
);
}
}