@prisma-next/psl-parser 0.14.0-dev.5 → 0.14.0-dev.50

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 (62) hide show
  1. package/README.md +28 -10
  2. package/dist/declarations-DR6To8_k.mjs +1060 -0
  3. package/dist/declarations-DR6To8_k.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 +239 -3
  8. package/dist/index.d.mts.map +1 -1
  9. package/dist/index.mjs +785 -3
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/parse-3-vr14ej.mjs +626 -0
  12. package/dist/parse-3-vr14ej.mjs.map +1 -0
  13. package/dist/{parse-BjZ1LPe6.d.mts → parse-BazJr7Ye.d.mts} +128 -51
  14. package/dist/parse-BazJr7Ye.d.mts.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 +9 -25
  20. package/src/attribute-spec/combinators/bool.ts +19 -0
  21. package/src/attribute-spec/combinators/diagnostic.ts +15 -0
  22. package/src/attribute-spec/combinators/entity-ref.ts +24 -0
  23. package/src/attribute-spec/combinators/field-ref.ts +36 -0
  24. package/src/attribute-spec/combinators/identifier.ts +16 -0
  25. package/src/attribute-spec/combinators/int.ts +19 -0
  26. package/src/attribute-spec/combinators/list.ts +43 -0
  27. package/src/attribute-spec/combinators/one-of.ts +29 -0
  28. package/src/attribute-spec/combinators/record.ts +43 -0
  29. package/src/attribute-spec/combinators/str.ts +19 -0
  30. package/src/attribute-spec/field-attribute.ts +27 -0
  31. package/src/attribute-spec/interpret.ts +154 -0
  32. package/src/attribute-spec/model-attribute.ts +27 -0
  33. package/src/attribute-spec/optional.ts +8 -0
  34. package/src/attribute-spec/types.ts +72 -0
  35. package/src/block-reconstruction.ts +139 -0
  36. package/src/exports/index.ts +57 -3
  37. package/src/exports/syntax.ts +25 -5
  38. package/src/extension-block.ts +107 -0
  39. package/src/parse.ts +23 -5
  40. package/src/resolve.ts +123 -0
  41. package/src/source-file.ts +25 -0
  42. package/src/symbol-table.ts +446 -0
  43. package/src/syntax/ast/attributes.ts +5 -6
  44. package/src/syntax/ast/declarations.ts +51 -26
  45. package/src/syntax/ast/expressions.ts +12 -13
  46. package/src/syntax/ast/identifier.ts +2 -3
  47. package/src/syntax/ast/qualified-name.ts +28 -19
  48. package/src/syntax/ast/type-annotation.ts +4 -5
  49. package/src/syntax/ast-helpers.ts +27 -3
  50. package/src/syntax/navigation.ts +55 -0
  51. package/src/syntax/red.ts +317 -42
  52. package/dist/parse-B_3gIEFd.mjs +0 -1421
  53. package/dist/parse-B_3gIEFd.mjs.map +0 -1
  54. package/dist/parse-BjZ1LPe6.d.mts.map +0 -1
  55. package/dist/parser-CaplKvRs.mjs +0 -1145
  56. package/dist/parser-CaplKvRs.mjs.map +0 -1
  57. package/dist/parser-Dfi3Wfdq.d.mts +0 -7
  58. package/dist/parser-Dfi3Wfdq.d.mts.map +0 -1
  59. package/dist/parser.d.mts +0 -3
  60. package/dist/parser.mjs +0 -3
  61. package/src/exports/parser.ts +0 -4
  62. package/src/parser.ts +0 -1642
@@ -1,7 +1,6 @@
1
- import type { Token } from '../../tokenizer';
2
- import type { AstNode } from '../ast-helpers';
1
+ import type { AstNode, BracedBlock } from '../ast-helpers';
3
2
  import { filterChildren, findChildToken, findFirstChild } from '../ast-helpers';
4
- import { SyntaxNode } from '../red';
3
+ import { SyntaxNode, type SyntaxToken } from '../red';
5
4
  import { FieldAttributeAst, ModelAttributeAst } from './attributes';
6
5
  import type { ExpressionAst } from './expressions';
7
6
  import { castExpression } from './expressions';
@@ -18,6 +17,11 @@ export type NamespaceMemberAst =
18
17
  | CompositeTypeDeclarationAst
19
18
  | GenericBlockDeclarationAst;
20
19
 
20
+ export type DeclarationAst = NamespaceMemberAst | TypesBlockAst | NamespaceDeclarationAst;
21
+ export type AttributeAst = FieldAttributeAst | ModelAttributeAst;
22
+ export type BlockMemberAst = FieldDeclarationAst | ModelAttributeAst;
23
+ export type GenericBlockMemberAst = KeyValuePairAst | ModelAttributeAst;
24
+
21
25
  function castNamespaceMember(node: SyntaxNode): NamespaceMemberAst | undefined {
22
26
  return (
23
27
  ModelDeclarationAst.cast(node) ??
@@ -33,7 +37,7 @@ export class DocumentAst implements AstNode {
33
37
  this.syntax = syntax;
34
38
  }
35
39
 
36
- *declarations(): Iterable<NamespaceMemberAst | TypesBlockAst | NamespaceDeclarationAst> {
40
+ *declarations(): Iterable<DeclarationAst> {
37
41
  yield* filterChildren(
38
42
  this.syntax,
39
43
  (node) =>
@@ -46,14 +50,14 @@ export class DocumentAst implements AstNode {
46
50
  }
47
51
  }
48
52
 
49
- export class ModelDeclarationAst implements AstNode {
53
+ export class ModelDeclarationAst implements BracedBlock {
50
54
  readonly syntax: SyntaxNode;
51
55
 
52
56
  constructor(syntax: SyntaxNode) {
53
57
  this.syntax = syntax;
54
58
  }
55
59
 
56
- keyword(): Token | undefined {
60
+ keyword(): SyntaxToken | undefined {
57
61
  return findChildToken(this.syntax, 'Ident');
58
62
  }
59
63
 
@@ -61,11 +65,11 @@ export class ModelDeclarationAst implements AstNode {
61
65
  return findFirstChild(this.syntax, IdentifierAst.cast);
62
66
  }
63
67
 
64
- lbrace(): Token | undefined {
68
+ lbrace(): SyntaxToken | undefined {
65
69
  return findChildToken(this.syntax, 'LBrace');
66
70
  }
67
71
 
68
- rbrace(): Token | undefined {
72
+ rbrace(): SyntaxToken | undefined {
69
73
  return findChildToken(this.syntax, 'RBrace');
70
74
  }
71
75
 
@@ -77,19 +81,26 @@ export class ModelDeclarationAst implements AstNode {
77
81
  yield* filterChildren(this.syntax, ModelAttributeAst.cast);
78
82
  }
79
83
 
84
+ *members(): Iterable<BlockMemberAst> {
85
+ yield* filterChildren(
86
+ this.syntax,
87
+ (node) => FieldDeclarationAst.cast(node) ?? ModelAttributeAst.cast(node),
88
+ );
89
+ }
90
+
80
91
  static cast(node: SyntaxNode): ModelDeclarationAst | undefined {
81
92
  return node.kind === 'ModelDeclaration' ? new ModelDeclarationAst(node) : undefined;
82
93
  }
83
94
  }
84
95
 
85
- export class CompositeTypeDeclarationAst implements AstNode {
96
+ export class CompositeTypeDeclarationAst implements BracedBlock {
86
97
  readonly syntax: SyntaxNode;
87
98
 
88
99
  constructor(syntax: SyntaxNode) {
89
100
  this.syntax = syntax;
90
101
  }
91
102
 
92
- keyword(): Token | undefined {
103
+ keyword(): SyntaxToken | undefined {
93
104
  return findChildToken(this.syntax, 'Ident');
94
105
  }
95
106
 
@@ -97,11 +108,11 @@ export class CompositeTypeDeclarationAst implements AstNode {
97
108
  return findFirstChild(this.syntax, IdentifierAst.cast);
98
109
  }
99
110
 
100
- lbrace(): Token | undefined {
111
+ lbrace(): SyntaxToken | undefined {
101
112
  return findChildToken(this.syntax, 'LBrace');
102
113
  }
103
114
 
104
- rbrace(): Token | undefined {
115
+ rbrace(): SyntaxToken | undefined {
105
116
  return findChildToken(this.syntax, 'RBrace');
106
117
  }
107
118
 
@@ -113,6 +124,13 @@ export class CompositeTypeDeclarationAst implements AstNode {
113
124
  yield* filterChildren(this.syntax, ModelAttributeAst.cast);
114
125
  }
115
126
 
127
+ *members(): Iterable<BlockMemberAst> {
128
+ yield* filterChildren(
129
+ this.syntax,
130
+ (node) => FieldDeclarationAst.cast(node) ?? ModelAttributeAst.cast(node),
131
+ );
132
+ }
133
+
116
134
  static cast(node: SyntaxNode): CompositeTypeDeclarationAst | undefined {
117
135
  return node.kind === 'CompositeTypeDeclaration'
118
136
  ? new CompositeTypeDeclarationAst(node)
@@ -120,14 +138,14 @@ export class CompositeTypeDeclarationAst implements AstNode {
120
138
  }
121
139
  }
122
140
 
123
- export class NamespaceDeclarationAst implements AstNode {
141
+ export class NamespaceDeclarationAst implements BracedBlock {
124
142
  readonly syntax: SyntaxNode;
125
143
 
126
144
  constructor(syntax: SyntaxNode) {
127
145
  this.syntax = syntax;
128
146
  }
129
147
 
130
- keyword(): Token | undefined {
148
+ keyword(): SyntaxToken | undefined {
131
149
  return findChildToken(this.syntax, 'Ident');
132
150
  }
133
151
 
@@ -135,11 +153,11 @@ export class NamespaceDeclarationAst implements AstNode {
135
153
  return findFirstChild(this.syntax, IdentifierAst.cast);
136
154
  }
137
155
 
138
- lbrace(): Token | undefined {
156
+ lbrace(): SyntaxToken | undefined {
139
157
  return findChildToken(this.syntax, 'LBrace');
140
158
  }
141
159
 
142
- rbrace(): Token | undefined {
160
+ rbrace(): SyntaxToken | undefined {
143
161
  return findChildToken(this.syntax, 'RBrace');
144
162
  }
145
163
 
@@ -152,22 +170,22 @@ export class NamespaceDeclarationAst implements AstNode {
152
170
  }
153
171
  }
154
172
 
155
- export class TypesBlockAst implements AstNode {
173
+ export class TypesBlockAst implements BracedBlock {
156
174
  readonly syntax: SyntaxNode;
157
175
 
158
176
  constructor(syntax: SyntaxNode) {
159
177
  this.syntax = syntax;
160
178
  }
161
179
 
162
- keyword(): Token | undefined {
180
+ keyword(): SyntaxToken | undefined {
163
181
  return findChildToken(this.syntax, 'Ident');
164
182
  }
165
183
 
166
- lbrace(): Token | undefined {
184
+ lbrace(): SyntaxToken | undefined {
167
185
  return findChildToken(this.syntax, 'LBrace');
168
186
  }
169
187
 
170
- rbrace(): Token | undefined {
188
+ rbrace(): SyntaxToken | undefined {
171
189
  return findChildToken(this.syntax, 'RBrace');
172
190
  }
173
191
 
@@ -180,14 +198,14 @@ export class TypesBlockAst implements AstNode {
180
198
  }
181
199
  }
182
200
 
183
- export class GenericBlockDeclarationAst implements AstNode {
201
+ export class GenericBlockDeclarationAst implements BracedBlock {
184
202
  readonly syntax: SyntaxNode;
185
203
 
186
204
  constructor(syntax: SyntaxNode) {
187
205
  this.syntax = syntax;
188
206
  }
189
207
 
190
- keyword(): Token | undefined {
208
+ keyword(): SyntaxToken | undefined {
191
209
  return findChildToken(this.syntax, 'Ident');
192
210
  }
193
211
 
@@ -195,11 +213,11 @@ export class GenericBlockDeclarationAst implements AstNode {
195
213
  return findFirstChild(this.syntax, IdentifierAst.cast);
196
214
  }
197
215
 
198
- lbrace(): Token | undefined {
216
+ lbrace(): SyntaxToken | undefined {
199
217
  return findChildToken(this.syntax, 'LBrace');
200
218
  }
201
219
 
202
- rbrace(): Token | undefined {
220
+ rbrace(): SyntaxToken | undefined {
203
221
  return findChildToken(this.syntax, 'RBrace');
204
222
  }
205
223
 
@@ -211,6 +229,13 @@ export class GenericBlockDeclarationAst implements AstNode {
211
229
  yield* filterChildren(this.syntax, ModelAttributeAst.cast);
212
230
  }
213
231
 
232
+ *members(): Iterable<GenericBlockMemberAst> {
233
+ yield* filterChildren(
234
+ this.syntax,
235
+ (node) => KeyValuePairAst.cast(node) ?? ModelAttributeAst.cast(node),
236
+ );
237
+ }
238
+
214
239
  static cast(node: SyntaxNode): GenericBlockDeclarationAst | undefined {
215
240
  return node.kind === 'GenericBlockDeclaration'
216
241
  ? new GenericBlockDeclarationAst(node)
@@ -229,7 +254,7 @@ export class KeyValuePairAst implements AstNode {
229
254
  return findFirstChild(this.syntax, IdentifierAst.cast);
230
255
  }
231
256
 
232
- equals(): Token | undefined {
257
+ equals(): SyntaxToken | undefined {
233
258
  return findChildToken(this.syntax, 'Equals');
234
259
  }
235
260
 
@@ -288,7 +313,7 @@ export class NamedTypeDeclarationAst implements AstNode {
288
313
  return findFirstChild(this.syntax, IdentifierAst.cast);
289
314
  }
290
315
 
291
- equals(): Token | undefined {
316
+ equals(): SyntaxToken | undefined {
292
317
  return findChildToken(this.syntax, 'Equals');
293
318
  }
294
319
 
@@ -1,7 +1,6 @@
1
- import type { Token } from '../../tokenizer';
2
1
  import type { AstNode } from '../ast-helpers';
3
2
  import { filterChildren, findChildToken, findFirstChild } from '../ast-helpers';
4
- import { SyntaxNode } from '../red';
3
+ import { SyntaxNode, type SyntaxToken } from '../red';
5
4
  import { IdentifierAst } from './identifier';
6
5
  import { QualifiedNameAst } from './qualified-name';
7
6
 
@@ -32,11 +31,11 @@ export class FunctionCallAst implements AstNode {
32
31
  return segments;
33
32
  }
34
33
 
35
- lparen(): Token | undefined {
34
+ lparen(): SyntaxToken | undefined {
36
35
  return findChildToken(this.syntax, 'LParen');
37
36
  }
38
37
 
39
- rparen(): Token | undefined {
38
+ rparen(): SyntaxToken | undefined {
40
39
  return findChildToken(this.syntax, 'RParen');
41
40
  }
42
41
 
@@ -56,11 +55,11 @@ export class ArrayLiteralAst implements AstNode {
56
55
  this.syntax = syntax;
57
56
  }
58
57
 
59
- lbracket(): Token | undefined {
58
+ lbracket(): SyntaxToken | undefined {
60
59
  return findChildToken(this.syntax, 'LBracket');
61
60
  }
62
61
 
63
- rbracket(): Token | undefined {
62
+ rbracket(): SyntaxToken | undefined {
64
63
  return findChildToken(this.syntax, 'RBracket');
65
64
  }
66
65
 
@@ -156,7 +155,7 @@ export class StringLiteralExprAst implements AstNode {
156
155
  this.syntax = syntax;
157
156
  }
158
157
 
159
- token(): Token | undefined {
158
+ token(): SyntaxToken | undefined {
160
159
  return findChildToken(this.syntax, 'StringLiteral');
161
160
  }
162
161
 
@@ -178,7 +177,7 @@ export class NumberLiteralExprAst implements AstNode {
178
177
  this.syntax = syntax;
179
178
  }
180
179
 
181
- token(): Token | undefined {
180
+ token(): SyntaxToken | undefined {
182
181
  return findChildToken(this.syntax, 'NumberLiteral');
183
182
  }
184
183
 
@@ -200,7 +199,7 @@ export class BooleanLiteralExprAst implements AstNode {
200
199
  this.syntax = syntax;
201
200
  }
202
201
 
203
- token(): Token | undefined {
202
+ token(): SyntaxToken | undefined {
204
203
  return findChildToken(this.syntax, 'Ident');
205
204
  }
206
205
 
@@ -224,11 +223,11 @@ export class ObjectLiteralExprAst implements AstNode {
224
223
  this.syntax = syntax;
225
224
  }
226
225
 
227
- lbrace(): Token | undefined {
226
+ lbrace(): SyntaxToken | undefined {
228
227
  return findChildToken(this.syntax, 'LBrace');
229
228
  }
230
229
 
231
- rbrace(): Token | undefined {
230
+ rbrace(): SyntaxToken | undefined {
232
231
  return findChildToken(this.syntax, 'RBrace');
233
232
  }
234
233
 
@@ -279,7 +278,7 @@ export class ObjectFieldAst implements AstNode {
279
278
  return undefined;
280
279
  }
281
280
 
282
- colon(): Token | undefined {
281
+ colon(): SyntaxToken | undefined {
283
282
  return findChildToken(this.syntax, 'Colon');
284
283
  }
285
284
 
@@ -339,7 +338,7 @@ export class AttributeArgAst implements AstNode {
339
338
  return findFirstChild(this.syntax, IdentifierAst.cast);
340
339
  }
341
340
 
342
- colon(): Token | undefined {
341
+ colon(): SyntaxToken | undefined {
343
342
  return findChildToken(this.syntax, 'Colon');
344
343
  }
345
344
 
@@ -1,7 +1,6 @@
1
- import type { Token } from '../../tokenizer';
2
1
  import type { AstNode } from '../ast-helpers';
3
2
  import { findChildToken } from '../ast-helpers';
4
- import type { SyntaxNode } from '../red';
3
+ import type { SyntaxNode, SyntaxToken } from '../red';
5
4
 
6
5
  export class IdentifierAst implements AstNode {
7
6
  readonly syntax: SyntaxNode;
@@ -10,7 +9,7 @@ export class IdentifierAst implements AstNode {
10
9
  this.syntax = syntax;
11
10
  }
12
11
 
13
- token(): Token | undefined {
12
+ token(): SyntaxToken | undefined {
14
13
  return findChildToken(this.syntax, 'Ident');
15
14
  }
16
15
 
@@ -1,7 +1,6 @@
1
- import type { Token } from '../../tokenizer';
2
1
  import type { AstNode } from '../ast-helpers';
3
2
  import { filterChildren, findChildToken, findFirstChild } from '../ast-helpers';
4
- import { SyntaxNode } from '../red';
3
+ import { SyntaxNode, type SyntaxToken } from '../red';
5
4
  import { IdentifierAst } from './identifier';
6
5
 
7
6
  /** A namespace-qualified name, e.g. `pgvector.Vector` or `supabase:auth.User`. */
@@ -12,22 +11,20 @@ export class QualifiedNameAst implements AstNode {
12
11
  this.syntax = syntax;
13
12
  }
14
13
 
15
- #lastSegment(): IdentifierAst | undefined {
16
- let last: IdentifierAst | undefined;
14
+ #segmentBefore(boundary: number): IdentifierAst | undefined {
15
+ let found: IdentifierAst | undefined;
17
16
  for (const segment of filterChildren(this.syntax, IdentifierAst.cast)) {
18
- last = segment;
17
+ if (segment.syntax.offset >= boundary) break;
18
+ found = segment;
19
19
  }
20
- return last;
20
+ return found;
21
21
  }
22
22
 
23
- #penultimateSegment(): IdentifierAst | undefined {
24
- let last: IdentifierAst | undefined;
25
- let penultimate: IdentifierAst | undefined;
23
+ #segmentAfter(boundary: number): IdentifierAst | undefined {
26
24
  for (const segment of filterChildren(this.syntax, IdentifierAst.cast)) {
27
- penultimate = last;
28
- last = segment;
25
+ if (segment.syntax.offset > boundary) return segment;
29
26
  }
30
- return penultimate;
27
+ return undefined;
31
28
  }
32
29
 
33
30
  #separatorCount(kind: 'Dot' | 'Colon'): number {
@@ -38,26 +35,32 @@ export class QualifiedNameAst implements AstNode {
38
35
  return count;
39
36
  }
40
37
 
41
- colon(): Token | undefined {
38
+ colon(): SyntaxToken | undefined {
42
39
  return findChildToken(this.syntax, 'Colon');
43
40
  }
44
41
 
45
- dot(): Token | undefined {
42
+ dot(): SyntaxToken | undefined {
46
43
  return findChildToken(this.syntax, 'Dot');
47
44
  }
48
45
 
49
46
  space(): IdentifierAst | undefined {
50
- if (!this.colon()) return undefined;
51
- return findFirstChild(this.syntax, IdentifierAst.cast);
47
+ const colon = this.colon();
48
+ if (!colon) return undefined;
49
+ return this.#segmentBefore(colon.offset);
52
50
  }
53
51
 
54
52
  namespace(): IdentifierAst | undefined {
55
- if (!this.dot()) return undefined;
56
- return this.#penultimateSegment();
53
+ const dot = this.dot();
54
+ if (!dot) return undefined;
55
+ return this.#segmentBefore(dot.offset);
57
56
  }
58
57
 
59
58
  identifier(): IdentifierAst | undefined {
60
- return this.#lastSegment();
59
+ const dot = this.dot();
60
+ if (dot) return this.#segmentAfter(dot.offset);
61
+ const colon = this.colon();
62
+ if (colon) return this.#segmentAfter(colon.offset);
63
+ return findFirstChild(this.syntax, IdentifierAst.cast);
61
64
  }
62
65
 
63
66
  /**
@@ -73,6 +76,12 @@ export class QualifiedNameAst implements AstNode {
73
76
  return segments;
74
77
  }
75
78
 
79
+ /** True iff this is a single unqualified identifier whose text equals `name`. */
80
+ isSimpleName(name: string): boolean {
81
+ if (this.dot() !== undefined || this.colon() !== undefined) return false;
82
+ return this.identifier()?.token()?.text === name;
83
+ }
84
+
76
85
  /**
77
86
  * Flags a malformed name with more qualifier segments than allowed (a second
78
87
  * `:`-space or a second `.`-namespace).
@@ -1,7 +1,6 @@
1
- import type { Token } from '../../tokenizer';
2
1
  import type { AstNode } from '../ast-helpers';
3
2
  import { findChildToken, findFirstChild } from '../ast-helpers';
4
- import type { SyntaxNode } from '../red';
3
+ import type { SyntaxNode, SyntaxToken } from '../red';
5
4
  import { AttributeArgListAst } from './attributes';
6
5
  import { QualifiedNameAst } from './qualified-name';
7
6
 
@@ -26,15 +25,15 @@ export class TypeAnnotationAst implements AstNode {
26
25
  return this.argList() !== undefined;
27
26
  }
28
27
 
29
- lbracket(): Token | undefined {
28
+ lbracket(): SyntaxToken | undefined {
30
29
  return findChildToken(this.syntax, 'LBracket');
31
30
  }
32
31
 
33
- rbracket(): Token | undefined {
32
+ rbracket(): SyntaxToken | undefined {
34
33
  return findChildToken(this.syntax, 'RBracket');
35
34
  }
36
35
 
37
- questionMark(): Token | undefined {
36
+ questionMark(): SyntaxToken | undefined {
38
37
  return findChildToken(this.syntax, 'Question');
39
38
  }
40
39
 
@@ -1,11 +1,16 @@
1
- import type { Token, TokenKind } from '../tokenizer';
2
- import { SyntaxNode } from './red';
1
+ import type { TokenKind } from '../tokenizer';
2
+ import { SyntaxNode, type SyntaxToken } from './red';
3
3
 
4
4
  export interface AstNode {
5
5
  readonly syntax: SyntaxNode;
6
6
  }
7
7
 
8
- export function findChildToken(node: SyntaxNode, kind: TokenKind): Token | undefined {
8
+ export interface BracedBlock extends AstNode {
9
+ lbrace(): SyntaxToken | undefined;
10
+ rbrace(): SyntaxToken | undefined;
11
+ }
12
+
13
+ export function findChildToken(node: SyntaxNode, kind: TokenKind): SyntaxToken | undefined {
9
14
  for (const child of node.children()) {
10
15
  if (!(child instanceof SyntaxNode) && child.kind === kind) {
11
16
  return child;
@@ -35,6 +40,25 @@ export function* filterChildren<T>(
35
40
  }
36
41
  }
37
42
 
43
+ type CastTarget<C> = C extends (node: SyntaxNode) => infer R ? Exclude<R, undefined> : never;
44
+
45
+ export function any<Casts extends readonly ((node: SyntaxNode) => unknown)[]>(
46
+ ...casts: Casts
47
+ ): (node: SyntaxNode) => CastTarget<Casts[number]> | undefined;
48
+ export function any(
49
+ ...casts: ReadonlyArray<(node: SyntaxNode) => unknown>
50
+ ): (node: SyntaxNode) => unknown {
51
+ return (node) => {
52
+ for (const cast of casts) {
53
+ const result = cast(node);
54
+ if (result !== undefined) {
55
+ return result;
56
+ }
57
+ }
58
+ return undefined;
59
+ };
60
+ }
61
+
38
62
  /**
39
63
  * Raw source text of a CST node, verbatim (quotes and brackets preserved). For
40
64
  * the decoded value of a string literal, decode it instead.
@@ -0,0 +1,55 @@
1
+ import type { TokenKind } from '../tokenizer';
2
+ import { type SyntaxElement, SyntaxToken } from './red';
3
+
4
+ /** Direction of a sibling/token walk. */
5
+ export type Direction = 'next' | 'prev';
6
+
7
+ const TRIVIA_KINDS: ReadonlySet<TokenKind> = new Set<TokenKind>([
8
+ 'Whitespace',
9
+ 'Newline',
10
+ 'Comment',
11
+ ]);
12
+
13
+ /** Whether a token kind is trivia (whitespace, newline, or comment). */
14
+ export function isTriviaKind(kind: TokenKind): boolean {
15
+ return TRIVIA_KINDS.has(kind);
16
+ }
17
+
18
+ /** Whether a token is trivia. */
19
+ export function isTrivia(token: SyntaxToken): boolean {
20
+ return isTriviaKind(token.kind);
21
+ }
22
+
23
+ /**
24
+ * The first non-trivia token at or beyond `token` in `direction`. Returns
25
+ * `token` itself when it is already significant.
26
+ */
27
+ export function skipTriviaToken(token: SyntaxToken, direction: Direction): SyntaxToken | undefined {
28
+ let current: SyntaxToken | undefined = token;
29
+ while (current !== undefined && isTrivia(current)) {
30
+ current = direction === 'next' ? current.nextToken : current.prevToken;
31
+ }
32
+ return current;
33
+ }
34
+
35
+ /**
36
+ * The nearest sibling of `element` (within the same parent) in `direction` that
37
+ * is not a trivia token. Nodes are always significant.
38
+ */
39
+ export function nonTriviaSibling(
40
+ element: SyntaxElement,
41
+ direction: Direction,
42
+ ): SyntaxElement | undefined {
43
+ let sibling = step(element, direction);
44
+ while (sibling !== undefined) {
45
+ if (!(sibling instanceof SyntaxToken) || !isTrivia(sibling)) {
46
+ return sibling;
47
+ }
48
+ sibling = step(sibling, direction);
49
+ }
50
+ return undefined;
51
+ }
52
+
53
+ function step(element: SyntaxElement, direction: Direction): SyntaxElement | undefined {
54
+ return direction === 'next' ? element.nextSiblingOrToken : element.prevSiblingOrToken;
55
+ }