{"id":20786228,"url":"https://github.com/dryruby/ebnf","last_synced_at":"2025-04-05T07:05:33.340Z","repository":{"id":7104145,"uuid":"8396648","full_name":"dryruby/ebnf","owner":"dryruby","description":"EBNF parser and generic parser generator for Ruby.","archived":false,"fork":false,"pushed_at":"2023-12-26T21:30:03.000Z","size":1295,"stargazers_count":115,"open_issues_count":1,"forks_count":9,"subscribers_count":6,"default_branch":"develop","last_synced_at":"2024-04-26T10:03:31.474Z","etag":null,"topics":["ebnf","parser-generator","ruby","rubygems"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/ebnf","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dryruby.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-02-24T20:57:15.000Z","updated_at":"2024-06-18T16:56:30.366Z","dependencies_parsed_at":"2024-06-18T16:56:25.704Z","dependency_job_id":"a0ca1c4c-970d-4799-89a1-e762baf42949","html_url":"https://github.com/dryruby/ebnf","commit_stats":{"total_commits":326,"total_committers":6,"mean_commits":"54.333333333333336","dds":"0.35582822085889576","last_synced_commit":"74c4c16fbe975fff23c1075c0e06796706573560"},"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dryruby%2Febnf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dryruby%2Febnf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dryruby%2Febnf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dryruby%2Febnf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dryruby","download_url":"https://codeload.github.com/dryruby/ebnf/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247299832,"owners_count":20916190,"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":["ebnf","parser-generator","ruby","rubygems"],"created_at":"2024-11-17T14:51:15.840Z","updated_at":"2025-04-05T07:05:33.323Z","avatar_url":"https://github.com/dryruby.png","language":"Ruby","readme":"# EBNF \n\n[EBNF][] parser and generic parser generator.\n\n[![Gem Version](https://badge.fury.io/rb/ebnf.svg)](https://badge.fury.io/rb/ebnf)\n[![Build Status](https://github.com/dryruby/ebnf/workflows/CI/badge.svg?branch=develop)](https://github.com/dryruby/ebnf/actions?query=workflow%3ACI)\n[![Coverage Status](https://coveralls.io/repos/dryruby/ebnf/badge.svg?branch=develop)](https://coveralls.io/r/dryruby/ebnf?branch=develop)\n[![Gitter chat](https://badges.gitter.im/ruby-rdf/rdf.png)](https://gitter.im/ruby-rdf/rdf)\n\n## Description\nThis is a [Ruby][] implementation of an [EBNF][] and [BNF][] parser and parser generator.\n\n### [PEG][] / [Packrat][] Parser\nIn the primary mode, it supports a Parsing Expression Grammar ([PEG][]) parser generator. This performs more minimal transformations on the parsed grammar to extract sub-productions, which allows each component of a rule to generate its own parsing event.\n\nThe resulting {EBNF::PEG::Rule} objects then parse each associated rule according to the operator semantics and use a [Packrat][] memoizer to reduce extra work when backtracking.\n\nThese rules are driven using the {EBNF::PEG::Parser} module which calls invokes the starting rule and ensures that all input is consumed.\n\n### LL(1) Parser\nIn another mode, it parses [EBNF][] grammars to [BNF][], generates [First/Follow][] and Branch tables for [LL(1)][] grammars, which can be used with the stream [Tokenizer][] and [LL(1) Parser][].\n\nAs LL(1) grammars operate using `alt` and `seq` primitives, allowing for a match on alternative productions or a sequence of productions, generating a parser requires turning the [EBNF][] rules into [BNF][]:\n\n* Transform `a ::= b?` into `a ::= _empty | b`\n* Transform `a ::= b+` into `a ::= b b*`\n* Transform `a ::= b*` into `a ::= _empty | (b a)`\n* Transform `a ::= op1 (op2)` into two rules:\n\n        a     ::= op1 _a_1\n        _a_1_ ::= op2\n\nOf note in this implementation is that the tokenizer and parser are streaming, so that they can process inputs of arbitrary size.\n\nThe _exception operator_ (`A - B`) is only supported on terminals.\n\nSee {EBNF::LL1} and {EBNF::LL1::Parser} for further information.\n\n## Usage\n### Parsing an EBNF Grammar\n\n    require 'ebnf'\n\n    grammar = EBNF.parse(File.open('./etc/ebnf.ebnf'))\n\nOutput rules and terminals as [S-Expressions][S-Expression], [Turtle][], HTML or [BNF][]\n\n    puts grammar.to_sxp\n    puts grammar.to_ttl\n    puts grammar.to_html\n    puts grammar.to_s\n\nTransform [EBNF][] to [PEG][] (generates sub-rules for embedded expressions) and the RULES table as Ruby for parsing grammars:\n\n    grammar.make_peg\n    grammar.to_ruby\n\nTransform [EBNF][] to [BNF][] (generates sub-rules using `alt` or `seq` from `plus`, `star` or `opt`)\n\n    grammar.make_bnf\n\nGenerate [First/Follow][] rules for BNF grammars (using \"ebnf\" as the starting production):\n\n    grammar.first_follow(:ebnf)\n\nGenerate Terminal, [First/Follow][], Cleanup and Branch tables as Ruby for parsing grammars:\n\n    grammar.build_tables\n    grammar.to_ruby\n\nGenerate formatted grammar using HTML (requires [Haml][Haml] gem):\n\n    grammar.to_html\n\n### Parsing an ISO/IEC 14977 Grammar\n\nThe EBNF gem can also parse  [ISO/IEC 14977][] Grammars (ISOEBNF) to [S-Expressions][S-Expression].\n\n    grammar = EBNF.parse(File.open('./etc/iso-ebnf.isoebnf'), format: :isoebnf)\n\n### Parsing an ABNF Grammar\n\nThe EBNF gem can also parse [ABNF] Grammars to [S-Expressions][S-Expression].\n\n    grammar = EBNF.parse(File.open('./etc/abnf.abnf'), format: :abnf)\n\n### Parser Debugging\n\nInevitably while implementing a parser for some specific grammar, a developer will need greater insight into the operation of the parser. While this can involve sorting through a tremendous amount of data, the parser can be provided a [Logger][] instance which will output messages at varying levels of detail to document the state of the parser at any given point. Most useful is likely the `INFO` level of debugging, but even more detail is revealed using the `DEBUG` level. `WARN` and `ERROR` statements will typically also be provided as part of an exception if parsing fails, but can be shown in the context of other parsing state with appropriate indentation as part of the logger.\n\n### Writing Grammars\n\nThe {EBNF::Writer} class can be used to write parsed grammars out, either as formatted text, or HTML. Because grammars are written from the Abstract Syntax Tree, represented as [S-Expressions][S-Expression], this provides a means of transforming between grammar formats (e.g., W3C [EBNF][] to [ABNF][]), although with some potential loss in semantic fidelity (case-insensitive string matching vs. case-sensitive matching).\n\nThe formatted HTML results are designed to be appropriate for including in specifications.\n\n### Parser Errors\nOn a parsing failure, an exception is raised with information that may be useful in determining the source of the error.\n\n## EBNF Grammar\nThe [EBNF][] variant used here is based on [W3C](https://w3.org/) [EBNF][]\n(see [EBNF grammar](https://dryruby.github.io/ebnf/etc/ebnf.ebnf))\nas defined in the\n[XML 1.0 recommendation](https://www.w3.org/TR/REC-xml/), with minor extensions:\n\nNote that the grammar includes an optional `[number]` in front of rule names, which can be in conflict with the `RANGE` terminal. It is typically not a problem, but if it comes up, try parsing with the `native` parser,  add comments or sequences to disambiguate. EBNF does not have beginning of line checks as all whitespace is treated the same, so the common practice of identifying each rule inherently leads to such ambiguity.\n\nThe character set for EBNF is UTF-8.\n\nThe general form of a rule is:\n\n    symbol ::= expression\n\nwhich can also be proceeded by an optional number enclosed in square brackets to identify the rule number:\n\n    [1] symbol ::= expression\n\n(Note, introduces an ambiguity if the previous rule ends in a range or enum and the current rule has no number. The parsers dynamically determine the terminal rules for the `LHS` (the identifier, symbol, and `::=`) and `RANGE`).\n\nSymbols are written in CAPITAL CASE if they are the start symbol of a regular language (terminals), otherwise with they are treated as non-terminal rules. Literal strings are quoted.\n\nWithin the expression on the right-hand side of a rule, the following expressions are used to match strings of one or more characters:\n\n\u003ctable\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e#xN\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003ewhere \u003ccode\u003eN\u003c/code\u003e is a hexadecimal integer, the expression matches the character whose number (code point) in ISO/IEC 10646 is \u003ccode\u003eN\u003c/code\u003e. The number of leading zeros in the \u003ccode\u003e#xN\u003c/code\u003e form is insignificant.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e[a-zA-Z], [#xN-#xN]\u003c/code\u003e\n    \u003ctd\u003ematches any Char or HEX with a value in the range(s) indicated (inclusive).\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e[abc], [#xN#xN#xN]\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003ematches any UTF-8 R\\_CHAR or HEX with a value among the characters enumerated. The last component may be '-'. Enumerations and ranges may be mixed in one set of brackets.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e[^a-z], [^#xN-#xN]\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003ematches any UTF-8 Char or HEX a value outside the range indicated.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e[^abc], [^#xN#xN#xN]\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003ematches any UTF-8 R\\_CHAR or HEX with a value not among the characters given. The last component may be '-'. Enumerations and ranges of excluded values may be mixed in one set of brackets.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e\"string\"\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003ematches a literal string matching that given inside the double quotes case insensitively.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e'string'\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003ematches a literal string matching that given inside the single quotes.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA (B | C)\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003ccode\u003e(B | C)\u003c/code\u003e is treated as a unit and may be combined as described in this list.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA?\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003ematches A or nothing; optional A.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA B\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003ematches \u003ccode\u003eA\u003c/code\u003e followed by \u003ccode\u003eB\u003c/code\u003e. This operator has higher precedence than alternation; thus \u003ccode\u003eA B | C D\u003c/code\u003e is identical to \u003ccode\u003e(A B) | (C D)\u003c/code\u003e.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA | B\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003ematches \u003ccode\u003eA\u003c/code\u003e or \u003ccode\u003eB\u003c/code\u003e.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA - B\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003ematches any string that matches \u003ccode\u003eA\u003c/code\u003e but does not match \u003ccode\u003eB\u003c/code\u003e. (Only supported on Terminals in LL(1) BNF).\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA+\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003ematches one or more occurrences of \u003ccode\u003eA\u003c/code\u003e. Concatenation has higher precedence than alternation; thus \u003ccode\u003eA+ | B+\u003c/code\u003e is identical to \u003ccode\u003e(A+) | (B+)\u003c/code\u003e.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA*\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003ematches zero or more occurrences of \u003ccode\u003eA\u003c/code\u003e. Concatenation has higher precedence than alternation; thus \u003ccode\u003eA* | B*\u003c/code\u003e is identical to \u003ccode\u003e(A*) | (B*)\u003c/code\u003e.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e@pass \" \"*\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003eDefines consumed whitespace in the document. Any whitespace found between non-terminal rules is consumed and ignored.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e@terminals\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003eIntroduces terminal rules. All rules defined after this point are treated as terminals.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n* Comments include `//` and `#` through end of line (other than hex character) and `/* ... */ (* ... *) which may cross lines`\n* All rules **MAY** start with an number, contained within square brackets. For example `[1] rule`, where the value within the brackets is a symbol `([a-z] | [A-Z] | [0-9] | \"_\" | \".\")+`, which is not retained after parsing\n* Symbols **MAY** be enclosed in angle brackets `'\u003c'` and `\u003e`, which are dropped when parsing.\n* `@terminals` causes following rules to be treated as terminals. Any terminal which is all upper-case (eg`TERMINAL`), or any rules with expressions that match characters (`#xN`, `[a-z]`, `[^a-z]`, `[abc]`, `[^abc]`, `\"string\"`, `'string'`, or `A - B`), are also treated as terminals.\n* `@pass` defines the expression used to detect whitespace, which is removed in processing.\n* No support for `wfc` (well-formedness constraint) or `vc` (validity constraint).\n\nParsing this grammar yields an [S-Expression][S-Expression] version:\n[here](https://dryruby.github.io/ebnf/etc/ebnf.sxp)\n(or [LL(1)][] version\n[here](https://dryruby.github.io/ebnf/etc/ebnf.ll1.sxp)\nor [PEG][] version\n[here](https://dryruby.github.io/ebnf/etc/ebnf.peg.sxp)).\n\n### Parser S-Expressions\nIntermediate representations of the grammar may be serialized to Lisp-like [S-Expressions][S-Expression]. For example, the rule\n\n    [1] ebnf        ::= (declaration | rule)*\n\nis serialized as\n\n    (rule ebnf (star (alt declaration rule)))\n\nDifferent components of an EBNF rule expression are transformed into their own operator:\n\n\u003ctable\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e#xN\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(hex \"#xN\")\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e[a-z#xN-#xN]\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(range \"a-z#xN-#xN\")\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e[abc#xN]\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(range \"abc#xN\")\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e[^a-z#xN-#xN]\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(range \"^a-z#xN-#xN\")\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e[^abc#xN]\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(range \"^abc#xN\")\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e\"string\"\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e\"string\"\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e'string'\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e\"string\"\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA (B | C)\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(seq (A (alt B C)))\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA?\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(opt A)\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA B\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(seq A B)\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA | B\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(alt A B)\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA - B\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003ccode\u003e(diff A B) for terminals.\u003cbr/\u003e\n      \u003ccode\u003e(seq (not B) A) for non-terminals (PEG parsing only)\u003c/code\u003e\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA+\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(plus A)\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003eA*\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(star A)\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e@pass \" \"*\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(pass _pass (star \" \"))\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e@terminals\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\nOther rule operators are not directly supported in [EBNF][], but are included to support other notations (e.g., [ABNF][] and [ISO/IEC 14977][]):\n\n\u003ctable\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e%i\"StRiNg\"\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(istr \"StRiNg\")\u003c/code\u003e\u003c/td\u003e\u003ctd\u003eCase-insensitive string matching\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003e'' - A\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(not A)\u003c/code\u003e\u003c/td\u003e\u003ctd\u003eNegative look-ahead, used for non-terminal uses of `B - A`.\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ccode\u003en*mA\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003e(rept n m A)\u003c/code\u003e\u003c/td\u003e\u003ctd\u003eExplicit repetition.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\nAdditionally, rules defined with an UPPERCASE symbol are treated as terminals.\n\nFor an [LL(1)][] parser generator, the {EBNF::BNF.make_bnf} method can be used to transform the EBNF rule into a BNF rule.\n\n    (rule ebnf \"1\" (alt _empty _ebnf_2))\n    (rule _ebnf_1 \"1.1\" (alt declaration rule))\n    (rule _ebnf_2 \"1.2\" (seq _ebnf_1 ebnf))\n    (rule _ebnf_3 \"1.3\" (seq ebnf))\n\nThis allows [First/Follow][] and other tables used by a parser to parse examples of the associated grammar. For more, see {EBNF::LL1}.\n\nFor a [PEG][] parser generator, there is a simpler transformation that reduces rules containing sub-expressions (composed of `star`, `alt`, `seq` and similar expressions) and creates named rules to allow appropriate callbacks and for naming elements of the generating abstract syntax tree. The {EBNF::PEG.make_peg} method transforms the original rule into the following two rules:\n\n    (rule ebnf \"1\" (star _ebnf_1))\n    (rule _ebnf_1 \"1.1\" (alt declaration rule))\n\n## Example parsers\nFor a [PEG][] parser for a simple grammar implementing a calculator see [Calc example](https://dryruby.github.io/ebnf/examples/calc/doc/calc.html)\n\nFor an example parser built using this gem that parses the [EBNF][] grammar, see [EBNF PEG Parser example](https://dryruby.github.io/ebnf/examples/ebnf-peg-parser/doc/parser.html). This example creates a parser for the [EBNF][] grammar which generates the same Abstract Syntax Tree as the built-in parser in the gem.\n\nThere is also an\n[EBNF LL(1) Parser example](https://dryruby.github.io/ebnf/examples/ebnf-ll1-parser/doc/parser.html).\n\nThe [ISO EBNF Parser](https://dryruby.github.io/ebnf/examples/isoebnf/doc/parser.html) example parses [ISO/IEC 14977][] into [S-Expressions][S-Expression], which can be used to parse compatible grammars using this parser (either [PEG][] or [LL(1)][]).\n\nThe [ABNF Parser](https://dryruby.github.io/ebnf/examples/abnf/doc/parser.html) example parses [ABNF][] into [S-Expressions][S-Expression], which can be used to parse compatible grammars using this [PEG][] parser.\n\n##  Acknowledgements\nMuch of this work, particularly the generic parser, is inspired by work originally done by\nTim Berners-Lee's Python [predictive parser](https://www.w3.org/2000/10/swap/grammar/predictiveParser.py).\n\nThe [LL(1)][] parser was inspired by Dan Connolly's\n[EBNF to Turtle processor](https://www.w3.org/2000/10/swap/grammar/ebnf2turtle.py),\n[EBNF to BNF Notation-3 rules](https://www.w3.org/2000/10/swap/grammar/ebnf2bnf.n3),\nand [First Follow Notation-3 rules](https://www.w3.org/2000/10/swap/grammar/first_follow.n3). \n\n## Documentation\nFull documentation available on [Rubydoc.info][EBNF doc].\n\n## Change Log\n\nSee [Release Notes on GitHub](https://github.com/dryruby/ebnf/releases)\n\n## Future Work\n* Better LL(1) parser tests\n\n## Author\n* [Gregg Kellogg](https://github.com/gkellogg) - \u003chttps://greggkellogg.net/\u003e\n\n## Contributing\nThis repository uses [Git Flow](https://github.com/nvie/gitflow) to mange development and release activity. All submissions _must_ be on a feature branch based on the _develop_ branch to ease staging and integration.\n\n* Do your best to adhere to the existing coding conventions and idioms.\n* Don't use hard tabs, and don't leave trailing whitespace on any line.\n* Do document every method you add using [YARD][] annotations. Read the\n  [tutorial][YARD-GS] or just look at the existing code for examples.\n* Don't touch the `.gemspec`, `VERSION` or `AUTHORS` files. If you need to\n  change them, do so on your private branch only.\n* Do feel free to add yourself to the `CREDITS` file and the corresponding\n  list in the the `README`. Alphabetical order applies.\n* Do note that in order for us to merge any non-trivial changes (as a rule\n  of thumb, additions larger than about 15 lines of code), we need an\n  explicit [public domain dedication][PDD] on record from you,\n  which you will be asked to agree to on the first commit to a repo within the organization.\n\n## License\nThis is free and unencumbered public domain software. For more information,\nsee \u003chttps://unlicense.org/\u003e or the accompanying {file:UNLICENSE} file.\n\nA copy of the [Turtle EBNF][] and derived parser files are included in the repository, which are not covered under the UNLICENSE. These files are covered via the [W3C Document License](https://www.w3.org/Consortium/Legal/2002/copyright-documents-20021231).\n\n[Ruby]:         https://ruby-lang.org/\n[YARD]:         https://yardoc.org/\n[YARD-GS]:      https://rubydoc.info/docs/yard/file/docs/GettingStarted.md\n[PDD]:          https://lists.w3.org/Archives/Public/public-rdf-ruby/2010May/0013.html\n[ABNF]:         https://www.rfc-editor.org/rfc/rfc5234\n[BNF]:          https://en.wikipedia.org/wiki/Backus–Naur_form\n[EBNF]:         https://www.w3.org/TR/REC-xml/#sec-notation\n[EBNF doc]:     https://dryruby.github.io/ebnf\n[First/Follow]: https://en.wikipedia.org/wiki/LL_parser#Constructing_an_LL.281.29_parsing_table\n[ISO/IEC 14977]:https://www.iso.org/standard/26153.html\n[LL(1)]:        https://www.csd.uwo.ca/~moreno//CS447/Lectures/Syntax.html/node14.html\n[LL(1) Parser]: https://en.wikipedia.org/wiki/LL_parser\n[Logger]:       https://ruby-doc.org/stdlib-3.0.0/libdoc/logger/rdoc/Logger.html\n[S-expression]: https://en.wikipedia.org/wiki/S-expression\n[Tokenizer]:    https://en.wikipedia.org/wiki/Lexical_analysis#Tokenizer\n[Turtle]:       https://www.w3.org/TR/2012/WD-turtle-20120710/\n[Turtle EBNF]:  https://dvcs.w3.org/hg/rdf/file/default/rdf-turtle/turtle.bnf\n[Packrat]:      https://pdos.csail.mit.edu/~baford/packrat/thesis/\n[PEG]:          https://en.wikipedia.org/wiki/Parsing_expression_grammar\n[Haml]:         https://rubygems.org/gems/haml\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdryruby%2Febnf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdryruby%2Febnf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdryruby%2Febnf/lists"}