9 releases

0.2.6 Mar 24, 2022
0.2.4 Mar 11, 2022
0.2.2 Jan 20, 2020
0.2.1 Jul 17, 2019
0.1.5 Aug 4, 2018

#75 in Parser tooling

38 downloads per month
Used in fluxcap

MIT license

215KB
2.5K SLoC

Documentation

Abackus crate adds a layer on top of earlgrey crate to simplify writing a grammar. You can simply use an EBNF style String instead of manually adding rules.

You can describe your grammar like this:

let grammar = r#"
    S := S '+' N | N ;
    N := '[0-9]' ;
"#;

ParserBuilder::default()
  .plug_terminal("[0-9]", |n| "1234567890".contains(n))
  .plug_terminal("[+]", |c| c == "+")
  .into_parser("S")

Instead of the more verbose:

// Gramar:  S -> S + N | N;  N -> [0-9];
let g = earlgrey::GrammarBuilder::default()
  .nonterm("S")
  .nonterm("N")
  .terminal("[+]", |c| c == "+")
  .terminal("[0-9]", |n| "1234567890".contains(n))
  .rule("S", &["S", "[+]", "N"])
  .rule("S", &["N"])
  .rule("N", &["[0-9]"])
  .into_grammar("S")
  .unwrap();

earlgrey::EarleyParser::new(g)

How it works

Underneath the covers an earlgrey::EarleyParser is used to build a parser for EBNF grammar. (For details you can check earlgrey/ebnf.rs). That parser is then used to build a final parser for the grammar provided by the user.

Example

// NOTE: extract from abackus/examples/ebnftree.rs

fn main() {
  let grammar = r#"
    expr   := expr ('+'|'-') term | term ;
    term   := term ('*'|'/') factor | factor ;
    factor := '-' factor | power ;
    power  := ufact '^' factor | ufact ;
    ufact  := ufact '!' | group ;
    group  := num | '(' expr ')' ;
  "#;

  // Build a parser for our grammar and while at it, plug in an
  // evaluator to extract the resulting tree as S-expressions.
  use std::str::FromStr;
  let trif = abackus::ParserBuilder::default()
      .plug_terminal("num", |n| f64::from_str(n).is_ok())
      .sexprificator(&grammar, "expr");

  // Read some input from command-line
  let input = std::env::args().skip(1).
      collect::<Vec<String>>().join(" ");

  // Print resulting parse trees
  match trif(&mut tokenizer(input.chars())) {
      Ok(trees) => for t in trees { println!("{}", t.print()); },
      Err(e) => println!("{:?}", e)
  }
}

Dependencies