diff --git a/doc/manual/souce/development.md b/doc/manual/souce/development.md index b4ea1ee..b84f973 100644 --- a/doc/manual/souce/development.md +++ b/doc/manual/souce/development.md @@ -58,7 +58,11 @@ Important files: ```text site/decodal-site/src/pages/docs/[...slug].astro site/decodal-site/src/pages/playground.astro +site/decodal-site/src/scripts/playground.js +site/decodal-site/src/scripts/playground-examples.js site/decodal-site/src/layouts/ManualLayout.astro +site/decodal-site/src/lib/codemirror/decodal.js +site/decodal-site/src/lib/codemirror/decodal-parser.js site/decodal-site/src/lib/docs.js site/decodal-site/src/lib/highlight.js crates/decodal-wasm/src/lib.rs @@ -81,7 +85,8 @@ site/decodal-site/src/wasm/ These generated files are committed so the site can be built without requiring every consumer to regenerate the wasm package first. -The playground editor uses `tokenizeSource` from `decodal-wasm` for token spans and maps those tokens to HTML classes in `src/lib/highlight.js`. +The playground editor uses CodeMirror 6 with the generated Lezer parser in `src/lib/codemirror/decodal-parser.js`. +The canonical grammar is documented in `doc/manual/souce/language/grammar.md`; regenerate the Lezer parser when that grammar or `editors/lezer-decodal/decodal.grammar` changes. The documentation build still uses the lightweight JavaScript fallback highlighter so Astro can render Markdown without initializing WASM at build time. To run the site locally: @@ -156,12 +161,18 @@ The generated parser files under `editors/tree-sitter-decodal/src/` are committe When the Decodal syntax changes: -1. Update `grammar.js`. -2. Run `npx tree-sitter generate`. -3. Add or update corpus tests in `corpus/`. -4. Update highlight queries in `queries/highlights.scm` if token names changed. -5. Run `npx tree-sitter test`. -6. Run Rust checks from the repository root if the language parser or examples also changed. +1. Update the canonical EBNF in `doc/manual/souce/language/grammar.md`. +2. Update the Rust parser/tokenizer as needed. +3. Update `editors/tree-sitter-decodal/grammar.js` and run `npx tree-sitter generate` / `npx tree-sitter test`. +4. Update `editors/lezer-decodal/decodal.grammar` and regenerate the CodeMirror parser: + +```sh +cd site/decodal-site +npx lezer-generator ../../editors/lezer-decodal/decodal.grammar -o src/lib/codemirror/decodal-parser.js +``` + +5. Add or update corpus/tests/examples. +6. Run Rust and site checks from the repository root. ## Development shell diff --git a/doc/manual/souce/language/grammar.md b/doc/manual/souce/language/grammar.md new file mode 100644 index 0000000..08bbbbe --- /dev/null +++ b/doc/manual/souce/language/grammar.md @@ -0,0 +1,112 @@ +# Grammar + +This page is the canonical grammar reference for Decodal syntax. +Parser implementations such as the Rust parser, Tree-sitter grammar, and Lezer grammar should follow this grammar and may add implementation-specific precedence annotations where needed. + +## Lexical grammar + +```ebnf +source_character = ? any Unicode scalar value ? ; +newline = "\n" | "\r\n" | "\r" ; +space = " " | "\t" | newline ; +comment = "#" , { ? any character except newline ? } ; + +digit = "0" … "9" ; +letter = "A" … "Z" | "a" … "z" ; +identifier = letter , { letter | digit | "_" } ; + +integer = digit , { digit } ; +float = digit , { digit } , "." , digit , { digit } ; + +string = '"' , { string_character | escape } , '"' ; +string_character = ? any character except '"', "\\", or newline ? ; +escape = "\\" , source_character ; + +regex = "/" , regex_character , { regex_character } , "/" ; +regex_character = escape | ? any character except "/", "\\", or newline ? ; +``` + +Whitespace and comments separate tokens and are otherwise ignored by the parser. +Comments are retained by public tokenizer APIs for editor tooling. + +## Syntactic grammar + +```ebnf +module = { statement } ; +statement = field_definition , [ ";" ] + | expression , [ ";" ] ; + +expression = default_expression ; + +default_expression = patch_expression , [ "default" , default_expression ] ; +patch_expression = compose_expression , { "//" , compose_expression } ; +compose_expression = logical_or_expression , { "&" , logical_or_expression } ; + +logical_or_expression = logical_and_expression , { "||" , logical_and_expression } ; +logical_and_expression = comparison_expression , { "&&" , comparison_expression } ; +comparison_expression = concat_expression , [ comparison_operator , concat_expression ] ; +concat_expression = additive_expression , { "++" , additive_expression } ; +additive_expression = multiplicative_expression , { ( "+" | "-" ) , multiplicative_expression } ; +multiplicative_expression = unary_expression , { ( "*" | "/" ) , unary_expression } ; + +unary_expression = [ "!" | "-" ] , postfix_expression ; +postfix_expression = primary_expression , { call_suffix | path_suffix } ; +call_suffix = "(" , [ argument_list ] , ")" ; +path_suffix = "." , identifier ; + +primary_expression = literal + | identifier + | comparison_constraint + | object + | array + | let_expression + | function_expression + | match_expression + | import_expression + | "(" , expression , ")" ; + +literal = string | integer | float | "true" | "false" | regex ; +comparison_operator = "==" | "!=" | "<" | "<=" | ">" | ">=" ; +comparison_constraint = ( "<" | "<=" | ">" | ">=" ) , expression ; + +object = "{" , [ field_definition , { ";" , field_definition } , [ ";" ] ] , "}" ; +field_definition = field_path , "=" , expression ; +field_path = identifier , { "." , identifier } ; + +array = "[" , [ expression , { "," , expression } , [ "," ] ] , "]" ; + +let_expression = "let" , { field_definition , ";" } , "in" , expression ; +function_expression = "(" , [ parameter_list ] , ")" , "=>" , expression ; +parameter_list = parameter , { "," , parameter } , [ "," ] ; +parameter = identifier , [ ":" , expression ] ; + +match_expression = "match" , expression , "{" , [ match_arm , { ";" , match_arm } , [ ";" ] ] , "}" ; +match_arm = pattern , ":" , expression ; +pattern = "_" | expression ; + +import_expression = "import" , string ; +argument_list = expression , { "," , expression } , [ "," ] ; +``` + +## Precedence + +Precedence is highest first. + +1. function call and field path reference +2. unary `!` and `-` +3. `*` and `/` +4. `+` and `-` +5. `++` +6. `==`, `!=`, `<`, `<=`, `>`, `>=` +7. `&&` +8. `||` +9. `&` +10. `//` +11. `default` + +Binary operators are left-associative except `default`, which is right-associative. + +## Tooling mapping + +Syntax tooling should derive token categories from this grammar rather than making a tool-specific grammar canonical. +Tree-sitter and Lezer grammars are implementation artifacts that follow this page. diff --git a/editors/lezer-decodal/README.md b/editors/lezer-decodal/README.md new file mode 100644 index 0000000..4703182 --- /dev/null +++ b/editors/lezer-decodal/README.md @@ -0,0 +1,18 @@ +# lezer-decodal + +Lezer implementation of the Decodal grammar for CodeMirror 6. + +The canonical grammar is documented in: + +```text +../../doc/manual/souce/language/grammar.md +``` + +Regenerate the CodeMirror parser from the site directory: + +```sh +cd ../../site/decodal-site +npx lezer-generator ../../editors/lezer-decodal/decodal.grammar -o src/lib/codemirror/decodal-parser.js +``` + +The generated parser is committed with the site because the playground imports it directly. diff --git a/editors/lezer-decodal/decodal.grammar b/editors/lezer-decodal/decodal.grammar new file mode 100644 index 0000000..8914d3b --- /dev/null +++ b/editors/lezer-decodal/decodal.grammar @@ -0,0 +1,172 @@ +@top Source { Statement* } + +Statement { + FieldDefinition semicolon? | + Expression semicolon? +} + +Expression { DefaultExpression } + +DefaultExpression { + PatchExpression | + PatchExpression !default default DefaultExpression +} + +PatchExpression { + ComposeExpression | + PatchExpression !patch slashSlash ComposeExpression +} + +ComposeExpression { + LogicalOrExpression | + ComposeExpression !compose amp LogicalOrExpression +} + +LogicalOrExpression { + LogicalAndExpression | + LogicalOrExpression !or pipePipe LogicalAndExpression +} + +LogicalAndExpression { + ComparisonExpression | + LogicalAndExpression !and ampAmp ComparisonExpression +} + +ComparisonExpression { + ConcatExpression | + ConcatExpression !compare CompareOperator ConcatExpression +} + +ConcatExpression { + AdditiveExpression | + ConcatExpression !concat plusPlus AdditiveExpression +} + +AdditiveExpression { + MultiplicativeExpression | + AdditiveExpression !add (plus | minus) MultiplicativeExpression +} + +MultiplicativeExpression { + UnaryExpression | + MultiplicativeExpression !multiply (star | slash) UnaryExpression +} + +UnaryExpression { + PostfixExpression | + (bang | minus) !unary UnaryExpression +} + +PostfixExpression { + PrimaryExpression | + PostfixExpression !call CallSuffix | + PostfixExpression !path dot identifier +} + +CallSuffix { lParen ArgumentList? rParen } +ArgumentList { Expression (comma Expression)* comma? } + +PrimaryExpression { + Literal | + identifier | + ComparisonConstraint | + Object | + Array | + LetExpression | + FunctionExpression | + MatchExpression | + ImportExpression | + lParen Expression rParen +} + +Literal { string | integer | float | true | false | regex } +CompareOperator { equalEqual | bangEqual | lt | lte | gt | gte } +ComparisonConstraint { (lt | lte | gt | gte) Expression } + +Object { lBrace (FieldDefinition (semicolon FieldDefinition)* semicolon?)? rBrace } +FieldDefinition { FieldPath equal Expression } +FieldPath { identifier !fieldPath (dot identifier)* } + +Array { lBracket (Expression (comma Expression)* comma?)? rBracket } + +LetExpression { let FieldDefinitionList in Expression } +FieldDefinitionList { (FieldDefinition semicolon)* } + +FunctionExpression { lParen ParameterList? rParen arrow Expression } +ParameterList { Parameter (comma Parameter)* comma? } +Parameter { identifier colon Expression } + +MatchExpression { match Expression lBrace (MatchArm (semicolon MatchArm)* semicolon?)? rBrace } +MatchArm { Pattern colon Expression } +Pattern { underscore | Expression } + +ImportExpression { import string } + +@precedence { + fieldPath @left, + default @right, + patch @left, + compose @left, + or @left, + and @left, + compare @left, + concat @left, + add @left, + multiply @left, + unary, + call @left, + path @left +} + +@tokens { + let { "let" } + in { "in" } + match { "match" } + import { "import" } + default { "default" } + true { "true" } + false { "false" } + + identifier { $[A-Za-z_] $[A-Za-z0-9_]* } + integer { $[0-9]+ } + float { $[0-9]+ "." $[0-9]+ } + string { '"' (!["\\\n] | "\\" _)* '"' } + regex { "/" (![/\\\n] | "\\" _)+ "/" } + comment { "#" ![\n]* } + + lBrace { "{" } + rBrace { "}" } + lBracket { "[" } + rBracket { "]" } + lParen { "(" } + rParen { ")" } + semicolon { ";" } + comma { "," } + dot { "." } + colon { ":" } + equal { "=" } + equalEqual { "==" } + bang { "!" } + bangEqual { "!=" } + arrow { "=>" } + amp { "&" } + ampAmp { "&&" } + pipePipe { "||" } + plus { "+" } + plusPlus { "++" } + minus { "-" } + star { "*" } + slash { "/" } + slashSlash { "//" } + gt { ">" } + gte { ">=" } + lt { "<" } + lte { "<=" } + underscore { "_" } + + whitespace { @whitespace+ } + + @precedence { let, in, match, import, default, true, false, float, integer, underscore, identifier } +} + +@skip { whitespace | comment } diff --git a/editors/tree-sitter-decodal/README.md b/editors/tree-sitter-decodal/README.md new file mode 100644 index 0000000..ee39790 --- /dev/null +++ b/editors/tree-sitter-decodal/README.md @@ -0,0 +1,12 @@ +# tree-sitter-decodal + +Tree-sitter implementation of the Decodal grammar. + +The canonical grammar is documented in: + +```text +../../doc/manual/souce/language/grammar.md +``` + +This grammar is an editor/tooling implementation of that EBNF reference. +Generated parser files under `src/` are committed. diff --git a/editors/tree-sitter-decodal/grammar.js b/editors/tree-sitter-decodal/grammar.js index dbeeca9..0789d0e 100644 --- a/editors/tree-sitter-decodal/grammar.js +++ b/editors/tree-sitter-decodal/grammar.js @@ -1,3 +1,6 @@ +// Tree-sitter implementation of the canonical EBNF grammar in +// doc/manual/souce/language/grammar.md. + const PREC = { DEFAULT: 1, PATCH: 2, diff --git a/site/decodal-site/package-lock.json b/site/decodal-site/package-lock.json index 798b79e..24b45bb 100644 --- a/site/decodal-site/package-lock.json +++ b/site/decodal-site/package-lock.json @@ -9,10 +9,17 @@ "version": "0.1.0", "dependencies": { "@astrojs/check": "^0.9.4", + "@codemirror/language": "^6.12.4", + "@codemirror/state": "^6.7.1", + "@codemirror/view": "^6.43.6", + "@lezer/highlight": "^1.2.3", + "@lezer/lr": "^1.4.10", "astro": "^4.16.18", + "codemirror": "^6.0.2", "marked": "^12.0.2" }, "devDependencies": { + "@lezer/generator": "^1.8.0", "wrangler": "^4.104.0" } }, @@ -560,6 +567,87 @@ "node": ">=16" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.20.3", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz", + "integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.4.tgz", + "integrity": "sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.7.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.4.tgz", + "integrity": "sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.7.tgz", + "integrity": "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.42.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.1.tgz", + "integrity": "sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.37.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.1.tgz", + "integrity": "sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.43.6", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.6.tgz", + "integrity": "sha512-EVunGSYN1wz1p75WY1s3Xg7t3i8Yol0kGZGizNdX9BUFgMFILYVe8/u6EVpo7Ff5PwbZuILb4QAq7IZoKzIEQA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.7.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -1632,6 +1720,50 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lezer/common": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", + "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==", + "license": "MIT" + }, + "node_modules/@lezer/generator": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@lezer/generator/-/generator-1.8.0.tgz", + "integrity": "sha512-/SF4EDWowPqV1jOgoGSGTIFsE7Ezdr7ZYxyihl5eMKVO5tlnpIhFcDavgm1hHY5GEonoOAEnJ0CU0x+tvuAuUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.1.0", + "@lezer/lr": "^1.3.0" + }, + "bin": { + "lezer-generator": "src/lezer-generator.cjs" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", + "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.3.tgz", + "integrity": "sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==", + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2981,6 +3113,21 @@ "node": ">=6" } }, + "node_modules/codemirror": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -3055,6 +3202,12 @@ "node": ">= 0.6" } }, + "node_modules/crelt": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz", + "integrity": "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==", + "license": "MIT" + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -6429,6 +6582,12 @@ "node": ">=0.10.0" } }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", @@ -7126,6 +7285,12 @@ "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", "license": "MIT" }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, "node_modules/web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", diff --git a/site/decodal-site/package.json b/site/decodal-site/package.json index 8550a14..aa289d4 100644 --- a/site/decodal-site/package.json +++ b/site/decodal-site/package.json @@ -13,10 +13,17 @@ }, "dependencies": { "@astrojs/check": "^0.9.4", + "@codemirror/language": "^6.12.4", + "@codemirror/state": "^6.7.1", + "@codemirror/view": "^6.43.6", + "@lezer/highlight": "^1.2.3", + "@lezer/lr": "^1.4.10", "astro": "^4.16.18", + "codemirror": "^6.0.2", "marked": "^12.0.2" }, "devDependencies": { + "@lezer/generator": "^1.8.0", "wrangler": "^4.104.0" } } diff --git a/site/decodal-site/src/lib/codemirror/decodal-parser.js b/site/decodal-site/src/lib/codemirror/decodal-parser.js new file mode 100644 index 0000000..f4d9533 --- /dev/null +++ b/site/decodal-site/src/lib/codemirror/decodal-parser.js @@ -0,0 +1,16 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +import {LRParser} from "@lezer/lr" +export const parser = LRParser.deserialize({ + version: 14, + states: "7vQYQPOOO#nQPO'#C`OOQO'#Cn'#CnO$wQPO'#CoO&UQPO'#CpO&^QPO'#CqO&eQPO'#CrO&mQPO'#CmO$wQPO'#CwO&wQPO'#CzOOQO'#Cm'#CmOOQO'#Cl'#ClO&|QPO'#CkO$wQPO'#CkOOQO'#Cj'#CjO)fQPO'#CiO.XQPO'#ChO0]QPO'#CgOOQO'#Cf'#CfO2TQPO'#CeO3xQPO'#CdO5jQPO'#CcO7UQPO'#CbOOQO'#Ca'#CaO7`QPO'#C_O7eQPO'#C^OOQO'#DO'#DOQYQPOOO8xQPO'#DPO8}QPO,58zOOQO,59Z,59ZO9VQPO'#C`OOQO,59[,59[O9_QPO,59[OOQO,59],59]O9gQPO,59]O9oQPO'#DSO9tQPO'#CsO9|QPO,59^O:RQPO'#CmO:`QPO'#CuO:hQQO,59`O:mQPO,59`O:rQPO,59XO:wQPO,59cOOQO,59f,59fO:|QPO'#C{OOQO,59W,59WO;TQPO,59WOOQO,59V,59VO$wQPO,59UO$wQPO,59TO$wQPO,59SOOQO'#C}'#C}O$wQPO,59RO$wQPO,59QO$wQPO,59PO$wQPO,59OO$wQPO,58}O$wQPO,58|O$wQPO,58yOOQO,58x,58xOOQO-E6|-E6|OOQO,59k,59kOOQO-E6}-E6}O;YQPO1G.vO;bQPO1G.vOOQO1G.v1G.vO;jQPO1G.wO;qQPO1G.wOOQO1G.w1G.wOOQO,59n,59nOOQO-E7Q-E7QO$wQPO1G.xO$wQPO,59bO;yQPO,59aO context.column(context.node.from) + context.unit, + Array: context => context.column(context.node.from) + context.unit, + MatchExpression: context => context.column(context.node.from) + context.unit, + LetExpression: context => context.column(context.node.from) + context.unit, + }), + foldNodeProp.add({ + Object: foldInside, + Array: foldInside, + MatchExpression: foldInside, + }), + ], +}); + +export const decodalLanguage = LRLanguage.define({ + parser: parserWithMetadata, + languageData: { + commentTokens: { line: '#' }, + closeBrackets: { brackets: ['(', '[', '{', '"'] }, + }, +}); + +export const decodalHighlightStyle = HighlightStyle.define([ + { tag: t.keyword, color: '#93c5fd', fontWeight: '700' }, + { tag: t.variableName, color: 'inherit' }, + { tag: t.bool, color: '#fbbf24' }, + { tag: t.number, color: '#fbbf24' }, + { tag: t.string, color: '#86efac' }, + { tag: t.regexp, color: '#86efac' }, + { tag: t.lineComment, color: '#94a3b8', fontStyle: 'italic' }, + { tag: t.operator, color: '#f9a8d4' }, + { tag: [t.brace, t.squareBracket, t.paren, t.punctuation], color: '#f9a8d4' }, +]); + +export function decodal() { + return new LanguageSupport(decodalLanguage, [syntaxHighlighting(decodalHighlightStyle)]); +} diff --git a/site/decodal-site/src/lib/docs.js b/site/decodal-site/src/lib/docs.js index 8da72be..233e7f3 100644 --- a/site/decodal-site/src/lib/docs.js +++ b/site/decodal-site/src/lib/docs.js @@ -24,6 +24,7 @@ export const nav = [ slug: 'language', children: [ { title: 'Syntax', slug: 'language/syntax' }, + { title: 'Grammar', slug: 'language/grammar' }, { title: 'Value', slug: 'language/value', diff --git a/site/decodal-site/src/pages/playground.astro b/site/decodal-site/src/pages/playground.astro index 47f733c..09af8ab 100644 --- a/site/decodal-site/src/pages/playground.astro +++ b/site/decodal-site/src/pages/playground.astro @@ -29,13 +29,10 @@ import ManualLayout from '../layouts/ManualLayout.astro'; -