tree-sitter-swi-prolog 0.0.0-stage → 0.1.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Piotr Franczyk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,81 @@
1
- # Temporary Holding Version
1
+ # tree-sitter-swi-prolog
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![Kibi requirement health](https://looted.github.io/tree-sitter-swi-prolog/kibi-report/badge.svg)](https://looted.github.io/tree-sitter-swi-prolog/kibi-report/)
4
+
5
+ [SWI-Prolog](https://www.swi-prolog.org) grammar for [tree-sitter](https://tree-sitter.github.io/tree-sitter/).
6
+
7
+ It parses the Prolog that real SWI-Prolog projects are written in, not only ISO core:
8
+
9
+ - the default SWI-Prolog operator table, including `:` module qualification, `=>` single-sided unification, `=@=`, `>:<`, `:<`, `as`, `$`, and declaration prefixes such as `dynamic`, `discontiguous`, `meta_predicate`, `multifile` and `table`
10
+ - operators from common libraries: `library(clpfd)` (`#=`, `in`, `..`, ...), `library(chr)` (`<=>`, `==>`, `\`), `library(semweb/rdf_db)` (`^^`), `library(wasm)` (`#Value`, `Expr[Index]`) and the `rdf_meta`, `persistent`, `record` and `html_meta` declarations
11
+ - dicts (`_{name: "x"}`, `Tag{...}`) and functional notation on dicts (`D.key`, `D.put(k, V)`)
12
+ - strings, back-quoted strings, `0'c` character codes, radix integers (`16'FF`), digit groups (`1 000 000`), rationals (`1r3`) and special floats (`1.0Inf`)
13
+ - Unicode atoms and variables, multi-line quoted text, `%` and `/* */` comments, and a leading `#!` line
14
+
15
+ Clauses are structured for tools: every top-level term is a `clause` (with `head`, `neck` and `body` fields), a `directive` or a `query`.
16
+
17
+ ```
18
+ max(X, Y, X) :- X >= Y, !.
19
+
20
+ (clause
21
+ head: (compound_term functor: (atom) arguments: (arguments (variable) (variable) (variable)))
22
+ neck: (operator)
23
+ body: (binary_operation left: (binary_operation ...) operator: (operator) right: (atom))
24
+ (end))
25
+ ```
26
+
27
+ ## Coverage
28
+
29
+ CI parses SWI-Prolog's own `boot/` and `library/` sources and fails if fewer than 99.5% of top-level terms parse without errors. On SWI-Prolog's current `boot/` and `library/` 99.88% of terms parse cleanly, and on the full SWI-Prolog 10.0.2 library with packages (413 files, about 240,000 lines) 99.91%; the rest use prefix operators their files declare themselves.
30
+
31
+ ## Limits
32
+
33
+ A tree-sitter grammar has a fixed operator table, so operators declared at load time with `op/3` are not known while parsing:
34
+
35
+ - an unknown infix operator between two operands, symbolic or alphanumeric (for example `X ===> Y` or `X cis Y`), is read as an infix operator at priority 700, so the clause still parses, with the grouping that priority gives
36
+ - unknown prefix and postfix operators produce an `ERROR` node for that term only; the parser recovers at the next clause
37
+ - the parser does not lower operator priorities the way SWI-Prolog does in some argument positions, so `foo(:- a)` or `foo(a|b)` are errors
38
+
39
+ A negative number literal is parsed as the prefix operator `-` applied to the number.
40
+
41
+ ## Queries
42
+
43
+ - `queries/highlights.scm`: syntax highlighting
44
+ - `queries/tags.scm`: predicate definitions (clause heads, including module-qualified heads and single-sided-unification heads with guards), module declarations, and calls. `@name` is the predicate name without arity; count the head's arguments for the arity, and add two for DCG rules (neck `-->`)
45
+ - `queries/locals.scm`: variables are local to their clause, directive or query
46
+
47
+ ## Usage
48
+
49
+ Node, Rust, Python, Go, Swift and C bindings are generated by `tree-sitter init`. For the browser or other WebAssembly hosts, build the parser with:
50
+
51
+ ```sh
52
+ npx tree-sitter build --wasm
53
+ ```
54
+
55
+ and load `tree-sitter-swi_prolog.wasm` with [`web-tree-sitter`](https://www.npmjs.com/package/web-tree-sitter).
56
+
57
+ ## Development
58
+
59
+ ```sh
60
+ npm install
61
+ npx tree-sitter generate
62
+ npx tree-sitter test
63
+ npx tree-sitter build --wasm
64
+ node scripts/check-corpus.mjs path/to/prolog/sources
65
+ ```
66
+
67
+ `grammar.js` lists the operator tables at the top. To support another library's operators, add them to the matching table with their SWI-Prolog priority and type.
68
+
69
+ ## Releasing
70
+
71
+ Releases are made from [changesets](https://github.com/changesets/changesets).
72
+
73
+ 1. A pull request with a user-visible change adds a changeset: run `npx changeset`, pick the bump and describe the change for users.
74
+ 2. When it is merged, the **Release** workflow opens (or updates) a **Version packages** pull request that applies all pending changesets: it bumps the version in every manifest and writes their text into `CHANGELOG.md`.
75
+ 3. Merging that pull request releases the new version: the `vX.Y.Z` tag and GitHub release (with the changelog section as notes and the Wasm parser attached), npm, crates.io and PyPI.
76
+
77
+ The release step publishes only what is missing for the current version, so if a registry fails, run the **Release** workflow again from the Actions tab. Don't create tags or releases by hand.
78
+
79
+ ## License
80
+
81
+ MIT
package/binding.gyp ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "targets": [
3
+ {
4
+ "target_name": "tree_sitter_swi_prolog_binding",
5
+ "dependencies": [
6
+ "<!(node -p \"require('node-addon-api').targets\"):node_addon_api_except",
7
+ ],
8
+ "include_dirs": [
9
+ "src",
10
+ ],
11
+ "sources": [
12
+ "bindings/node/binding.cc",
13
+ "src/parser.c",
14
+ ],
15
+ "variables": {
16
+ "has_scanner": "<!(node -p \"fs.existsSync('src/scanner.c')\")"
17
+ },
18
+ "conditions": [
19
+ ["has_scanner=='true'", {
20
+ "sources+": ["src/scanner.c"],
21
+ }],
22
+ ["OS!='win'", {
23
+ "cflags_c": [
24
+ "-std=c11",
25
+ ],
26
+ }, { # OS == "win"
27
+ "cflags_c": [
28
+ "/std:c11",
29
+ "/utf-8",
30
+ ],
31
+ }],
32
+ ],
33
+ }
34
+ ]
35
+ }
@@ -0,0 +1,19 @@
1
+ #include <napi.h>
2
+
3
+ typedef struct TSLanguage TSLanguage;
4
+
5
+ extern "C" TSLanguage *tree_sitter_swi_prolog();
6
+
7
+ // "tree-sitter", "language" hashed with BLAKE2
8
+ const napi_type_tag LANGUAGE_TYPE_TAG = {
9
+ 0x8AF2E5212AD58ABF, 0xD5006CAD83ABBA16
10
+ };
11
+
12
+ Napi::Object Init(Napi::Env env, Napi::Object exports) {
13
+ auto language = Napi::External<TSLanguage>::New(env, tree_sitter_swi_prolog());
14
+ language.TypeTag(&LANGUAGE_TYPE_TAG);
15
+ exports["language"] = language;
16
+ return exports;
17
+ }
18
+
19
+ NODE_API_MODULE(tree_sitter_swi_prolog_binding, Init)
@@ -0,0 +1,11 @@
1
+ import assert from "node:assert";
2
+ import { test } from "node:test";
3
+ import Parser from "tree-sitter";
4
+
5
+ test("can load grammar", () => {
6
+ const parser = new Parser();
7
+ assert.doesNotReject(async () => {
8
+ const { default: language } = await import("./index.js");
9
+ parser.setLanguage(language);
10
+ });
11
+ });
@@ -0,0 +1,60 @@
1
+ type BaseNode = {
2
+ type: string;
3
+ named: boolean;
4
+ };
5
+
6
+ type ChildNode = {
7
+ multiple: boolean;
8
+ required: boolean;
9
+ types: BaseNode[];
10
+ };
11
+
12
+ type NodeInfo =
13
+ | (BaseNode & {
14
+ subtypes: BaseNode[];
15
+ })
16
+ | (BaseNode & {
17
+ fields: { [name: string]: ChildNode };
18
+ children: ChildNode[];
19
+ });
20
+
21
+ /**
22
+ * The tree-sitter language object for this grammar.
23
+ *
24
+ * @see {@linkcode https://tree-sitter.github.io/node-tree-sitter/interfaces/Parser.Language.html Parser.Language}
25
+ *
26
+ * @example
27
+ * import Parser from "tree-sitter";
28
+ * import SwiProlog from "tree-sitter-swi-prolog";
29
+ *
30
+ * const parser = new Parser();
31
+ * parser.setLanguage(SwiProlog);
32
+ */
33
+ declare const binding: {
34
+ /**
35
+ * The inner language object.
36
+ * @private
37
+ */
38
+ language: unknown;
39
+
40
+ /**
41
+ * The content of the `node-types.json` file for this grammar.
42
+ *
43
+ * @see {@linkplain https://tree-sitter.github.io/tree-sitter/using-parsers/6-static-node-types Static Node Types}
44
+ */
45
+ nodeTypeInfo: NodeInfo[];
46
+
47
+ /** The syntax highlighting query for this grammar. */
48
+ HIGHLIGHTS_QUERY?: string;
49
+
50
+ /** The language injection query for this grammar. */
51
+ INJECTIONS_QUERY?: string;
52
+
53
+ /** The local variable query for this grammar. */
54
+ LOCALS_QUERY?: string;
55
+
56
+ /** The symbol tagging query for this grammar. */
57
+ TAGS_QUERY?: string;
58
+ };
59
+
60
+ export default binding;
@@ -0,0 +1,37 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+
4
+ const root = fileURLToPath(new URL("../..", import.meta.url));
5
+
6
+ const binding = typeof process.versions.bun === "string"
7
+ // Support `bun build --compile` by being statically analyzable enough to find the .node file at build-time
8
+ ? await import(`${root}/prebuilds/${process.platform}-${process.arch}/tree-sitter-swi-prolog.node`)
9
+ : (await import("node-gyp-build")).default(root);
10
+
11
+ try {
12
+ const nodeTypes = await import(`${root}/src/node-types.json`, { with: { type: "json" } });
13
+ binding.nodeTypeInfo = nodeTypes.default;
14
+ } catch { }
15
+
16
+ const queries = [
17
+ ["HIGHLIGHTS_QUERY", `${root}/queries/highlights.scm`],
18
+ ["INJECTIONS_QUERY", `${root}/queries/injections.scm`],
19
+ ["LOCALS_QUERY", `${root}/queries/locals.scm`],
20
+ ["TAGS_QUERY", `${root}/queries/tags.scm`],
21
+ ];
22
+
23
+ for (const [prop, path] of queries) {
24
+ Object.defineProperty(binding, prop, {
25
+ configurable: true,
26
+ enumerable: true,
27
+ get() {
28
+ delete binding[prop];
29
+ try {
30
+ binding[prop] = readFileSync(path, "utf8");
31
+ } catch { }
32
+ return binding[prop];
33
+ }
34
+ });
35
+ }
36
+
37
+ export default binding;
package/grammar.js ADDED
@@ -0,0 +1,346 @@
1
+ /**
2
+ * @file SWI-Prolog grammar for tree-sitter
3
+ * @license MIT
4
+ */
5
+
6
+ /// <reference types="tree-sitter-cli/dsl" />
7
+ // @ts-check
8
+
9
+ // Default SWI-Prolog operator table (see `current_op/3` in a fresh `swipl`).
10
+ // Prolog priorities run the other way from tree-sitter precedences, so each
11
+ // rule uses `1200 - priority`.
12
+ //
13
+ // Operators at priority 1200 only appear as the neck of a clause, in a
14
+ // directive, or inside parentheses; see `clause`, `directive` and `_term_1200`.
15
+ const INFIX_HIGH = [
16
+ // [priority, type, operators]
17
+ // library(chr)
18
+ [1190, 'xfx', ['==>', '<=>']],
19
+ [1180, 'xfx', ['\\']],
20
+ [1150, 'xfx', ['pragma']],
21
+ [1105, 'xfy', ['|']],
22
+ [1100, 'xfy', [';']],
23
+ [1050, 'xfy', ['->', '*->']],
24
+ [1000, 'xfy', [',']],
25
+ ];
26
+
27
+ const INFIX_LOW = [
28
+ [990, 'xfx', [':=']],
29
+ // library(clpfd)
30
+ [760, 'yfx', ['#<==>']],
31
+ [750, 'xfy', ['#==>']],
32
+ [750, 'yfx', ['#<==']],
33
+ [740, 'yfx', ['#\\/']],
34
+ [730, 'yfx', ['#\\']],
35
+ [720, 'yfx', ['#/\\']],
36
+ [700, 'xfx', ['#>', '#<', '#>=', '#=<', '#=', '#\\=', 'in', 'ins', 'in_set']],
37
+ [450, 'xfx', ['..']],
38
+ [700, 'xfx', [
39
+ '=', '\\=', '==', '\\==', '@<', '@>', '@=<', '@>=', '=..', 'is', '=:=',
40
+ '=\\=', '<', '>', '=<', '>=', '>:<', ':<', 'as', '=@=', '\\=@=', '?=',
41
+ ]],
42
+ [600, 'xfy', [':']],
43
+ [500, 'yfx', ['+', '-', '/\\', '\\/', 'xor']],
44
+ [400, 'yfx', ['*', '/', '//', 'rem', 'mod', 'div', '<<', '>>', 'divmod', 'rdiv']],
45
+ [200, 'xfx', ['**']],
46
+ [200, 'xfy', ['^']],
47
+ // library(semweb/rdf_db) literals: Value^^Type, Text@Lang
48
+ [110, 'xfx', ['^^']],
49
+ ];
50
+
51
+ const PREFIX_HIGH = [
52
+ [1150, 'fx', [
53
+ 'dynamic', 'discontiguous', 'initialization', 'meta_predicate',
54
+ 'module_transparent', 'multifile', 'public', 'thread_local',
55
+ 'thread_initialization', 'volatile', 'table',
56
+ // declarations from common libraries
57
+ 'rdf_meta', 'persistent', 'record', 'html_meta', 'chr_constraint', 'chr_type', 'chr_option',
58
+ ]],
59
+ ];
60
+
61
+ const PREFIX_LOW = [
62
+ [900, 'fy', ['\\+']],
63
+ [710, 'fy', ['#\\']],
64
+ [500, 'fx', ['?']],
65
+ [200, 'fy', ['-', '+', '\\']],
66
+ [100, 'fx', ['@']],
67
+ // library(wasm)
68
+ [50, 'fx', ['#']],
69
+ [1, 'fx', ['$']],
70
+ ];
71
+
72
+ const NECKS = [':-', '-->', '=>'];
73
+
74
+ // Every operator name. Each one is also a plain atom wherever an operand is
75
+ // not expected, e.g. `foo(-, +)` or `X = (=)`.
76
+ const OPERATOR_NAMES = [...new Set([
77
+ ...NECKS, '?-',
78
+ ...[...INFIX_HIGH, ...INFIX_LOW, ...PREFIX_HIGH, ...PREFIX_LOW]
79
+ .flatMap(([, , ops]) => ops),
80
+ ])].filter(op => op !== ',' && op !== '|');
81
+
82
+ const precOf = priority => 1200 - priority;
83
+
84
+ function infix($, [priority, type, ops], operand) {
85
+ const op = field('operator', choice(...ops.map(o => alias(o, $.operator))));
86
+ const body = seq(field('left', operand), op, field('right', operand));
87
+ const p = precOf(priority);
88
+ return type === 'xfy' ? prec.right(p, body) : prec.left(p, body);
89
+ }
90
+
91
+ function prefix($, [priority, type, ops], operand) {
92
+ const op = field('operator', choice(...ops.map(o => alias(o, $.operator))));
93
+ const body = seq(op, field('argument', operand));
94
+ const p = precOf(priority);
95
+ return type === 'fy' ? prec.right(p, body) : prec.left(p, body);
96
+ }
97
+
98
+ const LOWER = /[\p{Ll}]/u.source;
99
+ const UPPER = /[\p{Lu}_]/u.source;
100
+ const ALNUM = /[\p{L}\p{N}_]/u.source;
101
+
102
+ export default grammar({
103
+ name: 'swi_prolog',
104
+
105
+ extras: $ => [/\s/, $.comment],
106
+
107
+ externals: $ => [$.end, $._error_sentinel],
108
+
109
+ word: $ => $._name,
110
+
111
+
112
+ rules: {
113
+ source_file: $ => seq(optional($.shebang), repeat(choice(
114
+ $.directive,
115
+ $.query,
116
+ $.clause,
117
+ ))),
118
+
119
+ shebang: _ => token(prec(2, /#![^\n]*/)),
120
+
121
+ directive: $ => seq(':-', field('body', $._term), $.end),
122
+
123
+ query: $ => seq('?-', field('body', $._term), $.end),
124
+
125
+ // A fact, rule, DCG rule or single-sided-unification rule. The head of a
126
+ // rule is any term below 1200, which covers `Head, Guard => Body`.
127
+ clause: $ => seq(
128
+ field('head', $._term),
129
+ optional(seq(
130
+ field('neck', choice(...NECKS.map(n => alias(n, $.operator)))),
131
+ field('body', $._term),
132
+ )),
133
+ $.end,
134
+ ),
135
+
136
+ // Priority 1199 and below.
137
+ _term: $ => choice(
138
+ $._arg,
139
+ alias($._binary_operation_high, $.binary_operation),
140
+ alias($._prefix_operation_high, $.prefix_operation),
141
+ ),
142
+
143
+ _binary_operation_high: $ => choice(
144
+ ...INFIX_HIGH.map(op => infix($, op, $._term)),
145
+ ),
146
+
147
+ _prefix_operation_high: $ => choice(
148
+ ...PREFIX_HIGH.map(op => prefix($, op, $._term)),
149
+ ),
150
+
151
+ // Priority 999 and below: list elements and arguments.
152
+ _arg: $ => choice(
153
+ $._primary,
154
+ $.binary_operation,
155
+ $.prefix_operation,
156
+ ),
157
+
158
+ binary_operation: $ => choice(
159
+ ...INFIX_LOW.map(op => infix($, op, $._arg)),
160
+ // Operators declared with op/3 are not known to the parser. A symbol
161
+ // atom or a plain name between two operands is read as an infix operator
162
+ // at 700: two adjacent operands are never valid Prolog otherwise.
163
+ prec.left(precOf(700), seq(
164
+ field('left', $._arg),
165
+ field('operator', alias(choice($._graphic, $._name), $.operator)),
166
+ field('right', $._arg),
167
+ )),
168
+ ),
169
+
170
+ prefix_operation: $ => choice(
171
+ ...PREFIX_LOW.map(op => prefix($, op, $._arg)),
172
+ ),
173
+
174
+ // Full 1200 term, only reachable inside brackets.
175
+ _term_1200: $ => choice(
176
+ $._term,
177
+ alias($._neck_operation, $.binary_operation),
178
+ alias($._prefix_neck_operation, $.prefix_operation),
179
+ ),
180
+
181
+ _neck_operation: $ => prec.left(0, seq(
182
+ field('left', $._term),
183
+ field('operator', choice(...NECKS.map(n => alias(n, $.operator)))),
184
+ field('right', $._term),
185
+ )),
186
+
187
+ _prefix_neck_operation: $ => seq(
188
+ field('operator', alias(choice(':-', '?-'), $.operator)),
189
+ field('argument', $._term),
190
+ ),
191
+
192
+ _primary: $ => choice(
193
+ $.atom,
194
+ $.variable,
195
+ $.integer,
196
+ $.float,
197
+ $.rational,
198
+ $.char_code,
199
+ $.string,
200
+ $.back_quoted_string,
201
+ $.compound_term,
202
+ $.list,
203
+ $.curly_term,
204
+ $.dict,
205
+ $.dict_call,
206
+ $.index_term,
207
+ $.parenthesized_term,
208
+ ),
209
+
210
+ // `Expr[Index]`, the postfix `[]` operator of library(wasm).
211
+ index_term: $ => prec.left(precOf(40), seq(
212
+ field('object', choice($.variable, $.dict_call, $.index_term)),
213
+ token.immediate('['),
214
+ field('index', $._arg),
215
+ ']',
216
+ )),
217
+
218
+ parenthesized_term: $ => seq('(', $._term_1200, ')'),
219
+
220
+ // `foo(...)`: the functor must touch the opening bracket.
221
+ compound_term: $ => seq(
222
+ field('functor', $.atom),
223
+ token.immediate('('),
224
+ field('arguments', $.arguments),
225
+ ')',
226
+ ),
227
+
228
+ arguments: $ => seq($._arg, repeat(seq(',', $._arg))),
229
+
230
+ list: $ => seq(
231
+ '[',
232
+ $._arg,
233
+ repeat(seq(',', $._arg)),
234
+ optional(seq('|', field('tail', $._arg))),
235
+ ']',
236
+ ),
237
+
238
+ curly_term: $ => seq('{', $._term_1200, '}'),
239
+
240
+ // `Tag{Key: Value, ...}`
241
+ dict: $ => seq(
242
+ field('tag', choice($.atom, $.variable)),
243
+ token.immediate('{'),
244
+ optional(seq(
245
+ $.dict_pair,
246
+ repeat(seq(',', $.dict_pair)),
247
+ )),
248
+ '}',
249
+ ),
250
+
251
+ dict_pair: $ => seq(
252
+ field('key', choice($.atom, $.integer)),
253
+ ':',
254
+ field('value', $._arg),
255
+ ),
256
+
257
+ // `Dict.key` and `Dict.put(...)`: the dot touches both sides.
258
+ dict_call: $ => prec.left(precOf(100), seq(
259
+ field('dict', choice($.variable, $.atom, $.dict, $.dict_call, $.index_term)),
260
+ token.immediate('.'),
261
+ field('function', choice(
262
+ alias($._immediate_name, $.atom),
263
+ alias($._immediate_variable, $.variable),
264
+ alias($._immediate_quoted_atom, $.atom),
265
+ alias($._immediate_compound, $.compound_term),
266
+ alias($._immediate_integer, $.integer),
267
+ )),
268
+ )),
269
+
270
+ _immediate_name: _ => token.immediate(new RegExp(`${LOWER}${ALNUM}*`, 'u')),
271
+ _immediate_variable: _ => token.immediate(new RegExp(`${UPPER}${ALNUM}*`, 'u')),
272
+ _immediate_quoted_atom: _ => token.immediate(seq(
273
+ '\'',
274
+ repeat(choice(/[^'\\]/, '\'\'', /\\(.|\n)/)),
275
+ '\'',
276
+ )),
277
+ _immediate_integer: _ => token.immediate(/\d+/),
278
+ _immediate_compound: $ => seq(
279
+ field('functor', alias($._immediate_name, $.atom)),
280
+ token.immediate('('),
281
+ field('arguments', $.arguments),
282
+ ')',
283
+ ),
284
+
285
+ atom: $ => choice(
286
+ $._name,
287
+ $._graphic,
288
+ $._quoted_atom,
289
+ seq('[', ']'),
290
+ seq('{', '}'),
291
+ '!',
292
+ ';',
293
+ prec(-10, choice(...OPERATOR_NAMES)),
294
+ ),
295
+
296
+ _name: _ => new RegExp(`${LOWER}${ALNUM}*`, 'u'),
297
+
298
+ // Symbol-character atoms that are not in the default operator table.
299
+ // A dot directly before layout is handled by the external `end` token.
300
+ _graphic: _ => token(/[#$&*+\-./:<=>?@^~\\]+/),
301
+
302
+ _quoted_atom: _ => token(seq(
303
+ '\'',
304
+ repeat(choice(/[^'\\]/, '\'\'', /\\(.|\n)/, /\\x[0-9a-fA-F]+\\?/, /\\[0-7]+\\?/)),
305
+ '\'',
306
+ )),
307
+
308
+ variable: _ => new RegExp(`${UPPER}${ALNUM}*`, 'u'),
309
+
310
+ integer: _ => token(choice(
311
+ /\d(_?\d)*/,
312
+ /0x[0-9a-fA-F](_?[0-9a-fA-F])*/,
313
+ /0o[0-7](_?[0-7])*/,
314
+ /0b[01](_?[01])*/,
315
+ /[1-9]\d*'[0-9a-zA-Z]+/,
316
+ // SWI digit groups: 1 000 000
317
+ /\d{1,3}( \d{3})+/,
318
+ )),
319
+
320
+ float: _ => token(choice(
321
+ /\d(_?\d)*\.\d+([eE][+-]?\d+)?(Inf|NaN)?/,
322
+ /\d(_?\d)*[eE][+-]?\d+/,
323
+ )),
324
+
325
+ rational: _ => token(/\d+r\d+/),
326
+
327
+ char_code: _ => token(seq('0\'', choice(/\\(.|x[0-9a-fA-F]+\\?|[0-7]+\\?)/, '\'\'', '\'', /[^\\']/))),
328
+
329
+ string: _ => token(seq(
330
+ '"',
331
+ repeat(choice(/[^"\\]/, '""', /\\(.|\n)/)),
332
+ '"',
333
+ )),
334
+
335
+ back_quoted_string: _ => token(seq(
336
+ '`',
337
+ repeat(choice(/[^`\\]/, '``', /\\(.|\n)/)),
338
+ '`',
339
+ )),
340
+
341
+ comment: _ => token(prec(1, choice(
342
+ seq('%', /.*/),
343
+ seq('/*', /[^*]*\*+([^/*][^*]*\*+)*/, '/'),
344
+ ))),
345
+ },
346
+ });
package/package.json CHANGED
@@ -1,6 +1,64 @@
1
1
  {
2
2
  "name": "tree-sitter-swi-prolog",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "SWI-Prolog grammar for tree-sitter",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Looted/tree-sitter-swi-prolog.git"
9
+ },
10
+ "license": "MIT",
11
+ "author": {
12
+ "name": "Piotr Franczyk"
13
+ },
14
+ "main": "bindings/node",
15
+ "types": "bindings/node",
16
+ "keywords": [
17
+ "incremental",
18
+ "parsing",
19
+ "tree-sitter",
20
+ "prolog",
21
+ "swi-prolog"
22
+ ],
23
+ "files": [
24
+ "grammar.js",
25
+ "tree-sitter.json",
26
+ "binding.gyp",
27
+ "prebuilds/**",
28
+ "bindings/node/*",
29
+ "queries/*",
30
+ "src/**",
31
+ "*.wasm"
32
+ ],
33
+ "dependencies": {
34
+ "node-addon-api": "^8.5.0",
35
+ "node-gyp-build": "^4.8.4"
36
+ },
37
+ "devDependencies": {
38
+ "@changesets/cli": "^2.31.1",
39
+ "kibi-cli": "^2.38.0",
40
+ "kibi-core": "^0.27.0",
41
+ "kibi-mcp": "^3.22.0",
42
+ "prebuildify": "^6.0.1",
43
+ "tree-sitter": "^0.25.0",
44
+ "tree-sitter-cli": "^0.26.7",
45
+ "web-tree-sitter": "^0.27.0"
46
+ },
47
+ "peerDependencies": {
48
+ "tree-sitter": "^0.25.0"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "tree-sitter": {
52
+ "optional": true
53
+ }
54
+ },
55
+ "scripts": {
56
+ "install": "node-gyp-build",
57
+ "prestart": "tree-sitter build --wasm",
58
+ "start": "tree-sitter playground",
59
+ "test": "node --test bindings/node/*_test.js",
60
+ "test:corpus": "node scripts/check-corpus.mjs",
61
+ "version-packages": "node scripts/version-packages.mjs",
62
+ "changeset": "changeset"
63
+ }
64
+ }