{"id":13439751,"url":"https://github.com/shnewto/bnf","last_synced_at":"2025-04-13T23:57:42.895Z","repository":{"id":26387642,"uuid":"107927382","full_name":"shnewto/bnf","owner":"shnewto","description":"Parse BNF grammar definitions","archived":false,"fork":false,"pushed_at":"2025-02-21T03:30:47.000Z","size":333,"stargazers_count":269,"open_issues_count":18,"forks_count":24,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-04-13T23:56:15.724Z","etag":null,"topics":["backus-naur-form","bnf","crates","earley","earley-parser","grammar","parse-forest","parse-tree","parser","rust","rust-crate","rust-lang","rustlang","sentence-generator","sppf"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shnewto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-23T03:25:02.000Z","updated_at":"2025-04-10T04:41:49.000Z","dependencies_parsed_at":"2023-11-12T22:22:22.094Z","dependency_job_id":"6b8360db-4c70-4ef8-9c27-4c654d85219c","html_url":"https://github.com/shnewto/bnf","commit_stats":{"total_commits":184,"total_committers":16,"mean_commits":11.5,"dds":0.4402173913043478,"last_synced_commit":"868ad58a70f8f614b433366340a5a999bb1c47c2"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shnewto%2Fbnf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shnewto%2Fbnf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shnewto%2Fbnf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shnewto%2Fbnf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shnewto","download_url":"https://codeload.github.com/shnewto/bnf/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248799927,"owners_count":21163403,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backus-naur-form","bnf","crates","earley","earley-parser","grammar","parse-forest","parse-tree","parser","rust","rust-crate","rust-lang","rustlang","sentence-generator","sppf"],"created_at":"2024-07-31T03:01:16.789Z","updated_at":"2025-04-13T23:57:42.873Z","avatar_url":"https://github.com/shnewto.png","language":"Rust","readme":"# bnf\n\n[![.github/workflows/ci.yml](https://github.com/shnewto/bnf/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/shnewto/bnf/actions)\n[![coveralls](https://coveralls.io/repos/github/shnewto/bnf/badge.svg?branch=main)](https://coveralls.io/github/shnewto/bnf?branch=main)\n[![Crates.io Version](https://img.shields.io/crates/v/bnf.svg)](https://crates.io/crates/bnf)\n[![Crates.io](https://img.shields.io/crates/d/bnf.svg)](https://crates.io/crates/bnf)\n[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/shnewto/bnf/blob/main/LICENSE)\n\nA library for parsing Backus–Naur form context-free grammars.\n\n## What does a parsable BNF grammar look like?\n\nThe following grammar from the\n[Wikipedia page on Backus-Naur form](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form#Example)\nexemplifies a compatible grammar. (*Note: parser allows for an optional ';'\nto indicate the end of a production)\n\n```text\n \u003cpostal-address\u003e ::= \u003cname-part\u003e \u003cstreet-address\u003e \u003czip-part\u003e\n\n      \u003cname-part\u003e ::= \u003cpersonal-part\u003e \u003clast-name\u003e \u003copt-suffix-part\u003e \u003cEOL\u003e\n                    | \u003cpersonal-part\u003e \u003cname-part\u003e\n\n  \u003cpersonal-part\u003e ::= \u003cinitial\u003e \".\" | \u003cfirst-name\u003e\n\n \u003cstreet-address\u003e ::= \u003chouse-num\u003e \u003cstreet-name\u003e \u003copt-apt-num\u003e \u003cEOL\u003e\n\n       \u003czip-part\u003e ::= \u003ctown-name\u003e \",\" \u003cstate-code\u003e \u003cZIP-code\u003e \u003cEOL\u003e\n\n\u003copt-suffix-part\u003e ::= \"Sr.\" | \"Jr.\" | \u003croman-numeral\u003e | \"\"\n    \u003copt-apt-num\u003e ::= \u003capt-num\u003e | \"\"\n```\n\n## Output\nTake the following grammar for DNA sequences to be input to this library's\n`parse` function.\n```text\n\u003cdna\u003e ::= \u003cbase\u003e | \u003cbase\u003e \u003cdna\u003e\n\u003cbase\u003e ::= \"A\" | \"C\" | \"G\" | \"T\"\n```\n\nThe output is a `Grammar` object representing a tree that looks like this:\n```text\nGrammar {\n    productions: [\n        Production {\n            lhs: Nonterminal(\n                \"dna\"\n            ),\n            rhs: [\n                Expression {\n                    terms: [\n                        Nonterminal(\n                            \"base\"\n                        )\n                    ]\n                },\n                Expression {\n                    terms: [\n                        Nonterminal(\n                            \"base\"\n                        ),\n                        Nonterminal(\n                            \"dna\"\n                        )\n                    ]\n                }\n            ]\n        },\n        Production {\n            lhs: Nonterminal(\n                \"base\"\n            ),\n            rhs: [\n                Expression {\n                    terms: [\n                        Terminal(\n                            \"A\"\n                        )\n                    ]\n                },\n                Expression {\n                    terms: [\n                        Terminal(\n                            \"C\"\n                        )\n                    ]\n                },\n                Expression {\n                    terms: [\n                        Terminal(\n                            \"G\"\n                        )\n                    ]\n                },\n                Expression {\n                    terms: [\n                        Terminal(\n                            \"T\"\n                        )\n                    ]\n                }\n            ]\n        }\n    ]\n}\n\n```\n\nOnce the `Grammar` object is populated, to generate a random sentence from it\ncall the object's generate function. `grammar.generate()`. For the above grammar\nyou could expect something like `TGGC` or `AG`.\n\nIf the generate function can't find a production for a nonterminal it tries\nto evaluate it will print the identifer as a nonterminal, i.e. `\u003cidentifier\u003e`.\n\nThe generate function will return an error if it detects an infinite loop caused\nby a production such as `\u003cPATTERN\u003e ::= \u003cPATTERN\u003e`.\n\n## Parse Example\n\n```rust\nuse bnf::Grammar;\n\nlet input =\n    \"\u003cpostal-address\u003e ::= \u003cname-part\u003e \u003cstreet-address\u003e \u003czip-part\u003e\n\n        \u003cname-part\u003e ::= \u003cpersonal-part\u003e \u003clast-name\u003e \u003copt-suffix-part\u003e \u003cEOL\u003e\n                        | \u003cpersonal-part\u003e \u003cname-part\u003e\n\n    \u003cpersonal-part\u003e ::= \u003cinitial\u003e '.' | \u003cfirst-name\u003e\n\n    \u003cstreet-address\u003e ::= \u003chouse-num\u003e \u003cstreet-name\u003e \u003copt-apt-num\u003e \u003cEOL\u003e\n\n        \u003czip-part\u003e ::= \u003ctown-name\u003e ',' \u003cstate-code\u003e \u003cZIP-code\u003e \u003cEOL\u003e\n\n    \u003copt-suffix-part\u003e ::= 'Sr.' | 'Jr.' | \u003croman-numeral\u003e | ''\n        \u003copt-apt-num\u003e ::= \u003capt-num\u003e | ''\";\n\nlet grammar: Result\u003cGrammar, _\u003e = input.parse();\nmatch grammar {\n    Ok(g) =\u003e println!(\"{:#?}\", g),\n    Err(e) =\u003e println!(\"Failed to make grammar from String: {}\", e),\n}\n```\n## Generate Example\n\n```rust\nuse bnf::Grammar;\n\nlet input =\n    \"\u003cdna\u003e ::= \u003cbase\u003e | \u003cbase\u003e \u003cdna\u003e\n    \u003cbase\u003e ::= 'A' | 'C' | 'G' | 'T'\";\nlet grammar: Grammar = input.parse().unwrap();\nlet sentence = grammar.generate();\nmatch sentence {\n    Ok(s) =\u003e println!(\"random sentence: {}\", s),\n    Err(e) =\u003e println!(\"something went wrong: {}!\", e)\n}\n```\n\n## Parse Sentence via Grammar\n```rust\nuse bnf::Grammar;\n\nlet input =\n    \"\u003cdna\u003e ::= \u003cbase\u003e | \u003cbase\u003e \u003cdna\u003e\n    \u003cbase\u003e ::= 'A' | 'C' | 'G' | 'T'\";\nlet grammar: Grammar = input.parse().unwrap();\n\nlet sentence = \"GATTACA\";\n\nlet mut parse_trees = grammar.parse_input(sentence);\nmatch parse_trees.next() {\n    Some(parse_tree) =\u003e println!(\"{}\", parse_tree),\n    _ =\u003e println!(\"Grammar could not parse sentence\"),\n}\n```\n","funding_links":[],"categories":["Libraries","库 Libraries","库","Rust"],"sub_categories":["Language specification","语言规范 Language specification","语言规范"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshnewto%2Fbnf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshnewto%2Fbnf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshnewto%2Fbnf/lists"}