tree-sitter-kdl 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.eslintrc.js +20 -0
  2. package/.gitattributes +3 -0
  3. package/.github/workflows/build.yml +51 -0
  4. package/.github/workflows/ci.yml +34 -0
  5. package/.github/workflows/fuzz.yml +21 -0
  6. package/Cargo.toml +22 -0
  7. package/README.md +5 -0
  8. package/binding.gyp +20 -0
  9. package/bindings/node/binding.cc +28 -0
  10. package/bindings/node/index.js +19 -0
  11. package/bindings/rust/build.rs +38 -0
  12. package/bindings/rust/lib.rs +52 -0
  13. package/examples/01-basic.kdl +1 -0
  14. package/examples/02-multiple-values.kdl +1 -0
  15. package/examples/03-properties.kdl +1 -0
  16. package/examples/04-child-nodes.kdl +6 -0
  17. package/examples/05-node-terminators.kdl +1 -0
  18. package/examples/06-string-formats.kdl +2 -0
  19. package/examples/07-multiline-string.kdl +3 -0
  20. package/examples/08-raw-string.kdl +1 -0
  21. package/examples/09-number.kdl +1 -0
  22. package/examples/10-number-forms.kdl +3 -0
  23. package/examples/11-number-with-underscores.kdl +1 -0
  24. package/examples/12-comments.kdl +11 -0
  25. package/examples/13-slashdash-comments.kdl +11 -0
  26. package/examples/14-type-annotations.kdl +4 -0
  27. package/examples/15-rich-example.kdl +21 -0
  28. package/grammar.js +304 -0
  29. package/package.json +38 -0
  30. package/queries/folds.scm +3 -0
  31. package/queries/highlights.scm +55 -0
  32. package/script/known_failures.txt +0 -0
  33. package/script/parse-examples +40 -0
  34. package/src/grammar.json +1396 -0
  35. package/src/node-types.json +626 -0
  36. package/src/parser.c +17477 -0
  37. package/src/scanner.c +30 -0
  38. package/src/tree_sitter/parser.h +224 -0
  39. package/test/corpus/node.txt +2784 -0
package/grammar.js ADDED
@@ -0,0 +1,304 @@
1
+ // deno-lint-ignore-file no-control-regex
2
+ /* eslint-disable camelcase */
3
+ /* eslint-disable-next-line spaced-comment */
4
+ /// <reference types="tree-sitter-cli/dsl" />
5
+ // @ts-check
6
+
7
+ const PREC = {
8
+ node: 1,
9
+ };
10
+
11
+ const ANNOTATION_BUILTINS = [
12
+ 'i8',
13
+ 'i16',
14
+ 'i32',
15
+ 'i64',
16
+ 'u8',
17
+ 'u16',
18
+ 'u32',
19
+ 'u64',
20
+ 'isize',
21
+ 'usize',
22
+ 'f32',
23
+ 'f64',
24
+ 'decimal64',
25
+ 'decimal128',
26
+ 'date-time',
27
+ 'time',
28
+ 'date',
29
+ 'duration',
30
+ 'decimal',
31
+ 'currency',
32
+ 'country-2',
33
+ 'country-3',
34
+ 'country-subdivision',
35
+ 'email',
36
+ 'idn-email',
37
+ 'hostname',
38
+ 'idn-hostname',
39
+ 'ipv4',
40
+ 'ipv6',
41
+ 'url',
42
+ 'url-reference',
43
+ 'irl',
44
+ 'iri-reference',
45
+ 'url-template',
46
+ 'uuid',
47
+ 'regex',
48
+ 'base64',
49
+ ];
50
+
51
+ module.exports = grammar({
52
+ name: 'kdl',
53
+
54
+ conflicts: ($) => [
55
+ [$.document],
56
+ [$._node_space],
57
+ [$.node_children],
58
+ ],
59
+
60
+ extras: ($) => [$.multi_line_comment],
61
+
62
+ externals: ($) => [$._eof],
63
+
64
+ word: ($) => $._normal_bare_identifier,
65
+
66
+ rules: {
67
+ // nodes := linespace* (node nodes?)? linespace*
68
+ document: ($) =>
69
+ seq(
70
+ repeat($._linespace),
71
+ optional(seq(
72
+ $.node,
73
+ repeat(seq(
74
+ repeat($._linespace),
75
+ $.node,
76
+ )),
77
+ )),
78
+ repeat($._linespace),
79
+ ),
80
+
81
+ // node := ('/-' node-space*)? type? identifier (node-space+ node-prop-or-arg)* (node-space* node-children ws*)? node-space* node-terminator
82
+ node: ($) => prec(PREC.node,
83
+ seq(
84
+ alias(optional(seq('/-', repeat($._node_space))), $.node_comment),
85
+ optional($.type),
86
+ $.identifier,
87
+ repeat(seq(repeat1($._node_space), $.node_field)),
88
+ optional(seq(repeat($._node_space), field('children', $.node_children), repeat($._ws))),
89
+ repeat($._node_space),
90
+ $._node_terminator,
91
+ ),
92
+ ),
93
+
94
+ // node-prop-or-arg (field) := ('/-' node-space*)? (prop | value)
95
+ // _node_prop_or_arg: ($) =>
96
+ // seq(
97
+ // alias(optional(seq('/-', repeat($._node_space))), $.node_prop_or_arg_slash_dash),
98
+ // field('node_prop_or_arg', choice($.prop, $.value)),
99
+ // ),
100
+ node_field: ($) => choice($._node_field_comment, $._node_field),
101
+ _node_field_comment: ($) => alias(seq('/-', repeat($._node_space), $._node_field), $.node_field_comment),
102
+ _node_field: ($) => choice($.prop, $.value),
103
+ // node-children := ('/-' node-space*)? '{' nodes '}'
104
+ node_children: ($) =>
105
+ seq(
106
+ optional(seq(alias('/-', $.node_children_comment), repeat($._node_space))),
107
+ '{',
108
+ seq(
109
+ repeat($._linespace),
110
+ optional(seq($.node, repeat(seq(repeat($._linespace), $.node)))),
111
+ repeat($._linespace),
112
+ ),
113
+ '}',
114
+ ),
115
+ // node-space := ws* escline ws* | ws+
116
+ _node_space: ($) =>
117
+ choice(
118
+ seq(repeat($._ws), $._escline, repeat($._ws)),
119
+ repeat1($._ws),
120
+ ),
121
+ // node-terminator := single-line-comment | newline | ';' | eof
122
+ _node_terminator: ($) =>
123
+ choice($.single_line_comment, $._newline, ';', $._eof),
124
+
125
+
126
+ // identifier := string | bare-identifier
127
+ identifier: ($) => choice($.string, $._bare_identifier),
128
+ // bare-identifier := ((identifier-char - digit - sign) identifier-char* | sign ((identifier-char - digit) identifier-char*)?) - keyword
129
+ _bare_identifier: ($) =>
130
+ choice(
131
+ $._normal_bare_identifier,
132
+ seq($._sign, optional(seq($.__identifier_char_no_digit, repeat($._identifier_char)))),
133
+ ),
134
+
135
+ // _normal_bare_identifier: ($) => $.__identifier_char_no_digit_sign,
136
+ _normal_bare_identifier: () => token(
137
+ seq(
138
+ /[\u4E00-\u9FFF\p{L}\p{M}\p{N}\p{Emoji}_~!@#\$%\^&\*.:'\|\?&&[^\s\d\/(){}<>;\[\]=,"]]/,
139
+ /[\u4E00-\u9FFF\p{L}\p{M}\p{N}\p{Emoji}\-_~!@#\$%\^&\*.:'\|\?+&&[^\s\/(){}<>;\[\]=,"]]*/,
140
+ ),
141
+ ),
142
+ // identifier-char := unicode - linespace - [\/(){}<>;[]=,"]
143
+ _identifier_char: () => token(
144
+ /[\u4E00-\u9FFF\p{L}\p{M}\p{N}\-_~!@#\$%\^&\*.:'\|\?+&&[^\s\/(){}<>;\[\]=,"]]/,
145
+ ),
146
+
147
+ // can't start with a digit
148
+ __identifier_char_no_digit: () => token(
149
+ /[\u4E00-\u9FFF\p{L}\p{M}\p{N}\-_~!@#\$%\^&\*.:'\|\?+&&[^\s\d\/(){}<>;\[\]=,"]]/,
150
+ ),
151
+
152
+ // can't start with a digit or sign
153
+ __identifier_char_no_digit_sign: () => token(
154
+ /[\u4E00-\u9FFF\p{L}\p{M}\p{N}\-_~!@#\$%\^&\*.:'\|\?&&[^\s\d\+\-\/(){}<>;\[\]=,"]]/,
155
+ ),
156
+
157
+ // keyword := boolean | 'null'
158
+ keyword: ($) => choice($.boolean, 'null'),
159
+ // type annotations
160
+ annotation_type: () => choice(...ANNOTATION_BUILTINS),
161
+ // prop := identifier '=' value
162
+ prop: ($) => seq($.identifier, '=', $.value),
163
+ // value := type? (string | number | keyword)
164
+ value: ($) => seq(optional($.type), choice($.string, $.number, $.keyword)),
165
+ // type := '(' identifier ')'
166
+ type: ($) => seq('(', choice($.identifier, $.annotation_type), ')'),
167
+
168
+ // String
169
+ // string := raw-string | escaped-string
170
+ string: ($) => choice($._raw_string, $._escaped_string),
171
+ // escaped-string := '"' character* '"'
172
+ _escaped_string: ($) => seq('"', alias(repeat(choice($.escape, /[^"]/)), $.string_fragment), '"'),
173
+ // character := '\' escape | [^\"]
174
+ _character: ($) => choice($.escape, /[^"]/),
175
+ // escape := ["\\/bfnrt] | 'u{' hex-digit{1, 6} '}'
176
+ escape: () =>
177
+ token.immediate(/\\\\|\\"|\\\/|\\b|\\f|\\n|\\r|\\t|\\u\{[0-9a-fA-F]{1,6}\}/),
178
+ // hex-digit := [0-9a-fA-F]
179
+ _hex_digit: () => /[0-9a-fA-F]/,
180
+
181
+ // // raw-string := 'r' raw-string-hash
182
+ // raw_string: ($) => seq('r', $._raw_string_hash),
183
+ // // raw-string-hash := '#' raw-string-hash '#' | raw-string-quotes
184
+ // _raw_string_hash: ($) => choice(seq('#', $._raw_string_hash, '#'), $._raw_string_quotes),
185
+ // // raw-string-quotes := '"' .* '"'
186
+ // _raw_string_quotes: () => seq('"', /.*/, '"'),
187
+ _raw_string: () =>
188
+ seq(
189
+ choice(
190
+ // raw-string-hash := '#' raw-string-hash '#' | raw-string-quotes
191
+ // yes this isn't perfect but it works, ideally this should be handled in an external scanner
192
+ seq(token.immediate(seq('r', repeat1('#'))), /[^#]*/, repeat1('#')),
193
+ // raw-string-quotes := '"' . '"'
194
+ seq(token.immediate(seq('r', '"')), /[^"]*/, '"'),
195
+ ),
196
+ ),
197
+
198
+
199
+ // number := decimal | hex | octal | binary
200
+ number: ($) => choice($._decimal, $._hex, $._octal, $._binary),
201
+
202
+ // decimal := sign? integer ('.' integer)? exponent?
203
+ _decimal: ($) =>
204
+ seq(
205
+ optional($._sign),
206
+ $._integer,
207
+ optional(seq('.', alias($._integer, $.decimal))),
208
+ optional(alias($._exponent, $.exponent)),
209
+ ),
210
+
211
+ // exponent := ('e' | 'E') sign? integer
212
+ _exponent: ($) => seq(choice('e', 'E'), optional($._sign), $._integer),
213
+ // integer := digit (digit | '_')*
214
+ _integer: ($) => seq($._digit, repeat(choice($._digit, '_'))),
215
+ // digit := [0-9]
216
+ _digit: () => /[0-9]/,
217
+ // sign := '+' | '-'
218
+ _sign: () => choice('+', '-'),
219
+
220
+ // hex := sign? '0x' hex-digit (hex-digit | '_')*
221
+ _hex: ($) => seq(optional($._sign), '0x', $._hex_digit, repeat(choice($._hex_digit, '_'))),
222
+ // octal := sign? '0o' [0-7] [0-7_]*
223
+ _octal: ($) => seq(optional($._sign), '0o', /[0-7]/, repeat(choice(/[0-7]/, '_'))),
224
+ // binary := sign? '0b' ('0' | '1') ('0' | '1' | '_')*
225
+ _binary: ($) => seq(optional($._sign), '0b', choice('0', '1'), repeat(choice('0', '1', '_'))),
226
+
227
+ // boolean := 'true' | 'false'
228
+ boolean: () => choice('true', 'false'),
229
+
230
+ // escline := '\\' ws* (single-line-comment | newline)
231
+ _escline: ($) => seq('\\', repeat($._ws), choice($.single_line_comment, $._newline)),
232
+
233
+ // linespace := newline | ws | single-line-comment
234
+ _linespace: ($) => choice($._newline, $._ws, $.single_line_comment),
235
+
236
+ // newline := See Table (All line-break white_space)
237
+ // Newline
238
+ // The following characters should be treated as new lines:
239
+ //
240
+ // ╭──────────────────────────────────────────────────────────╮
241
+ // │ Acronym Name Code Pt │
242
+ // │ CR Carriage Return U+000D │
243
+ // │ LF Line Feed U+000A │
244
+ // │ CRLF Carriage Return and Line Feed U+000D + U+000A │
245
+ // │ NEL Next Line U+0085 │
246
+ // │ FF Form Feed U+000C │
247
+ // │ LS Line Separator U+2028 │
248
+ // │ PS Paragraph Separator U+2029 │
249
+ // ╰──────────────────────────────────────────────────────────╯
250
+ // Note that for the purpose of new lines, CRLF is considered a single newline.
251
+ _newline: () => choice(/\r'/, /\n/, /\r\n/, /\u0085/, /\u000C/, /\u2028/, /\u2029/),
252
+
253
+ // ws := bom | unicode-space | multi-line-comment
254
+ _ws: ($) => choice($._bom, $._unicode_space, $.multi_line_comment),
255
+
256
+ // bom := '\u{FEFF}'
257
+ _bom: () => /\u{FEFF}/,
258
+
259
+ // unicode-space := See Table (All White_Space unicode characters which are not `newline`)
260
+ // Whitespace
261
+ // The following characters should be treated as non-Newline white space:
262
+ //
263
+ // ╭────────────────────────────────────╮
264
+ // │ Name Code Pt │
265
+ // │ Character Tabulation U+0009 │
266
+ // │ Space U+0020 │
267
+ // │ No-Break Space U+00A0 │
268
+ // │ Ogham Space Mark U+1680 │
269
+ // │ En Quad U+2000 │
270
+ // │ Em Quad U+2001 │
271
+ // │ En Space U+2002 │
272
+ // │ Em Space U+2003 │
273
+ // │ Three-Per-Em Space U+2004 │
274
+ // │ Four-Per-Em Space U+2005 │
275
+ // │ Six-Per-Em Space U+2006 │
276
+ // │ Figure Space U+2007 │
277
+ // │ Punctuation Space U+2008 │
278
+ // │ Thin Space U+2009 │
279
+ // │ Hair Space U+200A │
280
+ // │ Narrow No-Break Space U+202F │
281
+ // │ Medium Mathematical Space U+205F │
282
+ // │ Ideographic Space U+3000 │
283
+ // ╰────────────────────────────────────╯
284
+ _unicode_space: () =>
285
+ /[\u0009\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000]/,
286
+
287
+ // single-line-comment := '//' ^newline+ (newline | eof)
288
+ single_line_comment: ($) =>
289
+ seq(
290
+ '//',
291
+ repeat1(/[^\r\n\u0085\u000C\u2028\u2029]/),
292
+ choice($._newline, $._eof),
293
+ ),
294
+ // multi-line-comment := '/*' commented-block
295
+ multi_line_comment: ($) =>
296
+ seq('/*', repeat(prec.right(2, choice($.commented_block, /[^*\/]/))), '*/'),
297
+ // commented-block := '*/' | (multi-line-comment | '*' | '/' | [^*/]+) commented-block
298
+ commented_block: ($) =>
299
+ prec.right(1, seq(
300
+ /[^*\/]/,
301
+ repeat(choice($.multi_line_comment, /[^*\/]/)),
302
+ )),
303
+ },
304
+ });
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "tree-sitter-kdl",
3
+ "version": "0.0.1",
4
+ "description": "KDL grammar for tree-sitter",
5
+ "main": "bindings/node",
6
+ "keywords": [
7
+ "parser",
8
+ "lexer"
9
+ ],
10
+ "author": "Amaan Qureshi <amaanq12@gmail.com>",
11
+ "license": "MIT",
12
+ "bugs": {
13
+ "url": "https://github.com/amaanq/tree-sitter-kdl/issues"
14
+ },
15
+ "dependencies": {
16
+ "nan": "^2.17.0"
17
+ },
18
+ "devDependencies": {
19
+ "eslint": "^8.32.0",
20
+ "eslint-config-google": "^0.14.0",
21
+ "tree-sitter-cli": "^0.20.7"
22
+ },
23
+ "repository": "https://github.com/amaanq/tree-sitter-kdl",
24
+ "scripts": {
25
+ "build": "tree-sitter generate && node-gyp build",
26
+ "test": "tree-sitter test && script/parse-examples",
27
+ "parse": "tree-sitter parse",
28
+ "test-windows": "tree-sitter test"
29
+ },
30
+ "tree-sitter": [
31
+ {
32
+ "scope": "source.kdl",
33
+ "file-types": [
34
+ "kdl"
35
+ ]
36
+ }
37
+ ]
38
+ }
@@ -0,0 +1,3 @@
1
+ ; Folds
2
+
3
+ (node_children) @fold
@@ -0,0 +1,55 @@
1
+ ; Types
2
+
3
+ (node (identifier) @type)
4
+
5
+ (type) @type
6
+
7
+ (annotation_type) @type.builtin
8
+
9
+ ; Properties
10
+
11
+ (prop (identifier) @property)
12
+
13
+ ; Variables
14
+
15
+ (identifier) @variable
16
+
17
+ ; Operators
18
+ [
19
+ "="
20
+ "+"
21
+ "-"
22
+ ] @operator
23
+
24
+ ; Literals
25
+
26
+ (string) @string
27
+
28
+ (escape) @string.escape
29
+
30
+ (number) @number
31
+
32
+ (number (decimal) @float)
33
+ (number (exponent) @float)
34
+ (number (decimal) (exponent) @float)
35
+
36
+ (boolean) @boolean
37
+
38
+ ; Misc
39
+
40
+ "null" @constant.builtin
41
+
42
+ ["{" "}"] @punctuation.bracket
43
+
44
+ ["(" ")"] @punctuation.bracket
45
+
46
+ ; Comments
47
+
48
+ [
49
+ (single_line_comment)
50
+ (multi_line_comment)
51
+ ] @comment
52
+
53
+ (node (node_comment) (#set! "priority" 105)) @comment
54
+ (node (node_field (node_field_comment) (#set! "priority" 105)) @comment)
55
+ (node_children (node_children_comment) (#set! "priority" 105)) @comment
File without changes
@@ -0,0 +1,40 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ cd "$(dirname "$0")/.."
6
+
7
+ function checkout() {
8
+ repo=$1; url=$2; sha=$3
9
+
10
+ if [ ! -d "$repo" ]; then
11
+ git clone "https://github.com/$url" "$repo"
12
+ fi
13
+
14
+ pushd "$repo"
15
+ git fetch && git reset --hard "$sha"
16
+ popd
17
+ }
18
+
19
+ checkout examples/kdl kdl-org/kdl 6feeccc491808fe0b5425a18aa7ab4a8ac435e55
20
+ checkout examples/kdl-rs kdl-org/kdl-rs 8c028e5ea189470f07c5c848ab778f3d63ba89ab
21
+ checkout examples/kdl-js kdl-org/kdljs bb874a595e6f712950795ecaad62f24fd945f03d
22
+ checkout examples/kdl-php kdl-org/kdl-php 390fdc5ab8e2749feb01a113e8feb028de0861b3
23
+
24
+ known_failures="$(cat script/known_failures.txt)"
25
+
26
+ # Remove input directories
27
+ find examples -type d -name input -exec rm -rf {} +
28
+
29
+ tree-sitter parse -q \
30
+ 'examples/**/*.kdl' \
31
+ "$(for file in $known_failures; do echo "!${file}"; done)"
32
+
33
+ example_count=$(find examples -name '*.kdl' | wc -l)
34
+ failure_count=$(wc -w <<< "$known_failures")
35
+ success_count=$(( example_count - failure_count ))
36
+ success_percent=$(bc -l <<< "100*${success_count}/${example_count}")
37
+
38
+ printf \
39
+ "Successfully parsed %d of %d example files (%.1f%%)\n" \
40
+ $success_count "$example_count" "$success_percent"