├── .github └── FUNDING.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── crates ├── dprint-plugin-ebnf │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ └── src │ │ ├── configuration.rs │ │ ├── lib.rs │ │ ├── plugin.rs │ │ └── resolve_config.rs ├── ebnf-fmt │ ├── Cargo.toml │ ├── LICENSE │ └── src │ │ ├── configuration.rs │ │ ├── formatter.rs │ │ └── lib.rs ├── ebnf-parser │ ├── Cargo.toml │ ├── LICENSE │ ├── grammar.ebnf │ └── src │ │ ├── ast.rs │ │ ├── error.rs │ │ ├── lexer.rs │ │ ├── lib.rs │ │ ├── parser.rs │ │ ├── span.rs │ │ └── token.rs └── tree-sitter-ebnf │ ├── .gitattributes │ ├── .gitignore │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ ├── binding.gyp │ ├── bindings │ ├── node │ │ ├── binding.cc │ │ └── index.js │ └── rust │ │ ├── build.rs │ │ └── lib.rs │ ├── grammar.js │ ├── package.json │ ├── queries │ ├── ebnf │ │ └── highlights.scm │ └── highlights.scm │ └── src │ ├── grammar.json │ ├── node-types.json │ ├── parser.c │ └── tree_sitter │ └── parser.h └── dprint.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: rubixdev 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.19" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anyhow" 16 | version = "1.0.65" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.1.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 25 | 26 | [[package]] 27 | name = "bumpalo" 28 | version = "3.11.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" 31 | 32 | [[package]] 33 | name = "cc" 34 | version = "1.0.73" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 37 | 38 | [[package]] 39 | name = "dprint-core" 40 | version = "0.59.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "84600c297cc99fc088a9a916286d71915c988fa3a6f1bbc994ad9b93dde80c03" 43 | dependencies = [ 44 | "anyhow", 45 | "bumpalo", 46 | "indexmap", 47 | "rustc-hash", 48 | "serde", 49 | ] 50 | 51 | [[package]] 52 | name = "dprint-plugin-ebnf" 53 | version = "0.1.1" 54 | dependencies = [ 55 | "anyhow", 56 | "dprint-core", 57 | "ebnf-fmt", 58 | "heck-but-macros", 59 | "serde", 60 | "serde_json", 61 | ] 62 | 63 | [[package]] 64 | name = "ebnf-fmt" 65 | version = "0.1.0" 66 | dependencies = [ 67 | "ebnf-parser", 68 | "serde", 69 | "strum", 70 | ] 71 | 72 | [[package]] 73 | name = "ebnf-parser" 74 | version = "0.1.0" 75 | dependencies = [ 76 | "thiserror", 77 | ] 78 | 79 | [[package]] 80 | name = "hashbrown" 81 | version = "0.12.3" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 84 | 85 | [[package]] 86 | name = "heck" 87 | version = "0.3.3" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 90 | dependencies = [ 91 | "unicode-segmentation", 92 | ] 93 | 94 | [[package]] 95 | name = "heck" 96 | version = "0.4.0" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 99 | 100 | [[package]] 101 | name = "heck-but-macros" 102 | version = "0.0.1" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "77fddf3df190921d3eb12e2f4cff6e4c447aca603e44e4bf790473ea646a65b1" 105 | dependencies = [ 106 | "heck 0.3.3", 107 | ] 108 | 109 | [[package]] 110 | name = "indexmap" 111 | version = "1.9.1" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 114 | dependencies = [ 115 | "autocfg", 116 | "hashbrown", 117 | "serde", 118 | ] 119 | 120 | [[package]] 121 | name = "itoa" 122 | version = "1.0.3" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" 125 | 126 | [[package]] 127 | name = "memchr" 128 | version = "2.5.0" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 131 | 132 | [[package]] 133 | name = "proc-macro2" 134 | version = "1.0.46" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" 137 | dependencies = [ 138 | "unicode-ident", 139 | ] 140 | 141 | [[package]] 142 | name = "quote" 143 | version = "1.0.21" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 146 | dependencies = [ 147 | "proc-macro2", 148 | ] 149 | 150 | [[package]] 151 | name = "regex" 152 | version = "1.6.0" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 155 | dependencies = [ 156 | "aho-corasick", 157 | "memchr", 158 | "regex-syntax", 159 | ] 160 | 161 | [[package]] 162 | name = "regex-syntax" 163 | version = "0.6.27" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 166 | 167 | [[package]] 168 | name = "rustc-hash" 169 | version = "1.1.0" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 172 | 173 | [[package]] 174 | name = "rustversion" 175 | version = "1.0.9" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" 178 | 179 | [[package]] 180 | name = "ryu" 181 | version = "1.0.11" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 184 | 185 | [[package]] 186 | name = "serde" 187 | version = "1.0.145" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" 190 | dependencies = [ 191 | "serde_derive", 192 | ] 193 | 194 | [[package]] 195 | name = "serde_derive" 196 | version = "1.0.145" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" 199 | dependencies = [ 200 | "proc-macro2", 201 | "quote", 202 | "syn", 203 | ] 204 | 205 | [[package]] 206 | name = "serde_json" 207 | version = "1.0.85" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" 210 | dependencies = [ 211 | "itoa", 212 | "ryu", 213 | "serde", 214 | ] 215 | 216 | [[package]] 217 | name = "strum" 218 | version = "0.24.1" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" 221 | dependencies = [ 222 | "strum_macros", 223 | ] 224 | 225 | [[package]] 226 | name = "strum_macros" 227 | version = "0.24.3" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" 230 | dependencies = [ 231 | "heck 0.4.0", 232 | "proc-macro2", 233 | "quote", 234 | "rustversion", 235 | "syn", 236 | ] 237 | 238 | [[package]] 239 | name = "syn" 240 | version = "1.0.101" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" 243 | dependencies = [ 244 | "proc-macro2", 245 | "quote", 246 | "unicode-ident", 247 | ] 248 | 249 | [[package]] 250 | name = "thiserror" 251 | version = "1.0.37" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" 254 | dependencies = [ 255 | "thiserror-impl", 256 | ] 257 | 258 | [[package]] 259 | name = "thiserror-impl" 260 | version = "1.0.37" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" 263 | dependencies = [ 264 | "proc-macro2", 265 | "quote", 266 | "syn", 267 | ] 268 | 269 | [[package]] 270 | name = "tree-sitter" 271 | version = "0.20.9" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "d4423c784fe11398ca91e505cdc71356b07b1a924fc8735cfab5333afe3e18bc" 274 | dependencies = [ 275 | "cc", 276 | "regex", 277 | ] 278 | 279 | [[package]] 280 | name = "tree-sitter-ebnf" 281 | version = "0.1.0" 282 | dependencies = [ 283 | "cc", 284 | "tree-sitter", 285 | ] 286 | 287 | [[package]] 288 | name = "unicode-ident" 289 | version = "1.0.4" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" 292 | 293 | [[package]] 294 | name = "unicode-segmentation" 295 | version = "1.10.0" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" 298 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["crates/ebnf-parser", "crates/ebnf-fmt", "crates/dprint-plugin-ebnf", "crates/tree-sitter-ebnf"] 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EBNF 2 | 3 | Various tools for EBNF grammars 4 | 5 | ## Projects 6 | 7 | - [ebnf-parser](./crates/ebnf-parser): A Rust library for parsing EBNF grammars 8 | - [ebnf-fmt](./crates/ebnf-fmt): A Rust library for formatting EBNF grammars 9 | - [dprint-plugin-ebnf](./crates/dprint-plugin-ebnf): An EBNF plugin for 10 | [dprint](https://dprint.dev/) 11 | - [tree-sitter-ebnf](./crates/tree-sitter-ebnf): An EBNF parser for 12 | [tree-sitter](https://github.com/tree-sitter/tree-sitter) 13 | -------------------------------------------------------------------------------- /crates/dprint-plugin-ebnf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dprint-plugin-ebnf" 3 | version = "0.1.1" 4 | categories = ["development-tools"] 5 | edition = "2021" 6 | keywords = ["formatting", "formatter", "ebnf", "dprint"] 7 | license = "GPL-3.0-only" 8 | repository = "https://github.com/RubixDev/ebnf" 9 | description = "A dprint plugin for formatting the ISO 14977 EBNF notation" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [lib] 14 | crate-type = ["lib", "cdylib"] 15 | 16 | [dependencies] 17 | anyhow = "1.0.65" 18 | dprint-core = { version = "0.59.0", features = ["wasm"] } 19 | ebnf-fmt = { version = "0.1.0", path = "../ebnf-fmt", features = ["serde", "fromstr"] } 20 | heck-but-macros = "0.0.1" 21 | serde = { version = "1.0.145", features = ["derive"] } 22 | serde_json = "1.0.85" 23 | -------------------------------------------------------------------------------- /crates/dprint-plugin-ebnf/README.md: -------------------------------------------------------------------------------- 1 | # dprint-plugin-ebnf 2 | 3 | Format EBNF grammar notations through [dprint](https://dprint.dev/). 4 | 5 | ## Install 6 | 7 | Add the plugin to your config file by running `dprint config add RubixDev/ebnf`. 8 | 9 | Don't forget to add `ebnf` to your `includes` pattern. 10 | 11 | ## Configuration 12 | 13 | This plugin uses the `"ebnf"` config key. These options are available: 14 | 15 | | Name | Type | Default | Possible values | Description | 16 | | --------------------------- | ------------- | ----------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------- | 17 | | `lineWidth` | `u32` | global config or `100` | | Always wrap at the next possible point after this line width is reached | 18 | | `indentWidth` | `u8` | global config or `2` | | The number of spaces to indent multiline comments | 19 | | `newLineKind` | `NewLineKind` | global config or `"lf"` | `"auto"`, `"lf"`, `"crlf"`, `"system"` | The kind of line endings to use | 20 | | `quoteStyle` | `QuoteStyle` | `"Single"` | `"Single"`, `"Double"` | The preferred kind of quotes to use for terminal string | 21 | | `ignoreRuleCommentText` | `String` | `"dprint-ignore"` | | The text a comment should contain to ignore formatting for the next syntax rule | 22 | | `multilineCommentsMarkdown` | `bool` | `true` | `true`, `false` | Format multiline comments like markdown (requires `dprint-plugin-markdown` to be installed) | 23 | -------------------------------------------------------------------------------- /crates/dprint-plugin-ebnf/src/configuration.rs: -------------------------------------------------------------------------------- 1 | use dprint_core::configuration::{self, NewLineKind}; 2 | use ebnf_fmt::configuration::{NewlineKind, QuoteStyle}; 3 | use serde::Serialize; 4 | 5 | #[derive(Clone, Serialize)] 6 | #[serde(rename_all = "camelCase")] 7 | pub struct Configuration { 8 | pub line_width: u32, 9 | pub indent_width: u8, 10 | pub new_line_kind: NewLineKind, 11 | 12 | pub quote_style: QuoteStyle, 13 | pub ignore_rule_comment_text: String, 14 | pub multiline_comments_markdown: bool, 15 | } 16 | 17 | impl Configuration { 18 | pub fn to_fmt_config(&self, text: &str) -> ebnf_fmt::Configuration { 19 | ebnf_fmt::Configuration { 20 | line_width: self.line_width as usize, 21 | newline_kind: match configuration::resolve_new_line_kind(text, self.new_line_kind) { 22 | "\r\n" => NewlineKind::Windows, 23 | "\n" => NewlineKind::Unix, 24 | // Fall back to \n in case upstream function changes 25 | _ => NewlineKind::Unix, 26 | }, 27 | quote_style: self.quote_style, 28 | ignore_rule_comment_text: self.ignore_rule_comment_text.clone(), 29 | mutliline_comment_indent: self.indent_width as usize, 30 | } 31 | } 32 | } 33 | 34 | impl Default for Configuration { 35 | fn default() -> Self { 36 | let ebnf_fmt_default = ebnf_fmt::Configuration::default(); 37 | Self { 38 | line_width: ebnf_fmt_default.line_width as u32, 39 | indent_width: ebnf_fmt_default.mutliline_comment_indent as u8, 40 | new_line_kind: match ebnf_fmt_default.newline_kind { 41 | NewlineKind::Unix => NewLineKind::LineFeed, 42 | NewlineKind::Windows => NewLineKind::CarriageReturnLineFeed, 43 | }, 44 | quote_style: ebnf_fmt_default.quote_style, 45 | ignore_rule_comment_text: "dprint-ignore".to_string(), 46 | multiline_comments_markdown: true, 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crates/dprint-plugin-ebnf/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod configuration; 2 | mod plugin; 3 | mod resolve_config; 4 | -------------------------------------------------------------------------------- /crates/dprint-plugin-ebnf/src/plugin.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use dprint_core::{ 4 | configuration::{ConfigKeyMap, GlobalConfiguration, ResolveConfigurationResult}, 5 | plugins::{FormatResult, PluginInfo, SyncPluginHandler}, 6 | }; 7 | 8 | use crate::{configuration::Configuration, resolve_config::resolve_config}; 9 | 10 | pub struct EbnfPluginHandler; 11 | 12 | impl SyncPluginHandler for EbnfPluginHandler { 13 | fn resolve_config( 14 | &mut self, 15 | config: ConfigKeyMap, 16 | global_config: &GlobalConfiguration, 17 | ) -> ResolveConfigurationResult { 18 | resolve_config(config, global_config) 19 | } 20 | 21 | fn plugin_info(&mut self) -> PluginInfo { 22 | PluginInfo { 23 | name: env!("CARGO_PKG_NAME").to_string(), 24 | version: env!("CARGO_PKG_VERSION").to_string(), 25 | config_key: "ebnf".to_string(), 26 | file_extensions: vec!["ebnf".to_string()], 27 | file_names: vec![], 28 | help_url: concat!( 29 | env!("CARGO_PKG_REPOSITORY"), 30 | "/tree/main/crates/dprint-plugin-ebnf#readme" 31 | ) 32 | .to_string(), 33 | config_schema_url: "".to_string(), 34 | update_url: Some("https://plugins.dprint.dev/RubixDev/ebnf/latest.json".to_string()), 35 | } 36 | } 37 | 38 | fn license_text(&mut self) -> String { 39 | include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/LICENSE")).into() 40 | } 41 | 42 | fn format( 43 | &mut self, 44 | _file_path: &Path, 45 | file_text: &str, 46 | config: &Configuration, 47 | mut format_with_host: impl FnMut(&Path, String, &ConfigKeyMap) -> FormatResult, 48 | ) -> FormatResult { 49 | let fmt_config = config.to_fmt_config(file_text); 50 | 51 | let result = 52 | ebnf_fmt::format_code_with_comment_formatter(file_text, &fmt_config, move |text| { 53 | let mut md_config = ConfigKeyMap::new(); 54 | md_config.insert( 55 | "lineWidth".into(), 56 | ((fmt_config.line_width - fmt_config.mutliline_comment_indent) as i32).into(), 57 | ); 58 | match format_with_host(Path::new("file.md"), text.clone(), &md_config) { 59 | Ok(Some(output)) => output, 60 | _ => text, 61 | } 62 | })?; 63 | if result == file_text { 64 | Ok(None) 65 | } else { 66 | Ok(Some(result)) 67 | } 68 | } 69 | } 70 | 71 | #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] 72 | dprint_core::generate_plugin_code!(EbnfPluginHandler, EbnfPluginHandler); 73 | -------------------------------------------------------------------------------- /crates/dprint-plugin-ebnf/src/resolve_config.rs: -------------------------------------------------------------------------------- 1 | use dprint_core::configuration::{ 2 | self, ConfigKeyMap, GlobalConfiguration, ResolveConfigurationResult, 3 | }; 4 | 5 | use crate::configuration::Configuration; 6 | 7 | macro_rules! resolve_config { 8 | ($config:ident, $global_config:ident, $diagnostics:ident, $default_config:ident; $($field:ident $($global:ident)?),* $(,)?) => { 9 | Configuration {$( 10 | $field: configuration::get_value( 11 | &mut $config, 12 | heck_but_macros::stringify_mixed_case!($field), 13 | resolve_config!(@global $global_config, $default_config, $($global)? $field), 14 | &mut $diagnostics, 15 | ), 16 | )*} 17 | }; 18 | (@global $global_config:ident, $default_config:ident, global $field:ident) => { 19 | $global_config.$field.unwrap_or($default_config.$field) 20 | }; 21 | (@global $global_config:ident, $default_config:ident, $field:ident) => { 22 | $default_config.$field 23 | }; 24 | } 25 | 26 | pub fn resolve_config( 27 | config: ConfigKeyMap, 28 | global_config: &GlobalConfiguration, 29 | ) -> ResolveConfigurationResult { 30 | let mut config = config; 31 | let mut diagnostics = vec![]; 32 | 33 | let default_config = Configuration::default(); 34 | let resolved_config = resolve_config!( 35 | config, global_config, diagnostics, default_config; 36 | line_width global, 37 | indent_width global, 38 | new_line_kind global, 39 | quote_style, 40 | ignore_rule_comment_text, 41 | multiline_comments_markdown, 42 | ); 43 | 44 | diagnostics.extend(configuration::get_unknown_property_diagnostics(config)); 45 | 46 | ResolveConfigurationResult { 47 | diagnostics, 48 | config: resolved_config, 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /crates/ebnf-fmt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ebnf-fmt" 3 | version = "0.1.0" 4 | categories = ["development-tools"] 5 | edition = "2021" 6 | keywords = ["formatter", "ebnf"] 7 | license = "GPL-3.0-only" 8 | repository = "https://github.com/RubixDev/ebnf" 9 | description = "A formatting library for the ISO 14977 EBNF notation" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [features] 14 | default = [] 15 | fromstr = ["strum"] 16 | 17 | [dependencies] 18 | ebnf-parser = { version = "0.1.0", path = "../ebnf-parser" } 19 | serde = { version = "1.0.145", features = ["derive"], optional = true } 20 | strum = { version = "0.24.1", features = ["derive"], optional = true } 21 | -------------------------------------------------------------------------------- /crates/ebnf-fmt/LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . -------------------------------------------------------------------------------- /crates/ebnf-fmt/src/configuration.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | #[derive(Clone, Debug)] 4 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 5 | #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 6 | pub struct Configuration { 7 | pub line_width: usize, 8 | pub newline_kind: NewlineKind, 9 | pub quote_style: QuoteStyle, 10 | pub ignore_rule_comment_text: String, 11 | pub mutliline_comment_indent: usize, 12 | } 13 | 14 | impl Default for Configuration { 15 | fn default() -> Self { 16 | Self { 17 | line_width: 100, 18 | newline_kind: NewlineKind::Unix, 19 | quote_style: QuoteStyle::Single, 20 | ignore_rule_comment_text: "ebnf-fmt ignore".to_string(), 21 | mutliline_comment_indent: 2, 22 | } 23 | } 24 | } 25 | 26 | #[derive(Clone, Copy, Debug)] 27 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 28 | #[cfg_attr(feature = "strum", derive(strum::EnumString))] 29 | pub enum QuoteStyle { 30 | Single, 31 | Double, 32 | } 33 | 34 | #[derive(Clone, Copy, Debug)] 35 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 36 | #[cfg_attr(feature = "strum", derive(strum::EnumString))] 37 | pub enum NewlineKind { 38 | Unix, 39 | Windows, 40 | } 41 | -------------------------------------------------------------------------------- /crates/ebnf-fmt/src/formatter.rs: -------------------------------------------------------------------------------- 1 | use std::vec; 2 | 3 | #[cfg(debug_assertions)] 4 | use ebnf_parser::Token; 5 | use ebnf_parser::{ast::*, CommentMap, ParseResult, TokenKind}; 6 | 7 | use crate::configuration::{Configuration, NewlineKind, QuoteStyle}; 8 | 9 | enum Special { 10 | /// A newline according to the current config 11 | Newline, 12 | /// The current indent as spaces 13 | Indent, 14 | /// A Newline followed by an Indent 15 | NewlineIndent, 16 | /// The current indent as spaces minus the given length 17 | RestIndent(usize), 18 | /// A MergingSpace or NewlineIndent depending on the current line length 19 | SpaceOrNewline, 20 | /// A space when the previous character is not a space 21 | MergingSpace, 22 | } 23 | 24 | enum PushKind<'a> { 25 | Char(char), 26 | Str(&'a str), 27 | Special(Special), 28 | } 29 | 30 | impl From for PushKind<'_> { 31 | fn from(c: char) -> Self { 32 | Self::Char(c) 33 | } 34 | } 35 | 36 | impl<'a> From<&'a str> for PushKind<'a> { 37 | fn from(s: &'a str) -> Self { 38 | Self::Str(s) 39 | } 40 | } 41 | 42 | impl From for PushKind<'_> { 43 | fn from(s: Special) -> Self { 44 | Self::Special(s) 45 | } 46 | } 47 | 48 | pub struct Formatter<'src, 'config, CommentFormatter> 49 | where 50 | CommentFormatter: FnMut(String) -> String, 51 | { 52 | syntax: Option>, 53 | text: &'src str, 54 | config: &'config Configuration, 55 | indent: usize, 56 | output: String, 57 | curr_line_len: usize, 58 | #[cfg(debug_assertions)] 59 | tokens: vec::IntoIter>, 60 | #[cfg(debug_assertions)] 61 | curr_tok: Option>, 62 | tok_index: usize, 63 | comments: CommentMap<'src>, 64 | /// Is true while ignoring formatting for a rule to prevent pushing to `output` while still 65 | /// progressing `tokens`. 66 | no_push: bool, 67 | /// A custom function to format the text inside multiline comments 68 | comment_formatter: CommentFormatter, 69 | } 70 | 71 | impl<'src, 'config, CommentFormatter> Formatter<'src, 'config, CommentFormatter> 72 | where 73 | CommentFormatter: FnMut(String) -> String, 74 | { 75 | pub fn new( 76 | parse_result: ParseResult<'src>, 77 | text: &'src str, 78 | config: &'config Configuration, 79 | comment_formatter: CommentFormatter, 80 | ) -> Self { 81 | Self { 82 | syntax: Some(parse_result.syntax), 83 | text, 84 | config, 85 | indent: 0, 86 | output: String::new(), 87 | curr_line_len: 0, 88 | #[cfg(debug_assertions)] 89 | tokens: parse_result.tokens.into_iter(), 90 | #[cfg(debug_assertions)] 91 | curr_tok: None, 92 | tok_index: usize::MAX, 93 | comments: parse_result.comments, 94 | no_push: false, 95 | comment_formatter, 96 | } 97 | } 98 | 99 | pub fn format(mut self) -> String { 100 | self.next_tok(); 101 | let syntax = self 102 | .syntax 103 | .take() 104 | .expect("set to Some(..) in Formatter::new and this method is only called once"); 105 | self.format_syntax(syntax); 106 | self.output 107 | } 108 | 109 | fn next_tok(&mut self) { 110 | #[cfg(debug_assertions)] 111 | { 112 | self.curr_tok = self.tokens.next(); 113 | } 114 | self.tok_index = self.tok_index.wrapping_add(1); 115 | } 116 | 117 | fn push(&mut self, kind: PushKind) { 118 | match kind { 119 | PushKind::Char(c) => self.push_char(c), 120 | PushKind::Str(s) => self.push_str(s), 121 | PushKind::Special(s) => self.push_special(s), 122 | } 123 | } 124 | 125 | fn push_char(&mut self, char: char) { 126 | if self.no_push { 127 | return; 128 | } 129 | self.curr_line_len += 1; 130 | self.output.push(char); 131 | } 132 | 133 | fn push_str(&mut self, text: &str) { 134 | if self.no_push { 135 | return; 136 | } 137 | self.curr_line_len += text.chars().count(); 138 | self.output.push_str(text); 139 | } 140 | 141 | fn push_special(&mut self, special: Special) { 142 | if self.no_push { 143 | return; 144 | } 145 | match special { 146 | Special::Newline => { 147 | // Trim trailing spaces 148 | self.output 149 | .truncate(self.output.trim_end_matches(' ').len()); 150 | 151 | match self.config.newline_kind { 152 | NewlineKind::Unix => self.output.push('\n'), 153 | NewlineKind::Windows => self.output.push_str("\r\n"), 154 | }; 155 | self.curr_line_len = 0; 156 | } 157 | Special::Indent => self.push_str(&" ".repeat(self.indent)), 158 | Special::NewlineIndent => { 159 | self.push_special(Special::Newline); 160 | self.push_special(Special::Indent); 161 | } 162 | Special::RestIndent(len) => self.push_str(&" ".repeat(self.indent - len)), 163 | Special::SpaceOrNewline => { 164 | if self.curr_line_len >= self.config.line_width { 165 | self.push_special(Special::NewlineIndent); 166 | } else { 167 | self.push_special(Special::MergingSpace); 168 | } 169 | } 170 | Special::MergingSpace => { 171 | if !self.output.ends_with(' ') { 172 | self.push_char(' '); 173 | } 174 | } 175 | } 176 | } 177 | 178 | fn push_token(&mut self, token: TokenKind, prefix: Option, suffix: Option) { 179 | self.check_comments(); 180 | #[cfg(debug_assertions)] 181 | { 182 | debug_assert_eq!( 183 | token, 184 | self.curr_tok 185 | .as_ref() 186 | .unwrap_or_else(|| panic!("expected TokenKind {:?} but was None", token)) 187 | .kind 188 | ); 189 | } 190 | self.next_tok(); 191 | 192 | if let Some(prefix) = prefix { 193 | self.push(prefix); 194 | } 195 | match token { 196 | TokenKind::Terminal(text) => { 197 | let quote = match self.config.quote_style { 198 | QuoteStyle::Single if text.contains('\'') => '"', 199 | QuoteStyle::Single => '\'', 200 | QuoteStyle::Double if text.contains('"') => '\'', 201 | QuoteStyle::Double => '"', 202 | }; 203 | self.push_char(quote); 204 | self.push_str(text); 205 | self.push_char(quote); 206 | } 207 | _ => self.push_str(&token.to_string()), 208 | } 209 | if let Some(suffix) = suffix { 210 | self.push(suffix); 211 | } 212 | } 213 | 214 | fn check_comments(&mut self) { 215 | if let Some(comments) = self.comments.remove(&self.tok_index) { 216 | let mut prev_comment: Option = None; 217 | for comment in comments { 218 | // Insert blank line when there was one before 219 | if let Some(prev_comment) = prev_comment { 220 | let text_between = &self.text[prev_comment.span.end..comment.span.start]; 221 | if text_between.contains("\n\n") || text_between.contains("\r\n\r\n") { 222 | self.push_special(Special::Newline); 223 | } 224 | } 225 | 226 | self.format_comment(comment.text); 227 | prev_comment = Some(comment); 228 | } 229 | } 230 | } 231 | 232 | fn format_syntax(&mut self, node: Syntax) { 233 | let mut blocks: Vec> = vec![vec![]]; 234 | for node in node.rules { 235 | if let Some(prev_node) = blocks 236 | .last() 237 | .expect("Vector initialized with one element and never remove any element") 238 | .last() 239 | { 240 | let text_between = &self.text[prev_node.span.end..node.span.start]; 241 | if text_between.contains("\n\n") || text_between.contains("\r\n\r\n") { 242 | blocks.push(vec![]); 243 | } 244 | } 245 | blocks 246 | .last_mut() 247 | .expect("Vector initialized with one element and never remove any element") 248 | .push(node); 249 | } 250 | let last = blocks.len().saturating_sub(1); 251 | for (index, block) in blocks.into_iter().enumerate() { 252 | self.format_rule_block(block); 253 | if index != last { 254 | self.push_special(Special::Newline); 255 | } 256 | } 257 | self.check_comments(); 258 | } 259 | 260 | fn format_rule_block(&mut self, block: Vec) { 261 | self.indent = block 262 | .iter() 263 | .map(|rule| rule.name.len()) 264 | .max() 265 | .expect("Every block consists of at least one rule") 266 | + 1; 267 | for rule in block { 268 | self.format_syntax_rule(rule); 269 | } 270 | } 271 | 272 | fn format_syntax_rule(&mut self, node: SyntaxRule) { 273 | // Check for ignore comment 274 | if let Some(comments) = self.comments.get(&self.tok_index) { 275 | if comments 276 | .iter() 277 | .any(|comment| comment.text.contains(&self.config.ignore_rule_comment_text)) 278 | { 279 | self.check_comments(); 280 | let raw_text = &self.text[node.span.start..node.span.end]; 281 | for line in raw_text.split('\n') { 282 | self.push_str(line.trim_end_matches('\r')); 283 | self.push_special(Special::Newline); 284 | } 285 | self.no_push = true; 286 | } 287 | } 288 | 289 | // Format 290 | self.push_token(TokenKind::Identifier(node.name), None, None); 291 | self.push_special(Special::RestIndent(node.name.len())); 292 | self.push_token(TokenKind::Equal, None, Some(' '.into())); 293 | self.format_definitions_list(node.definitions); 294 | self.push_token( 295 | TokenKind::Semicolon, 296 | Some(Special::MergingSpace.into()), 297 | None, 298 | ); 299 | self.push_special(Special::Newline); 300 | 301 | // Allow further formatting 302 | self.no_push = false; 303 | } 304 | 305 | fn format_definitions_list(&mut self, node: Vec) { 306 | // Format inline when every definition has length 1 307 | let inline = node.iter().all(|node| node.terms.len() == 1); 308 | 309 | let last = node.len().saturating_sub(1); 310 | for (index, node) in node.into_iter().enumerate() { 311 | self.format_single_definition(node); 312 | if index != last { 313 | self.push_token( 314 | TokenKind::Pipe, 315 | Some(match inline { 316 | true => Special::SpaceOrNewline.into(), 317 | false => Special::NewlineIndent.into(), 318 | }), 319 | Some(' '.into()), 320 | ); 321 | } 322 | } 323 | } 324 | 325 | fn format_single_definition(&mut self, node: SingleDefinition) { 326 | let last = node.terms.len().saturating_sub(1); 327 | for (index, node) in node.terms.into_iter().enumerate() { 328 | self.format_syntactic_term(node); 329 | if index != last { 330 | self.push_token( 331 | TokenKind::Comma, 332 | Some(Special::SpaceOrNewline.into()), 333 | Some(' '.into()), 334 | ); 335 | } 336 | } 337 | } 338 | 339 | fn format_syntactic_term(&mut self, node: SyntacticTerm) { 340 | let prefix = match (&node.factor, &node.exception) { 341 | ( 342 | SyntacticFactor { 343 | primary: 344 | SyntacticPrimary { 345 | kind: SyntacticPrimaryKind::RepeatedSequence(_), 346 | .. 347 | }, 348 | .. 349 | }, 350 | .., 351 | ) => None, 352 | _ => Some(Special::MergingSpace.into()), 353 | }; 354 | 355 | self.format_syntactic_factor(node.factor); 356 | if let Some(exception) = node.exception { 357 | self.push_token(TokenKind::Dash, prefix, Some(' '.into())); 358 | self.format_syntactic_factor(exception); 359 | } 360 | } 361 | 362 | fn format_syntactic_factor(&mut self, node: SyntacticFactor) { 363 | if let Some(repetition) = node.repetition { 364 | self.push_token(TokenKind::Integer(repetition), None, None); 365 | self.push_token( 366 | TokenKind::Star, 367 | Some(Special::MergingSpace.into()), 368 | Some(' '.into()), 369 | ); 370 | } 371 | self.format_syntactic_primary(node.primary); 372 | } 373 | 374 | fn format_syntactic_primary(&mut self, node: SyntacticPrimary) { 375 | match node.kind { 376 | SyntacticPrimaryKind::OptionalSequence(node) => self.format_delimited_definitions_list( 377 | node, 378 | TokenKind::LBracket, 379 | TokenKind::RBracket, 380 | ), 381 | SyntacticPrimaryKind::RepeatedSequence(node) => { 382 | self.format_delimited_definitions_list(node, TokenKind::LBrace, TokenKind::RBrace) 383 | } 384 | SyntacticPrimaryKind::GroupedSequence(node) => { 385 | self.format_delimited_definitions_list(node, TokenKind::LParen, TokenKind::RParen) 386 | } 387 | SyntacticPrimaryKind::MetaIdentifier(name) => { 388 | self.push_token(TokenKind::Identifier(name), None, None) 389 | } 390 | SyntacticPrimaryKind::TerminalString(text) => { 391 | self.push_token(TokenKind::Terminal(text), None, None) 392 | } 393 | SyntacticPrimaryKind::SpecialSequence(text) => { 394 | self.push_token(TokenKind::SpecialSeq(text), None, None) 395 | } 396 | SyntacticPrimaryKind::EmptySequence => {} 397 | } 398 | } 399 | 400 | fn format_delimited_definitions_list( 401 | &mut self, 402 | node: Vec, 403 | open: TokenKind, 404 | close: TokenKind, 405 | ) { 406 | let saved_indent = self.indent; 407 | self.indent = self.curr_line_len; 408 | self.push_token(open, None, Some(' '.into())); 409 | self.format_definitions_list(node); 410 | self.push_token(close, Some(Special::MergingSpace.into()), None); 411 | self.indent = saved_indent; 412 | } 413 | 414 | fn format_comment(&mut self, mut text: &str) { 415 | if self.curr_line_len != 0 { 416 | self.push_special(Special::MergingSpace); 417 | self.push_str("(* "); 418 | self.push_str(text.trim()); 419 | self.push_str(" *) "); 420 | } else if text.contains('\n') { 421 | let saved_indent = self.indent; 422 | self.indent = self.config.mutliline_comment_indent; 423 | 424 | self.push_str("(*"); 425 | self.push_special(Special::Newline); 426 | 427 | let current_comment_indent = text 428 | .trim_start_matches(|c| c == '\n' || c == '\r') 429 | .chars() 430 | .take_while(|c| *c == ' ') 431 | .count(); 432 | text = text.trim(); 433 | 434 | let mut trimmed_lines = vec![]; 435 | for line in text.split('\n') { 436 | // Trim any existing indent up to `current_comment_indent` 437 | let mut line_start = 0; 438 | while line_start < current_comment_indent 439 | && line.as_bytes().get(line_start) == Some(&b' ') 440 | { 441 | line_start += 1; 442 | } 443 | 444 | trimmed_lines.push(line[line_start..].trim_end_matches('\r')); 445 | } 446 | 447 | let formatted = (self.comment_formatter)(trimmed_lines.join("\n")); 448 | for line in formatted.trim().split('\n') { 449 | if !line.trim().is_empty() { 450 | self.push_special(Special::Indent); 451 | } 452 | self.push_str(line.trim_end_matches('\r')); 453 | self.push_special(Special::Newline); 454 | } 455 | 456 | self.push_str("*)"); 457 | self.indent = saved_indent; 458 | self.push_special(Special::Newline); 459 | } else { 460 | self.push_str("(* "); 461 | self.push_str(text.trim()); 462 | self.push_str(" *)"); 463 | self.push_special(Special::Newline); 464 | } 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /crates/ebnf-fmt/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod configuration; 2 | mod formatter; 3 | 4 | pub use configuration::Configuration; 5 | use ebnf_parser::{error::SyntaxError, Lexer, Parser}; 6 | pub use formatter::Formatter; 7 | 8 | pub fn format_code(text: &str, config: &Configuration) -> Result { 9 | Ok(Formatter::new( 10 | Parser::new(Lexer::new(text)).parse()?, 11 | text, 12 | config, 13 | |text| text, 14 | ) 15 | .format()) 16 | } 17 | 18 | pub fn format_code_with_comment_formatter( 19 | text: &str, 20 | config: &Configuration, 21 | comment_formatter: impl FnMut(String) -> String, 22 | ) -> Result { 23 | Ok(Formatter::new( 24 | Parser::new(Lexer::new(text)).parse()?, 25 | text, 26 | config, 27 | comment_formatter, 28 | ) 29 | .format()) 30 | } 31 | 32 | #[cfg(test)] 33 | mod tests { 34 | use crate::configuration::Configuration; 35 | 36 | use super::*; 37 | 38 | #[test] 39 | fn format() { 40 | let input = include_str!("../../ebnf-parser/grammar.ebnf"); 41 | let output = format_code(input, &Configuration::default()).unwrap(); 42 | println!("{output}"); 43 | assert!(!output.ends_with("\n\n")); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /crates/ebnf-parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ebnf-parser" 3 | version = "0.1.0" 4 | edition = "2021" 5 | keywords = ["parser", "ebnf"] 6 | license = "GPL-3.0-only" 7 | repository = "https://github.com/RubixDev/ebnf" 8 | description = "An LL(1) parser for the ISO 14977 EBNF notation" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | thiserror = "1.0.37" 14 | -------------------------------------------------------------------------------- /crates/ebnf-parser/LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . -------------------------------------------------------------------------------- /crates/ebnf-parser/grammar.ebnf: -------------------------------------------------------------------------------- 1 | letter = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' 2 | | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' 3 | | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' 4 | | 'w' | 'x' | 'y' | 'z' ; 5 | digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ; 6 | (* The ISO 14977 standard only allows characters from the ISO/IEC 646:1991 character set *) 7 | character = ? any utf8 character ? ; 8 | 9 | repetition_symbol = '*' ; 10 | except_symbol = '-' ; 11 | concatenate_symbol = ',' ; 12 | definition_separator_symbol = '|' ; 13 | defining_symbol = '=' ; 14 | terminator_symbol = ';' ; 15 | first_quote_symbol = "'" ; 16 | second_quote_symbol = '"' ; 17 | start_comment_symbol = '(*' ; 18 | end_comment_symbol = '*)' ; 19 | start_group_symbol = '(' ; 20 | end_group_symbol = ')' ; 21 | start_option_symbol = '[' ; 22 | end_option_symbol = ']' ; 23 | start_repeat_symbol = '{' ; 24 | end_repeat_symbol = '}' ; 25 | special_sequence_symbol = '?' ; 26 | 27 | syntax = { syntax_rule }- ; 28 | syntax_rule = meta_identifier , defining_symbol , definitions_list , terminator_symbol ; 29 | definitions_list = single_definition , { definition_separator_symbol , single_definition } ; 30 | single_definition = syntactic_term , { concatenate_symbol , syntactic_term } ; 31 | syntactic_term = syntactic_factor , [ except_symbol , syntactic_exception ] ; 32 | syntactic_exception = syntactic_factor (* but only regular grammar *) ; 33 | syntactic_factor = [ integer , repetition_symbol ] , syntactic_primary ; 34 | integer = { digit }- ; 35 | syntactic_primary = optional_sequence | repeated_sequence | grouped_sequence | meta_identifier 36 | | terminal_string | special_sequence | empty_sequence ; 37 | optional_sequence = start_option_symbol , definitions_list , end_option_symbol ; 38 | repeated_sequence = start_repeat_symbol , definitions_list , end_repeat_symbol ; 39 | grouped_sequence = start_group_symbol , definitions_list , end_group_symbol ; 40 | meta_identifier = letter , { meta_identifier_character } ; 41 | (* The underscore is not part of the ISO 14977 standard *) 42 | meta_identifier_character = letter | digit | '_' ; 43 | terminal_string = first_quote_symbol , { first_terminal_character }- , first_quote_symbol 44 | | second_quote_symbol , { second_terminal_character }- , second_quote_symbol ; 45 | first_terminal_character = character - first_quote_symbol ; 46 | second_terminal_character = character - second_quote_symbol ; 47 | special_sequence = special_sequence_symbol , { special_sequence_character } , special_sequence_symbol ; 48 | special_sequence_character = character - special_sequence_symbol ; 49 | empty_sequence = ; 50 | 51 | (* Comments are not part of the syntax tree *) 52 | comment = start_comment_symbol , { character } , end_comment_symbol ; 53 | -------------------------------------------------------------------------------- /crates/ebnf-parser/src/ast.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | span::Span, 3 | token::{Token, TokenKind}, 4 | }; 5 | 6 | #[derive(Debug, PartialEq, Clone)] 7 | pub struct Syntax<'src> { 8 | pub span: Span, 9 | pub rules: Vec>, 10 | } 11 | 12 | #[derive(Debug, PartialEq, Clone)] 13 | pub struct SyntaxRule<'src> { 14 | pub span: Span, 15 | pub name: &'src str, 16 | pub definitions: Vec>, 17 | } 18 | 19 | #[derive(Debug, PartialEq, Clone)] 20 | pub struct SingleDefinition<'src> { 21 | pub span: Span, 22 | pub terms: Vec>, 23 | } 24 | 25 | #[derive(Debug, PartialEq, Clone)] 26 | pub struct SyntacticTerm<'src> { 27 | pub span: Span, 28 | pub factor: SyntacticFactor<'src>, 29 | pub exception: Option>, 30 | } 31 | 32 | pub type SyntacticException<'src> = SyntacticFactor<'src>; 33 | 34 | #[derive(Debug, PartialEq, Clone)] 35 | pub struct SyntacticFactor<'src> { 36 | pub span: Span, 37 | pub repetition: Option, 38 | pub primary: SyntacticPrimary<'src>, 39 | } 40 | 41 | #[derive(Debug, PartialEq, Clone)] 42 | pub struct SyntacticPrimary<'src> { 43 | pub span: Span, 44 | pub kind: SyntacticPrimaryKind<'src>, 45 | } 46 | 47 | #[derive(Debug, PartialEq, Clone)] 48 | pub enum SyntacticPrimaryKind<'src> { 49 | OptionalSequence(Vec>), 50 | RepeatedSequence(Vec>), 51 | GroupedSequence(Vec>), 52 | MetaIdentifier(&'src str), 53 | TerminalString(&'src str), 54 | SpecialSequence(&'src str), 55 | EmptySequence, 56 | } 57 | 58 | #[derive(Debug, PartialEq, Eq, Clone)] 59 | pub struct Comment<'src> { 60 | pub span: Span, 61 | pub text: &'src str, 62 | } 63 | 64 | impl<'src> TryFrom> for Comment<'src> { 65 | type Error = &'static str; 66 | 67 | fn try_from(value: Token<'src>) -> Result { 68 | match value.kind { 69 | TokenKind::Comment(text) => Ok(Comment { 70 | span: value.span, 71 | text, 72 | }), 73 | _ => Err("Comment node can only be constructed from Comment TokenKind"), 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /crates/ebnf-parser/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use thiserror::Error; 4 | 5 | use crate::span::Span; 6 | 7 | #[derive(Debug, Clone, Error)] 8 | #[error("invalid syntax at {span}: {message}")] 9 | pub struct SyntaxError { 10 | pub span: Span, 11 | pub message: String, 12 | } 13 | 14 | impl SyntaxError { 15 | pub(crate) fn new(span: Span, message: Cow) -> Self { 16 | Self { 17 | span, 18 | message: message.into_owned(), 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /crates/ebnf-parser/src/lexer.rs: -------------------------------------------------------------------------------- 1 | use std::{mem, str::Chars}; 2 | 3 | use crate::{ 4 | error::SyntaxError, 5 | span::Span, 6 | token::{Token, TokenKind}, 7 | }; 8 | 9 | macro_rules! simple_token { 10 | ($self:ident, $token:expr) => {{ 11 | let start = $self.index; 12 | $self.next(); 13 | Ok(Token::new($token, Span::new(start, $self.index))) 14 | }}; 15 | } 16 | 17 | pub struct Lexer<'src> { 18 | text: &'src str, 19 | src: Chars<'src>, 20 | curr_char: Option, 21 | next_char: Option, 22 | pub(crate) index: usize, 23 | } 24 | 25 | impl<'src> Lexer<'src> { 26 | pub fn new(text: &'src str) -> Self { 27 | let mut lexer = Lexer { 28 | text, 29 | src: text.chars(), 30 | curr_char: None, 31 | next_char: None, 32 | index: 0, 33 | }; 34 | lexer.next(); 35 | lexer.next(); 36 | lexer 37 | } 38 | 39 | fn next(&mut self) { 40 | if let Some(curr_char) = self.curr_char { 41 | self.index += curr_char.len_utf8(); 42 | } 43 | mem::swap(&mut self.curr_char, &mut self.next_char); 44 | self.next_char = self.src.next(); 45 | } 46 | 47 | pub fn next_token(&mut self) -> Result>, SyntaxError> { 48 | while let Some(' ' | '\n' | '\t' | '\r') = self.curr_char { 49 | self.next(); 50 | } 51 | if let Some(curr_char) = self.curr_char { 52 | let token_result = match curr_char { 53 | '{' => simple_token!(self, TokenKind::LBrace), 54 | '}' => simple_token!(self, TokenKind::RBrace), 55 | '[' => simple_token!(self, TokenKind::LBracket), 56 | ']' => simple_token!(self, TokenKind::RBracket), 57 | '(' if self.next_char != Some('*') => simple_token!(self, TokenKind::LParen), 58 | ')' => simple_token!(self, TokenKind::RParen), 59 | '|' => simple_token!(self, TokenKind::Pipe), 60 | ',' => simple_token!(self, TokenKind::Comma), 61 | ';' => simple_token!(self, TokenKind::Semicolon), 62 | '=' => simple_token!(self, TokenKind::Equal), 63 | '*' => simple_token!(self, TokenKind::Star), 64 | '-' => simple_token!(self, TokenKind::Dash), 65 | '(' => self.parse_comment(), 66 | '\'' | '"' => self.parse_terminal(), 67 | '?' => self.parse_special_seq(), 68 | c if c.is_ascii_alphabetic() => self.parse_identifier(), 69 | c if c.is_ascii_digit() => self.parse_integer(), 70 | c => { 71 | let span_start = self.index; 72 | self.next(); 73 | Err(SyntaxError::new( 74 | Span::new(span_start, self.index), 75 | format!("Illegal character '{}'", c).into(), 76 | )) 77 | } 78 | }; 79 | match token_result { 80 | Ok(token) => Ok(Some(token)), 81 | Err(err) => Err(err), 82 | } 83 | } else { 84 | Ok(None) 85 | } 86 | } 87 | 88 | fn delimeted_str(&mut self, delimeter: Option) -> &'src str { 89 | self.next(); // opening delimeter 90 | let content_start = self.index; 91 | while self.curr_char.is_some() && self.curr_char != delimeter { 92 | self.next(); 93 | } 94 | let content_end = self.index; 95 | self.next(); // closing delimeter 96 | &self.text[content_start..content_end] 97 | } 98 | 99 | fn parse_comment(&mut self) -> Result, SyntaxError> { 100 | debug_assert!( 101 | self.curr_char == Some('(') && self.next_char == Some('*'), 102 | "Expected '(' and '*', was {:?} and {:?}", 103 | self.curr_char, 104 | self.next_char, 105 | ); 106 | 107 | let span_start = self.index; 108 | 109 | self.next(); 110 | self.next(); 111 | let content_start = self.index; 112 | while self.curr_char.is_some() 113 | && !(self.curr_char == Some('*') && self.next_char == Some(')')) 114 | { 115 | self.next(); 116 | } 117 | let content_end = self.index; 118 | self.next(); 119 | self.next(); 120 | 121 | let content = &self.text[content_start..content_end]; 122 | 123 | Ok(Token::new( 124 | TokenKind::Comment(content), 125 | Span::new(span_start, self.index), 126 | )) 127 | } 128 | 129 | fn parse_terminal(&mut self) -> Result, SyntaxError> { 130 | debug_assert!( 131 | self.curr_char == Some('\'') || self.curr_char == Some('"'), 132 | "Expected quote, was {:?}", 133 | self.curr_char, 134 | ); 135 | 136 | let quote = self.curr_char; 137 | let span_start = self.index; 138 | let content = self.delimeted_str(quote).trim(); 139 | 140 | Ok(Token::new( 141 | TokenKind::Terminal(content), 142 | Span::new(span_start, self.index), 143 | )) 144 | } 145 | 146 | fn parse_special_seq(&mut self) -> Result, SyntaxError> { 147 | debug_assert!( 148 | self.curr_char == Some('?'), 149 | "Expected '?', was {:?}", 150 | self.curr_char, 151 | ); 152 | 153 | let span_start = self.index; 154 | let content = self.delimeted_str(Some('?')).trim(); 155 | 156 | Ok(Token::new( 157 | TokenKind::SpecialSeq(content), 158 | Span::new(span_start, self.index), 159 | )) 160 | } 161 | 162 | fn parse_identifier(&mut self) -> Result, SyntaxError> { 163 | debug_assert!( 164 | self.curr_char.map_or(false, |c| c.is_ascii_alphabetic()), 165 | "Expected letter, was {:?}", 166 | self.curr_char, 167 | ); 168 | 169 | let span_start = self.index; 170 | let content_start = self.index; 171 | self.next(); // first letter 172 | while self 173 | .curr_char 174 | .map_or(false, |c| c.is_ascii_alphanumeric() || c == '_') 175 | { 176 | self.next(); 177 | } 178 | let content_end = self.index; 179 | let content = &self.text[content_start..content_end]; 180 | 181 | Ok(Token::new( 182 | TokenKind::Identifier(content), 183 | Span::new(span_start, self.index), 184 | )) 185 | } 186 | 187 | fn parse_integer(&mut self) -> Result, SyntaxError> { 188 | debug_assert!( 189 | self.curr_char.map_or(false, |c| c.is_ascii_digit()), 190 | "Expected digit, was {:?}", 191 | self.curr_char, 192 | ); 193 | 194 | let span_start = self.index; 195 | let content_start = self.index; 196 | self.next(); // first digit 197 | while self.curr_char.map_or(false, |c| c.is_ascii_digit()) { 198 | self.next(); 199 | } 200 | let content_end = self.index; 201 | let slice = &self.text[content_start..content_end]; 202 | let num = match slice.parse() { 203 | Ok(num) => num, 204 | Err(_) => { 205 | return Err(SyntaxError::new( 206 | Span::new(span_start, self.index), 207 | "Number does not fit into `usize` type".into(), 208 | )) 209 | } 210 | }; 211 | 212 | Ok(Token::new( 213 | TokenKind::Integer(num), 214 | Span::new(span_start, self.index), 215 | )) 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /crates/ebnf-parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod ast; 2 | pub mod error; 3 | mod lexer; 4 | mod parser; 5 | pub mod span; 6 | mod token; 7 | 8 | pub use lexer::Lexer; 9 | pub use parser::*; 10 | pub use token::*; 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use super::*; 15 | 16 | #[test] 17 | fn lex() { 18 | // let mut lexer = lexer::Lexer::new(include_str!("../grammar.ebnf")); 19 | let mut lexer = Lexer::new("'a' , \"ba\" | () {} [] ; ? asdasd ? (* as (d *) asd"); 20 | while let Ok(Some(token)) = lexer.next_token() { 21 | println!("{token:?}"); 22 | } 23 | } 24 | 25 | #[test] 26 | fn parse() { 27 | let text = include_str!("../grammar.ebnf"); 28 | let res = Parser::new(Lexer::new(text)).parse(); 29 | match res { 30 | Ok(res) => { 31 | println!("{:#?}", res.syntax); 32 | for (k, v) in res.comments { 33 | println!( 34 | "{}: {:?} -- {:?}", 35 | k, 36 | v.iter().map(|c| c.text).collect::>(), 37 | res.tokens.get(k), 38 | ); 39 | } 40 | } 41 | Err(err) => eprintln!( 42 | "\x1b[31m{}\x1b[1m{}\x1b[22m{}\x1b[0m\n{}", 43 | &text[(err.span.start - 20)..(err.span.start)], 44 | &text[err.span.start..err.span.end], 45 | &text[(err.span.end)..(err.span.end + 20)], 46 | err.message, 47 | ), 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /crates/ebnf-parser/src/parser.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, mem}; 2 | 3 | use crate::{ 4 | ast::*, 5 | error::SyntaxError, 6 | span::Span, 7 | token::{Token, TokenKind}, 8 | Lexer, 9 | }; 10 | 11 | pub type CommentMap<'src> = HashMap>>; 12 | 13 | #[derive(Debug, Clone)] 14 | pub struct ParseResult<'src> { 15 | pub comments: CommentMap<'src>, 16 | pub tokens: Vec>, 17 | pub syntax: Syntax<'src>, 18 | } 19 | 20 | pub struct Parser<'src> { 21 | lexer: Lexer<'src>, 22 | prev_tok: Option>, 23 | curr_tok: Option>, 24 | prev_span: Span, 25 | curr_span: Span, 26 | tokens: Vec>, 27 | comments: CommentMap<'src>, 28 | } 29 | 30 | impl<'src> Parser<'src> { 31 | pub fn new(lexer: Lexer<'src>) -> Self { 32 | Self { 33 | lexer, 34 | prev_tok: None, 35 | curr_tok: None, 36 | prev_span: Span::new(0, 1), 37 | curr_span: Span::new(0, 1), 38 | tokens: vec![], 39 | comments: HashMap::new(), 40 | } 41 | } 42 | 43 | pub fn parse(mut self) -> Result, SyntaxError> { 44 | self.next()?; 45 | let syntax = self.syntax()?; 46 | self.next()?; // add prev_tok to tokens list 47 | 48 | if let Some(curr_tok) = &self.curr_tok { 49 | return Err(SyntaxError::new(curr_tok.span, "Expected EOF".into())); 50 | } 51 | Ok(ParseResult { 52 | comments: self.comments, 53 | tokens: self.tokens, 54 | syntax, 55 | }) 56 | } 57 | 58 | fn next(&mut self) -> Result<(), SyntaxError> { 59 | if let Some(prev_tok) = self.prev_tok.take() { 60 | self.tokens.push(prev_tok); 61 | } 62 | 63 | self.prev_tok = self.curr_tok.take(); 64 | self.curr_tok = self.lexer.next_token()?; 65 | 66 | let mut comments = vec![]; 67 | while let Some(Token { 68 | kind: TokenKind::Comment(_), 69 | .. 70 | }) = self.curr_tok 71 | { 72 | let comment = self.curr_tok.take().expect("`curr_tok` is a comment token"); 73 | comments.push(Comment::try_from(comment).expect("`comment` is a comment token")); 74 | self.curr_tok = self.lexer.next_token()?; 75 | } 76 | if !comments.is_empty() { 77 | self.comments.insert( 78 | self.tokens.len() + self.prev_tok.is_some() as usize, 79 | comments, 80 | ); 81 | } 82 | 83 | mem::swap(&mut self.prev_span, &mut self.curr_span); 84 | if let Some(curr_tok) = &self.curr_tok { 85 | self.curr_span = curr_tok.span; 86 | } else { 87 | self.curr_span = Span::new(self.lexer.index, self.lexer.index + 1); 88 | } 89 | Ok(()) 90 | } 91 | 92 | fn is_kind(&mut self, kind: TokenKind) -> Result { 93 | Ok(matches!(self.curr_tok, Some(Token { kind: tok_kind, .. }) if tok_kind == kind)) 94 | } 95 | 96 | fn expect(&mut self, kind: TokenKind) -> Result<(), SyntaxError> { 97 | match self.curr_tok.as_ref().map(|tok| &tok.kind) { 98 | Some(tok_kind) if tok_kind == &kind => { 99 | self.next()?; 100 | Ok(()) 101 | } 102 | Some(tok_kind) => Err(SyntaxError::new( 103 | self.curr_span, 104 | format!("Expected '{kind}', was '{tok_kind}'").into(), 105 | )), 106 | None => Err(SyntaxError::new( 107 | self.curr_span, 108 | format!("Expected '{kind}'").into(), 109 | )), 110 | } 111 | } 112 | 113 | fn syntax(&mut self) -> Result, SyntaxError> { 114 | let start = self.curr_span.start; 115 | let mut rules = vec![]; 116 | 117 | while self.curr_tok.is_some() { 118 | rules.push(self.syntax_rule()?); 119 | } 120 | if rules.is_empty() { 121 | return Err(SyntaxError::new( 122 | self.curr_span, 123 | "Syntax requires at least on syntax rule".into(), 124 | )); 125 | } 126 | 127 | Ok(Syntax { 128 | span: Span::new(start, self.prev_span.end), 129 | rules, 130 | }) 131 | } 132 | 133 | fn syntax_rule(&mut self) -> Result, SyntaxError> { 134 | let start = self.curr_span.start; 135 | 136 | let name = match self.curr_tok { 137 | Some(Token { 138 | kind: TokenKind::Identifier(name), 139 | .. 140 | }) => name, 141 | _ => { 142 | return Err(SyntaxError::new( 143 | self.curr_span, 144 | "Expected identifier".into(), 145 | )) 146 | } 147 | }; 148 | self.next()?; 149 | 150 | self.expect(TokenKind::Equal)?; 151 | let definitions = self.definitions_list()?; 152 | self.expect(TokenKind::Semicolon)?; 153 | 154 | Ok(SyntaxRule { 155 | span: Span::new(start, self.prev_span.end), 156 | name, 157 | definitions, 158 | }) 159 | } 160 | 161 | fn definitions_list(&mut self) -> Result>, SyntaxError> { 162 | let mut definitions = vec![self.single_definition()?]; 163 | 164 | while self.is_kind(TokenKind::Pipe)? { 165 | self.next()?; 166 | definitions.push(self.single_definition()?); 167 | } 168 | 169 | Ok(definitions) 170 | } 171 | 172 | fn single_definition(&mut self) -> Result, SyntaxError> { 173 | let start = self.curr_span.start; 174 | let mut terms = vec![self.syntactic_term()?]; 175 | 176 | while self.is_kind(TokenKind::Comma)? { 177 | self.next()?; 178 | terms.push(self.syntactic_term()?); 179 | } 180 | 181 | Ok(SingleDefinition { 182 | span: Span::new(start, self.prev_span.end), 183 | terms, 184 | }) 185 | } 186 | 187 | fn syntactic_term(&mut self) -> Result, SyntaxError> { 188 | let start = self.curr_span.start; 189 | let factor = self.syntactic_factor()?; 190 | let exception = match self.is_kind(TokenKind::Dash)? { 191 | true => { 192 | self.next()?; 193 | Some(self.syntactic_exception()?) 194 | } 195 | false => None, 196 | }; 197 | 198 | Ok(SyntacticTerm { 199 | span: Span::new(start, self.prev_span.end), 200 | factor, 201 | exception, 202 | }) 203 | } 204 | 205 | #[inline] 206 | fn syntactic_exception(&mut self) -> Result, SyntaxError> { 207 | self.syntactic_factor() 208 | } 209 | 210 | fn syntactic_factor(&mut self) -> Result, SyntaxError> { 211 | let start = self.curr_span.start; 212 | let repetition = match self.curr_tok { 213 | Some(Token { 214 | kind: TokenKind::Integer(num), 215 | .. 216 | }) => { 217 | self.next()?; 218 | self.expect(TokenKind::Star)?; 219 | Some(num) 220 | } 221 | _ => None, 222 | }; 223 | let primary = self.syntactic_primary()?; 224 | 225 | Ok(SyntacticFactor { 226 | span: Span::new(start, self.prev_span.end), 227 | repetition, 228 | primary, 229 | }) 230 | } 231 | 232 | fn syntactic_primary(&mut self) -> Result, SyntaxError> { 233 | let start = self.curr_span.start; 234 | let kind = match self 235 | .curr_tok 236 | .as_ref() 237 | .map_or(TokenKind::Semicolon, |tok| tok.kind) 238 | { 239 | TokenKind::LBracket => SyntacticPrimaryKind::OptionalSequence( 240 | self.delimited_definitions_list(TokenKind::RBracket)?, 241 | ), 242 | TokenKind::LBrace => SyntacticPrimaryKind::RepeatedSequence( 243 | self.delimited_definitions_list(TokenKind::RBrace)?, 244 | ), 245 | TokenKind::LParen => SyntacticPrimaryKind::GroupedSequence( 246 | self.delimited_definitions_list(TokenKind::RParen)?, 247 | ), 248 | TokenKind::Identifier(name) => { 249 | self.next()?; 250 | SyntacticPrimaryKind::MetaIdentifier(name) 251 | } 252 | TokenKind::Terminal(text) => { 253 | self.next()?; 254 | SyntacticPrimaryKind::TerminalString(text) 255 | } 256 | TokenKind::SpecialSeq(text) => { 257 | self.next()?; 258 | SyntacticPrimaryKind::SpecialSequence(text) 259 | } 260 | _ => SyntacticPrimaryKind::EmptySequence, 261 | }; 262 | 263 | Ok(SyntacticPrimary { 264 | span: Span::new(start, self.prev_span.end), 265 | kind, 266 | }) 267 | } 268 | 269 | fn delimited_definitions_list( 270 | &mut self, 271 | right_delimiter: TokenKind, 272 | ) -> Result>, SyntaxError> { 273 | self.next()?; 274 | let definitions = self.definitions_list()?; 275 | self.expect(right_delimiter)?; 276 | 277 | Ok(definitions) 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /crates/ebnf-parser/src/span.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Display}; 2 | 3 | #[derive(Clone, Copy, PartialEq, Eq)] 4 | pub struct Span { 5 | pub start: usize, 6 | pub end: usize, 7 | } 8 | 9 | impl Span { 10 | pub(crate) fn new(start: usize, end: usize) -> Self { 11 | Self { start, end } 12 | } 13 | } 14 | 15 | impl Display for Span { 16 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 17 | write!(f, "{}..{}", self.start, self.end) 18 | } 19 | } 20 | 21 | impl Debug for Span { 22 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 23 | write!(f, "{}", self) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /crates/ebnf-parser/src/token.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Display}; 2 | 3 | use crate::span::Span; 4 | 5 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 6 | pub enum TokenKind<'src> { 7 | Identifier(&'src str), 8 | Terminal(&'src str), 9 | Comment(&'src str), 10 | SpecialSeq(&'src str), 11 | Integer(usize), 12 | 13 | LBrace, 14 | RBrace, 15 | LBracket, 16 | RBracket, 17 | LParen, 18 | RParen, 19 | Pipe, 20 | Comma, 21 | Semicolon, 22 | Equal, 23 | Star, 24 | Dash, 25 | } 26 | 27 | impl Display for TokenKind<'_> { 28 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 29 | match self { 30 | TokenKind::Identifier(name) => write!(f, "{name}"), 31 | TokenKind::Terminal(text) => write!(f, "\"{text}\""), 32 | TokenKind::Comment(text) => write!(f, "(* {text} *)"), 33 | TokenKind::SpecialSeq(text) => write!(f, "? {text} ?"), 34 | TokenKind::Integer(num) => write!(f, "{num}"), 35 | TokenKind::LBrace => write!(f, "{{"), 36 | TokenKind::RBrace => write!(f, "}}"), 37 | TokenKind::LBracket => write!(f, "["), 38 | TokenKind::RBracket => write!(f, "]"), 39 | TokenKind::LParen => write!(f, "("), 40 | TokenKind::RParen => write!(f, ")"), 41 | TokenKind::Pipe => write!(f, "|"), 42 | TokenKind::Comma => write!(f, ","), 43 | TokenKind::Semicolon => write!(f, ";"), 44 | TokenKind::Equal => write!(f, "="), 45 | TokenKind::Star => write!(f, "*"), 46 | TokenKind::Dash => write!(f, "-"), 47 | } 48 | } 49 | } 50 | 51 | /// A token with positional information 52 | #[derive(Clone)] 53 | pub struct Token<'src> { 54 | pub kind: TokenKind<'src>, 55 | pub span: Span, 56 | } 57 | 58 | impl<'src> Token<'src> { 59 | pub(crate) fn new(kind: TokenKind<'src>, span: Span) -> Self { 60 | Self { kind, span } 61 | } 62 | } 63 | 64 | impl Debug for Token<'_> { 65 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 66 | write!(f, "({:?} @ {:?})", self.kind, self.span) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/.gitattributes: -------------------------------------------------------------------------------- 1 | /src/** linguist-vendored 2 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | target 4 | *.log 5 | package-lock.json 6 | Cargo.lock 7 | /.build 8 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-sitter-ebnf" 3 | version = "0.1.0" 4 | categories = ["parsing", "text-editors"] 5 | edition = "2018" 6 | keywords = ["incremental", "parsing", "ebnf"] 7 | license = "MIT" 8 | repository = "https://github.com/RubixDev/ebnf" 9 | description = "EBNF grammar for the tree-sitter parsing library" 10 | 11 | build = "bindings/rust/build.rs" 12 | include = [ 13 | "bindings/rust/*", 14 | "grammar.js", 15 | "queries/*", 16 | "src/*", 17 | ] 18 | 19 | [lib] 20 | path = "bindings/rust/lib.rs" 21 | 22 | [dependencies] 23 | tree-sitter = "~0.20.3" 24 | 25 | [build-dependencies] 26 | cc = "1.0" 27 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 RubixDev 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/README.md: -------------------------------------------------------------------------------- 1 | # tree-sitter-ebnf 2 | 3 | EBNF grammar for [tree-sitter](https://github.com/tree-sitter/tree-sitter) 4 | 5 | ## Reference 6 | 7 | This parser implements the EBNF syntax as described by the 8 | [ISO/IEC 14977:1996 standard](https://www.iso.org/standard/26153.html) with two 9 | notable differences: 10 | 11 | 1. The ISO standard does not allow underscores `_` in meta-identifiers 12 | 2. The ISO standard only allows characters from the 13 | [ISO/IEC 646:1991 character set](https://www.iso.org/standard/4777.html) 14 | 15 | ## Usage in Neovim 16 | 17 | ### Parser Installation 18 | 19 | The [nvim-treesitter plugin](https://github.com/nvim-treesitter/nvim-treesitter) 20 | does not include this parser 21 | [currently](https://github.com/nvim-treesitter/nvim-treesitter/pull/3574). To 22 | use it you must instead manually add it to your tree-sitter config and then 23 | install it with `:TSInstall ebnf` or by adding it to your `ensure_installed` 24 | list: 25 | 26 | ```lua 27 | require('nvim-treesitter.parsers').get_parser_configs().ebnf = { 28 | install_info = { 29 | url = 'https://github.com/RubixDev/ebnf.git', 30 | files = { 'src/parser.c' }, 31 | location = 'crates/tree-sitter-ebnf', 32 | branch = 'main', 33 | }, 34 | } 35 | ``` 36 | 37 | ### File type detection 38 | 39 | You will likely also have to add the `ebnf` file type: 40 | 41 | ```lua 42 | vim.filetype.add { extension = { ebnf = 'ebnf' } } 43 | ``` 44 | 45 | ### Highlighting 46 | 47 | If you want to use this parser for highlighting, you will also have to add this 48 | repository as a plugin, for example for 49 | [packer.nvim](https://github.com/wbthomason/packer.nvim) add this: 50 | 51 | ```lua 52 | use { 53 | 'RubixDev/ebnf', 54 | rtp = 'crates/tree-sitter-ebnf', 55 | } 56 | ``` 57 | 58 | I also recommend customizing these highlights: 59 | 60 | - `@string.grammar`: terminal symbols enclosed with `'` or `"`, falls back to 61 | `@string` 62 | - `@string.special.grammar`: special sequences enclosed with `?`, falls back to 63 | `@string.special` 64 | - `@symbol.grammar`: non-terminal symbols, i.e., identifiers, falls back to 65 | `@symbol` 66 | - `@symbol.grammar.pascal`: non-terminal symbols in PascalCase 67 | - `@symbol.grammar.camel`: non-terminal symbols in camelCase 68 | - `@symbol.grammar.upper`: non-terminal symbols in UPPERCASE 69 | - `@symbol.grammar.lower`: non-terminal symbols in lowercase 70 | 71 | As an example, here is my personal configuration: 72 | 73 | ```lua 74 | vim.api.nvim_set_hl(0, '@string.special.grammar', { link = '@string.regex' }) 75 | vim.api.nvim_set_hl(0, '@symbol.grammar.pascal', { link = '@type' }) 76 | vim.api.nvim_set_hl(0, '@symbol.grammar.camel', { link = '@property' }) 77 | vim.api.nvim_set_hl(0, '@symbol.grammar.upper', { link = '@constant' }) 78 | vim.api.nvim_set_hl(0, '@symbol.grammar.lower', { link = '@parameter' }) 79 | ``` 80 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "tree_sitter_ebnf_binding", 5 | "include_dirs": [ 6 | " 3 | #include "nan.h" 4 | 5 | using namespace v8; 6 | 7 | extern "C" TSLanguage * tree_sitter_ebnf(); 8 | 9 | namespace { 10 | 11 | NAN_METHOD(New) {} 12 | 13 | void Init(Local exports, Local module) { 14 | Local tpl = Nan::New(New); 15 | tpl->SetClassName(Nan::New("Language").ToLocalChecked()); 16 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 17 | 18 | Local constructor = Nan::GetFunction(tpl).ToLocalChecked(); 19 | Local instance = constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked(); 20 | Nan::SetInternalFieldPointer(instance, 0, tree_sitter_ebnf()); 21 | 22 | Nan::Set(instance, Nan::New("name").ToLocalChecked(), Nan::New("ebnf").ToLocalChecked()); 23 | Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance); 24 | } 25 | 26 | NODE_MODULE(tree_sitter_ebnf_binding, Init) 27 | 28 | } // namespace 29 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/bindings/node/index.js: -------------------------------------------------------------------------------- 1 | try { 2 | module.exports = require("../../build/Release/tree_sitter_ebnf_binding"); 3 | } catch (error1) { 4 | if (error1.code !== 'MODULE_NOT_FOUND') { 5 | throw error1; 6 | } 7 | try { 8 | module.exports = require("../../build/Debug/tree_sitter_ebnf_binding"); 9 | } catch (error2) { 10 | if (error2.code !== 'MODULE_NOT_FOUND') { 11 | throw error2; 12 | } 13 | throw error1 14 | } 15 | } 16 | 17 | try { 18 | module.exports.nodeTypeInfo = require("../../src/node-types.json"); 19 | } catch (_) {} 20 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/bindings/rust/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let src_dir = std::path::Path::new("src"); 3 | 4 | let mut c_config = cc::Build::new(); 5 | c_config.include(&src_dir); 6 | c_config 7 | .flag_if_supported("-Wno-unused-parameter") 8 | .flag_if_supported("-Wno-unused-but-set-variable") 9 | .flag_if_supported("-Wno-trigraphs"); 10 | let parser_path = src_dir.join("parser.c"); 11 | c_config.file(&parser_path); 12 | 13 | // If your language uses an external scanner written in C, 14 | // then include this block of code: 15 | 16 | /* 17 | let scanner_path = src_dir.join("scanner.c"); 18 | c_config.file(&scanner_path); 19 | println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); 20 | */ 21 | 22 | c_config.compile("parser"); 23 | println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); 24 | 25 | // If your language uses an external scanner written in C++, 26 | // then include this block of code: 27 | 28 | /* 29 | let mut cpp_config = cc::Build::new(); 30 | cpp_config.cpp(true); 31 | cpp_config.include(&src_dir); 32 | cpp_config 33 | .flag_if_supported("-Wno-unused-parameter") 34 | .flag_if_supported("-Wno-unused-but-set-variable"); 35 | let scanner_path = src_dir.join("scanner.cc"); 36 | cpp_config.file(&scanner_path); 37 | cpp_config.compile("scanner"); 38 | println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); 39 | */ 40 | } 41 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/bindings/rust/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides ebnf language support for the [tree-sitter][] parsing library. 2 | //! 3 | //! Typically, you will use the [language][language func] function to add this language to a 4 | //! tree-sitter [Parser][], and then use the parser to parse some code: 5 | //! 6 | //! ``` 7 | //! let code = ""; 8 | //! let mut parser = tree_sitter::Parser::new(); 9 | //! parser.set_language(tree_sitter_ebnf::language()).expect("Error loading ebnf grammar"); 10 | //! let tree = parser.parse(code, None).unwrap(); 11 | //! ``` 12 | //! 13 | //! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 14 | //! [language func]: fn.language.html 15 | //! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html 16 | //! [tree-sitter]: https://tree-sitter.github.io/ 17 | 18 | use tree_sitter::Language; 19 | 20 | extern "C" { 21 | fn tree_sitter_ebnf() -> Language; 22 | } 23 | 24 | /// Get the tree-sitter [Language][] for this grammar. 25 | /// 26 | /// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 27 | pub fn language() -> Language { 28 | unsafe { tree_sitter_ebnf() } 29 | } 30 | 31 | /// The content of the [`node-types.json`][] file for this grammar. 32 | /// 33 | /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types 34 | pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json"); 35 | 36 | // Uncomment these to include any queries that this grammar contains 37 | 38 | pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm"); 39 | // pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm"); 40 | // pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm"); 41 | // pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm"); 42 | 43 | #[cfg(test)] 44 | mod tests { 45 | #[test] 46 | fn test_can_load_grammar() { 47 | let mut parser = tree_sitter::Parser::new(); 48 | parser 49 | .set_language(super::language()) 50 | .expect("Error loading ebnf language"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/grammar.js: -------------------------------------------------------------------------------- 1 | module.exports = grammar({ 2 | name: 'ebnf', 3 | 4 | extras: $ => [ 5 | / |\n|\t|\r/, 6 | $.comment, 7 | ], 8 | 9 | rules: { 10 | syntax: $ => repeat1($.syntax_rule), 11 | 12 | terminal: $ => /'[^']*'|"[^"]*"/, 13 | identifier: $ => /[a-zA-Z][a-zA-Z0-9_]*/, 14 | integer: $ => /[0-9]+/, 15 | comment: $ => /\(\*[^*]*\*+(?:[^)*][^*]*\*+)*\)/, 16 | special_sequence: $ => /\?[^?]*\?/, 17 | 18 | syntax_rule: $ => seq(field('name', $.identifier), '=', field('definition', optional($._expression)), ';'), 19 | 20 | _expression: $ => 21 | choice( 22 | $._atom, 23 | $.binary_expression, 24 | $.group, 25 | ), 26 | _atom: $ => 27 | choice( 28 | $.identifier, 29 | $.terminal, 30 | $.special_sequence, 31 | ), 32 | binary_expression: $ => 33 | choice( 34 | ...[ 35 | [$._expression, '|', $._expression], 36 | [$._expression, ',', $._expression], 37 | [$._expression, '-', optional($._expression)], 38 | [$.integer, '*', $._expression], 39 | ].map(([left, op, right], index) => 40 | prec.left( 41 | index + 1, 42 | seq( 43 | field('left', left), 44 | field('operator', op), 45 | field('right', right), 46 | ), 47 | ) 48 | ), 49 | ), 50 | group: $ => 51 | choice( 52 | seq('[', optional($._expression), ']'), 53 | seq('{', optional($._expression), '}'), 54 | seq('(', optional($._expression), ')'), 55 | ), 56 | }, 57 | }) 58 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-ebnf", 3 | "version": "0.1.0", 4 | "description": "EBNF grammar for tree-sitter", 5 | "main": "bindings/node", 6 | "scripts": { 7 | "build": "tree-sitter generate && node-gyp build", 8 | "test": "tree-sitter test" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/RubixDev/ebnf.git", 13 | "directory": "packages/tree-sitter-ebnf" 14 | }, 15 | "keywords": ["parser", "lexer"], 16 | "author": "RubixDev", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/RubixDev/tree-sitter-ebnf/issues" 20 | }, 21 | "homepage": "https://github.com/RubixDev/tree-sitter-ebnf#readme", 22 | "dependencies": { 23 | "nan": "^2.16.0" 24 | }, 25 | "devDependencies": { 26 | "tree-sitter-cli": "^0.20.7" 27 | }, 28 | "tree-sitter": [{ 29 | "scope": "source.ebnf", 30 | "file-types": ["ebnf"] 31 | }] 32 | } 33 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/queries/ebnf/highlights.scm: -------------------------------------------------------------------------------- 1 | ;;;; Simple tokens ;;;; 2 | (terminal) @string.grammar 3 | 4 | (special_sequence) @string.special.grammar 5 | 6 | (integer) @number 7 | 8 | (comment) @comment.block 9 | 10 | ;;;; Identifiers ;;;; 11 | (identifier) @symbol.grammar 12 | 13 | ; Allow different highlighting for specific casings 14 | ((identifier) @symbol.grammar.pascal 15 | (#match? @symbol.grammar.pascal "^[A-Z]")) 16 | 17 | ((identifier) @symbol.grammar.camel 18 | (#match? @symbol.grammar.camel "^[a-z]")) 19 | 20 | ((identifier) @symbol.grammar.upper 21 | (#match? @symbol.grammar.upper "^[A-Z][A-Z0-9_]+$")) 22 | 23 | ((identifier) @symbol.grammar.lower 24 | (#match? @symbol.grammar.lower "^[a-z][a-z0-9_]+$")) 25 | 26 | ;;; Punctuation ;;;; 27 | [ 28 | ";" 29 | "," 30 | ] @punctuation.delimiter 31 | 32 | [ 33 | "|" 34 | "*" 35 | "-" 36 | ] @operator 37 | 38 | "=" @keyword.operator 39 | 40 | [ 41 | "(" 42 | ")" 43 | "[" 44 | "]" 45 | "{" 46 | "}" 47 | ] @punctuation.bracket 48 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/queries/highlights.scm: -------------------------------------------------------------------------------- 1 | ;;;; Simple tokens ;;;; 2 | (terminal) @string.grammar 3 | 4 | (special_sequence) @string.special.grammar 5 | 6 | (integer) @number 7 | 8 | (comment) @comment.block 9 | 10 | ;;;; Identifiers ;;;; 11 | ; Allow different highlighting for specific casings 12 | ((identifier) @symbol.grammar.upper 13 | (#match? @symbol.grammar.upper "^[A-Z][A-Z0-9_]+$")) 14 | 15 | ((identifier) @symbol.grammar.lower 16 | (#match? @symbol.grammar.lower "^[a-z][a-z0-9_]+$")) 17 | ((identifier) @symbol.grammar.pascal 18 | (#match? @symbol.grammar.pascal "^[A-Z]")) 19 | 20 | ((identifier) @symbol.grammar.camel 21 | (#match? @symbol.grammar.camel "^[a-z]")) 22 | 23 | (identifier) @symbol.grammar 24 | 25 | ;;; Punctuation ;;;; 26 | [ 27 | ";" 28 | "," 29 | ] @punctuation.delimiter 30 | 31 | [ 32 | "|" 33 | "*" 34 | "-" 35 | ] @operator 36 | 37 | "=" @keyword.operator 38 | 39 | [ 40 | "(" 41 | ")" 42 | "[" 43 | "]" 44 | "{" 45 | "}" 46 | ] @punctuation.bracket 47 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/src/grammar.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ebnf", 3 | "rules": { 4 | "syntax": { 5 | "type": "REPEAT1", 6 | "content": { 7 | "type": "SYMBOL", 8 | "name": "syntax_rule" 9 | } 10 | }, 11 | "terminal": { 12 | "type": "PATTERN", 13 | "value": "'[^']*'|\"[^\"]*\"" 14 | }, 15 | "identifier": { 16 | "type": "PATTERN", 17 | "value": "[a-zA-Z][a-zA-Z0-9_]*" 18 | }, 19 | "integer": { 20 | "type": "PATTERN", 21 | "value": "[0-9]+" 22 | }, 23 | "comment": { 24 | "type": "PATTERN", 25 | "value": "\\(\\*[^*]*\\*+(?:[^)*][^*]*\\*+)*\\)" 26 | }, 27 | "special_sequence": { 28 | "type": "PATTERN", 29 | "value": "\\?[^?]*\\?" 30 | }, 31 | "syntax_rule": { 32 | "type": "SEQ", 33 | "members": [ 34 | { 35 | "type": "FIELD", 36 | "name": "name", 37 | "content": { 38 | "type": "SYMBOL", 39 | "name": "identifier" 40 | } 41 | }, 42 | { 43 | "type": "STRING", 44 | "value": "=" 45 | }, 46 | { 47 | "type": "FIELD", 48 | "name": "definition", 49 | "content": { 50 | "type": "CHOICE", 51 | "members": [ 52 | { 53 | "type": "SYMBOL", 54 | "name": "_expression" 55 | }, 56 | { 57 | "type": "BLANK" 58 | } 59 | ] 60 | } 61 | }, 62 | { 63 | "type": "STRING", 64 | "value": ";" 65 | } 66 | ] 67 | }, 68 | "_expression": { 69 | "type": "CHOICE", 70 | "members": [ 71 | { 72 | "type": "SYMBOL", 73 | "name": "_atom" 74 | }, 75 | { 76 | "type": "SYMBOL", 77 | "name": "binary_expression" 78 | }, 79 | { 80 | "type": "SYMBOL", 81 | "name": "group" 82 | } 83 | ] 84 | }, 85 | "_atom": { 86 | "type": "CHOICE", 87 | "members": [ 88 | { 89 | "type": "SYMBOL", 90 | "name": "identifier" 91 | }, 92 | { 93 | "type": "SYMBOL", 94 | "name": "terminal" 95 | }, 96 | { 97 | "type": "SYMBOL", 98 | "name": "special_sequence" 99 | } 100 | ] 101 | }, 102 | "binary_expression": { 103 | "type": "CHOICE", 104 | "members": [ 105 | { 106 | "type": "PREC_LEFT", 107 | "value": 1, 108 | "content": { 109 | "type": "SEQ", 110 | "members": [ 111 | { 112 | "type": "FIELD", 113 | "name": "left", 114 | "content": { 115 | "type": "SYMBOL", 116 | "name": "_expression" 117 | } 118 | }, 119 | { 120 | "type": "FIELD", 121 | "name": "operator", 122 | "content": { 123 | "type": "STRING", 124 | "value": "|" 125 | } 126 | }, 127 | { 128 | "type": "FIELD", 129 | "name": "right", 130 | "content": { 131 | "type": "SYMBOL", 132 | "name": "_expression" 133 | } 134 | } 135 | ] 136 | } 137 | }, 138 | { 139 | "type": "PREC_LEFT", 140 | "value": 2, 141 | "content": { 142 | "type": "SEQ", 143 | "members": [ 144 | { 145 | "type": "FIELD", 146 | "name": "left", 147 | "content": { 148 | "type": "SYMBOL", 149 | "name": "_expression" 150 | } 151 | }, 152 | { 153 | "type": "FIELD", 154 | "name": "operator", 155 | "content": { 156 | "type": "STRING", 157 | "value": "," 158 | } 159 | }, 160 | { 161 | "type": "FIELD", 162 | "name": "right", 163 | "content": { 164 | "type": "SYMBOL", 165 | "name": "_expression" 166 | } 167 | } 168 | ] 169 | } 170 | }, 171 | { 172 | "type": "PREC_LEFT", 173 | "value": 3, 174 | "content": { 175 | "type": "SEQ", 176 | "members": [ 177 | { 178 | "type": "FIELD", 179 | "name": "left", 180 | "content": { 181 | "type": "SYMBOL", 182 | "name": "_expression" 183 | } 184 | }, 185 | { 186 | "type": "FIELD", 187 | "name": "operator", 188 | "content": { 189 | "type": "STRING", 190 | "value": "-" 191 | } 192 | }, 193 | { 194 | "type": "FIELD", 195 | "name": "right", 196 | "content": { 197 | "type": "CHOICE", 198 | "members": [ 199 | { 200 | "type": "SYMBOL", 201 | "name": "_expression" 202 | }, 203 | { 204 | "type": "BLANK" 205 | } 206 | ] 207 | } 208 | } 209 | ] 210 | } 211 | }, 212 | { 213 | "type": "PREC_LEFT", 214 | "value": 4, 215 | "content": { 216 | "type": "SEQ", 217 | "members": [ 218 | { 219 | "type": "FIELD", 220 | "name": "left", 221 | "content": { 222 | "type": "SYMBOL", 223 | "name": "integer" 224 | } 225 | }, 226 | { 227 | "type": "FIELD", 228 | "name": "operator", 229 | "content": { 230 | "type": "STRING", 231 | "value": "*" 232 | } 233 | }, 234 | { 235 | "type": "FIELD", 236 | "name": "right", 237 | "content": { 238 | "type": "SYMBOL", 239 | "name": "_expression" 240 | } 241 | } 242 | ] 243 | } 244 | } 245 | ] 246 | }, 247 | "group": { 248 | "type": "CHOICE", 249 | "members": [ 250 | { 251 | "type": "SEQ", 252 | "members": [ 253 | { 254 | "type": "STRING", 255 | "value": "[" 256 | }, 257 | { 258 | "type": "CHOICE", 259 | "members": [ 260 | { 261 | "type": "SYMBOL", 262 | "name": "_expression" 263 | }, 264 | { 265 | "type": "BLANK" 266 | } 267 | ] 268 | }, 269 | { 270 | "type": "STRING", 271 | "value": "]" 272 | } 273 | ] 274 | }, 275 | { 276 | "type": "SEQ", 277 | "members": [ 278 | { 279 | "type": "STRING", 280 | "value": "{" 281 | }, 282 | { 283 | "type": "CHOICE", 284 | "members": [ 285 | { 286 | "type": "SYMBOL", 287 | "name": "_expression" 288 | }, 289 | { 290 | "type": "BLANK" 291 | } 292 | ] 293 | }, 294 | { 295 | "type": "STRING", 296 | "value": "}" 297 | } 298 | ] 299 | }, 300 | { 301 | "type": "SEQ", 302 | "members": [ 303 | { 304 | "type": "STRING", 305 | "value": "(" 306 | }, 307 | { 308 | "type": "CHOICE", 309 | "members": [ 310 | { 311 | "type": "SYMBOL", 312 | "name": "_expression" 313 | }, 314 | { 315 | "type": "BLANK" 316 | } 317 | ] 318 | }, 319 | { 320 | "type": "STRING", 321 | "value": ")" 322 | } 323 | ] 324 | } 325 | ] 326 | } 327 | }, 328 | "extras": [ 329 | { 330 | "type": "PATTERN", 331 | "value": " |\\n|\\t|\\r" 332 | }, 333 | { 334 | "type": "SYMBOL", 335 | "name": "comment" 336 | } 337 | ], 338 | "conflicts": [], 339 | "precedences": [], 340 | "externals": [], 341 | "inline": [], 342 | "supertypes": [] 343 | } 344 | 345 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/src/node-types.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "binary_expression", 4 | "named": true, 5 | "fields": { 6 | "left": { 7 | "multiple": false, 8 | "required": true, 9 | "types": [ 10 | { 11 | "type": "binary_expression", 12 | "named": true 13 | }, 14 | { 15 | "type": "group", 16 | "named": true 17 | }, 18 | { 19 | "type": "identifier", 20 | "named": true 21 | }, 22 | { 23 | "type": "integer", 24 | "named": true 25 | }, 26 | { 27 | "type": "special_sequence", 28 | "named": true 29 | }, 30 | { 31 | "type": "terminal", 32 | "named": true 33 | } 34 | ] 35 | }, 36 | "operator": { 37 | "multiple": false, 38 | "required": true, 39 | "types": [ 40 | { 41 | "type": "*", 42 | "named": false 43 | }, 44 | { 45 | "type": ",", 46 | "named": false 47 | }, 48 | { 49 | "type": "-", 50 | "named": false 51 | }, 52 | { 53 | "type": "|", 54 | "named": false 55 | } 56 | ] 57 | }, 58 | "right": { 59 | "multiple": false, 60 | "required": false, 61 | "types": [ 62 | { 63 | "type": "binary_expression", 64 | "named": true 65 | }, 66 | { 67 | "type": "group", 68 | "named": true 69 | }, 70 | { 71 | "type": "identifier", 72 | "named": true 73 | }, 74 | { 75 | "type": "special_sequence", 76 | "named": true 77 | }, 78 | { 79 | "type": "terminal", 80 | "named": true 81 | } 82 | ] 83 | } 84 | } 85 | }, 86 | { 87 | "type": "group", 88 | "named": true, 89 | "fields": {}, 90 | "children": { 91 | "multiple": false, 92 | "required": false, 93 | "types": [ 94 | { 95 | "type": "binary_expression", 96 | "named": true 97 | }, 98 | { 99 | "type": "group", 100 | "named": true 101 | }, 102 | { 103 | "type": "identifier", 104 | "named": true 105 | }, 106 | { 107 | "type": "special_sequence", 108 | "named": true 109 | }, 110 | { 111 | "type": "terminal", 112 | "named": true 113 | } 114 | ] 115 | } 116 | }, 117 | { 118 | "type": "syntax", 119 | "named": true, 120 | "fields": {}, 121 | "children": { 122 | "multiple": true, 123 | "required": true, 124 | "types": [ 125 | { 126 | "type": "syntax_rule", 127 | "named": true 128 | } 129 | ] 130 | } 131 | }, 132 | { 133 | "type": "syntax_rule", 134 | "named": true, 135 | "fields": { 136 | "definition": { 137 | "multiple": false, 138 | "required": false, 139 | "types": [ 140 | { 141 | "type": "binary_expression", 142 | "named": true 143 | }, 144 | { 145 | "type": "group", 146 | "named": true 147 | }, 148 | { 149 | "type": "identifier", 150 | "named": true 151 | }, 152 | { 153 | "type": "special_sequence", 154 | "named": true 155 | }, 156 | { 157 | "type": "terminal", 158 | "named": true 159 | } 160 | ] 161 | }, 162 | "name": { 163 | "multiple": false, 164 | "required": true, 165 | "types": [ 166 | { 167 | "type": "identifier", 168 | "named": true 169 | } 170 | ] 171 | } 172 | } 173 | }, 174 | { 175 | "type": "(", 176 | "named": false 177 | }, 178 | { 179 | "type": ")", 180 | "named": false 181 | }, 182 | { 183 | "type": "*", 184 | "named": false 185 | }, 186 | { 187 | "type": ",", 188 | "named": false 189 | }, 190 | { 191 | "type": "-", 192 | "named": false 193 | }, 194 | { 195 | "type": ";", 196 | "named": false 197 | }, 198 | { 199 | "type": "=", 200 | "named": false 201 | }, 202 | { 203 | "type": "[", 204 | "named": false 205 | }, 206 | { 207 | "type": "]", 208 | "named": false 209 | }, 210 | { 211 | "type": "comment", 212 | "named": true 213 | }, 214 | { 215 | "type": "identifier", 216 | "named": true 217 | }, 218 | { 219 | "type": "integer", 220 | "named": true 221 | }, 222 | { 223 | "type": "special_sequence", 224 | "named": true 225 | }, 226 | { 227 | "type": "terminal", 228 | "named": true 229 | }, 230 | { 231 | "type": "{", 232 | "named": false 233 | }, 234 | { 235 | "type": "|", 236 | "named": false 237 | }, 238 | { 239 | "type": "}", 240 | "named": false 241 | } 242 | ] -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/src/parser.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if defined(__GNUC__) || defined(__clang__) 4 | #pragma GCC diagnostic push 5 | #pragma GCC diagnostic ignored "-Wmissing-field-initializers" 6 | #endif 7 | 8 | #define LANGUAGE_VERSION 14 9 | #define STATE_COUNT 27 10 | #define LARGE_STATE_COUNT 7 11 | #define SYMBOL_COUNT 25 12 | #define ALIAS_COUNT 0 13 | #define TOKEN_COUNT 18 14 | #define EXTERNAL_TOKEN_COUNT 0 15 | #define FIELD_COUNT 5 16 | #define MAX_ALIAS_SEQUENCE_LENGTH 4 17 | #define PRODUCTION_ID_COUNT 5 18 | 19 | enum { 20 | sym_terminal = 1, 21 | sym_identifier = 2, 22 | sym_integer = 3, 23 | sym_comment = 4, 24 | sym_special_sequence = 5, 25 | anon_sym_EQ = 6, 26 | anon_sym_SEMI = 7, 27 | anon_sym_PIPE = 8, 28 | anon_sym_COMMA = 9, 29 | anon_sym_DASH = 10, 30 | anon_sym_STAR = 11, 31 | anon_sym_LBRACK = 12, 32 | anon_sym_RBRACK = 13, 33 | anon_sym_LBRACE = 14, 34 | anon_sym_RBRACE = 15, 35 | anon_sym_LPAREN = 16, 36 | anon_sym_RPAREN = 17, 37 | sym_syntax = 18, 38 | sym_syntax_rule = 19, 39 | sym__expression = 20, 40 | sym__atom = 21, 41 | sym_binary_expression = 22, 42 | sym_group = 23, 43 | aux_sym_syntax_repeat1 = 24, 44 | }; 45 | 46 | static const char * const ts_symbol_names[] = { 47 | [ts_builtin_sym_end] = "end", 48 | [sym_terminal] = "terminal", 49 | [sym_identifier] = "identifier", 50 | [sym_integer] = "integer", 51 | [sym_comment] = "comment", 52 | [sym_special_sequence] = "special_sequence", 53 | [anon_sym_EQ] = "=", 54 | [anon_sym_SEMI] = ";", 55 | [anon_sym_PIPE] = "|", 56 | [anon_sym_COMMA] = ",", 57 | [anon_sym_DASH] = "-", 58 | [anon_sym_STAR] = "*", 59 | [anon_sym_LBRACK] = "[", 60 | [anon_sym_RBRACK] = "]", 61 | [anon_sym_LBRACE] = "{", 62 | [anon_sym_RBRACE] = "}", 63 | [anon_sym_LPAREN] = "(", 64 | [anon_sym_RPAREN] = ")", 65 | [sym_syntax] = "syntax", 66 | [sym_syntax_rule] = "syntax_rule", 67 | [sym__expression] = "_expression", 68 | [sym__atom] = "_atom", 69 | [sym_binary_expression] = "binary_expression", 70 | [sym_group] = "group", 71 | [aux_sym_syntax_repeat1] = "syntax_repeat1", 72 | }; 73 | 74 | static const TSSymbol ts_symbol_map[] = { 75 | [ts_builtin_sym_end] = ts_builtin_sym_end, 76 | [sym_terminal] = sym_terminal, 77 | [sym_identifier] = sym_identifier, 78 | [sym_integer] = sym_integer, 79 | [sym_comment] = sym_comment, 80 | [sym_special_sequence] = sym_special_sequence, 81 | [anon_sym_EQ] = anon_sym_EQ, 82 | [anon_sym_SEMI] = anon_sym_SEMI, 83 | [anon_sym_PIPE] = anon_sym_PIPE, 84 | [anon_sym_COMMA] = anon_sym_COMMA, 85 | [anon_sym_DASH] = anon_sym_DASH, 86 | [anon_sym_STAR] = anon_sym_STAR, 87 | [anon_sym_LBRACK] = anon_sym_LBRACK, 88 | [anon_sym_RBRACK] = anon_sym_RBRACK, 89 | [anon_sym_LBRACE] = anon_sym_LBRACE, 90 | [anon_sym_RBRACE] = anon_sym_RBRACE, 91 | [anon_sym_LPAREN] = anon_sym_LPAREN, 92 | [anon_sym_RPAREN] = anon_sym_RPAREN, 93 | [sym_syntax] = sym_syntax, 94 | [sym_syntax_rule] = sym_syntax_rule, 95 | [sym__expression] = sym__expression, 96 | [sym__atom] = sym__atom, 97 | [sym_binary_expression] = sym_binary_expression, 98 | [sym_group] = sym_group, 99 | [aux_sym_syntax_repeat1] = aux_sym_syntax_repeat1, 100 | }; 101 | 102 | static const TSSymbolMetadata ts_symbol_metadata[] = { 103 | [ts_builtin_sym_end] = { 104 | .visible = false, 105 | .named = true, 106 | }, 107 | [sym_terminal] = { 108 | .visible = true, 109 | .named = true, 110 | }, 111 | [sym_identifier] = { 112 | .visible = true, 113 | .named = true, 114 | }, 115 | [sym_integer] = { 116 | .visible = true, 117 | .named = true, 118 | }, 119 | [sym_comment] = { 120 | .visible = true, 121 | .named = true, 122 | }, 123 | [sym_special_sequence] = { 124 | .visible = true, 125 | .named = true, 126 | }, 127 | [anon_sym_EQ] = { 128 | .visible = true, 129 | .named = false, 130 | }, 131 | [anon_sym_SEMI] = { 132 | .visible = true, 133 | .named = false, 134 | }, 135 | [anon_sym_PIPE] = { 136 | .visible = true, 137 | .named = false, 138 | }, 139 | [anon_sym_COMMA] = { 140 | .visible = true, 141 | .named = false, 142 | }, 143 | [anon_sym_DASH] = { 144 | .visible = true, 145 | .named = false, 146 | }, 147 | [anon_sym_STAR] = { 148 | .visible = true, 149 | .named = false, 150 | }, 151 | [anon_sym_LBRACK] = { 152 | .visible = true, 153 | .named = false, 154 | }, 155 | [anon_sym_RBRACK] = { 156 | .visible = true, 157 | .named = false, 158 | }, 159 | [anon_sym_LBRACE] = { 160 | .visible = true, 161 | .named = false, 162 | }, 163 | [anon_sym_RBRACE] = { 164 | .visible = true, 165 | .named = false, 166 | }, 167 | [anon_sym_LPAREN] = { 168 | .visible = true, 169 | .named = false, 170 | }, 171 | [anon_sym_RPAREN] = { 172 | .visible = true, 173 | .named = false, 174 | }, 175 | [sym_syntax] = { 176 | .visible = true, 177 | .named = true, 178 | }, 179 | [sym_syntax_rule] = { 180 | .visible = true, 181 | .named = true, 182 | }, 183 | [sym__expression] = { 184 | .visible = false, 185 | .named = true, 186 | }, 187 | [sym__atom] = { 188 | .visible = false, 189 | .named = true, 190 | }, 191 | [sym_binary_expression] = { 192 | .visible = true, 193 | .named = true, 194 | }, 195 | [sym_group] = { 196 | .visible = true, 197 | .named = true, 198 | }, 199 | [aux_sym_syntax_repeat1] = { 200 | .visible = false, 201 | .named = false, 202 | }, 203 | }; 204 | 205 | enum { 206 | field_definition = 1, 207 | field_left = 2, 208 | field_name = 3, 209 | field_operator = 4, 210 | field_right = 5, 211 | }; 212 | 213 | static const char * const ts_field_names[] = { 214 | [0] = NULL, 215 | [field_definition] = "definition", 216 | [field_left] = "left", 217 | [field_name] = "name", 218 | [field_operator] = "operator", 219 | [field_right] = "right", 220 | }; 221 | 222 | static const TSFieldMapSlice ts_field_map_slices[PRODUCTION_ID_COUNT] = { 223 | [1] = {.index = 0, .length = 1}, 224 | [2] = {.index = 1, .length = 2}, 225 | [3] = {.index = 3, .length = 2}, 226 | [4] = {.index = 5, .length = 3}, 227 | }; 228 | 229 | static const TSFieldMapEntry ts_field_map_entries[] = { 230 | [0] = 231 | {field_name, 0}, 232 | [1] = 233 | {field_definition, 2}, 234 | {field_name, 0}, 235 | [3] = 236 | {field_left, 0}, 237 | {field_operator, 1}, 238 | [5] = 239 | {field_left, 0}, 240 | {field_operator, 1}, 241 | {field_right, 2}, 242 | }; 243 | 244 | static const TSSymbol ts_alias_sequences[PRODUCTION_ID_COUNT][MAX_ALIAS_SEQUENCE_LENGTH] = { 245 | [0] = {0}, 246 | }; 247 | 248 | static const uint16_t ts_non_terminal_alias_map[] = { 249 | 0, 250 | }; 251 | 252 | static const TSStateId ts_primary_state_ids[STATE_COUNT] = { 253 | [0] = 0, 254 | [1] = 1, 255 | [2] = 2, 256 | [3] = 3, 257 | [4] = 4, 258 | [5] = 5, 259 | [6] = 6, 260 | [7] = 7, 261 | [8] = 8, 262 | [9] = 9, 263 | [10] = 10, 264 | [11] = 11, 265 | [12] = 12, 266 | [13] = 13, 267 | [14] = 14, 268 | [15] = 15, 269 | [16] = 16, 270 | [17] = 17, 271 | [18] = 18, 272 | [19] = 19, 273 | [20] = 20, 274 | [21] = 21, 275 | [22] = 22, 276 | [23] = 23, 277 | [24] = 24, 278 | [25] = 25, 279 | [26] = 26, 280 | }; 281 | 282 | static bool ts_lex(TSLexer *lexer, TSStateId state) { 283 | START_LEXER(); 284 | eof = lexer->eof(lexer); 285 | switch (state) { 286 | case 0: 287 | if (eof) ADVANCE(6); 288 | if (lookahead == '\t' || 289 | lookahead == '\n' || 290 | lookahead == '\r' || 291 | lookahead == ' ') SKIP(0) 292 | if (lookahead == '"') ADVANCE(1); 293 | if (lookahead == '\'') ADVANCE(2); 294 | if (lookahead == '(') ADVANCE(22); 295 | if (lookahead == ')') ADVANCE(23); 296 | if (lookahead == '*') ADVANCE(17); 297 | if (lookahead == ',') ADVANCE(15); 298 | if (lookahead == '-') ADVANCE(16); 299 | if (lookahead == ';') ADVANCE(13); 300 | if (lookahead == '=') ADVANCE(12); 301 | if (lookahead == '?') ADVANCE(5); 302 | if (lookahead == '[') ADVANCE(18); 303 | if (lookahead == ']') ADVANCE(19); 304 | if (lookahead == '{') ADVANCE(20); 305 | if (lookahead == '|') ADVANCE(14); 306 | if (lookahead == '}') ADVANCE(21); 307 | if (('0' <= lookahead && lookahead <= '9')) ADVANCE(9); 308 | if (('A' <= lookahead && lookahead <= 'Z') || 309 | ('a' <= lookahead && lookahead <= 'z')) ADVANCE(8); 310 | END_STATE(); 311 | case 1: 312 | if (lookahead == '"') ADVANCE(7); 313 | if (lookahead != 0) ADVANCE(1); 314 | END_STATE(); 315 | case 2: 316 | if (lookahead == '\'') ADVANCE(7); 317 | if (lookahead != 0) ADVANCE(2); 318 | END_STATE(); 319 | case 3: 320 | if (lookahead == ')') ADVANCE(10); 321 | if (lookahead == '*') ADVANCE(3); 322 | if (lookahead != 0) ADVANCE(4); 323 | END_STATE(); 324 | case 4: 325 | if (lookahead == '*') ADVANCE(3); 326 | if (lookahead != 0) ADVANCE(4); 327 | END_STATE(); 328 | case 5: 329 | if (lookahead == '?') ADVANCE(11); 330 | if (lookahead != 0) ADVANCE(5); 331 | END_STATE(); 332 | case 6: 333 | ACCEPT_TOKEN(ts_builtin_sym_end); 334 | END_STATE(); 335 | case 7: 336 | ACCEPT_TOKEN(sym_terminal); 337 | END_STATE(); 338 | case 8: 339 | ACCEPT_TOKEN(sym_identifier); 340 | if (('0' <= lookahead && lookahead <= '9') || 341 | ('A' <= lookahead && lookahead <= 'Z') || 342 | lookahead == '_' || 343 | ('a' <= lookahead && lookahead <= 'z')) ADVANCE(8); 344 | END_STATE(); 345 | case 9: 346 | ACCEPT_TOKEN(sym_integer); 347 | if (('0' <= lookahead && lookahead <= '9')) ADVANCE(9); 348 | END_STATE(); 349 | case 10: 350 | ACCEPT_TOKEN(sym_comment); 351 | END_STATE(); 352 | case 11: 353 | ACCEPT_TOKEN(sym_special_sequence); 354 | END_STATE(); 355 | case 12: 356 | ACCEPT_TOKEN(anon_sym_EQ); 357 | END_STATE(); 358 | case 13: 359 | ACCEPT_TOKEN(anon_sym_SEMI); 360 | END_STATE(); 361 | case 14: 362 | ACCEPT_TOKEN(anon_sym_PIPE); 363 | END_STATE(); 364 | case 15: 365 | ACCEPT_TOKEN(anon_sym_COMMA); 366 | END_STATE(); 367 | case 16: 368 | ACCEPT_TOKEN(anon_sym_DASH); 369 | END_STATE(); 370 | case 17: 371 | ACCEPT_TOKEN(anon_sym_STAR); 372 | END_STATE(); 373 | case 18: 374 | ACCEPT_TOKEN(anon_sym_LBRACK); 375 | END_STATE(); 376 | case 19: 377 | ACCEPT_TOKEN(anon_sym_RBRACK); 378 | END_STATE(); 379 | case 20: 380 | ACCEPT_TOKEN(anon_sym_LBRACE); 381 | END_STATE(); 382 | case 21: 383 | ACCEPT_TOKEN(anon_sym_RBRACE); 384 | END_STATE(); 385 | case 22: 386 | ACCEPT_TOKEN(anon_sym_LPAREN); 387 | if (lookahead == '*') ADVANCE(4); 388 | END_STATE(); 389 | case 23: 390 | ACCEPT_TOKEN(anon_sym_RPAREN); 391 | END_STATE(); 392 | default: 393 | return false; 394 | } 395 | } 396 | 397 | static const TSLexMode ts_lex_modes[STATE_COUNT] = { 398 | [0] = {.lex_state = 0}, 399 | [1] = {.lex_state = 0}, 400 | [2] = {.lex_state = 0}, 401 | [3] = {.lex_state = 0}, 402 | [4] = {.lex_state = 0}, 403 | [5] = {.lex_state = 0}, 404 | [6] = {.lex_state = 0}, 405 | [7] = {.lex_state = 0}, 406 | [8] = {.lex_state = 0}, 407 | [9] = {.lex_state = 0}, 408 | [10] = {.lex_state = 0}, 409 | [11] = {.lex_state = 0}, 410 | [12] = {.lex_state = 0}, 411 | [13] = {.lex_state = 0}, 412 | [14] = {.lex_state = 0}, 413 | [15] = {.lex_state = 0}, 414 | [16] = {.lex_state = 0}, 415 | [17] = {.lex_state = 0}, 416 | [18] = {.lex_state = 0}, 417 | [19] = {.lex_state = 0}, 418 | [20] = {.lex_state = 0}, 419 | [21] = {.lex_state = 0}, 420 | [22] = {.lex_state = 0}, 421 | [23] = {.lex_state = 0}, 422 | [24] = {.lex_state = 0}, 423 | [25] = {.lex_state = 0}, 424 | [26] = {.lex_state = 0}, 425 | }; 426 | 427 | static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = { 428 | [0] = { 429 | [ts_builtin_sym_end] = ACTIONS(1), 430 | [sym_terminal] = ACTIONS(1), 431 | [sym_identifier] = ACTIONS(1), 432 | [sym_integer] = ACTIONS(1), 433 | [sym_comment] = ACTIONS(3), 434 | [sym_special_sequence] = ACTIONS(1), 435 | [anon_sym_EQ] = ACTIONS(1), 436 | [anon_sym_SEMI] = ACTIONS(1), 437 | [anon_sym_PIPE] = ACTIONS(1), 438 | [anon_sym_COMMA] = ACTIONS(1), 439 | [anon_sym_DASH] = ACTIONS(1), 440 | [anon_sym_STAR] = ACTIONS(1), 441 | [anon_sym_LBRACK] = ACTIONS(1), 442 | [anon_sym_RBRACK] = ACTIONS(1), 443 | [anon_sym_LBRACE] = ACTIONS(1), 444 | [anon_sym_RBRACE] = ACTIONS(1), 445 | [anon_sym_LPAREN] = ACTIONS(1), 446 | [anon_sym_RPAREN] = ACTIONS(1), 447 | }, 448 | [1] = { 449 | [sym_syntax] = STATE(26), 450 | [sym_syntax_rule] = STATE(21), 451 | [aux_sym_syntax_repeat1] = STATE(21), 452 | [sym_identifier] = ACTIONS(5), 453 | [sym_comment] = ACTIONS(3), 454 | }, 455 | [2] = { 456 | [sym__expression] = STATE(10), 457 | [sym__atom] = STATE(10), 458 | [sym_binary_expression] = STATE(10), 459 | [sym_group] = STATE(10), 460 | [sym_terminal] = ACTIONS(7), 461 | [sym_identifier] = ACTIONS(7), 462 | [sym_integer] = ACTIONS(9), 463 | [sym_comment] = ACTIONS(3), 464 | [sym_special_sequence] = ACTIONS(7), 465 | [anon_sym_SEMI] = ACTIONS(11), 466 | [anon_sym_PIPE] = ACTIONS(11), 467 | [anon_sym_COMMA] = ACTIONS(11), 468 | [anon_sym_DASH] = ACTIONS(11), 469 | [anon_sym_LBRACK] = ACTIONS(13), 470 | [anon_sym_RBRACK] = ACTIONS(11), 471 | [anon_sym_LBRACE] = ACTIONS(15), 472 | [anon_sym_RBRACE] = ACTIONS(11), 473 | [anon_sym_LPAREN] = ACTIONS(17), 474 | [anon_sym_RPAREN] = ACTIONS(11), 475 | }, 476 | [3] = { 477 | [sym__expression] = STATE(16), 478 | [sym__atom] = STATE(16), 479 | [sym_binary_expression] = STATE(16), 480 | [sym_group] = STATE(16), 481 | [sym_terminal] = ACTIONS(19), 482 | [sym_identifier] = ACTIONS(19), 483 | [sym_integer] = ACTIONS(9), 484 | [sym_comment] = ACTIONS(3), 485 | [sym_special_sequence] = ACTIONS(19), 486 | [anon_sym_SEMI] = ACTIONS(21), 487 | [anon_sym_LBRACK] = ACTIONS(13), 488 | [anon_sym_LBRACE] = ACTIONS(15), 489 | [anon_sym_LPAREN] = ACTIONS(17), 490 | }, 491 | [4] = { 492 | [sym__expression] = STATE(18), 493 | [sym__atom] = STATE(18), 494 | [sym_binary_expression] = STATE(18), 495 | [sym_group] = STATE(18), 496 | [sym_terminal] = ACTIONS(23), 497 | [sym_identifier] = ACTIONS(23), 498 | [sym_integer] = ACTIONS(9), 499 | [sym_comment] = ACTIONS(3), 500 | [sym_special_sequence] = ACTIONS(23), 501 | [anon_sym_LBRACK] = ACTIONS(13), 502 | [anon_sym_RBRACK] = ACTIONS(25), 503 | [anon_sym_LBRACE] = ACTIONS(15), 504 | [anon_sym_LPAREN] = ACTIONS(17), 505 | }, 506 | [5] = { 507 | [sym__expression] = STATE(19), 508 | [sym__atom] = STATE(19), 509 | [sym_binary_expression] = STATE(19), 510 | [sym_group] = STATE(19), 511 | [sym_terminal] = ACTIONS(27), 512 | [sym_identifier] = ACTIONS(27), 513 | [sym_integer] = ACTIONS(9), 514 | [sym_comment] = ACTIONS(3), 515 | [sym_special_sequence] = ACTIONS(27), 516 | [anon_sym_LBRACK] = ACTIONS(13), 517 | [anon_sym_LBRACE] = ACTIONS(15), 518 | [anon_sym_RBRACE] = ACTIONS(25), 519 | [anon_sym_LPAREN] = ACTIONS(17), 520 | }, 521 | [6] = { 522 | [sym__expression] = STATE(17), 523 | [sym__atom] = STATE(17), 524 | [sym_binary_expression] = STATE(17), 525 | [sym_group] = STATE(17), 526 | [sym_terminal] = ACTIONS(29), 527 | [sym_identifier] = ACTIONS(29), 528 | [sym_integer] = ACTIONS(9), 529 | [sym_comment] = ACTIONS(3), 530 | [sym_special_sequence] = ACTIONS(29), 531 | [anon_sym_LBRACK] = ACTIONS(13), 532 | [anon_sym_LBRACE] = ACTIONS(15), 533 | [anon_sym_LPAREN] = ACTIONS(17), 534 | [anon_sym_RPAREN] = ACTIONS(25), 535 | }, 536 | }; 537 | 538 | static const uint16_t ts_small_parse_table[] = { 539 | [0] = 7, 540 | ACTIONS(3), 1, 541 | sym_comment, 542 | ACTIONS(9), 1, 543 | sym_integer, 544 | ACTIONS(13), 1, 545 | anon_sym_LBRACK, 546 | ACTIONS(15), 1, 547 | anon_sym_LBRACE, 548 | ACTIONS(17), 1, 549 | anon_sym_LPAREN, 550 | ACTIONS(31), 3, 551 | sym_terminal, 552 | sym_identifier, 553 | sym_special_sequence, 554 | STATE(11), 4, 555 | sym__expression, 556 | sym__atom, 557 | sym_binary_expression, 558 | sym_group, 559 | [27] = 7, 560 | ACTIONS(3), 1, 561 | sym_comment, 562 | ACTIONS(9), 1, 563 | sym_integer, 564 | ACTIONS(13), 1, 565 | anon_sym_LBRACK, 566 | ACTIONS(15), 1, 567 | anon_sym_LBRACE, 568 | ACTIONS(17), 1, 569 | anon_sym_LPAREN, 570 | ACTIONS(33), 3, 571 | sym_terminal, 572 | sym_identifier, 573 | sym_special_sequence, 574 | STATE(14), 4, 575 | sym__expression, 576 | sym__atom, 577 | sym_binary_expression, 578 | sym_group, 579 | [54] = 7, 580 | ACTIONS(3), 1, 581 | sym_comment, 582 | ACTIONS(9), 1, 583 | sym_integer, 584 | ACTIONS(13), 1, 585 | anon_sym_LBRACK, 586 | ACTIONS(15), 1, 587 | anon_sym_LBRACE, 588 | ACTIONS(17), 1, 589 | anon_sym_LPAREN, 590 | ACTIONS(35), 3, 591 | sym_terminal, 592 | sym_identifier, 593 | sym_special_sequence, 594 | STATE(12), 4, 595 | sym__expression, 596 | sym__atom, 597 | sym_binary_expression, 598 | sym_group, 599 | [81] = 2, 600 | ACTIONS(3), 1, 601 | sym_comment, 602 | ACTIONS(37), 7, 603 | anon_sym_SEMI, 604 | anon_sym_PIPE, 605 | anon_sym_COMMA, 606 | anon_sym_DASH, 607 | anon_sym_RBRACK, 608 | anon_sym_RBRACE, 609 | anon_sym_RPAREN, 610 | [94] = 3, 611 | ACTIONS(3), 1, 612 | sym_comment, 613 | ACTIONS(39), 1, 614 | anon_sym_DASH, 615 | ACTIONS(37), 6, 616 | anon_sym_SEMI, 617 | anon_sym_PIPE, 618 | anon_sym_COMMA, 619 | anon_sym_RBRACK, 620 | anon_sym_RBRACE, 621 | anon_sym_RPAREN, 622 | [109] = 4, 623 | ACTIONS(3), 1, 624 | sym_comment, 625 | ACTIONS(39), 1, 626 | anon_sym_DASH, 627 | ACTIONS(41), 1, 628 | anon_sym_COMMA, 629 | ACTIONS(37), 5, 630 | anon_sym_SEMI, 631 | anon_sym_PIPE, 632 | anon_sym_RBRACK, 633 | anon_sym_RBRACE, 634 | anon_sym_RPAREN, 635 | [126] = 2, 636 | ACTIONS(3), 1, 637 | sym_comment, 638 | ACTIONS(43), 7, 639 | anon_sym_SEMI, 640 | anon_sym_PIPE, 641 | anon_sym_COMMA, 642 | anon_sym_DASH, 643 | anon_sym_RBRACK, 644 | anon_sym_RBRACE, 645 | anon_sym_RPAREN, 646 | [139] = 2, 647 | ACTIONS(3), 1, 648 | sym_comment, 649 | ACTIONS(37), 7, 650 | anon_sym_SEMI, 651 | anon_sym_PIPE, 652 | anon_sym_COMMA, 653 | anon_sym_DASH, 654 | anon_sym_RBRACK, 655 | anon_sym_RBRACE, 656 | anon_sym_RPAREN, 657 | [152] = 2, 658 | ACTIONS(3), 1, 659 | sym_comment, 660 | ACTIONS(45), 7, 661 | anon_sym_SEMI, 662 | anon_sym_PIPE, 663 | anon_sym_COMMA, 664 | anon_sym_DASH, 665 | anon_sym_RBRACK, 666 | anon_sym_RBRACE, 667 | anon_sym_RPAREN, 668 | [165] = 5, 669 | ACTIONS(3), 1, 670 | sym_comment, 671 | ACTIONS(39), 1, 672 | anon_sym_DASH, 673 | ACTIONS(41), 1, 674 | anon_sym_COMMA, 675 | ACTIONS(47), 1, 676 | anon_sym_SEMI, 677 | ACTIONS(49), 1, 678 | anon_sym_PIPE, 679 | [181] = 5, 680 | ACTIONS(3), 1, 681 | sym_comment, 682 | ACTIONS(39), 1, 683 | anon_sym_DASH, 684 | ACTIONS(41), 1, 685 | anon_sym_COMMA, 686 | ACTIONS(49), 1, 687 | anon_sym_PIPE, 688 | ACTIONS(51), 1, 689 | anon_sym_RPAREN, 690 | [197] = 5, 691 | ACTIONS(3), 1, 692 | sym_comment, 693 | ACTIONS(39), 1, 694 | anon_sym_DASH, 695 | ACTIONS(41), 1, 696 | anon_sym_COMMA, 697 | ACTIONS(49), 1, 698 | anon_sym_PIPE, 699 | ACTIONS(51), 1, 700 | anon_sym_RBRACK, 701 | [213] = 5, 702 | ACTIONS(3), 1, 703 | sym_comment, 704 | ACTIONS(39), 1, 705 | anon_sym_DASH, 706 | ACTIONS(41), 1, 707 | anon_sym_COMMA, 708 | ACTIONS(49), 1, 709 | anon_sym_PIPE, 710 | ACTIONS(51), 1, 711 | anon_sym_RBRACE, 712 | [229] = 4, 713 | ACTIONS(3), 1, 714 | sym_comment, 715 | ACTIONS(53), 1, 716 | ts_builtin_sym_end, 717 | ACTIONS(55), 1, 718 | sym_identifier, 719 | STATE(20), 2, 720 | sym_syntax_rule, 721 | aux_sym_syntax_repeat1, 722 | [243] = 4, 723 | ACTIONS(3), 1, 724 | sym_comment, 725 | ACTIONS(5), 1, 726 | sym_identifier, 727 | ACTIONS(58), 1, 728 | ts_builtin_sym_end, 729 | STATE(20), 2, 730 | sym_syntax_rule, 731 | aux_sym_syntax_repeat1, 732 | [257] = 2, 733 | ACTIONS(3), 1, 734 | sym_comment, 735 | ACTIONS(60), 2, 736 | ts_builtin_sym_end, 737 | sym_identifier, 738 | [265] = 2, 739 | ACTIONS(3), 1, 740 | sym_comment, 741 | ACTIONS(62), 2, 742 | ts_builtin_sym_end, 743 | sym_identifier, 744 | [273] = 2, 745 | ACTIONS(3), 1, 746 | sym_comment, 747 | ACTIONS(64), 1, 748 | anon_sym_EQ, 749 | [280] = 2, 750 | ACTIONS(3), 1, 751 | sym_comment, 752 | ACTIONS(66), 1, 753 | anon_sym_STAR, 754 | [287] = 2, 755 | ACTIONS(3), 1, 756 | sym_comment, 757 | ACTIONS(68), 1, 758 | ts_builtin_sym_end, 759 | }; 760 | 761 | static const uint32_t ts_small_parse_table_map[] = { 762 | [SMALL_STATE(7)] = 0, 763 | [SMALL_STATE(8)] = 27, 764 | [SMALL_STATE(9)] = 54, 765 | [SMALL_STATE(10)] = 81, 766 | [SMALL_STATE(11)] = 94, 767 | [SMALL_STATE(12)] = 109, 768 | [SMALL_STATE(13)] = 126, 769 | [SMALL_STATE(14)] = 139, 770 | [SMALL_STATE(15)] = 152, 771 | [SMALL_STATE(16)] = 165, 772 | [SMALL_STATE(17)] = 181, 773 | [SMALL_STATE(18)] = 197, 774 | [SMALL_STATE(19)] = 213, 775 | [SMALL_STATE(20)] = 229, 776 | [SMALL_STATE(21)] = 243, 777 | [SMALL_STATE(22)] = 257, 778 | [SMALL_STATE(23)] = 265, 779 | [SMALL_STATE(24)] = 273, 780 | [SMALL_STATE(25)] = 280, 781 | [SMALL_STATE(26)] = 287, 782 | }; 783 | 784 | static const TSParseActionEntry ts_parse_actions[] = { 785 | [0] = {.entry = {.count = 0, .reusable = false}}, 786 | [1] = {.entry = {.count = 1, .reusable = false}}, RECOVER(), 787 | [3] = {.entry = {.count = 1, .reusable = true}}, SHIFT_EXTRA(), 788 | [5] = {.entry = {.count = 1, .reusable = true}}, SHIFT(24), 789 | [7] = {.entry = {.count = 1, .reusable = true}}, SHIFT(10), 790 | [9] = {.entry = {.count = 1, .reusable = true}}, SHIFT(25), 791 | [11] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_binary_expression, 2, .production_id = 3), 792 | [13] = {.entry = {.count = 1, .reusable = true}}, SHIFT(4), 793 | [15] = {.entry = {.count = 1, .reusable = true}}, SHIFT(5), 794 | [17] = {.entry = {.count = 1, .reusable = false}}, SHIFT(6), 795 | [19] = {.entry = {.count = 1, .reusable = true}}, SHIFT(16), 796 | [21] = {.entry = {.count = 1, .reusable = true}}, SHIFT(23), 797 | [23] = {.entry = {.count = 1, .reusable = true}}, SHIFT(18), 798 | [25] = {.entry = {.count = 1, .reusable = true}}, SHIFT(15), 799 | [27] = {.entry = {.count = 1, .reusable = true}}, SHIFT(19), 800 | [29] = {.entry = {.count = 1, .reusable = true}}, SHIFT(17), 801 | [31] = {.entry = {.count = 1, .reusable = true}}, SHIFT(11), 802 | [33] = {.entry = {.count = 1, .reusable = true}}, SHIFT(14), 803 | [35] = {.entry = {.count = 1, .reusable = true}}, SHIFT(12), 804 | [37] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_binary_expression, 3, .production_id = 4), 805 | [39] = {.entry = {.count = 1, .reusable = true}}, SHIFT(2), 806 | [41] = {.entry = {.count = 1, .reusable = true}}, SHIFT(7), 807 | [43] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_group, 3), 808 | [45] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_group, 2), 809 | [47] = {.entry = {.count = 1, .reusable = true}}, SHIFT(22), 810 | [49] = {.entry = {.count = 1, .reusable = true}}, SHIFT(9), 811 | [51] = {.entry = {.count = 1, .reusable = true}}, SHIFT(13), 812 | [53] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_syntax_repeat1, 2), 813 | [55] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_syntax_repeat1, 2), SHIFT_REPEAT(24), 814 | [58] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_syntax, 1), 815 | [60] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_syntax_rule, 4, .production_id = 2), 816 | [62] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_syntax_rule, 3, .production_id = 1), 817 | [64] = {.entry = {.count = 1, .reusable = true}}, SHIFT(3), 818 | [66] = {.entry = {.count = 1, .reusable = true}}, SHIFT(8), 819 | [68] = {.entry = {.count = 1, .reusable = true}}, ACCEPT_INPUT(), 820 | }; 821 | 822 | #ifdef __cplusplus 823 | extern "C" { 824 | #endif 825 | #ifdef _WIN32 826 | #define extern __declspec(dllexport) 827 | #endif 828 | 829 | extern const TSLanguage *tree_sitter_ebnf(void) { 830 | static const TSLanguage language = { 831 | .version = LANGUAGE_VERSION, 832 | .symbol_count = SYMBOL_COUNT, 833 | .alias_count = ALIAS_COUNT, 834 | .token_count = TOKEN_COUNT, 835 | .external_token_count = EXTERNAL_TOKEN_COUNT, 836 | .state_count = STATE_COUNT, 837 | .large_state_count = LARGE_STATE_COUNT, 838 | .production_id_count = PRODUCTION_ID_COUNT, 839 | .field_count = FIELD_COUNT, 840 | .max_alias_sequence_length = MAX_ALIAS_SEQUENCE_LENGTH, 841 | .parse_table = &ts_parse_table[0][0], 842 | .small_parse_table = ts_small_parse_table, 843 | .small_parse_table_map = ts_small_parse_table_map, 844 | .parse_actions = ts_parse_actions, 845 | .symbol_names = ts_symbol_names, 846 | .field_names = ts_field_names, 847 | .field_map_slices = ts_field_map_slices, 848 | .field_map_entries = ts_field_map_entries, 849 | .symbol_metadata = ts_symbol_metadata, 850 | .public_symbol_map = ts_symbol_map, 851 | .alias_map = ts_non_terminal_alias_map, 852 | .alias_sequences = &ts_alias_sequences[0][0], 853 | .lex_modes = ts_lex_modes, 854 | .lex_fn = ts_lex, 855 | .primary_state_ids = ts_primary_state_ids, 856 | }; 857 | return &language; 858 | } 859 | #ifdef __cplusplus 860 | } 861 | #endif 862 | -------------------------------------------------------------------------------- /crates/tree-sitter-ebnf/src/tree_sitter/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_PARSER_H_ 2 | #define TREE_SITTER_PARSER_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define ts_builtin_sym_error ((TSSymbol)-1) 13 | #define ts_builtin_sym_end 0 14 | #define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024 15 | 16 | typedef uint16_t TSStateId; 17 | 18 | #ifndef TREE_SITTER_API_H_ 19 | typedef uint16_t TSSymbol; 20 | typedef uint16_t TSFieldId; 21 | typedef struct TSLanguage TSLanguage; 22 | #endif 23 | 24 | typedef struct { 25 | TSFieldId field_id; 26 | uint8_t child_index; 27 | bool inherited; 28 | } TSFieldMapEntry; 29 | 30 | typedef struct { 31 | uint16_t index; 32 | uint16_t length; 33 | } TSFieldMapSlice; 34 | 35 | typedef struct { 36 | bool visible; 37 | bool named; 38 | bool supertype; 39 | } TSSymbolMetadata; 40 | 41 | typedef struct TSLexer TSLexer; 42 | 43 | struct TSLexer { 44 | int32_t lookahead; 45 | TSSymbol result_symbol; 46 | void (*advance)(TSLexer *, bool); 47 | void (*mark_end)(TSLexer *); 48 | uint32_t (*get_column)(TSLexer *); 49 | bool (*is_at_included_range_start)(const TSLexer *); 50 | bool (*eof)(const TSLexer *); 51 | }; 52 | 53 | typedef enum { 54 | TSParseActionTypeShift, 55 | TSParseActionTypeReduce, 56 | TSParseActionTypeAccept, 57 | TSParseActionTypeRecover, 58 | } TSParseActionType; 59 | 60 | typedef union { 61 | struct { 62 | uint8_t type; 63 | TSStateId state; 64 | bool extra; 65 | bool repetition; 66 | } shift; 67 | struct { 68 | uint8_t type; 69 | uint8_t child_count; 70 | TSSymbol symbol; 71 | int16_t dynamic_precedence; 72 | uint16_t production_id; 73 | } reduce; 74 | uint8_t type; 75 | } TSParseAction; 76 | 77 | typedef struct { 78 | uint16_t lex_state; 79 | uint16_t external_lex_state; 80 | } TSLexMode; 81 | 82 | typedef union { 83 | TSParseAction action; 84 | struct { 85 | uint8_t count; 86 | bool reusable; 87 | } entry; 88 | } TSParseActionEntry; 89 | 90 | struct TSLanguage { 91 | uint32_t version; 92 | uint32_t symbol_count; 93 | uint32_t alias_count; 94 | uint32_t token_count; 95 | uint32_t external_token_count; 96 | uint32_t state_count; 97 | uint32_t large_state_count; 98 | uint32_t production_id_count; 99 | uint32_t field_count; 100 | uint16_t max_alias_sequence_length; 101 | const uint16_t *parse_table; 102 | const uint16_t *small_parse_table; 103 | const uint32_t *small_parse_table_map; 104 | const TSParseActionEntry *parse_actions; 105 | const char * const *symbol_names; 106 | const char * const *field_names; 107 | const TSFieldMapSlice *field_map_slices; 108 | const TSFieldMapEntry *field_map_entries; 109 | const TSSymbolMetadata *symbol_metadata; 110 | const TSSymbol *public_symbol_map; 111 | const uint16_t *alias_map; 112 | const TSSymbol *alias_sequences; 113 | const TSLexMode *lex_modes; 114 | bool (*lex_fn)(TSLexer *, TSStateId); 115 | bool (*keyword_lex_fn)(TSLexer *, TSStateId); 116 | TSSymbol keyword_capture_token; 117 | struct { 118 | const bool *states; 119 | const TSSymbol *symbol_map; 120 | void *(*create)(void); 121 | void (*destroy)(void *); 122 | bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist); 123 | unsigned (*serialize)(void *, char *); 124 | void (*deserialize)(void *, const char *, unsigned); 125 | } external_scanner; 126 | const TSStateId *primary_state_ids; 127 | }; 128 | 129 | /* 130 | * Lexer Macros 131 | */ 132 | 133 | #define START_LEXER() \ 134 | bool result = false; \ 135 | bool skip = false; \ 136 | bool eof = false; \ 137 | int32_t lookahead; \ 138 | goto start; \ 139 | next_state: \ 140 | lexer->advance(lexer, skip); \ 141 | start: \ 142 | skip = false; \ 143 | lookahead = lexer->lookahead; 144 | 145 | #define ADVANCE(state_value) \ 146 | { \ 147 | state = state_value; \ 148 | goto next_state; \ 149 | } 150 | 151 | #define SKIP(state_value) \ 152 | { \ 153 | skip = true; \ 154 | state = state_value; \ 155 | goto next_state; \ 156 | } 157 | 158 | #define ACCEPT_TOKEN(symbol_value) \ 159 | result = true; \ 160 | lexer->result_symbol = symbol_value; \ 161 | lexer->mark_end(lexer); 162 | 163 | #define END_STATE() return result; 164 | 165 | /* 166 | * Parse Table Macros 167 | */ 168 | 169 | #define SMALL_STATE(id) id - LARGE_STATE_COUNT 170 | 171 | #define STATE(id) id 172 | 173 | #define ACTIONS(id) id 174 | 175 | #define SHIFT(state_value) \ 176 | {{ \ 177 | .shift = { \ 178 | .type = TSParseActionTypeShift, \ 179 | .state = state_value \ 180 | } \ 181 | }} 182 | 183 | #define SHIFT_REPEAT(state_value) \ 184 | {{ \ 185 | .shift = { \ 186 | .type = TSParseActionTypeShift, \ 187 | .state = state_value, \ 188 | .repetition = true \ 189 | } \ 190 | }} 191 | 192 | #define SHIFT_EXTRA() \ 193 | {{ \ 194 | .shift = { \ 195 | .type = TSParseActionTypeShift, \ 196 | .extra = true \ 197 | } \ 198 | }} 199 | 200 | #define REDUCE(symbol_val, child_count_val, ...) \ 201 | {{ \ 202 | .reduce = { \ 203 | .type = TSParseActionTypeReduce, \ 204 | .symbol = symbol_val, \ 205 | .child_count = child_count_val, \ 206 | __VA_ARGS__ \ 207 | }, \ 208 | }} 209 | 210 | #define RECOVER() \ 211 | {{ \ 212 | .type = TSParseActionTypeRecover \ 213 | }} 214 | 215 | #define ACCEPT_INPUT() \ 216 | {{ \ 217 | .type = TSParseActionTypeAccept \ 218 | }} 219 | 220 | #ifdef __cplusplus 221 | } 222 | #endif 223 | 224 | #endif // TREE_SITTER_PARSER_H_ 225 | -------------------------------------------------------------------------------- /dprint.json: -------------------------------------------------------------------------------- 1 | { 2 | "lineWidth": 120, 3 | "indentWidth": 4, 4 | "useTabs": false, 5 | 6 | "typescript": { 7 | "semiColons": "asi", 8 | "quoteStyle": "preferSingle", 9 | "quoteProps": "asNeeded", 10 | "singleBodyPosition": "sameLine", 11 | "nextControlFlowPosition": "sameLine", 12 | "arrowFunction.useParentheses": "preferNone", 13 | "enumDeclaration.memberSpacing": "newLine" 14 | }, 15 | 16 | "json": { 17 | "indentWidth": 2, 18 | "array.preferSingleLine": true 19 | }, 20 | 21 | "markdown": { 22 | "lineWidth": 80, 23 | "textWrap": "always" 24 | }, 25 | 26 | "toml": { 27 | }, 28 | 29 | "dockerfile": { 30 | }, 31 | 32 | "stylua": { 33 | "verify": true, 34 | "quoteStyle": "AutoPreferSingle", 35 | "callParentheses": "NoSingleTable", 36 | "collapseSimpleStatement": "Always" 37 | }, 38 | 39 | "prettier": { 40 | "printWidth": 100, 41 | "tabWidth": 4, 42 | "semi": false, 43 | "singleQuote": true, 44 | "trailingComma": "all", 45 | "arrowParens": "avoid", 46 | "proseWrap": "always", 47 | 48 | "plugin.jsDoc": true, 49 | "yml.tabWidth": 2, 50 | "yaml.tabWidth": 2, 51 | "json.tabWidth": 2, 52 | "jsonc.tabWidth": 2, 53 | "json5.tabWidth": 2 54 | }, 55 | 56 | "rustfmt": { 57 | "max_width": 100 58 | }, 59 | 60 | "sql": { 61 | "uppercase": true, 62 | "linesBetweenQueries": 2 63 | }, 64 | 65 | "ebnf": { 66 | "lineWidth": 100, 67 | "indentWidth": 2 68 | }, 69 | 70 | "includes": ["**/*"], 71 | "excludes": ["**/target", "**/node_modules", "**/*-lock.json"], 72 | "plugins": [ 73 | "https://plugins.dprint.dev/typescript-0.74.0.wasm", 74 | "https://plugins.dprint.dev/json-0.15.6.wasm", 75 | "https://plugins.dprint.dev/markdown-0.14.1.wasm", 76 | "https://plugins.dprint.dev/toml-0.5.4.wasm", 77 | "https://plugins.dprint.dev/dockerfile-0.3.0.wasm", 78 | "https://plugins.dprint.dev/sql-0.1.2.wasm", 79 | "https://plugins.dprint.dev/rustfmt-0.6.2.json@886c6f3161cf020c2d75160262b0f56d74a521e05cfb91ec4f956650c8ca76ca", 80 | "https://plugins.dprint.dev/RubixDev/stylua-v0.1.0.wasm", 81 | "https://plugins.dprint.dev/prettier-0.12.0.json@f8c34316196cf4d0fb2f51b19073331258e15e71a934ee04a7b3328db684c98f", 82 | "https://plugins.dprint.dev/RubixDev/ebnf-v0.1.1.wasm" 83 | ] 84 | } 85 | --------------------------------------------------------------------------------