Add canonical grammar and CodeMirror playground
This commit is contained in:
parent
a07f4c48aa
commit
ee031bd2e8
|
|
@ -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
|
||||
|
||||
|
|
|
|||
112
doc/manual/souce/language/grammar.md
Normal file
112
doc/manual/souce/language/grammar.md
Normal file
|
|
@ -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.
|
||||
18
editors/lezer-decodal/README.md
Normal file
18
editors/lezer-decodal/README.md
Normal file
|
|
@ -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.
|
||||
172
editors/lezer-decodal/decodal.grammar
Normal file
172
editors/lezer-decodal/decodal.grammar
Normal file
|
|
@ -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 }
|
||||
12
editors/tree-sitter-decodal/README.md
Normal file
12
editors/tree-sitter-decodal/README.md
Normal file
|
|
@ -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.
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
165
site/decodal-site/package-lock.json
generated
165
site/decodal-site/package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
site/decodal-site/src/lib/codemirror/decodal-parser.js
Normal file
16
site/decodal-site/src/lib/codemirror/decodal-parser.js
Normal file
|
|
@ -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<RQPO,59aO$wQPO1G.zO<ZQQO1G.zOOQO1G.s1G.sO<`QPO1G.}O<jQPO'#C|OOQO,59g,59gO<rQPO,59gOOQO1G.r1G.rOOQO1G.p1G.pO<wQPO1G.oO@_QPO1G.nOAyQPO1G.mOOQO1G.l1G.lOCqQPO1G.kOEfQPO1G.jOFxQPO1G.iOOQO1G.h1G.hOOQO1G.e1G.eOOQO1G.o1G.oOOQO1G.n1G.nOOQO1G.m1G.mOOQO1G.k1G.kOOQO1G.j1G.jOOQO1G.i1G.iOOQO,59l,59lOOQO7+$b7+$bOHfQPO7+$bOOQO-E7O-E7OOOQO,59m,59mOOQO7+$c7+$cOHnQPO7+$cOOQO-E7P-E7POOQO7+$d7+$dOOQO1G.|1G.|OHuQPO'#CvOOQO,59o,59oOHzQPO1G.{OOQO-E7R-E7ROOQO7+$f7+$fO$wQPO7+$fOOQO'#Cy'#CyOISQPO'#CxOOQO7+$i7+$iOIXQPO7+$iOIaQPO,59hOIhQPO,59hOOQO1G/R1G/ROOQO<<G|<<G|PIpQPO'#DQOOQO<<G}<<G}P$wQPO'#DRPIuQPO'#DTOOQO<<HQ<<HQO$wQPO,59dOIzQPO<<HTOJUQPO<<HTOOQO<<HT<<HTOJ^QPO1G/SOOQO1G/O1G/OOOQO,59p,59pOOQOAN=oAN=oOJeQPOAN=oOOQO-E7S-E7SOOQOG23ZG23ZP<cQPO'#DUO$wQPO,59TO$wQPO,59SO$wQPO,59RO$wQPO,59PO$wQPO,59OO$wQPO,58}OJoQPO1G.nOKVQPO'#ChOKjQPO'#CgOL^QPO'#CdOM^QPO'#CcONaQPO'#CbO$wQPO,59SO$wQPO,59QO$wQPO,58|O! jQPO1G.mO!!QQPO'#CgO!#_QPO'#CeO$wQPO,59RO$wQPO,59QO$wQPO,59PO$wQPO,59OO$wQPO,58}O$wQPO,58|O$wQPO,58yO!$[QPO1G.kO!$rQPO1G.jO!%YQPO1G.iO!%zQPO'#CeO!&rQPO'#CdO!'gQPO'#CcO!(XQPO'#CbO!(vQPO'#C_",
|
||||
stateData: "!)Y~O{OS|OS~O}PO!QQO!RQO!SQO!TQO!UQO!VQO!WRO!XRO!YRO!ZRO![SO!_TO!bUO!dVO!hWO!jXO!k]O!l]O~O!WaX!XaX!YaX!ZaX!daX!laX!maX!naX!oaX!paX!qaX!raX!saX!taX!uaX!vaX!waX~O!OlOyaX}aX!PSX!QaX!RaX!SaX!TaX!UaX!VaX![aX!]aX!_aX!baX!haX!jaX!kaX~P!gO}YO!QQO!RQO!SQO!TQO!UQO!VQO!WRO!XRO!YRO!ZRO![SO!_TO!bUO!dVO!hWO!jXO!k]O!l]O~O}oO!^pO~O!arO~P$wO}oO!cgP~O}wO!fyO~P]O!Q}O~O!O!QO!d!OOy_X}_X!Q_X!R_X!S_X!T_X!U_X!V_X!W_X!X_X!Y_X!Z_X![_X!]_X!__X!b_X!h_X!j_X!k_X!l_X!m_X!n_X!o_X!p_X!q_X!r_X!s_X!t_X!u_X!v_X!w_X!`_X!a_X!f_X!^_X!e_X~O!m!SO!n!SOy]X}]X!Q]X!R]X!S]X!T]X!U]X!V]X!W]X!X]X!Y]X!Z]X![]X!]]X!_]X!b]X!d]X!h]X!j]X!k]X!l]X!o]X!p]X!q]X!r]X!s]X!t]X!u]X!v]X!w]X!O]X!`]X!a]X!f]X!^]X!e]X~Oy[X}[X!Q[X!R[X!S[X!T[X!U[X!V[X!W[X!X[X!Y[X!Z[X![[X!][X!_[X!b[X!d[X!h[X!j[X!k[X!p[X!q[X!r[X!s[X!t[X!u[X!v[X!w[X!`[X!a[X!f[X!^[X!e[X~O!l!TO!o!TO~P,OO!W!VO!X!VO!Y!VO!Z!VO!q!VO!r!VOyZX}ZX!QZX!RZX!SZX!TZX!UZX!VZX![ZX!]ZX!_ZX!bZX!dZX!hZX!jZX!kZX!lZX!sZX!tZX!uZX!vZX!wZX~O!p!UO~P.cOyXX}XX!QXX!RXX!SXX!TXX!UXX!VXX!WXX!XXX!YXX!ZXX![XX!]XX!_XX!bXX!dXX!hXX!jXX!kXX!lXX!tXX!uXX!vXX!wXX~O!s!XO~P0dOyWX}WX!QWX!RWX!SWX!TWX!UWX!VWX!WWX!XWX!YWX!ZWX![WX!]WX!_WX!bWX!dWX!hWX!jWX!kWX!lWX!uWX!vWX!wWX~O!t!YO~P2[OyVX}VX!QVX!RVX!SVX!TVX!UVX!VVX!WVX!XVX!YVX!ZVX![VX!]VX!_VX!bVX!dVX!hVX!jVX!kVX!lVX!vVX!wVX~O!u!ZO~P4POyUX}UX!QUX!RUX!SUX!TUX!UUX!VUX!WUX!XUX!YUX!ZUX![UX!]UX!_UX!bUX!dUX!hUX!jUX!kUX!lUX~O!v![O!w!]O~P5qO!P!^O~O!]!_OyQX}QX!QQX!RQX!SQX!TQX!UQX!VQX!WQX!XQX!YQX!ZQX![QX!_QX!bQX!dQX!hQX!jQX!kQX!lQX~O}!aO~O!OlO!PSa~O!OlO!PSX~O!]!cO!^!eO~O!`!fO!a!hO~O!]!iO~O}oO!cgX~O!c!kO~O!e!lO!OaX!faX~P!gO!`!mO!fiX~O!g!oO~O!f!pO~O!f!qO~O![!rO~O!f!tO~P$wO}!vO~O}oO!^#YO~O!]#ZO!^#YO~O!a#^O~P$wO!`#_O!a#^O~O}#cO!fia~O!`#eO!fia~O!g#hO~O!^#kO!i#iO~P$wO!`#mO!fpX~O!f#oO~O!m!SO!n!SOy]i}]i!Q]i!R]i!S]i!T]i!U]i!V]i!W]i!X]i!Y]i!Z]i![]i!]]i!_]i!b]i!d]i!h]i!j]i!k]i!l]i!o]i!p]i!q]i!r]i!s]i!t]i!u]i!v]i!w]i!`]i!a]i!f]i!^]i!e]i~O!o!TO!W[i!X[i!Y[i!Z[i![[i!][i!p[i!q[i!r[i!s[i!t[i!u[i!v[i!w[i~Oy[i}[i!Q[i!R[i!S[i!T[i!U[i!V[i!_[i!b[i!d[i!h[i!j[i!k[i!l[i~P?^O![Zi!]Zi!sZi!tZi!uZi!vZi!wZi~O!p!UOyZi}Zi!QZi!RZi!SZi!TZi!UZi!VZi!WZi!XZi!YZi!ZZi!_Zi!bZi!dZi!hZi!jZi!kZi!lZi~PAbO![Xi!]Xi!tXi!uXi!vXi!wXi~O!s!XOyXi}Xi!QXi!RXi!SXi!TXi!UXi!VXi!WXi!XXi!YXi!ZXi!_Xi!bXi!dXi!hXi!jXi!kXi!lXi~PC]O![Wi!]Wi!uWi!vWi!wWi~O!t!YOyWi}Wi!QWi!RWi!SWi!TWi!UWi!VWi!WWi!XWi!YWi!ZWi!_Wi!bWi!dWi!hWi!jWi!kWi!lWi~PETO!u!ZOyVi}Vi!QVi!RVi!SVi!TVi!UVi!VVi!WVi!XVi!YVi!ZVi![Vi!]Vi!_Vi!bVi!dVi!hVi!jVi!kVi!lVi!vVi!wVi~O}oO!^#pO~O!a#rO~P$wO!e!lO~O}#cO!fii~O!e#vO~O!]#wO!^#yO~O!fpa~P$wO!`#zO!fpa~O}oO~O}#cO~O!^#}O!i#iO~P$wO!]$OO!^#}O~O!fpi~P$wO!^$QO!i#iO~P$wO!l!TO!`[i!a[i!f[i!^[i!e[i~P?^O!l$SO!o$SO!O[X!m[X!n[X~P,OO!p$TO!OZX!mZX!nZX!oZX!`ZX!aZX!fZX!^ZX!eZX~P.cO!t$VO!OWX!mWX!nWX!oWX!pWX!qWX!rWX!sWX!`WX!aWX!fWX!^WX!eWX~P2[O!u$WO!OVX!mVX!nVX!oVX!pVX!qVX!rVX!sVX!tVX!`VX!aVX!fVX!^VX!eVX~P4PO!v$XO!w$bO!OUX!mUX!nUX!oUX!pUX!qUX!rUX!sUX!tUX!uUX!`UX!aUX!fUX!^UX!eUX~P5qO!p$`O!`Zi!aZi!fZi!^Zi!eZi~PAbO!W!VO!X!VO!Y!VO!Z!VO!p$`O!q!VO!r!VO!`ZX!aZX!sZX!tZX!uZX!vZX!wZX!fZX![ZX!]ZX!^ZX!eZX~O!s$aO!OXX!mXX!nXX!oXX!pXX!qXX!rXX!`XX!aXX!fXX!^XX!eXX~P0dO!s$gO!`Xi!aXi!fXi!^Xi!eXi~PC]O!t$hO!`Wi!aWi!fWi!^Wi!eWi~PETO!u$iO!`Vi!aVi!vVi!wVi!fVi![Vi!]Vi!^Vi!eVi~O!s$gO!`XX!aXX!tXX!uXX!vXX!wXX!fXX![XX!]XX!^XX!eXX~O!t$hO!`WX!aWX!uWX!vWX!wWX!fWX![WX!]WX!^WX!eWX~O!u$iO!`VX!aVX!vVX!wVX!fVX![VX!]VX!^VX!eVX~O!v$jO!w$kO!`UX!aUX!fUX![UX!]UX!^UX!eUX~O!P$lO~O!b!c!h!j!w!T!U!S!R!i}!T~",
|
||||
goto: "2pyPPz!O!`!l#q$a%R%|&z'{(w*O+W,^-f.j/n/n/n/n/n0r/n0u0x/n1Q1Y/n1`1c1f1o1u1|2S2^2d2jTjOkSiOkQqSStUuV#X!c#Z#qShOk]$tSUu!c#Z#qSiOkQnRQsTQ{VQ|WQ!s!OS#Q!^$lY#]!f#_#m#s#zQ#a!kQ#b!lQ#g!oW#i!r#w$O$RQ#u#hR#{#v!OgORTVWk!O!^!f!k!l!o!r#_#h#m#s#v#w#z$O$R$lV#P!]$b$kWfOk!]!^Y$_R!k!o#h$bs$sTVW!O!f!l!r#_#m#s#v#w#z$O$R$k$lWeOk!]!^Q#O![Q#W$XY$^R!k!o#h$bQ$o$js$rTVW!O!f!l!r#_#m#s#v#w#z$O$R$k$lYdOk![!]!^Q!}!ZQ#V$W[$]R!k!o#h$X$bQ$n$iu$qTVW!O!f!l!r#_#m#s#v#w#z$O$R$j$k$l[cOk!Z![!]!^Q!|!YQ#U$V^$eR!k!o#h$W$X$bQ$m$hw$pTVW!O!f!l!r#_#m#s#v#w#z$O$R$i$j$k$l!hbORTVWk!O!Y!Z![!]!^!f!k!l!o!r#_#h#m#s#v#w#z$O$R$V$W$X$b$h$i$j$k$lV!{!X$a$g`aOk!X!Y!Z![!]!^Q!z!WQ#T$Ub$[R!k!o#h$V$W$X$a$bQ$c$f{$dTVW!O!f!l!r#_#m#s#v#w#z$O$R$g$h$i$j$k$l!``OTVWk!O!W!X!Y!Z![!]!^!f!l!r#_#m#s#v#w#z$O$R$f$g$h$i$j$k$lQ!y!UQ#S$TQ$Y$`e$ZR!k!o#h$U$V$W$X$a$b!z_ORTVWk!O!U!W!X!Y!Z![!]!^!f!k!l!o!r#_#h#m#s#v#w#z$O$R$T$U$V$W$X$`$a$b$f$g$h$i$j$k$lQ!x!TR#R$S#O^ORTVWk!O!T!U!W!X!Y!Z![!]!^!f!k!l!o!r#_#h#m#s#v#w#z$O$R$S$T$U$V$W$X$`$a$b$f$g$h$i$j$k$lQ!R]R!w!S#T[ORTVW]k!O!S!T!U!W!X!Y!Z![!]!^!f!k!l!o!r#_#h#m#s#v#w#z$O$R$S$T$U$V$W$X$`$a$b$f$g$h$i$j$k$l#TZORTVW]k!O!S!T!U!W!X!Y!Z![!]!^!f!k!l!o!r#_#h#m#s#v#w#z$O$R$S$T$U$V$W$X$`$a$b$f$g$h$i$j$k$l#TYORTVW]k!O!S!T!U!W!X!Y!Z![!]!^!f!k!l!o!r#_#h#m#s#v#w#z$O$R$S$T$U$V$W$X$`$a$b$f$g$h$i$j$k$lRvURzVQxVV#d!m#e#tQ#l!rV#|#w$O$RX#j!r#w$O$RR!P[R!u!OQ!WaQ$U$[R$f$dQkOR!`kSmPoR!bmQ!dqR#[!dQ!gsS#`!g#nR#n!sQuUR!juQ!nxR#f!nQ#x#lR$P#x",
|
||||
nodeNames: "⚠ Source Statement FieldDefinition FieldPath Expression DefaultExpression PatchExpression ComposeExpression LogicalOrExpression LogicalAndExpression ComparisonExpression ConcatExpression AdditiveExpression MultiplicativeExpression UnaryExpression PostfixExpression PrimaryExpression Literal ComparisonConstraint Object Array LetExpression FieldDefinitionList FunctionExpression ParameterList Parameter MatchExpression MatchArm Pattern ImportExpression CallSuffix ArgumentList CompareOperator",
|
||||
maxTerm: 85,
|
||||
skippedNodes: [0],
|
||||
repeatNodeCount: 7,
|
||||
tokenData: "=T~R!PX^$Upq$Uqr$yrs%Wst&zvw'cxy'pyz'uz{'z{|(P|}(^}!O(c!O!P(h!P!Q(m!Q![+T![!]+n!]!^+s!^!_+x!_!`,V!`!a,l!c!},y!}#O-[#P#Q-a#R#S-f#T#W,y#W#X-y#X#Y,y#Y#Z1Z#Z#],y#]#^3k#^#`,y#`#a6}#a#b8b#b#h,y#h#i:r#i#o,y#o#p<n#p#q<s#q#r=O#y#z$U$f$g$U#BY#BZ$U$IS$I_$U$I|$JO$U$JT$JU$U$KV$KW$U&FU&FV$U~$ZY{~X^$Upq$U#y#z$U$f$g$U#BY#BZ$U$IS$I_$U$I|$JO$U$JT$JU$U$KV$KW$U&FU&FV$U~%OP!k~!_!`%R~%WO!r~~%ZWOY%WZr%Wrs%ss#O%W#O#P%x#P;'S%W;'S;=`&t<%lO%W~%xO!Q~~%{RO;'S%W;'S;=`&U;=`O%W~&XXOY%WZr%Wrs%ss#O%W#O#P%x#P;'S%W;'S;=`&t;=`<%l%W<%lO%W~&wP;=`<%l%W~'PS|~OY&zZ;'S&z;'S;=`']<%lO&z~'`P;=`<%l&z~'hP!u~vw'k~'pO!s~~'uO!d~~'zO!f~~(PO!m~~(UP!o~{|(X~(^O!p~~(cO!`~~(hO!l~~(mO!O~~(rW!n~OY)[Z!P)[!P!Q+O!Q#O)[#O#P)|#P;'S)[;'S;=`*x<%lO)[~)_WOY)[Z!P)[!P!Q)w!Q#O)[#O#P)|#P;'S)[;'S;=`*x<%lO)[~)|O!V~~*PRO;'S)[;'S;=`*Y;=`O)[~*]XOY)[Z!P)[!P!Q)w!Q#O)[#O#P)|#P;'S)[;'S;=`*x;=`<%l)[<%lO)[~*{P;=`<%l)[~+TO!v~~+YQ!R~!O!P+`!Q![+T~+cP!Q![+f~+kP!S~!Q![+f~+sO!e~~+xO!]~~+}P!W~!_!`,Q~,VO!X~~,[Q!PP!_!`,b!`!a,g~,gO!q~Q,lO!gQ~,qP!Y~!_!`,t~,yO!Z~~-OS}~!Q![,y!c!},y#R#S,y#T#o,y~-aO!_~~-fO!a~~-mS!i~}~!Q![,y!c!},y#R#S,y#T#o,y~.OU}~!Q![,y!c!},y#R#S,y#T#X,y#X#Y.b#Y#o,y~.gU}~!Q![,y!c!},y#R#S,y#T#Y,y#Y#Z.y#Z#o,y~/OT}~!Q![,y!c!},y#R#S,y#T#U/_#U#o,y~/dU}~!Q![,y!c!},y#R#S,y#T#i,y#i#j/v#j#o,y~/{U}~!Q![,y!c!},y#R#S,y#T#`,y#`#a0_#a#o,y~0dU}~!Q![,y!c!},y#R#S,y#T#h,y#h#i0v#i#o,y~0}S!w~}~!Q![,y!c!},y#R#S,y#T#o,y~1`T}~!Q![,y!c!},y#R#S,y#T#U1o#U#o,y~1tU}~!Q![,y!c!},y#R#S,y#T#`,y#`#a2W#a#o,y~2]U}~!Q![,y!c!},y#R#S,y#T#g,y#g#h2o#h#o,y~2tU}~!Q![,y!c!},y#R#S,y#T#X,y#X#Y3W#Y#o,y~3_S!U~}~!Q![,y!c!},y#R#S,y#T#o,y~3pV}~!Q![,y!c!},y#R#S,y#T#a,y#a#b4V#b#c6j#c#o,y~4[U}~!Q![,y!c!},y#R#S,y#T#d,y#d#e4n#e#o,y~4sU}~!Q![,y!c!},y#R#S,y#T#c,y#c#d5V#d#o,y~5[U}~!Q![,y!c!},y#R#S,y#T#f,y#f#g5n#g#o,y~5sU}~!Q![,y!c!},y#R#S,y#T#h,y#h#i6V#i#o,y~6^S!j~}~!Q![,y!c!},y#R#S,y#T#o,y~6qS!c~}~!Q![,y!c!},y#R#S,y#T#o,y~7SU}~!Q![,y!c!},y#R#S,y#T#X,y#X#Y7f#Y#o,y~7kU}~!Q![,y!c!},y#R#S,y#T#h,y#h#i7}#i#o,y~8US!b~}~!Q![,y!c!},y#R#S,y#T#o,y~8gT}~!Q![,y!c!},y#R#S,y#T#U8v#U#o,y~8{U}~!Q![,y!c!},y#R#S,y#T#h,y#h#i9_#i#o,y~9dU}~!Q![,y!c!},y#R#S,y#T#V,y#V#W9v#W#o,y~9{U}~!Q![,y!c!},y#R#S,y#T#[,y#[#]:_#]#o,y~:fS!h~}~!Q![,y!c!},y#R#S,y#T#o,y~:wU}~!Q![,y!c!},y#R#S,y#T#f,y#f#g;Z#g#o,y~;`U}~!Q![,y!c!},y#R#S,y#T#i,y#i#j;r#j#o,y~;wU}~!Q![,y!c!},y#R#S,y#T#X,y#X#Y<Z#Y#o,y~<bS!T~}~!Q![,y!c!},y#R#S,y#T#o,y~<sO![~~<vP#p#q<y~=OO!t~~=TO!^~",
|
||||
tokenizers: [0, 1],
|
||||
topRules: {"Source":[0,1]},
|
||||
tokenPrec: 2481
|
||||
})
|
||||
35
site/decodal-site/src/lib/codemirror/decodal-parser.terms.js
Normal file
35
site/decodal-site/src/lib/codemirror/decodal-parser.terms.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||
export const
|
||||
Source = 1,
|
||||
Statement = 2,
|
||||
FieldDefinition = 3,
|
||||
FieldPath = 4,
|
||||
Expression = 5,
|
||||
DefaultExpression = 6,
|
||||
PatchExpression = 7,
|
||||
ComposeExpression = 8,
|
||||
LogicalOrExpression = 9,
|
||||
LogicalAndExpression = 10,
|
||||
ComparisonExpression = 11,
|
||||
ConcatExpression = 12,
|
||||
AdditiveExpression = 13,
|
||||
MultiplicativeExpression = 14,
|
||||
UnaryExpression = 15,
|
||||
PostfixExpression = 16,
|
||||
PrimaryExpression = 17,
|
||||
Literal = 18,
|
||||
ComparisonConstraint = 19,
|
||||
Object = 20,
|
||||
Array = 21,
|
||||
LetExpression = 22,
|
||||
FieldDefinitionList = 23,
|
||||
FunctionExpression = 24,
|
||||
ParameterList = 25,
|
||||
Parameter = 26,
|
||||
MatchExpression = 27,
|
||||
MatchArm = 28,
|
||||
Pattern = 29,
|
||||
ImportExpression = 30,
|
||||
CallSuffix = 31,
|
||||
ArgumentList = 32,
|
||||
CompareOperator = 33
|
||||
57
site/decodal-site/src/lib/codemirror/decodal.js
Normal file
57
site/decodal-site/src/lib/codemirror/decodal.js
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { LRLanguage, LanguageSupport, HighlightStyle, syntaxHighlighting, foldNodeProp, foldInside, indentNodeProp } from '@codemirror/language';
|
||||
import { styleTags, tags as t } from '@lezer/highlight';
|
||||
import { parser } from './decodal-parser.js';
|
||||
|
||||
const parserWithMetadata = parser.configure({
|
||||
props: [
|
||||
styleTags({
|
||||
'let in match import default': t.keyword,
|
||||
'true false': t.bool,
|
||||
identifier: t.variableName,
|
||||
string: t.string,
|
||||
regex: t.regexp,
|
||||
'integer float': t.number,
|
||||
comment: t.lineComment,
|
||||
'plus minus star slash plusPlus equal equalEqual bang bangEqual amp ampAmp pipePipe slashSlash gt gte lt lte arrow default': t.operator,
|
||||
'lBrace rBrace': t.brace,
|
||||
'lBracket rBracket': t.squareBracket,
|
||||
'lParen rParen': t.paren,
|
||||
'semicolon comma dot colon': t.punctuation,
|
||||
}),
|
||||
indentNodeProp.add({
|
||||
Object: context => 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)]);
|
||||
}
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -29,13 +29,10 @@ import ManualLayout from '../layouts/ManualLayout.astro';
|
|||
<button id="delete-file" class="danger-button" type="button">Delete file</button>
|
||||
</aside>
|
||||
|
||||
<label class="pane input-pane">
|
||||
<section class="pane input-pane">
|
||||
<span id="active-file">Input</span>
|
||||
<div class="editor-wrap">
|
||||
<pre id="source-highlight" aria-hidden="true"></pre>
|
||||
<textarea id="source" spellcheck="false"></textarea>
|
||||
</div>
|
||||
</label>
|
||||
<div id="editor" class="code-editor"></div>
|
||||
</section>
|
||||
|
||||
<section class="pane output-pane">
|
||||
<span>Output</span>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import init, { evaluateProject, tokenizeSource } from '../wasm/decodal_wasm.js';
|
||||
import { highlightDecodal, highlightDecodalTokens } from '../lib/highlight.js';
|
||||
import init, { evaluateProject } from '../wasm/decodal_wasm.js';
|
||||
import { EditorView, basicSetup } from 'codemirror';
|
||||
import { keymap } from '@codemirror/view';
|
||||
import { decodal } from '../lib/codemirror/decodal.js';
|
||||
import { playgroundExamples } from './playground-examples.js';
|
||||
|
||||
const STORAGE_KEY = 'decodal-playground-project-v1';
|
||||
const starterProject = playgroundExamples[0];
|
||||
|
||||
const source = document.getElementById('source');
|
||||
const sourceHighlight = document.getElementById('source-highlight');
|
||||
const editorHost = document.getElementById('editor');
|
||||
const output = document.getElementById('output');
|
||||
const run = document.getElementById('run');
|
||||
const status = document.getElementById('status');
|
||||
|
|
@ -18,7 +19,63 @@ const exampleSelect = document.getElementById('example-select');
|
|||
const loadExample = document.getElementById('load-example');
|
||||
|
||||
const project = loadProject();
|
||||
let wasmReady = false;
|
||||
|
||||
const editorTheme = EditorView.theme({
|
||||
'&': {
|
||||
backgroundColor: '#0f172a',
|
||||
color: '#e5e7eb',
|
||||
height: '100%',
|
||||
},
|
||||
'.cm-scroller': {
|
||||
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace',
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.5',
|
||||
overflow: 'auto',
|
||||
},
|
||||
'.cm-content': {
|
||||
caretColor: '#e5e7eb',
|
||||
minHeight: '100%',
|
||||
padding: '12px',
|
||||
},
|
||||
'.cm-gutters': {
|
||||
backgroundColor: '#111827',
|
||||
borderRight: '1px solid #334155',
|
||||
color: '#94a3b8',
|
||||
},
|
||||
'&.cm-focused': {
|
||||
outline: 'none',
|
||||
},
|
||||
'&.cm-focused .cm-cursor': {
|
||||
borderLeftColor: '#e5e7eb',
|
||||
},
|
||||
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground, ::selection': {
|
||||
backgroundColor: 'rgb(59 130 246 / 0.35)',
|
||||
},
|
||||
}, { dark: true });
|
||||
|
||||
const editor = new EditorView({
|
||||
doc: project.files[project.activePath] ?? '',
|
||||
parent: editorHost,
|
||||
extensions: [
|
||||
basicSetup,
|
||||
decodal(),
|
||||
editorTheme,
|
||||
keymap.of([
|
||||
{
|
||||
key: 'Mod-Enter',
|
||||
run() {
|
||||
execute();
|
||||
return true;
|
||||
},
|
||||
},
|
||||
]),
|
||||
EditorView.updateListener.of((update) => {
|
||||
if (!update.docChanged) return;
|
||||
project.files[project.activePath] = update.state.doc.toString();
|
||||
saveProject();
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
for (const example of playgroundExamples) {
|
||||
const option = document.createElement('option');
|
||||
|
|
@ -30,7 +87,6 @@ exampleSelect.value = starterProject.id;
|
|||
|
||||
setActiveFile(project.activePath);
|
||||
renderFileTree();
|
||||
updateHighlight();
|
||||
|
||||
function loadProject() {
|
||||
try {
|
||||
|
|
@ -84,41 +140,36 @@ function normalizePath(path) {
|
|||
return parts.join('/');
|
||||
}
|
||||
|
||||
function getEditorText() {
|
||||
return editor.state.doc.toString();
|
||||
}
|
||||
|
||||
function setEditorText(value) {
|
||||
editor.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: editor.state.doc.length,
|
||||
insert: value,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function setActiveFile(path) {
|
||||
const normalized = normalizePath(path);
|
||||
if (project.files[normalized] === undefined) return;
|
||||
if (project.activePath && project.files[project.activePath] !== undefined) {
|
||||
project.files[project.activePath] = getEditorText();
|
||||
}
|
||||
project.activePath = normalized;
|
||||
source.value = project.files[normalized];
|
||||
setEditorText(project.files[normalized]);
|
||||
activeFile.textContent = normalized;
|
||||
deleteFile.disabled = Object.keys(project.files).length <= 1;
|
||||
updateHighlight();
|
||||
syncHighlightScroll();
|
||||
renderFileTree();
|
||||
saveProject();
|
||||
}
|
||||
|
||||
function updateHighlight() {
|
||||
sourceHighlight.innerHTML = `${highlightSource(source.value)}\n`;
|
||||
}
|
||||
|
||||
function highlightSource(value) {
|
||||
if (!wasmReady) return highlightDecodal(value);
|
||||
try {
|
||||
const result = JSON.parse(tokenizeSource(value));
|
||||
if (result.ok) return highlightDecodalTokens(value, result.tokens);
|
||||
} catch (_error) {
|
||||
// Fall back to the lightweight JavaScript highlighter.
|
||||
}
|
||||
return highlightDecodal(value);
|
||||
}
|
||||
|
||||
function syncHighlightScroll() {
|
||||
sourceHighlight.scrollTop = source.scrollTop;
|
||||
sourceHighlight.scrollLeft = source.scrollLeft;
|
||||
}
|
||||
|
||||
function execute() {
|
||||
project.files[project.activePath] = source.value;
|
||||
project.files[project.activePath] = getEditorText();
|
||||
saveProject();
|
||||
const result = JSON.parse(evaluateProject(project.activePath, JSON.stringify(project.files)));
|
||||
output.textContent = result.ok ? result.output : result.error;
|
||||
|
|
@ -177,8 +228,6 @@ function compareNodes(a, b) {
|
|||
|
||||
try {
|
||||
await init();
|
||||
wasmReady = true;
|
||||
updateHighlight();
|
||||
run.disabled = false;
|
||||
status.textContent = '';
|
||||
execute();
|
||||
|
|
@ -209,13 +258,3 @@ deleteFile.addEventListener('click', () => {
|
|||
delete project.files[project.activePath];
|
||||
setActiveFile(Object.keys(project.files).sort()[0]);
|
||||
});
|
||||
source.addEventListener('input', () => {
|
||||
project.files[project.activePath] = source.value;
|
||||
updateHighlight();
|
||||
syncHighlightScroll();
|
||||
saveProject();
|
||||
});
|
||||
source.addEventListener('scroll', syncHighlightScroll);
|
||||
source.addEventListener('keydown', (event) => {
|
||||
if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') execute();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -419,43 +419,26 @@ button:disabled {
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
.editor-wrap {
|
||||
.code-editor {
|
||||
background: var(--editor-bg);
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.editor-wrap pre,
|
||||
.editor-wrap textarea {
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
font: 14px/1.5 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
||||
inset: 0;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
padding: 12px;
|
||||
position: absolute;
|
||||
tab-size: 2;
|
||||
white-space: pre;
|
||||
.code-editor .cm-editor {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.editor-wrap pre {
|
||||
color: var(--editor-text);
|
||||
pointer-events: none;
|
||||
.code-editor .cm-line {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.editor-wrap textarea {
|
||||
background: transparent;
|
||||
caret-color: var(--editor-text);
|
||||
color: transparent;
|
||||
outline: none;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
.code-editor .cm-activeLine {
|
||||
background: rgb(59 130 246 / 0.08);
|
||||
}
|
||||
|
||||
.editor-wrap textarea::selection {
|
||||
background: var(--selection);
|
||||
.code-editor .cm-activeLineGutter {
|
||||
background: rgb(59 130 246 / 0.12);
|
||||
}
|
||||
|
||||
.output-pane pre {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user