From d825cac26d2b1e157bb45e2664d610f8a31f4c74 Mon Sep 17 00:00:00 2001 From: Eric Rodrigues Pires Date: Sun, 23 Nov 2025 18:31:30 -0300 Subject: [PATCH] Support \UHHHHHHHH and add EBNF grammar to website --- Cargo.lock | 8 +- README.md | 2 +- duper-js-wasm/rust/Cargo.toml | 31 +- duper-vs-code/syntaxes/duper.tmLanguage.json | 2 +- duper/CHANGELOG.md | 7 + duper/Cargo.toml | 2 +- duper/src/escape.rs | 30 +- duper/src/parser/mod.rs | 30 + duper_lsp/CHANGELOG.md | 11 + duper_lsp/Cargo.toml | 6 +- duper_website/.gitignore | 1 + duper_website/docs/.vitepress/config.mts | 8 +- duper_website/docs/.vitepress/source.ebnf.ts | 201 +++ duper_website/docs/.vitepress/theme/index.ts | 21 +- duper_website/docs/components/DuperEditor.vue | 16 +- duper_website/docs/public/diagram.html | 1220 ++++++++++++++ duper_website/docs/spec.md | 41 +- duper_website/duper.ebnf | 166 ++ duper_website/duper.tmLanguage.duper | 2 +- duper_website/package-lock.json | 1462 ++++++++++++++--- duper_website/package.json | 5 +- duperfmt/CHANGELOG.md | 11 + duperfmt/Cargo.toml | 4 +- tree-sitter-duper/CHANGELOG.md | 11 + tree-sitter-duper/CMakeLists.txt | 2 +- tree-sitter-duper/Cargo.toml | 2 +- tree-sitter-duper/Makefile | 2 +- tree-sitter-duper/grammar.js | 4 +- tree-sitter-duper/package.json | 2 +- tree-sitter-duper/pyproject.toml | 2 +- tree-sitter-duper/src/grammar.json | 4 +- tree-sitter-duper/src/parser.c | 884 ++++++---- tree-sitter-duper/src/scanner.c | 64 +- tree-sitter-duper/tree-sitter.json | 4 +- 34 files changed, 3598 insertions(+), 670 deletions(-) create mode 100644 duper_lsp/CHANGELOG.md create mode 100644 duper_website/docs/.vitepress/source.ebnf.ts create mode 100644 duper_website/docs/public/diagram.html create mode 100644 duper_website/duper.ebnf create mode 100644 duperfmt/CHANGELOG.md create mode 100644 tree-sitter-duper/CHANGELOG.md diff --git a/Cargo.lock b/Cargo.lock index f40bd22..81a5a5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -741,7 +741,7 @@ dependencies = [ [[package]] name = "duper" -version = "0.4.1" +version = "0.4.2" dependencies = [ "ariadne", "base64", @@ -774,7 +774,7 @@ dependencies = [ [[package]] name = "duper_lsp" -version = "0.1.0" +version = "0.1.1" dependencies = [ "async-lsp", "base64", @@ -821,7 +821,7 @@ dependencies = [ [[package]] name = "duperfmt" -version = "0.1.0" +version = "0.1.1" dependencies = [ "clap", "miette", @@ -2897,7 +2897,7 @@ dependencies = [ [[package]] name = "tree-sitter-duper" -version = "0.1.0" +version = "0.1.1" dependencies = [ "cc", "tree-sitter", diff --git a/README.md b/README.md index 48b852f..6d2b66c 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Duper excels in a variety of use cases: ## For implementers -See [the specification](https://duper.dev.br/spec.html) for more details. +See [the specification](https://duper.dev.br/spec.html) or the [EBNF grammar](https://github.com/EpicEric/duper/blob/main/duper.ebnf) for more details. ## Workspace structure diff --git a/duper-js-wasm/rust/Cargo.toml b/duper-js-wasm/rust/Cargo.toml index c50830b..6b4fb75 100644 --- a/duper-js-wasm/rust/Cargo.toml +++ b/duper-js-wasm/rust/Cargo.toml @@ -1,24 +1,23 @@ -# Generated by uniffi-bindgen-react-native -[package] -name = "duper-js-wasm" -version = "0.4.0" -edition = "2018" +[dependencies] +wasm-bindgen = "*" -[lib] -crate-type = ["cdylib", "rlib"] +[dependencies.duper_uniffi] +path = "../../duper_uniffi" + +[dependencies.uniffi-runtime-javascript] +features = ["wasm32"] +version = "=0.29.3-1" [features] default = [] -[dependencies] -duper_uniffi = { path = "../../duper_uniffi" } -# We want to ensure that the version of wasm-bindgen is selected by the -# uniffi-runtime-javascript crate. -# cargo is smart enough to do this if we don't put any further restrictions -# on it. -uniffi-runtime-javascript = { version = "=0.29.3-1", features = ["wasm32"] } -wasm-bindgen = "*" +[lib] +crate-type = ["cdylib", "rlib"] + +[package] +edition = "2018" +name = "duper-js-wasm" +version = "0.4.0" [profile.release] -# Tell `rustc` to optimize for small code size. opt-level = "s" diff --git a/duper-vs-code/syntaxes/duper.tmLanguage.json b/duper-vs-code/syntaxes/duper.tmLanguage.json index e9e47b2..a2c9b51 100644 --- a/duper-vs-code/syntaxes/duper.tmLanguage.json +++ b/duper-vs-code/syntaxes/duper.tmLanguage.json @@ -80,7 +80,7 @@ "patterns": [ { "name": "constant.character.escape.duper", - "match": "\\\\([\\\\\"\\/bfnrt0]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})" + "match": "\\\\([\\\\\"\\/bfnrt0]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})" }, { "name": "invalid.illegal.unrecognized-string-escape.duper", diff --git a/duper/CHANGELOG.md b/duper/CHANGELOG.md index 4091451..01be987 100644 --- a/duper/CHANGELOG.md +++ b/duper/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +### Changed + +- Implement changes from specification 0.4.1. +- Better conformity to specification. + +## 0.4.1 (2025-11-22) + ### Added - Expose `escape`, `format`, and `validate` modules. diff --git a/duper/Cargo.toml b/duper/Cargo.toml index 44602ee..cd04708 100644 --- a/duper/Cargo.toml +++ b/duper/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "duper" description = "The format that's super!" -version = "0.4.1" +version = "0.4.2" edition = "2024" rust-version = "1.88" license = "MIT" diff --git a/duper/src/escape.rs b/duper/src/escape.rs index 01605db..69cb920 100644 --- a/duper/src/escape.rs +++ b/duper/src/escape.rs @@ -99,6 +99,17 @@ pub fn unescape_str<'a>(input: &'a str) -> Result, UnescapeError> { return Err(UnescapeError::InvalidUnicode(hex_str)); } } + Some('U') => { + let hex_str: String = chars.by_ref().take(8).collect(); + if hex_str.len() == 8 + && let Ok(code_point) = u32::from_str_radix(&hex_str, 16) + && let Some(unicode_char) = char::from_u32(code_point) + { + result.push(unicode_char); + } else { + return Err(UnescapeError::InvalidUnicode(hex_str)); + } + } Some(other) => { result.push('\\'); result.push(other); @@ -184,7 +195,11 @@ pub fn escape_str<'a>(input: &'a Cow<'a, str>) -> Cow<'a, str> { c if is_invisible_unicode(c) => { result = Some({ let mut result = result.unwrap_or_else(|| input.split_at(i).0.to_string()); - result.push_str(&format!("\\u{:04x}", c as u32)); + if c.len_utf8() > 2 { + result.push_str(&format!("\\U{:08x}", c as u32)); + } else { + result.push_str(&format!("\\u{:04x}", c as u32)); + } result }); } @@ -246,6 +261,17 @@ pub fn unescape_bytes<'a>(input: &'a str) -> Result, UnescapeError return Err(UnescapeError::InvalidUnicode(hex_str)); } } + Some('U') => { + let hex_str: String = chars.by_ref().take(8).collect(); + if hex_str.len() == 8 + && let Ok(code_point) = u32::from_str_radix(&hex_str, 16) + && let Some(unicode_char) = char::from_u32(code_point) + { + result.extend_from_slice(unicode_char.encode_utf8(&mut buf).as_bytes()); + } else { + return Err(UnescapeError::InvalidUnicode(hex_str)); + } + } Some(other) => { result.push(b'\\'); result.extend_from_slice(other.encode_utf8(&mut buf).as_bytes()); @@ -279,7 +305,7 @@ pub fn escape_bytes<'a>(input: &'a Cow<'a, [u8]>) -> Cow<'a, str> { b'\n' => either::Left(br"\n".iter().copied()), b'\\' => either::Left(br"\\".iter().copied()), b'\'' => either::Left(br"\'".iter().copied()), - b'"' => either::Left(b"\\\"".iter().copied()), + b'"' => either::Left(br#"\""#.iter().copied()), b'\0' => either::Left(br"\0".iter().copied()), _ => either::Right(ascii::escape_default(byte)), }) diff --git a/duper/src/parser/mod.rs b/duper/src/parser/mod.rs index 92fa69b..2238fa8 100644 --- a/duper/src/parser/mod.rs +++ b/duper/src/parser/mod.rs @@ -330,10 +330,12 @@ pub(crate) fn quoted_inner<'a>() one_of("\"\\/bfnrt0").to_slice(), just('x').then(hex_digit().repeated().exactly(2)).to_slice(), just('u').then(hex_digit().repeated().exactly(4)).to_slice(), + just('U').then(hex_digit().repeated().exactly(8)).to_slice(), ))) .to_slice(); none_of("\"\\") + .and_is(control_character().not()) .to_slice() .or(escaped_characters) .repeated() @@ -356,6 +358,15 @@ pub(crate) fn raw_string<'a>() .repeated() .configure(|repeated, ctx| repeated.exactly(*ctx)), ) + .ignored() + .or(choice(( + one_of('\u{0000}'..='\u{0009}'), + one_of('\u{000b}'..='\u{001f}'), + just('\u{007f}'), + )) + .labelled("a control character or tab, excluding new line, excluding new line") + .repeated() + .at_least(1)) .not(), ) .repeated() @@ -387,6 +398,15 @@ pub(crate) fn raw_bytes<'a>() .repeated() .configure(|repeated, ctx| repeated.exactly(*ctx)), ) + .ignored() + .or(choice(( + one_of('\u{0000}'..='\u{0009}'), + one_of('\u{000b}'..='\u{001f}'), + just('\u{007f}'), + )) + .labelled("a control character or tab, excluding new line") + .repeated() + .at_least(1)) .not(), ) .repeated() @@ -561,6 +581,16 @@ pub(crate) fn octal_digit<'a>() -> impl Parser<'a, &'a str, char, extra::Err() +-> impl Parser<'a, &'a str, char, extra::Err>> + Clone { + choice(( + one_of('\u{0000}'..='\u{0009}'), + one_of('\u{000b}'..='\u{001f}'), + just('\u{007f}'), + )) + .labelled("a control character or tab, excluding new line") +} + #[cfg(test)] mod duper_parser_tests { use crate::{ diff --git a/duper_lsp/CHANGELOG.md b/duper_lsp/CHANGELOG.md new file mode 100644 index 0000000..d14d194 --- /dev/null +++ b/duper_lsp/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +## Unreleased + +### Changed + +- Update `tree-sitter-duper` to 0.1.1. + +## 0.1.0 (2025-11-22) + +Initial release. diff --git a/duper_lsp/Cargo.toml b/duper_lsp/Cargo.toml index a5fe813..a20063b 100644 --- a/duper_lsp/Cargo.toml +++ b/duper_lsp/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "duper_lsp" description = "LSP implementation for Duper." -version = "0.1.0" +version = "0.1.1" edition = "2024" rust-version = "1.88" license = "MIT" @@ -14,8 +14,8 @@ readme = "README.md" async-lsp = { version = "0.2.2", features = ["tokio"] } base64 = "0.22.1" clap = { version = "4.5.53", features = ["derive"] } -duper = { version = "0.4.1", path = "../duper" } -duperfmt = { version = "0.1.0", path = "../duperfmt" } +duper = { version = "0.4.2", path = "../duper" } +duperfmt = { version = "0.1.1", path = "../duperfmt" } futures = "0.3.31" line-index = "0.1.2" lsp-types = "0.95.0" diff --git a/duper_website/.gitignore b/duper_website/.gitignore index b590324..234b1e1 100644 --- a/duper_website/.gitignore +++ b/duper_website/.gitignore @@ -4,3 +4,4 @@ dist/ dist-ssr/ pkg/ *.local +*.war diff --git a/duper_website/docs/.vitepress/config.mts b/duper_website/docs/.vitepress/config.mts index 08137dc..46a4bad 100644 --- a/duper_website/docs/.vitepress/config.mts +++ b/duper_website/docs/.vitepress/config.mts @@ -1,14 +1,15 @@ -import { parse } from "@duper-js/wasm"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; +import { parse } from "@duper-js/wasm"; import { globalConst } from "vite-plugin-global-const"; import topLevelAwait from "vite-plugin-top-level-await"; import wasm from "vite-plugin-wasm"; import type { DefaultTheme, UserConfig } from "vitepress"; +import ebnfGrammar from "./source.ebnf"; const DUPER_GRAMMAR = parse( readFileSync(resolve(__dirname, "../../duper.tmLanguage.duper"), "utf-8"), - true + true, ); export default async () => { @@ -27,7 +28,7 @@ export default async () => { ], markdown: { - languages: [DUPER_GRAMMAR], + languages: [DUPER_GRAMMAR, ebnfGrammar], }, themeConfig: { @@ -65,6 +66,7 @@ export default async () => { items: [{ text: "Visual Studio Code", link: "/vs-code" }], }, { text: "Specification", link: "/spec" }, + { text: "Grammar", link: "/grammar" }, ], outline: { diff --git a/duper_website/docs/.vitepress/source.ebnf.ts b/duper_website/docs/.vitepress/source.ebnf.ts new file mode 100644 index 0000000..7f6486e --- /dev/null +++ b/duper_website/docs/.vitepress/source.ebnf.ts @@ -0,0 +1,201 @@ +// https://github.com/mfederczuk/w3c-ebnf-vscode +// Licensed under MPL-2.0 AND Apache-2.0 + +const ebnfGrammar = { + $schema: + "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + name: "W3C EBNF", + scopeName: "source.w3c-ebnf", + aliases: ["ebnf"], + patterns: [ + { include: "#comments" }, + { include: "#rule" }, + { include: "#non-whitespace-illegal" }, + ], + repository: { + comments: { + patterns: [ + { + name: "comment.line.double-slash.w3c-ebnf", + begin: "//", + end: "$", + }, + { + name: "comment.block.w3c-ebnf", + begin: "/\\*", + end: "\\*/", + }, + ], + }, + grammar: { + name: "variable.language.w3c-ebnf", + match: "\\b(grammar|document)\\b", + }, + rule: { + name: "meta.rule.w3c-ebnf", + begin: "^\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*(::=)\\s*", + end: "(;)|(?=^\\s*[a-zA-Z_][a-zA-Z0-9_]*\\s*::=)", + beginCaptures: { + "1": { + patterns: [ + { + name: "invalid.illegal.w3c-ebnf", + match: "\\bEOF\\b", + }, + { + include: "#grammar", + }, + { + name: "variable.other.w3c-ebnf", + match: ".*", + }, + ], + }, + "2": { name: "keyword.operator.new.w3c-ebnf" }, + }, + endCaptures: { + "1": { name: "punctuation.rule-terminator.w3c-ebnf" }, + }, + patterns: [{ include: "#expression" }], + }, + expression: { + patterns: [ + { + include: "#comments", + }, + { + name: "string.quoted.double.w3c-ebnf", + begin: "[\"']", + end: "\\0", + }, + { + name: "constant.character.escape.w3c-ebnf", + match: "#x[0-9a-fA-F]+\\b", + }, + { + name: "invalid.illegal.w3c-ebnf", + match: "\\[\\^?\\]", + }, + { + name: "keyword.other.w3c-ebnf", + match: "\\[\\s*([vV][cC]|[wW][fF][cC])\\s*:[^\\]]*\\]", + }, + { + name: "meta.character-class.w3c-ebnf", + match: "(\\[\\^?)(-{2,})(\\])", + captures: { + "1": { name: "keyword.other.w3c-ebnf" }, + "2": { name: "invalid.illegal.w3c-ebnf" }, + "3": { name: "keyword.other.w3c-ebnf" }, + }, + }, + { + name: "meta.character-class.w3c-ebnf", + match: "(\\[\\^?)(#x[0-9a-fA-F]+[^\\]]*)(\\])", + captures: { + "1": { + name: "keyword.other.w3c-ebnf", + }, + "2": { + patterns: [ + { + name: "meta.character-class.range.w3c-ebnf", + match: "(#x[0-9a-fA-F]+)(-)(#x[0-9a-fA-F]+)", + captures: { + "1": { name: "constant.character.escape.w3c-ebnf" }, + "2": { name: "keyword.other.w3c-ebnf" }, + "3": { name: "constant.character.escape.w3c-ebnf" }, + }, + }, + { + name: "constant.character.escape.w3c-ebnf", + match: "#x[0-9a-fA-F]+", + }, + { + name: "invalid.illegal.w3c-ebnf", + match: ".", + }, + ], + }, + "3": { + name: "keyword.other.w3c-ebnf", + }, + }, + }, + { + name: "meta.character-class.w3c-ebnf", + match: "(\\[\\^?)([^\\]]+)(\\])", + captures: { + "1": { + name: "keyword.other.w3c-ebnf", + }, + "2": { + patterns: [ + { + name: "meta.character-class.range.w3c-ebnf", + match: "(.)(-)(.)", + captures: { + "1": { name: "string.unquoted.w3c-ebnf" }, + "2": { name: "keyword.other.w3c-ebnf" }, + "3": { name: "string.unquoted.w3c-ebnf" }, + }, + }, + { + name: "invalid.illegal.w3c-ebnf", + match: "#x[0-9a-fA-F]+", + }, + { + name: "string.unquoted.w3c-ebnf", + match: ".", + }, + ], + }, + "3": { + name: "keyword.other.w3c-ebnf", + }, + }, + }, + { + name: "meta.parens.w3c-ebnf", + begin: "\\(", + end: "\\)", + beginCaptures: { + "0": { name: "punctuation.parens.open.w3c-ebnf" }, + }, + endCaptures: { + "0": { name: "punctuation.parens.closed.w3c-ebnf" }, + }, + patterns: [{ include: "#expression" }], + }, + { + name: "constant.language.w3e-ebnf", + match: "\\bEOF\\b", + }, + { + include: "#grammar", + }, + { + name: "variable.other.w3c-ebnf", + match: "\\b[a-zA-Z_][a-zA-Z0-9_]*\\b", + }, + { + name: "keyword.operator.w3c-ebnf", + match: "[?|+*-]", + }, + { + include: "#non-whitespace-illegal", + }, + ], + }, + "non-whitespace-illegal": { + patterns: [ + { + name: "invalid.illegal.w3c-ebnf", + match: "\\S", + }, + ], + }, + }, +}; + +export default ebnfGrammar; diff --git a/duper_website/docs/.vitepress/theme/index.ts b/duper_website/docs/.vitepress/theme/index.ts index 224a8a0..7e51cef 100644 --- a/duper_website/docs/.vitepress/theme/index.ts +++ b/duper_website/docs/.vitepress/theme/index.ts @@ -1,17 +1,18 @@ // https://vitepress.dev/guide/custom-theme -import { h } from "vue"; + import type { Theme } from "vitepress"; import DefaultTheme from "vitepress/theme"; +import { h } from "vue"; import "./style.css"; export default { - extends: DefaultTheme, - Layout: () => { - return h(DefaultTheme.Layout, null, { - // https://vitepress.dev/guide/extending-default-theme#layout-slots - }); - }, - enhanceApp({ app, router, siteData }) { - // ... - }, + extends: DefaultTheme, + Layout: () => { + return h(DefaultTheme.Layout, null, { + // https://vitepress.dev/guide/extending-default-theme#layout-slots + }); + }, + enhanceApp() { + // ... + }, } satisfies Theme; diff --git a/duper_website/docs/components/DuperEditor.vue b/duper_website/docs/components/DuperEditor.vue index 8761963..fc2515d 100644 --- a/duper_website/docs/components/DuperEditor.vue +++ b/duper_website/docs/components/DuperEditor.vue @@ -8,22 +8,13 @@
- - -
@@ -74,6 +65,7 @@ onMounted(async () => { yamlGrammar, tomlGrammar, import.meta.env.DUPER_GRAMMAR, + import.meta.env.EBNF_GRAMMAR, ], engine: createOnigurumaEngine(shikiWasm), }); diff --git a/duper_website/docs/public/diagram.html b/duper_website/docs/public/diagram.html new file mode 100644 index 0000000..eb574b4 --- /dev/null +++ b/duper_website/docs/public/diagram.html @@ -0,0 +1,1220 @@ + + + + + + + + + + +

DuperTrunk:

+ + + + + Identifier + + ( + + + Object + + + Array + + + Tuple + + ) + + + Object + + + Array + + + Tuple + +

+

+ +
         ::= Identifier '(' ( Object | Array | Tuple ) ')'
+
           | Object
+
           | Array
+
           | Tuple
+

+

no references


DuperValue:

+ + + + + IdentifiedValue + + + _Value + +

+

+ +
         ::= IdentifiedValue
+
           | _Value
+

+

referenced by: +

+


IdentifiedValue:

+ + + + + Identifier + + ( + + + _Value + + ) + + +

+

+ +
         ::= Identifier '(' _Value ')'
+

+

referenced by: +

+


_Value:

+ + + + + Object + + + Array + + + Tuple + + + String + + + Bytes + + + Temporal + + + Float + + + Integer + + + Boolean + + null + + +

+

+
_Value   ::= Object
+
           | Array
+
           | Tuple
+
           | String
+
           | Bytes
+
           | Temporal
+
           | Float
+
           | Integer
+
           | Boolean
+
           | 'null'
+

+

referenced by: +

+


Identifier:

+ + + + + [A-Z] + + + _ + + + - + + + [a-z] + + + [A-Z] + + + [0-9] + + +

+

+ +
         ::= [A-Z] ( [_#x2D]? [a-zA-Z0-9] )*
+
          /* ws: explicit */
+

+

referenced by: +

+


Object:

+ + + + + { + + + ObjectEntry + + , + + + , + + + } + + +

+

+
Object   ::= '{' ( ObjectEntry ( ',' ObjectEntry )* ','? )? '}'
+

+

referenced by: +

+


Array:

+ + + + + [ + + + DuperValue + + , + + + , + + + ] + + +

+

+
Array    ::= '[' ( DuperValue ( ',' DuperValue )* )? ','? ']'
+

+

referenced by: +

+


Tuple:

+ + + + + ( + + + DuperValue + + , + + + , + + + ) + + +

+

+
Tuple    ::= '(' ( DuperValue ( ',' DuperValue )* )? ','? ')'
+

+

referenced by: +

+


String:

+ + + + + QuotedString + + + RawString + +

+

+ +
           | RawString
+

+

referenced by: +

+


Bytes:

+ + + + + QuotedBytes + + + RawBytes + + + Base64Bytes + +

+

+
Bytes    ::= QuotedBytes
+
           | RawBytes
+
           | Base64Bytes
+

+

referenced by: +

+


Temporal:

+ + + + + ' + + + TemporalContent + + ' + + +

+

+ +
          /* ws: explicit */
+

+

referenced by: +

+


Integer:

+ + + + + DecimalInteger + + + HexInteger + + + OctalInteger + + + BinaryInteger + +

+

+ +
           | HexInteger
+
           | OctalInteger
+
           | BinaryInteger
+

+

referenced by: +

+


Float:

+ + + + + + + + + - + + + [0-9] + + + [1-9] + + + _ + + + [0-9] + + + . + + + [0-9] + + + _ + + + e + + + E + + + + + + + - + + + [0-9] + + + [1-9] + + + _ + + + [0-9] + + + e + + + E + + + + + + + - + + + [0-9] + + + [1-9] + + + _ + + + [0-9] + + +

+

+
Float    ::= [+#x2D]? ( [0-9] | [1-9] ( '_'? [0-9] )+ ) ( '.' [0-9] ( '_'? [0-9] )* ( [eE] + [+#x2D]? ( [0-9] | [1-9] ( '_'? [0-9] )+ ) )? | [eE] [+#x2D]? ( [0-9] | [1-9] ( '_'? + [0-9] )+ ) )
+
          /* ws: explicit */
+

+

referenced by: +

+


Boolean:

+ + + + + true + + + false + + +

+

+
Boolean  ::= 'true'
+
           | 'false'
+

+

referenced by: +

+


ObjectEntry:

+ + + + + ObjectKey + + : + + + DuperValue + +

+

+ +
         ::= ObjectKey ':' DuperValue
+

+

referenced by: +

+


ObjectKey:

+ + + + + PlainKey + + + QuotedString + + + RawString + +

+

+ +
         ::= PlainKey
+
           | QuotedString
+
           | RawString
+

+

referenced by: +

+


PlainKey:

+ + + + + _ + + + [a-z] + + + [A-Z] + + + [0-9] + + + [a-z] + + + [A-Z] + + + _ + + + - + + + [a-z] + + + [A-Z] + + + [0-9] + + +

+

+
PlainKey ::= ( '_' [a-zA-Z0-9] | [a-zA-Z] ) ( [_#x2D]? [a-zA-Z0-9] )*
+
          /* ws: explicit */
+

+

referenced by: +

+


QuotedString:

+ + + + + " + + + QuotedContent + + " + + +

+

+ +
         ::= '"' QuotedContent '"'
+
          /* ws: explicit */
+

+

referenced by: +

+


RawString:

+ + + + + r + + + _RawInner + +

+

+ +
         ::= 'r' _RawInner
+
          /* ws: explicit */
+

+

referenced by: +

+


QuotedBytes:

+ + + + + b" + + + QuotedContent + + " + + +

+

+ +
         ::= 'b"' QuotedContent '"'
+
          /* ws: explicit */
+

+

referenced by: +

+


RawBytes:

+ + + + + br + + + _RawInner + +

+

+ +
          /* ws: explicit */
+

+

referenced by: +

+


Base64Bytes:

+ + + + + b64" + + + Base64Content + + " + + +

+

+ +
         ::= 'b64"' Base64Content '"'
+
          /* ws: explicit */
+

+

referenced by: +

+


_RawInner:

+ + + + + # + + + _RawInner + + # + + + " + + + RawContent + + " + + +

+

+ +
         ::= '#' _RawInner '#'
+
           | '"' RawContent '"'
+

+

referenced by: +

+


RawContent:

+ + + + + [^#x00-#x09#x0A-#x1F#x7F] + + +

+

+ +
         ::= [^#x00-#x09#x0A-#x1F#x7F]*
+
          /* ws: explicit */
+

+

referenced by: +

+


QuotedContent:

+ + + + + \ + + + 0 + + + b + + + t + + + n + + + f + + + r + + + " + + + \ + + + x + + + u + + + U + + + _HexDigit + + + _HexDigit + + + _HexDigit + + + _HexDigit + + + _HexDigit + + + _HexDigit + + + _HexDigit + + + _HexDigit + + [^"\] + + +

+

+ +
         ::= ( '\' ( [0btnfr"\] | ( 'x' | ( 'u' | 'U' _HexDigit _HexDigit _HexDigit _HexDigit ) _HexDigit _HexDigit ) _HexDigit _HexDigit ) | [^"\] )*
+
          /* ws: explicit */
+

+

referenced by: +

+


Base64Content:

+ + + + + [a-z] + + + [A-Z] + + + [0-9] + + + + + + + / + + + Whitespace + + + _Base64Padding + + + _Base64Padding + + + Whitespace + +

+

+ +
         ::= ( [a-zA-Z0-9+/] | Whitespace )* ( _Base64Padding _Base64Padding? )? Whitespace*
+
          /* ws: explicit */
+

+

referenced by: +

+


_Base64Padding:

+ + + + + = + + + Whitespace + +

+

+ +
         ::= '=' Whitespace*
+
          /* ws: explicit */
+

+

referenced by: +

+


TemporalContent:

+ + + + + Whitespace + + [^'] + + + [^#x09#x0A#x0D#x20] + + + [^'] + + + [^'] + + + [^#x09#x0A#x0D#x20] + + + Whitespace + +

+

+ +
         ::= Whitespace* ( [^'] | [^#x09#x0A#x0D#x20] ) [^']+ ( [^'] | [^#x09#x0A#x0D#x20] ) Whitespace*
+
          /* ws: explicit */
+

+

referenced by: +

+


DecimalInteger:

+ + + + + + + + + - + + + [0-9] + + + [1-9] + + + _ + + + [0-9] + + +

+

+ +
         ::= [+#x2D]? ( [0-9] | [1-9] ( '_'? [0-9] )+ )
+
          /* ws: explicit */
+

+

referenced by: +

+


HexInteger:

+ + + + + 0x + + + _HexDigit + + _ + + +

+

+ +
         ::= '0x' _HexDigit ( '_'? _HexDigit )*
+
          /* ws: explicit */
+

+

referenced by: +

+


OctalInteger:

+ + + + + 0o + + + [0-7] + + + _ + + +

+

+ +
         ::= '0o' [0-7] ( '_'? [0-7] )*
+
          /* ws: explicit */
+

+

referenced by: +

+


BinaryInteger:

+ + + + + 0b + + + 0 + + + 1 + + + _ + + +

+

+ +
         ::= '0b' [0-1] ( '_'? [0-1] )*
+
          /* ws: explicit */
+

+

referenced by: +

+


_HexDigit:

+ + + + + [0-9] + + + [a-f] + + + [A-F] + + +

+

+ +
         ::= [0-9a-fA-F]
+

+

referenced by: +

+


LineComment:

+ + + + + // + + + [^#x0A#x0D] + + +

+

+ +
         ::= '//' [^#x0A#x0D]*
+
          /* ws: explicit */
+

+

referenced by: +

+


BlockComment:

+ + + + + /* + + + [^*] + + + * + + + [^/*] + + + / + + +

+

+ +
         ::= '/*' [^*]* '*'+ ( [^/*] [^*]* '*'+ )* '/'
+
          /* ws: explicit */
+

+

referenced by: +

+


Whitespace:

+ + + + + [#x09] + + + [#x0A] + + + [#x0D] + + + [#x20] + + +

+

+ +
         ::= [#x09#x0A#x0D#x20]
+

+

referenced by: +

+


Comment:

+ + + + + LineComment + + + BlockComment + +

+

+ +
           | BlockComment
+

+

no references



+

+ + + + + + +
  + ... generated by RR - Railroad Diagram Generator + + + + + + + R + R +
+

+ + \ No newline at end of file diff --git a/duper_website/docs/spec.md b/duper_website/docs/spec.md index ccb6827..f56dc5d 100644 --- a/duper_website/docs/spec.md +++ b/duper_website/docs/spec.md @@ -1,5 +1,5 @@ --- -version: "0.4.0" +version: "0.4.1" ---