# bnf [![.github/workflows/ci.yml](https://github.com/shnewto/bnf/workflows/.github/workflows/ci.yml/badge.svg?branch=main)](https://github.com/shnewto/bnf/actions) [![coveralls](https://coveralls.io/repos/github/shnewto/bnf/badge.svg?branch=main)](https://coveralls.io/github/shnewto/bnf?branch=main) [![Crates.io Version](https://img.shields.io/crates/v/bnf.svg)](https://crates.io/crates/bnf) [![Crates.io](https://img.shields.io/crates/d/bnf.svg)](https://crates.io/crates/bnf) [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/shnewto/bnf/blob/main/LICENSE) A library for parsing Backus–Naur form context-free grammars. ## What does a parsable BNF grammar look like? The following grammar from the [Wikipedia page on Backus-Naur form](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form#Example) exemplifies a compatible grammar. (*Note: parser allows for an optional ';' to indicate the end of a production) ```text ::= ::= | ::= "." | ::= ::= "," ::= "Sr." | "Jr." | | "" ::= | "" ``` ## Output Take the following grammar for DNA sequences to be input to this library's `parse` function. ```text ::= | ::= "A" | "C" | "G" | "T" ``` The output is a `Grammar` object representing a tree that looks like this: ```text Grammar { productions: [ Production { lhs: Nonterminal( "dna" ), rhs: [ Expression { terms: [ Nonterminal( "base" ) ] }, Expression { terms: [ Nonterminal( "base" ), Nonterminal( "dna" ) ] } ] }, Production { lhs: Nonterminal( "base" ), rhs: [ Expression { terms: [ Terminal( "A" ) ] }, Expression { terms: [ Terminal( "C" ) ] }, Expression { terms: [ Terminal( "G" ) ] }, Expression { terms: [ Terminal( "T" ) ] } ] } ] } ``` Once the `Grammar` object is populated, to generate a random sentence from it call the object's generate function. `grammar.generate()`. For the above grammar you could expect something like `TGGC` or `AG`. If the generate function can't find a production for a nonterminal it tries to evaluate it will print the identifer as a nonterminal, i.e. ``. The generate function will return an error if it detects an infinite loop caused by a production such as ` ::= `. ## Parse Example ```rust use bnf::Grammar; let input = " ::= ::= | ::= '.' | ::= ::= ',' ::= 'Sr.' | 'Jr.' | | '' ::= | ''"; let grammar: Result = input.parse(); match grammar { Ok(g) => println!("{:#?}", g), Err(e) => println!("Failed to make grammar from String: {}", e), } ``` ## Generate Example ```rust use bnf::Grammar; let input = " ::= | ::= 'A' | 'C' | 'G' | 'T'"; let grammar: Grammar = input.parse().unwrap(); let sentence = grammar.generate(); match sentence { Ok(s) => println!("random sentence: {}", s), Err(e) => println!("something went wrong: {}!", e) } ``` ## Parse Sentence via Grammar ```rust use bnf::Grammar; let input = " ::= | ::= 'A' | 'C' | 'G' | 'T'"; let grammar: Grammar = input.parse().unwrap(); let sentence = "GATTACA"; let mut parse_trees = grammar.parse_input(sentence); match parse_trees.next() { Some(parse_tree) => println!("{}", parse_tree), _ => println!("Grammar could not parse sentence"), } ```