@prisma-next/psl-parser 0.14.0-dev.4 → 0.14.0-dev.40

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 (47) hide show
  1. package/README.md +28 -10
  2. package/dist/declarations-CPDuQm7x.mjs +1055 -0
  3. package/dist/declarations-CPDuQm7x.mjs.map +1 -0
  4. package/dist/format.d.mts +1 -1
  5. package/dist/format.mjs +2 -1
  6. package/dist/format.mjs.map +1 -1
  7. package/dist/index.d.mts +137 -3
  8. package/dist/index.d.mts.map +1 -1
  9. package/dist/index.mjs +474 -3
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/{parse-BjZ1LPe6.d.mts → parse-BKN_-hW8.d.mts} +126 -51
  12. package/dist/parse-BKN_-hW8.d.mts.map +1 -0
  13. package/dist/parse-Du30Ca0-.mjs +626 -0
  14. package/dist/parse-Du30Ca0-.mjs.map +1 -0
  15. package/dist/syntax.d.mts +20 -2
  16. package/dist/syntax.d.mts.map +1 -1
  17. package/dist/syntax.mjs +43 -2
  18. package/dist/syntax.mjs.map +1 -0
  19. package/package.json +5 -6
  20. package/src/block-reconstruction.ts +139 -0
  21. package/src/exports/index.ts +27 -3
  22. package/src/exports/syntax.ts +25 -5
  23. package/src/extension-block.ts +107 -0
  24. package/src/parse.ts +23 -5
  25. package/src/resolve.ts +123 -0
  26. package/src/source-file.ts +25 -0
  27. package/src/symbol-table.ts +446 -0
  28. package/src/syntax/ast/attributes.ts +5 -6
  29. package/src/syntax/ast/declarations.ts +51 -26
  30. package/src/syntax/ast/expressions.ts +12 -13
  31. package/src/syntax/ast/identifier.ts +2 -3
  32. package/src/syntax/ast/qualified-name.ts +22 -19
  33. package/src/syntax/ast/type-annotation.ts +4 -5
  34. package/src/syntax/ast-helpers.ts +27 -3
  35. package/src/syntax/navigation.ts +55 -0
  36. package/src/syntax/red.ts +317 -42
  37. package/dist/parse-B_3gIEFd.mjs +0 -1421
  38. package/dist/parse-B_3gIEFd.mjs.map +0 -1
  39. package/dist/parse-BjZ1LPe6.d.mts.map +0 -1
  40. package/dist/parser-CaplKvRs.mjs +0 -1145
  41. package/dist/parser-CaplKvRs.mjs.map +0 -1
  42. package/dist/parser-Dfi3Wfdq.d.mts +0 -7
  43. package/dist/parser-Dfi3Wfdq.d.mts.map +0 -1
  44. package/dist/parser.d.mts +0 -2
  45. package/dist/parser.mjs +0 -2
  46. package/src/exports/parser.ts +0 -1
  47. package/src/parser.ts +0 -1642
package/src/syntax/red.ts CHANGED
@@ -1,27 +1,154 @@
1
- import type { Token } from '../tokenizer';
2
- import type { GreenElement, GreenNode } from './green';
1
+ import type { Token, TokenKind } from '../tokenizer';
2
+ import type { GreenElement, GreenNode, GreenToken } from './green';
3
3
  import type { SyntaxKind } from './syntax-kind';
4
4
 
5
5
  /**
6
6
  * A token in the red tree. Unlike the green-layer {@link Token} (kind + text
7
- * only), a red token also carries its absolute `offset` within the source,
8
- * computed lazily as the tree is walked.
7
+ * only), a red token also carries its absolute `offset` within the source and a
8
+ * link back to its `parent` {@link SyntaxNode}, so a cursor anchored on a token
9
+ * can walk outward (parent, previous/next token, siblings) without re-scanning
10
+ * from the document root.
9
11
  */
10
- export interface SyntaxToken extends Token {
12
+ export class SyntaxToken implements Token {
13
+ readonly green: GreenToken;
14
+ readonly kind: TokenKind;
15
+ readonly text: string;
11
16
  readonly offset: number;
17
+ readonly parent: SyntaxNode;
18
+ /** Position within the parent's children, enabling O(1) sibling navigation without rescanning the green layer. */
19
+ readonly index: number;
20
+
21
+ constructor(green: GreenToken, offset: number, parent: SyntaxNode, index: number) {
22
+ this.green = green;
23
+ this.kind = green.kind;
24
+ this.text = green.text;
25
+ this.offset = offset;
26
+ this.parent = parent;
27
+ this.index = index;
28
+ }
29
+
30
+ get textLength(): number {
31
+ return this.text.length;
32
+ }
33
+
34
+ get endOffset(): number {
35
+ return this.offset + this.textLength;
36
+ }
37
+
38
+ /** Whether `offset` falls within this token, inclusive of both ends. */
39
+ isInside(offset: number): boolean {
40
+ return offset >= this.offset && offset <= this.endOffset;
41
+ }
42
+
43
+ isOutside(offset: number): boolean {
44
+ return !this.isInside(offset);
45
+ }
46
+
47
+ /** The sibling element immediately after this token within its parent. */
48
+ get nextSiblingOrToken(): SyntaxElement | undefined {
49
+ return childAt(this.parent, this.index + 1);
50
+ }
51
+
52
+ /** The sibling element immediately before this token within its parent. */
53
+ get prevSiblingOrToken(): SyntaxElement | undefined {
54
+ return childAt(this.parent, this.index - 1);
55
+ }
56
+
57
+ /** The next token in document order, crossing node boundaries. */
58
+ get nextToken(): SyntaxToken | undefined {
59
+ for (let el = climbingNext(this); el !== undefined; el = climbingNext(el)) {
60
+ const token = firstToken(el);
61
+ if (token !== undefined) return token;
62
+ }
63
+ return undefined;
64
+ }
65
+
66
+ /** The previous token in document order, crossing node boundaries. */
67
+ get prevToken(): SyntaxToken | undefined {
68
+ for (let el = climbingPrev(this); el !== undefined; el = climbingPrev(el)) {
69
+ const token = lastToken(el);
70
+ if (token !== undefined) return token;
71
+ }
72
+ return undefined;
73
+ }
12
74
  }
13
75
 
14
76
  export type SyntaxElement = SyntaxNode | SyntaxToken;
15
77
 
78
+ /**
79
+ * The result of {@link SyntaxNode.tokenAtOffset}: an offset can fall outside
80
+ * every token (`none`), strictly inside a single token (`single`), or exactly on
81
+ * the seam between two adjacent tokens (`between`). `leftBiased` / `rightBiased`
82
+ * collapse the seam case to one side; for `single` both return the same token,
83
+ * for `none` both return `undefined`.
84
+ */
85
+ type TokenAtOffsetState =
86
+ | { readonly kind: 'none' }
87
+ | { readonly kind: 'single'; readonly token: SyntaxToken }
88
+ | { readonly kind: 'between'; readonly left: SyntaxToken; readonly right: SyntaxToken };
89
+
90
+ export class TokenAtOffset {
91
+ readonly #state: TokenAtOffsetState;
92
+
93
+ private constructor(state: TokenAtOffsetState) {
94
+ this.#state = state;
95
+ }
96
+
97
+ static none(): TokenAtOffset {
98
+ return new TokenAtOffset({ kind: 'none' });
99
+ }
100
+
101
+ static single(token: SyntaxToken): TokenAtOffset {
102
+ return new TokenAtOffset({ kind: 'single', token });
103
+ }
104
+
105
+ static between(left: SyntaxToken, right: SyntaxToken): TokenAtOffset {
106
+ return new TokenAtOffset({ kind: 'between', left, right });
107
+ }
108
+
109
+ get isEmpty(): boolean {
110
+ return this.#state.kind === 'none';
111
+ }
112
+
113
+ get isBetween(): boolean {
114
+ return this.#state.kind === 'between';
115
+ }
116
+
117
+ leftBiased(): SyntaxToken | undefined {
118
+ switch (this.#state.kind) {
119
+ case 'none':
120
+ return undefined;
121
+ case 'single':
122
+ return this.#state.token;
123
+ case 'between':
124
+ return this.#state.left;
125
+ }
126
+ }
127
+
128
+ rightBiased(): SyntaxToken | undefined {
129
+ switch (this.#state.kind) {
130
+ case 'none':
131
+ return undefined;
132
+ case 'single':
133
+ return this.#state.token;
134
+ case 'between':
135
+ return this.#state.right;
136
+ }
137
+ }
138
+ }
139
+
16
140
  export class SyntaxNode {
17
141
  readonly green: GreenNode;
18
142
  readonly offset: number;
19
143
  readonly parent: SyntaxNode | undefined;
144
+ /** Position within the parent's children, enabling O(1) sibling navigation without rescanning the green layer. */
145
+ readonly index: number;
20
146
 
21
- constructor(green: GreenNode, offset: number, parent: SyntaxNode | undefined) {
147
+ constructor(green: GreenNode, offset: number, parent: SyntaxNode | undefined, index: number) {
22
148
  this.green = green;
23
149
  this.offset = offset;
24
150
  this.parent = parent;
151
+ this.index = index;
25
152
  }
26
153
 
27
154
  get kind(): SyntaxKind {
@@ -32,6 +159,19 @@ export class SyntaxNode {
32
159
  return this.green.textLength;
33
160
  }
34
161
 
162
+ get endOffset(): number {
163
+ return this.offset + this.textLength;
164
+ }
165
+
166
+ /** Whether `offset` falls within this node, inclusive of both ends. */
167
+ isInside(offset: number): boolean {
168
+ return offset >= this.offset && offset <= this.endOffset;
169
+ }
170
+
171
+ isOutside(offset: number): boolean {
172
+ return !this.isInside(offset);
173
+ }
174
+
35
175
  get firstChild(): SyntaxElement | undefined {
36
176
  return childAt(this, 0);
37
177
  }
@@ -43,44 +183,40 @@ export class SyntaxNode {
43
183
  }
44
184
 
45
185
  get nextSibling(): SyntaxElement | undefined {
46
- if (!this.parent) return undefined;
47
- const siblings = this.parent.green.children;
48
- let offset = this.parent.offset;
49
- let found = false;
50
- for (const child of siblings) {
51
- if (found) {
52
- return wrapElement(child, offset, this.parent);
53
- }
54
- const childLen = elementTextLength(child);
55
- if (child.type === 'node' && offset === this.offset && child === this.green) {
56
- found = true;
57
- }
58
- offset += childLen;
59
- }
60
- return undefined;
186
+ return this.parent === undefined ? undefined : childAt(this.parent, this.index + 1);
61
187
  }
62
188
 
63
189
  get prevSibling(): SyntaxElement | undefined {
64
- if (!this.parent) return undefined;
65
- const siblings = this.parent.green.children;
66
- let offset = this.parent.offset;
67
- let prev: { green: GreenElement; offset: number } | undefined;
68
- for (const child of siblings) {
69
- if (child.type === 'node' && offset === this.offset && child === this.green) {
70
- if (!prev) return undefined;
71
- return wrapElement(prev.green, prev.offset, this.parent);
72
- }
73
- prev = { green: child, offset };
74
- offset += elementTextLength(child);
75
- }
76
- return undefined;
190
+ return this.parent === undefined ? undefined : childAt(this.parent, this.index - 1);
191
+ }
192
+
193
+ /** The sibling element immediately after this node within its parent. */
194
+ get nextSiblingOrToken(): SyntaxElement | undefined {
195
+ return this.nextSibling;
196
+ }
197
+
198
+ /** The sibling element immediately before this node within its parent. */
199
+ get prevSiblingOrToken(): SyntaxElement | undefined {
200
+ return this.prevSibling;
201
+ }
202
+
203
+ /** The first token in this subtree (depth-first), or `undefined` if empty. */
204
+ get firstToken(): SyntaxToken | undefined {
205
+ return firstToken(this);
206
+ }
207
+
208
+ /** The last token in this subtree (depth-first), or `undefined` if empty. */
209
+ get lastToken(): SyntaxToken | undefined {
210
+ return lastToken(this);
77
211
  }
78
212
 
79
213
  *children(): Iterable<SyntaxElement> {
80
214
  let offset = this.offset;
215
+ let index = 0;
81
216
  for (const child of this.green.children) {
82
- yield wrapElement(child, offset, this);
217
+ yield wrapElement(child, offset, this, index);
83
218
  offset += elementTextLength(child);
219
+ index++;
84
220
  }
85
221
  }
86
222
 
@@ -98,6 +234,21 @@ export class SyntaxNode {
98
234
  }
99
235
  }
100
236
 
237
+ /** The nearest match, testing this node itself before walking its ancestors. */
238
+ findAncestor<T>(cast: (node: SyntaxNode) => T | undefined): T | undefined {
239
+ const self = cast(this);
240
+ if (self !== undefined) {
241
+ return self;
242
+ }
243
+ for (const ancestor of this.ancestors()) {
244
+ const result = cast(ancestor);
245
+ if (result !== undefined) {
246
+ return result;
247
+ }
248
+ }
249
+ return undefined;
250
+ }
251
+
101
252
  *descendants(): Iterable<SyntaxElement> {
102
253
  const stack: SyntaxElement[] = [this];
103
254
  for (let el = stack.pop(); el !== undefined; el = stack.pop()) {
@@ -116,23 +267,147 @@ export class SyntaxNode {
116
267
 
117
268
  *tokens(): Iterable<SyntaxToken> {
118
269
  for (const el of this.descendants()) {
119
- if (!(el instanceof SyntaxNode)) {
270
+ if (el instanceof SyntaxToken) {
120
271
  yield el;
121
272
  }
122
273
  }
123
274
  }
275
+
276
+ /**
277
+ * The token(s) at `offset`. The between-two-tokens case (offset exactly on a
278
+ * token seam) is represented explicitly so callers can left/right bias.
279
+ */
280
+ tokenAtOffset(offset: number): TokenAtOffset {
281
+ return tokenAtOffsetOf(this, offset);
282
+ }
283
+
284
+ /**
285
+ * The smallest element fully containing the range `[start, end]`. At a seam
286
+ * (and for empty ranges) the left-hand element is preferred, matching
287
+ * {@link containsOffset}'s inclusive span.
288
+ */
289
+ coveringElement(start: number, end: number): SyntaxElement {
290
+ let result: SyntaxElement = this;
291
+ for (;;) {
292
+ if (result instanceof SyntaxToken) return result;
293
+ let next: SyntaxElement | undefined;
294
+ for (const child of result.children()) {
295
+ if (containsRange(child, start, end)) {
296
+ next = child;
297
+ break;
298
+ }
299
+ }
300
+ if (next === undefined) return result;
301
+ result = next;
302
+ }
303
+ }
124
304
  }
125
305
 
126
306
  function elementTextLength(el: GreenElement): number {
127
307
  return el.type === 'token' ? el.text.length : el.textLength;
128
308
  }
129
309
 
130
- function wrapElement(green: GreenElement, offset: number, parent: SyntaxNode): SyntaxElement {
310
+ function elementLength(el: SyntaxElement): number {
311
+ return el instanceof SyntaxToken ? el.text.length : el.textLength;
312
+ }
313
+
314
+ /**
315
+ * Whether `el` contains `offset`. The span is inclusive on both ends so a seam
316
+ * offset touches both neighbours.
317
+ */
318
+ function containsOffset(el: SyntaxElement, offset: number): boolean {
319
+ const start = el.offset;
320
+ const len = elementLength(el);
321
+ return offset >= start && offset <= start + len;
322
+ }
323
+
324
+ function containsRange(el: SyntaxElement, start: number, end: number): boolean {
325
+ const elStart = el.offset;
326
+ const len = elementLength(el);
327
+ return elStart <= start && end <= elStart + len;
328
+ }
329
+
330
+ function tokenAtOffsetOf(el: SyntaxElement, offset: number): TokenAtOffset {
331
+ if (el instanceof SyntaxToken) {
332
+ return TokenAtOffset.single(el);
333
+ }
334
+ let left: SyntaxElement | undefined;
335
+ let right: SyntaxElement | undefined;
336
+ for (const child of el.children()) {
337
+ if (!containsOffset(child, offset)) continue;
338
+ if (left === undefined) {
339
+ left = child;
340
+ } else {
341
+ right = child;
342
+ break;
343
+ }
344
+ }
345
+ if (left === undefined) return TokenAtOffset.none();
346
+ if (right === undefined) return tokenAtOffsetOf(left, offset);
347
+ const leftToken = tokenAtOffsetOf(left, offset).rightBiased();
348
+ const rightToken = tokenAtOffsetOf(right, offset).leftBiased();
349
+ if (leftToken !== undefined && rightToken !== undefined) {
350
+ return TokenAtOffset.between(leftToken, rightToken);
351
+ }
352
+ if (leftToken !== undefined) return TokenAtOffset.single(leftToken);
353
+ if (rightToken !== undefined) return TokenAtOffset.single(rightToken);
354
+ return TokenAtOffset.none();
355
+ }
356
+
357
+ function firstToken(el: SyntaxElement): SyntaxToken | undefined {
358
+ if (el instanceof SyntaxToken) return el;
359
+ for (const child of el.children()) {
360
+ const token = firstToken(child);
361
+ if (token !== undefined) return token;
362
+ }
363
+ return undefined;
364
+ }
365
+
366
+ function lastToken(el: SyntaxElement): SyntaxToken | undefined {
367
+ if (el instanceof SyntaxToken) return el;
368
+ const children = Array.from(el.children());
369
+ for (let i = children.length - 1; i >= 0; i--) {
370
+ const child = children[i];
371
+ if (child !== undefined) {
372
+ const token = lastToken(child);
373
+ if (token !== undefined) return token;
374
+ }
375
+ }
376
+ return undefined;
377
+ }
378
+
379
+ function climbingNext(el: SyntaxElement): SyntaxElement | undefined {
380
+ let current: SyntaxElement = el;
381
+ for (;;) {
382
+ const parent = current.parent;
383
+ if (parent === undefined) return undefined;
384
+ const sibling = childAt(parent, current.index + 1);
385
+ if (sibling !== undefined) return sibling;
386
+ current = parent;
387
+ }
388
+ }
389
+
390
+ function climbingPrev(el: SyntaxElement): SyntaxElement | undefined {
391
+ let current: SyntaxElement = el;
392
+ for (;;) {
393
+ const parent = current.parent;
394
+ if (parent === undefined) return undefined;
395
+ const sibling = childAt(parent, current.index - 1);
396
+ if (sibling !== undefined) return sibling;
397
+ current = parent;
398
+ }
399
+ }
400
+
401
+ function wrapElement(
402
+ green: GreenElement,
403
+ offset: number,
404
+ parent: SyntaxNode,
405
+ index: number,
406
+ ): SyntaxElement {
131
407
  if (green.type === 'token') {
132
- const token: SyntaxToken = { kind: green.kind, text: green.text, offset };
133
- return token;
408
+ return new SyntaxToken(green, offset, parent, index);
134
409
  }
135
- return new SyntaxNode(green, offset, parent);
410
+ return new SyntaxNode(green, offset, parent, index);
136
411
  }
137
412
 
138
413
  function childAt(node: SyntaxNode, index: number): SyntaxElement | undefined {
@@ -146,9 +421,9 @@ function childAt(node: SyntaxNode, index: number): SyntaxElement | undefined {
146
421
  offset += elementTextLength(child);
147
422
  }
148
423
  }
149
- return wrapElement(target, offset, node);
424
+ return wrapElement(target, offset, node, index);
150
425
  }
151
426
 
152
427
  export function createSyntaxTree(green: GreenNode): SyntaxNode {
153
- return new SyntaxNode(green, 0, undefined);
428
+ return new SyntaxNode(green, 0, undefined, 0);
154
429
  }