From 8f3322e55627127015e8cae30e432b74cdf72c78 Mon Sep 17 00:00:00 2001 From: Eric Rodrigues Pires Date: Sat, 1 Nov 2025 15:14:21 -0300 Subject: [PATCH] Update spec and Duperify website --- CONTRIBUTING.md | 4 +- duper/src/parser.rs | 20 +- duper_website/docs/.vitepress/config.mts | 23 +- duper_website/docs/components/DuperEditor.vue | 5 +- duper_website/docs/intro-to-duper.md | 4 + duper_website/docs/spec.md | 138 +++++--- duper_website/duper.tmLanguage.duper | 325 ++++++++++++++++++ duper_website/duper.tmLanguage.json | 316 ----------------- duper_website/package-lock.json | 25 ++ duper_website/package.json | 2 + duper_website/tsconfig.json | 18 + 11 files changed, 504 insertions(+), 376 deletions(-) create mode 100644 duper_website/duper.tmLanguage.duper delete mode 100644 duper_website/duper.tmLanguage.json create mode 100644 duper_website/tsconfig.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 35c435f..010bdf8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,9 +19,9 @@ If you wish to contribute changes to Duper, please [fork the repository](https:/ If possible, make sure that your changes pass all tests and linting/formatting checks before creating a pull request by running `just test` and `just lint`, respectively. This should ensure that your PR will pass the CI pipeline. These commands require the following tools to be installed in your system: - [just](https://github.com/casey/just) -- [An up-to-date Rust compiler](https://rustup.rs/) +- [Rust >=1.88](https://rustup.rs/) - [uv](https://docs.astral.sh/uv/) (for Python tests) -- [Node.js (normally installed via NVM)](https://github.com/nvm-sh/nvm) +- [Node.js >=20](https://github.com/nvm-sh/nvm) - [wasm-bindgen-cli](https://github.com/wasm-bindgen/wasm-bindgen/) (for WASM tests) Please add a short description of any user-facing changes to the top of the CHANGELOG.md of the appropriate module(s), under the "Unreleased" section (or create one if it does not exist). The changelog should adhere to [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and it must emphasize any breaking changes. diff --git a/duper/src/parser.rs b/duper/src/parser.rs index 64f5610..b78fc3f 100644 --- a/duper/src/parser.rs +++ b/duper/src/parser.rs @@ -607,9 +607,9 @@ mod duper_parser_tests { assert!(matches!(duper.inner, DuperInner::Tuple(_))); let input = r#" - {duper: 1337} + {duper: 1337, _123: b"bar"} "#; - let duper = DuperParser::parse_duper_trunk(input).unwrap(); + let duper = DuperParser::parse_duper_value(input).unwrap(); assert!(matches!(duper.inner, DuperInner::Object(_))); let input = r#" @@ -694,7 +694,9 @@ mod duper_parser_tests { assert!(matches!(duper.inner, DuperInner::Tuple(_))); let input = r#" - {duper: 1337} + {duper: 1337, _123: b"bar", _a-b_c + : + true, "_": false, "": null} "#; let duper = DuperParser::parse_duper_value(input).unwrap(); assert!(matches!(duper.inner, DuperInner::Object(_))); @@ -1023,6 +1025,18 @@ mod duper_parser_tests { {a-_b: "no hyphen followed by underscore in key"} "#; assert!(DuperParser::parse_duper_value(input).is_err()); + let input = r#" + {_-a: "no starting underscore followed by hyphen in key"} + "#; + assert!(DuperParser::parse_duper_value(input).is_err()); + let input = r#" + {__a: "no starting double underscore in key"} + "#; + assert!(DuperParser::parse_duper_value(input).is_err()); + let input = r#" + {_: "no singular underscore in key"} + "#; + assert!(DuperParser::parse_duper_value(input).is_err()); let input = r#" {b"value as key": null} "#; diff --git a/duper_website/docs/.vitepress/config.mts b/duper_website/docs/.vitepress/config.mts index a298fe5..d679a01 100644 --- a/duper_website/docs/.vitepress/config.mts +++ b/duper_website/docs/.vitepress/config.mts @@ -1,8 +1,16 @@ +import { parse } from "@duper-js/wasm"; import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { globalConst } from "vite-plugin-global-const"; import topLevelAwait from "vite-plugin-top-level-await"; import wasm from "vite-plugin-wasm"; import { defineConfig } from "vitepress"; +const DUPER_GRAMMAR = parse( + readFileSync(resolve(__dirname, "../../duper.tmLanguage.duper"), "utf-8"), + true +); + export default defineConfig({ title: "Duper", description: "The format that's super!", @@ -18,7 +26,7 @@ export default defineConfig({ ], markdown: { - languages: [JSON.parse(readFileSync("duper.tmLanguage.json", "utf-8"))], + languages: [DUPER_GRAMMAR], }, themeConfig: { @@ -77,9 +85,20 @@ export default defineConfig({ }, vite: { - plugins: [wasm(), topLevelAwait()], + plugins: [ + wasm(), + topLevelAwait(), + globalConst({ + DUPER_GRAMMAR, + }), + ], ssr: { noExternal: ["monaco-editor"], }, + resolve: { + alias: { + "@": resolve(__dirname, "../.."), + }, + }, }, }); diff --git a/duper_website/docs/components/DuperEditor.vue b/duper_website/docs/components/DuperEditor.vue index 48f6d17..bce314a 100644 --- a/duper_website/docs/components/DuperEditor.vue +++ b/duper_website/docs/components/DuperEditor.vue @@ -40,8 +40,7 @@ import { createOnigurumaEngine } from "shiki/engine/oniguruma"; import shikiWasm from "shiki/wasm"; import { useData } from 'vitepress' import { onMounted, onUnmounted, ref, watch } from "vue"; -import duperGrammar from "../../duper.tmLanguage.json" with { type: "json" }; -import { convertDuper } from "../../pkg/duper_website"; +import { convertDuper } from "@/pkg/duper_website"; const props = defineProps<{ initial?: string; @@ -61,7 +60,7 @@ onMounted(async () => { const highlighter = await createHighlighterCore({ themes: [githubDarkTheme, githubLightTheme], - langs: [jsonGrammar, yamlGrammar, tomlGrammar, duperGrammar], + langs: [jsonGrammar, yamlGrammar, tomlGrammar, import.meta.env.DUPER_GRAMMAR], engine: createOnigurumaEngine(shikiWasm), }); diff --git a/duper_website/docs/intro-to-duper.md b/duper_website/docs/intro-to-duper.md index 97c9bbc..5ca15ac 100644 --- a/duper_website/docs/intro-to-duper.md +++ b/duper_website/docs/intro-to-duper.md @@ -128,3 +128,7 @@ Product({ ``` Finally, Duper has the notion of _identifiers_: optional type-like annotations (`MyIdentifier(...)`) to help with readability, or to suggest that the parser validates the data in a specific manner. + +--- + +Want to learn more? [Check out the specification](/spec). diff --git a/duper_website/docs/spec.md b/duper_website/docs/spec.md index 0383bde..4c9ce67 100644 --- a/duper_website/docs/spec.md +++ b/duper_website/docs/spec.md @@ -1,5 +1,5 @@ --- -version: "0.3.0" +version: "0.3.1" ---

@@ -43,7 +43,7 @@ The area delimited by a forward slash immediately followed by an asterisk `/*`, */ key: "value", another: "/* This is not a comment, -despite spanning multiple lines */" + despite spanning multiple lines */" } ``` @@ -64,7 +64,7 @@ Keys are on the left of the colon `:`, and values are on the right. Whitespace i } ``` -There must be a comma `,` between key/value pairs. +There must be a comma `,` between key-value pairs. ```duper { @@ -98,15 +98,24 @@ Values must have one of the following types: A key may be either plain, quoted, or raw. -**Plain keys** may only contain ASCII letters, ASCII digits, underscores `_`, and hyphens `-`. They must start with an ASCII letter or an underscore. Sequences of underscores and hyphens are not allowed, and bare keys must not end with them. +**Plain keys** may only contain ASCII letters, ASCII digits, underscores `_`, and hyphens `-`. They must start with an ASCII letter, or an underscore followed by a letter or digit. Sequences of underscores and hyphens are not allowed, and plain keys must not end with them. ```duper { + // Allowed key: "value", - bare_key: "value", - b4re-k3y: "value", + plain_key: "value", + pla1n-k3y: "value", _1234: "value", - _: "value", + + // Allowed but discouraged + Capitalized: "value", + + // Not allowed + _: "value", // INVALID + útf8: "value", // INVALID + : "value", // INVALID + kebabest--case: "value", // INVALID } ``` @@ -116,7 +125,8 @@ A key may be either plain, quoted, or raw. { "127.0.0.1": "value", "character encoding": "value", - "ʎǝʞ": "value", + "maçã": "value", + "_": "value", "": "value", } ``` @@ -130,16 +140,15 @@ A key may be either plain, quoted, or raw. } ``` -Indentation is treated as whitespace and ignored. +Indentation around keys is treated as whitespace and ignored. Defining a key multiple times is invalid. Note that plain keys, quoted keys, and raw keys are equivalent. ```duper { name: "Eric", - name: "Erick", // INVALID "n\x61me": "Erik", // INVALID - r"name": "Heryk", // INVALID + r"name": "Erick", // INVALID } ``` @@ -151,11 +160,15 @@ A string may be either quoted or raw. ```duper { - str: "I'm a string. \"You can quote me\". Name\tJos\xE9\nLocation\tBR.", + str1: "I'm a string.", + str2: "\"You can quote me\"", + str3: "Name\tJos\xE9\nLocation\tBR.", + str4: " padded ", + str5: "ඞ", // U+0D9E SINHALA LETTER KANTAJA NAASIKYAYA } ``` -For convenience, some popular characters have a compact escape sequence: +For convenience, some characters have a compact escape sequence: ```duper [ @@ -172,11 +185,11 @@ For convenience, some popular characters have a compact escape sequence: ] ``` -Any Unicode character may be escaped with `\uHHHH` or a sequence of one or more `\xHH`. The escape codes must be valid Unicode [scalar values](https://unicode.org/glossary/#unicode_scalar_value). +Any Unicode character may be escaped with `\uHHHH` or a sequence of one or more `\xHH`, where `H` is a hexadecimal digit. The escape codes must be valid Unicode [scalar values](https://unicode.org/glossary/#unicode_scalar_value). -Keep in mind that Duper strings are sequences of Unicode characters, _not_ byte sequences. Parsers should raise an error if a string contains invalid Unicode. For binary data, use [byte strings](#byte-strings). +Keep in mind that Duper strings are sequences of Unicode characters, _not_ byte sequences. Parsers should raise an error if a string decodes into invalid Unicode. For binary data, use [byte strings](#byte-strings). -**Raw strings** start with the lowercase letter R `r`, immediately followed by zero or more hash symbols `#`, immediately followed by a quotation mark `"`. They end with a quotation mark, followed by the same number of starting hash symbols. They allow newlines and have no escaping whatsoever. +**Raw strings** start with the lowercase letter R, immediately followed by zero or more hash symbols `#`, immediately followed by a quotation mark `"`. They end with a quotation mark, followed by the same number of starting hash symbols. (for example: `r"..."`, `r#"..."#`, `r##"..."##`, and so on.). They allow newlines and have no escaping whatsoever. ```duper { @@ -187,7 +200,18 @@ Keep in mind that Duper strings are sequences of Unicode characters, _not_ byte lines: r" The first newline is not trimmed. All whitespace is - preserved.", + preserved in here. ", +} +``` + +The hashtags are required to disambiguate quotes (`"`, or `"#`, or `"##`, etc.) which are part of the value from the raw string terminator. + +```duper +{ + inner_quotes: r"Well, "that" just happened.", // INVALID + too_few_ending_hashes: r#"", // INVALID + too_many_ending_hashes: r#""##, // INVALID + not_enough_hashes: r#"will "# close the string"#, // INVALID } ``` @@ -197,7 +221,7 @@ Control characters, including tabs, are not permitted in a raw string. Byte strings are similar to strings, but represent binary data. Like strings, they come in quoted or raw variants. -**Quoted byte strings** start with the lowercase letter B immediately followed by a quotation mark `b"`, and end with a quotation mark `"`. The escape sequences are the same as quoted strings, although they are not required to form valid UTF-8 codepoints. +**Quoted byte strings** start with the lowercase letter B immediately followed by a quotation mark `b"`, and end with a quotation mark `"`. The escape sequences are the same as in quoted strings, although they are not required to form valid UTF-8 codepoints. ```duper { @@ -212,8 +236,8 @@ Byte strings are similar to strings, but represent binary data. Like strings, th ```duper { path: br"C:\Windows\System32", - special_characters: br#"/\*"#, - binary_data: br##"Raw bytes with "# symbol"##, + shrug: br#" "Whatever." ¯\_(ツ)_/¯ "#, + rust_expression: br##"{ let str = r#"meta string"#; }"##, } ``` @@ -234,19 +258,22 @@ For large numbers, you may use underscores between digits to enhance readability ```duper { + // Allowed int5: 1_000, int6: 5_349_221, int7: 53_49_221, int8: 1_2_3_4_5, - INVALID1: 1__2, - INVALID2: _12, - INVALID3: 12_, + + // Not allowed + wrong1: 1__2, // INVALID + wrong2: _12, // INVALID + wrong3: 12_, // INVALID } ``` Leading zeros are not allowed. Integer values `-0` and `+0` are valid and identical to an unprefixed zero. -Non-negative integer values may also be expressed in hexadecimal, octal, or binary. In these formats, leading `+` is not allowed and leading zeros are allowed (after the prefix). Hex values are case-insensitive. Underscores are allowed between digits (but not between the prefix and the value). +Non-negative integer values may also be expressed in hexadecimal (`0x...`), octal (`0o...`), or binary (`0b...`). In these formats, plus or minus signs `+` or `-` are not allowed, but leading zeros (after the prefix) are allowed. Hexadecimal values are case-insensitive. Underscores are allowed between digits (but not between the prefix and the value). ```duper { @@ -259,12 +286,12 @@ Non-negative integer values may also be expressed in hexadecimal, octal, or bina oct2: 0o01_234_567, // Binary with prefix `0b` - bin1: 0b11010110, + bin1: 0b1101, bin2: 0b0101_0101, } ``` -Implementations are free to support any integer size. It's recommended that at least 64-bit signed integers (i.e. long integers, from −2^63 to 2^63−1) are accepted and handled losslessly. If an integer cannot be represented in the chosen integer size, implementations may convert it losslessly into a float or a string, using an appropriate [identifier](#identifiers) for its original type in both cases. +Implementations are free to support any integer size. It's recommended that at least 64-bit signed integers (i.e. long integers, from −2^63 to 2^63−1) are accepted and handled losslessly. If an integer cannot be represented in the chosen integer size, implementations may raise an error or convert it losslessly into a float or a string, using an appropriate [identifier](#identifiers) for its original type in both cases. ## Floats @@ -289,7 +316,7 @@ A float consists of an integer part (which follows the same rules as decimal int A fractional part is a decimal point followed by one or more digits. -An exponent part is an E (upper or lower case) followed by an integer part (which follows the same rules as decimal integer values but may include leading zeros). +An exponent part is an `e` (upper or lower case) followed by an integer part (which follows the same rules as decimal integer values, but may include leading zeros). The decimal point, if used, must be surrounded by at least one digit on each side. @@ -298,7 +325,6 @@ The decimal point, if used, must be surrounded by at least one digit on each sid invalid_float_1: .7, // INVALID invalid_float_2: 7., // INVALID invalid_float_3: 3.e+20, // INVALID - } ``` @@ -307,10 +333,11 @@ Similar to integers, you may use underscores to enhance readability. Each unders ```duper { float8: 224_617.445_991_228, + float9: 1e2_00, } ``` -Float values `-0.0` and `+0.0` are valid and should map according to IEEE 754. Infinity and NaN are invalid values. +Float values `-0.0` and `+0.0` are valid and should map according to IEEE 754. Infinity and NaN are not allowed. Implementations are free to support any precision level. It's recommended that at least IEEE 754 64-bit floating point values (i.e. doubles) are supported. @@ -320,8 +347,8 @@ Booleans are one of `true` or `false`. ```duper { - truthyBool: true, - falsyBool: false, + tis: true, + nah: false, } ``` @@ -331,7 +358,7 @@ Null is always `null`. ```duper { - nullValue: null, + nuclear_launch_code: null, } ``` @@ -341,19 +368,22 @@ Arrays are ordered values surrounded by square brackets `[` and `]`. Whitespace ```duper { - empty: [], - another_empty: [,], + empty_array: [], + another_empty_array: [,], integers: [1, 2, 3], - colors: ["red", "yellow", "green"], - nested_arrays_of_ints: [[1, 2], [3, 4, 5]], - nested_mixed_array: [[1, 2], ["a", "b", "c"]], - string_array: ["all strings", r"are the", r#"same type"#], + colors: ["red", "yellow", r"green"], + unflattened_ints: [[1, 2], [3, 4, 5]], // Mixed-type arrays are allowed numbers: [0.1, 0.2, 0.5, 1, 2, 5], + nested_mixed_array: [[1, "a"], [2, "b", {}]], contributors: [ "Foo Bar ", - {name: "Baz Qux", email: "bazqux@example.com", url: "https://example.com/bazqux"}, + { + name: "Baz Qux", + email: "bazqux@example.com", + url: "https://example.com/bazqux", + }, ], } ``` @@ -369,16 +399,21 @@ Arrays can span multiple lines. A terminating comma (also called a trailing comm ## Tuples -Tuples are similar to arrays, although parsers may choose to handle them differently. They are surrounded by parenthesis `(` and `)`. Unlike arrays, empty tuples must be represented with joined parenthesis `()`, or with a comma optionally surrounded by whitespace and comments. +Tuples are similar to arrays, although parsers may choose to handle them differently. They are surrounded by parenthesis `(` and `)`. ```duper { - empty: (), - another_empty: (,), - single: (1), - another_single: (1,), - tuple_of_arrays: ([1, 2], [3, 4, 5]), - array_of_tuples: [(true, 1.0), ("x", "y", "z")], + empty_tuple: (), + another_empty_tuple: (,), + single_element: (1), + another_single_element: (1,), + tuple_of_arrays: ([true, 1.0], ["x", "y", "z"]), + array_of_tuples: [(1, null), (3, 4.0, 5)], + multiline_tuple: ( + "Vec", + "Cow", + "Arc", + ), } ``` @@ -388,13 +423,13 @@ Any parenthesized expression must be interpreted as a tuple by parsers. Identifiers are optional type-like annotations that wrap any kind of value, providing semantic meaning or hinting at special handling during parsing/validation. Identified values are composed of the identifier name, followed by the value wrapped in parenthesis `(` and `)`. -The first character must be an ASCII uppercase letter, followed by ASCII letters, ASCII digits, underscores `_`, and hyphens `-`. Sequences of underscores and hyphens are not allowed in the identifier, and identifiers may not start or end with either of them. +The first character must be an ASCII uppercase letter, followed by zero or more ASCII letters, ASCII digits, underscores `_`, and hyphens `-`. Sequences of underscores and hyphens are not allowed in the identifier, and identifiers may not start or end with either of them. ```duper { user_id: Uuid("550e8400-e29b-41d4-a716-446655440000"), created: DateTime("2024-01-15T10:30:00Z"), - birthday: ISO-8601("1990-05-20"), + birthday: ISO-8601("2025-10-20"), price: Decimal("19.99"), weight: Kilograms(2.5), color: RGB((255, 0, 128)), // Two sets of parenthesis for tuples @@ -403,6 +438,7 @@ The first character must be an ASCII uppercase letter, followed by ASCII letters version: Version("1.2.3"), hash: SHA_256(b"\xde\xad\xbe\xef"), }), + minimal: A(null), } ``` @@ -419,13 +455,13 @@ Values may not contain more than one identifier. ```duper { - many: IpAddress(Ipv4Address("192.168.0.1")) // INVALID + too_many: IpAddress(Ipv4Address("192.168.0.1")) // INVALID } ``` Parsers should preserve identifier information on a best-effort basis. Deserializers may ignore identifiers, or use them for validation. Serializers may choose to output or omit identifiers by the user's request. -Implementations are free to define their own identifiers with specific semantics. For example, in strongly-typed or OOP languages, they may use them as annotations for the underlying types, handling any (potentially lossy) conversion between Duper identifiers and type names. +Implementations are free to define their own identifiers with specific semantics. For example, in strongly-typed or OOP languages, serializers may use them as annotations for the underlying types. ## Filename extension @@ -434,3 +470,5 @@ Duper files should use the extension `.duper`. ## MIME type When transferring Duper files over the internet, the appropriate MIME type is `application/duper`. + +Webservers should also accept the `application/x-duper` MIME type. diff --git a/duper_website/duper.tmLanguage.duper b/duper_website/duper.tmLanguage.duper new file mode 100644 index 0000000..fec7ae8 --- /dev/null +++ b/duper_website/duper.tmLanguage.duper @@ -0,0 +1,325 @@ +TextMateGrammar({ + "$schema": JsonSchema("https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json"), + name: "Duper", + scopeName: "source.duper", + aliases: [ + "duper", + ], + patterns: [ + { + include: "#values", + }, + { + include: "#identifiers", + }, + ], + repository: { + keywords: { + patterns: [ + { + name: "keyword.control.duper", + match: Regex(r"\b(true|false|null)\b"), + }, + ], + }, + strings: { + patterns: [ + { + name: "string.quoted.double.duper", + begin: Regex("b?\""), + end: Regex("\""), + patterns: [ + { + include: "#stringescapes", + }, + { + include: "#stringcontent", + }, + ], + }, + { + name: "string.quoted.double.raw_one.duper", + begin: Regex("b?r(#*)\""), + beginCaptures: { + "1": { + name: "punctuation.definition.string.raw.duper", + }, + }, + end: Regex(r#""(\1)"#), + endCaptures: { + "1": { + name: "punctuation.definition.string.raw.duper", + }, + }, + }, + ], + }, + identifiers: { + patterns: [ + { + begin: Regex(r"([A-Z](?:[_-]?[A-Za-z0-9])*)(\()"), + beginCaptures: { + "1": { + name: "entity.name.type.duper", + }, + "2": { + name: "punctuation.definition.identifiers.duper", + }, + }, + end: Regex(r"(\))"), + endCaptures: { + "1": { + name: "punctuation.definition.identifiers.duper", + }, + }, + patterns: [ + { + begin: Regex(r"([A-Z](?:[_-]?[A-Za-z0-9])*)(\()"), + beginCaptures: { + "1": { + name: "invalid.illegal.nested-identifier.duper", + }, + "2": { + name: "invalid.illegal.nested-identifier.duper", + }, + }, + end: Regex(r"(\))"), + endCaptures: { + "1": { + name: "invalid.illegal.nested-identifier.duper", + }, + }, + patterns: [ + { + include: "#values", + }, + ], + }, + { + include: "#values", + }, + ], + }, + ], + }, + plainkeys: { + patterns: [ + { + name: "string.unquoted.duper", + match: Regex("(_[A-Za-z0-9]?|[A-Za-z])([_-]?[A-Za-z0-9])*"), + }, + ], + }, + numbers: { + patterns: [ + { + name: "constant.numeric.duper", + match: Regex(r"0x[0-9a-fA-F](_?[0-9a-fA-F])*|0o[0-7](_?[0-7])*|0b[0-7](_?[0-1])*|[+-]?(0|[1-9](_?[0-9])*)(\.[0-9](_?[0-9])*)?(([eE][+-]?[0-9](_?[0-9])*)?)?"), + }, + ], + }, + comments: { + patterns: [ + { + name: "comment.line.duper", + match: Regex(r"(//).*$\n?"), + }, + ], + }, + multilinecomments: { + patterns: [ + { + name: "comment.block.duper", + begin: Regex(r"(/\*)(?:\s*((@)internal)(?=\s|(\*/)))?"), + end: Regex(r"\*/"), + patterns: [ + { + include: "#multilinecomments", + }, + ], + }, + ], + }, + arrays: { + begin: Regex(r"\["), + beginCaptures: { + "0": { + name: "punctuation.definition.arrays.duper", + }, + }, + end: Regex(r"\]"), + endCaptures: { + "0": { + name: "punctuation.definition.arrays.duper", + }, + }, + patterns: [ + { + include: "#values", + }, + { + include: "#comments", + }, + { + include: "#multilinecomments", + }, + { + match: Regex(","), + name: "punctuation.separator.arrays.duper", + }, + { + match: Regex(r"[^\s\]]"), + name: "invalid.illegal.expected-arrays-separator.duper", + }, + ], + }, + tuples: { + begin: Regex(r"\("), + beginCaptures: { + "0": { + name: "punctuation.definition.tuple.duper", + }, + }, + end: Regex(r"\)"), + endCaptures: { + "0": { + name: "punctuation.definition.tuple.duper", + }, + }, + name: "meta.structure.tuple.duper", + patterns: [ + { + include: "#values", + }, + { + include: "#comments", + }, + { + include: "#multilinecomments", + }, + { + match: Regex(","), + name: "punctuation.separator.tuple.duper", + }, + { + match: Regex(r"[^\s\)]"), + name: "invalid.illegal.expected-tuple-separator.duper", + }, + ], + }, + objects: { + begin: Regex(r"\{"), + beginCaptures: { + "0": { + name: "punctuation.definition.dictionary.duper", + }, + }, + end: Regex(r"\}"), + endCaptures: { + "0": { + name: "punctuation.definition.dictionary.duper", + }, + }, + patterns: [ + { + include: "#objectkeys", + }, + { + include: "#comments", + }, + { + include: "#multilinecomments", + }, + { + begin: Regex(":"), + beginCaptures: { + "0": { + name: "punctuation.separator.dictionary.key.duper", + }, + }, + end: Regex(r"(,)|(?=\})"), + endCaptures: { + "1": { + name: "punctuation.separator.dictionary.pair.duper", + }, + }, + name: "meta.structure.dictionary.value.duper", + patterns: [ + { + include: "#values", + }, + { + match: Regex(r"[^\s,]"), + name: "invalid.illegal.expected-dictionary-separator.duper", + }, + ], + }, + { + match: Regex(r"[^\s\}]"), + name: "invalid.illegal.expected-dictionary-separator.duper", + }, + ], + }, + objectkeys: { + patterns: [ + { + include: "#strings", + }, + { + include: "#plainkeys", + }, + ], + }, + stringcontent: { + patterns: [ + { + match: Regex("."), + name: "string.quoted.double.duper", + }, + ], + }, + stringescapes: { + patterns: [ + { + name: "constant.character.escape.duper", + match: Regex(r#"\\([\\"\/bfnrt0]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})"#), + }, + { + name: "invalid.illegal.unrecognized-string-escape.duper", + match: Regex(r"\\."), + }, + ], + }, + values: { + patterns: [ + { + include: "#keywords", + }, + { + include: "#numbers", + }, + { + include: "#identifiers", + }, + { + include: "#strings", + }, + { + include: "#objects", + }, + { + include: "#arrays", + }, + { + include: "#tuples", + }, + { + include: "#comments", + }, + { + include: "#multilinecomments", + }, + ], + }, + }, +}) diff --git a/duper_website/duper.tmLanguage.json b/duper_website/duper.tmLanguage.json deleted file mode 100644 index a413998..0000000 --- a/duper_website/duper.tmLanguage.json +++ /dev/null @@ -1,316 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", - "name": "Duper", - "scopeName": "source.duper", - "aliases": ["duper"], - "patterns": [ - { - "include": "#values" - }, - { - "include": "#identifiers" - } - ], - "repository": { - "keywords": { - "patterns": [ - { - "name": "keyword.control.duper", - "match": "\\b(true|false|null)\\b" - } - ] - }, - "strings": { - "patterns": [ - { - "name": "string.quoted.double.duper", - "begin": "b?\"", - "end": "\"", - "patterns": [ - { - "include": "#stringescapes" - }, - { - "include": "#stringcontent" - } - ] - }, - { - "name": "string.quoted.double.raw_one.duper", - "begin": "b?r(#*)\"", - "beginCaptures": { - "1": { - "name": "punctuation.definition.string.raw.duper" - } - }, - "end": "\"(\\1)", - "endCaptures": { - "1": { - "name": "punctuation.definition.string.raw.duper" - } - } - } - ] - }, - "identifiers": { - "patterns": [ - { - "begin": "([A-Z](?:[_-]?[A-Za-z0-9])*)(\\()", - "beginCaptures": { - "1": { - "name": "entity.name.type.duper" - }, - "2": { - "name": "punctuation.definition.identifiers.duper" - } - }, - "end": "(\\))", - "endCaptures": { - "1": { - "name": "punctuation.definition.identifiers.duper" - } - }, - "patterns": [ - { - "begin": "([A-Z](?:[_-]?[A-Za-z0-9])*)(\\()", - "beginCaptures": { - "1": { - "name": "invalid.illegal.nested-identifier.duper" - }, - "2": { - "name": "invalid.illegal.nested-identifier.duper" - } - }, - "end": "(\\))", - "endCaptures": { - "1": { - "name": "invalid.illegal.nested-identifier.duper" - } - }, - "patterns": [ - { - "include": "#values" - } - ] - }, - { - "include": "#values" - } - ] - } - ] - }, - "plainkeys": { - "patterns": [ - { - "name": "string.unquoted.duper", - "match": "(_[A-Za-z0-9]?|[A-Za-z])([_-]?[A-Za-z0-9])*" - } - ] - }, - "numbers": { - "patterns": [ - { - "name": "constant.numeric.duper", - "match": "0x[0-9a-fA-F](_?[0-9a-fA-F])*|0o[0-7](_?[0-7])*|0b[0-7](_?[0-1])*|[+-]?(0|[1-9](_?[0-9])*)(\\.[0-9](_?[0-9])*)?(([eE][+-]?[0-9](_?[0-9])*)?)?" - } - ] - }, - "comments": { - "patterns": [ - { - "name": "comment.line.duper", - "match": "(//).*$\\n?" - } - ] - }, - "multilinecomments": { - "patterns": [ - { - "name": "comment.block.duper", - "begin": "(/\\*)(?:\\s*((@)internal)(?=\\s|(\\*/)))?", - "end": "\\*/", - "patterns": [ - { - "include": "#multilinecomments" - } - ] - } - ] - }, - "arrays": { - "begin": "\\[", - "beginCaptures": { - "0": { - "name": "punctuation.definition.arrays.duper" - } - }, - "end": "\\]", - "endCaptures": { - "0": { - "name": "punctuation.definition.arrays.duper" - } - }, - "patterns": [ - { - "include": "#values" - }, - { - "include": "#comments" - }, - { - "include": "#multilinecomments" - }, - { - "match": ",", - "name": "punctuation.separator.arrays.duper" - }, - { - "match": "[^\\s\\]]", - "name": "invalid.illegal.expected-arrays-separator.duper" - } - ] - }, - "tuples": { - "begin": "\\(", - "beginCaptures": { - "0": { - "name": "punctuation.definition.tuple.duper" - } - }, - "end": "\\)", - "endCaptures": { - "0": { - "name": "punctuation.definition.tuple.duper" - } - }, - "name": "meta.structure.tuple.duper", - "patterns": [ - { - "include": "#values" - }, - { - "include": "#comments" - }, - { - "include": "#multilinecomments" - }, - { - "match": ",", - "name": "punctuation.separator.tuple.duper" - }, - { - "match": "[^\\s\\)]", - "name": "invalid.illegal.expected-tuple-separator.duper" - } - ] - }, - "objects": { - "begin": "\\{", - "beginCaptures": { - "0": { - "name": "punctuation.definition.dictionary.duper" - } - }, - "end": "\\}", - "endCaptures": { - "0": { - "name": "punctuation.definition.dictionary.duper" - } - }, - "patterns": [ - { - "include": "#objectkeys" - }, - { - "include": "#comments" - }, - { - "include": "#multilinecomments" - }, - { - "begin": ":", - "beginCaptures": { - "0": { - "name": "punctuation.separator.dictionary.key.duper" - } - }, - "end": "(,)|(?=\\})", - "endCaptures": { - "1": { - "name": "punctuation.separator.dictionary.pair.duper" - } - }, - "name": "meta.structure.dictionary.value.duper", - "patterns": [ - { - "include": "#values" - }, - { - "match": "[^\\s,]", - "name": "invalid.illegal.expected-dictionary-separator.duper" - } - ] - }, - { - "match": "[^\\s\\}]", - "name": "invalid.illegal.expected-dictionary-separator.duper" - } - ] - }, - "objectkeys": { - "patterns": [{ "include": "#strings" }, { "include": "#plainkeys" }] - }, - "stringcontent": { - "patterns": [ - { - "match": ".", - "name": "string.quoted.double.duper" - } - ] - }, - "stringescapes": { - "patterns": [ - { - "name": "constant.character.escape.duper", - "match": "\\\\([\\\\\"\\/bfnrt0]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})" - }, - { - "name": "invalid.illegal.unrecognized-string-escape.duper", - "match": "\\\\." - } - ] - }, - "values": { - "patterns": [ - { - "include": "#keywords" - }, - { - "include": "#numbers" - }, - { - "include": "#identifiers" - }, - { - "include": "#strings" - }, - { - "include": "#objects" - }, - { - "include": "#arrays" - }, - { - "include": "#tuples" - }, - { - "include": "#comments" - }, - { - "include": "#multilinecomments" - } - ] - } - } -} diff --git a/duper_website/package-lock.json b/duper_website/package-lock.json index 101b8d7..d6dcd29 100644 --- a/duper_website/package-lock.json +++ b/duper_website/package-lock.json @@ -15,15 +15,29 @@ }, "devDependencies": { "@biomejs/biome": "2.3.1", + "@duper-js/wasm": "../duper-js-wasm", "@types/js-yaml": "^4.0.9", "@types/node": "^24.9.1", "vite": "^7.1.12", + "vite-plugin-global-const": "^0.0.6", "vite-plugin-top-level-await": "^1.6.0", "vite-plugin-wasm": "^3.5.0", "vitepress": "^2.0.0-alpha.12", "vue": "^3.5.22" } }, + "../duper-js-wasm": { + "name": "@duper-js/wasm", + "version": "0.2.1", + "dev": true, + "license": "MIT", + "devDependencies": { + "vite": "^7.1.12", + "vite-plugin-top-level-await": "^1.6.0", + "vite-plugin-wasm": "^3.5.0", + "vitest": "^4.0.6" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -251,6 +265,10 @@ "dev": true, "license": "MIT" }, + "node_modules/@duper-js/wasm": { + "resolved": "../duper-js-wasm", + "link": true + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.11", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", @@ -2728,6 +2746,13 @@ } } }, + "node_modules/vite-plugin-global-const": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/vite-plugin-global-const/-/vite-plugin-global-const-0.0.6.tgz", + "integrity": "sha512-iaAzqzXmB8R1Q+GYKIoysQr1EyXKoqn2/fGEDfIN69SGDIbv0zyI2TSB2ZAgXirJ5dsa1LZyqJzJHHVyNaMm/g==", + "dev": true, + "license": "Apache License 2.0" + }, "node_modules/vite-plugin-top-level-await": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.6.0.tgz", diff --git a/duper_website/package.json b/duper_website/package.json index eb93795..2801125 100644 --- a/duper_website/package.json +++ b/duper_website/package.json @@ -28,9 +28,11 @@ }, "devDependencies": { "@biomejs/biome": "2.3.1", + "@duper-js/wasm": "../duper-js-wasm", "@types/js-yaml": "^4.0.9", "@types/node": "^24.9.1", "vite": "^7.1.12", + "vite-plugin-global-const": "^0.0.6", "vite-plugin-top-level-await": "^1.6.0", "vite-plugin-wasm": "^3.5.0", "vitepress": "^2.0.0-alpha.12", diff --git a/duper_website/tsconfig.json b/duper_website/tsconfig.json new file mode 100644 index 0000000..c856002 --- /dev/null +++ b/duper_website/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./*"] + }, + "types": ["vitepress/client"], + "moduleResolution": "bundler", + "target": "ES2020", + "module": "ES2020", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["**/*.ts", "**/*.vue", "**/*.d.ts"], + "exclude": ["node_modules", "dist"] +} -- 2.51.2