@prisma-next/psl-parser 0.14.0-dev.6 → 0.14.0-dev.61

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 (58) hide show
  1. package/dist/{declarations-D9h_ihD3.mjs → declarations-DR6To8_k.mjs} +295 -55
  2. package/dist/declarations-DR6To8_k.mjs.map +1 -0
  3. package/dist/format.d.mts +1 -1
  4. package/dist/format.mjs +2 -2
  5. package/dist/index.d.mts +95 -112
  6. package/dist/index.d.mts.map +1 -1
  7. package/dist/index.mjs +374 -60
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/interpret.d.mts +29 -0
  10. package/dist/interpret.d.mts.map +1 -0
  11. package/dist/interpret.mjs +9 -0
  12. package/dist/interpret.mjs.map +1 -0
  13. package/dist/{parse-DhEV6av6.mjs → parse-3-vr14ej.mjs} +26 -5
  14. package/dist/parse-3-vr14ej.mjs.map +1 -0
  15. package/dist/{parse-BjZ1LPe6.d.mts → parse-BazJr7Ye.d.mts} +128 -51
  16. package/dist/parse-BazJr7Ye.d.mts.map +1 -0
  17. package/dist/symbol-table-C-AH04Ug.d.mts +127 -0
  18. package/dist/symbol-table-C-AH04Ug.d.mts.map +1 -0
  19. package/dist/syntax.d.mts +20 -2
  20. package/dist/syntax.d.mts.map +1 -1
  21. package/dist/syntax.mjs +43 -3
  22. package/dist/syntax.mjs.map +1 -0
  23. package/package.json +7 -5
  24. package/src/attribute-spec/combinators/bool.ts +19 -0
  25. package/src/attribute-spec/combinators/diagnostic.ts +15 -0
  26. package/src/attribute-spec/combinators/entity-ref.ts +24 -0
  27. package/src/attribute-spec/combinators/field-ref.ts +36 -0
  28. package/src/attribute-spec/combinators/identifier.ts +16 -0
  29. package/src/attribute-spec/combinators/int.ts +19 -0
  30. package/src/attribute-spec/combinators/list.ts +43 -0
  31. package/src/attribute-spec/combinators/one-of.ts +29 -0
  32. package/src/attribute-spec/combinators/record.ts +43 -0
  33. package/src/attribute-spec/combinators/str.ts +19 -0
  34. package/src/attribute-spec/field-attribute.ts +27 -0
  35. package/src/attribute-spec/interpret.ts +154 -0
  36. package/src/attribute-spec/model-attribute.ts +27 -0
  37. package/src/attribute-spec/optional.ts +8 -0
  38. package/src/attribute-spec/types.ts +72 -0
  39. package/src/block-reconstruction.ts +1 -0
  40. package/src/exports/index.ts +30 -0
  41. package/src/exports/interpret.ts +2 -0
  42. package/src/exports/syntax.ts +25 -5
  43. package/src/interpret.ts +40 -0
  44. package/src/parse.ts +23 -5
  45. package/src/resolve.ts +4 -1
  46. package/src/source-file.ts +25 -0
  47. package/src/syntax/ast/attributes.ts +5 -6
  48. package/src/syntax/ast/declarations.ts +51 -26
  49. package/src/syntax/ast/expressions.ts +12 -13
  50. package/src/syntax/ast/identifier.ts +2 -3
  51. package/src/syntax/ast/qualified-name.ts +28 -19
  52. package/src/syntax/ast/type-annotation.ts +4 -5
  53. package/src/syntax/ast-helpers.ts +27 -3
  54. package/src/syntax/navigation.ts +55 -0
  55. package/src/syntax/red.ts +317 -42
  56. package/dist/declarations-D9h_ihD3.mjs.map +0 -1
  57. package/dist/parse-BjZ1LPe6.d.mts.map +0 -1
  58. package/dist/parse-DhEV6av6.mjs.map +0 -1
@@ -7,7 +7,13 @@ export {
7
7
  FieldAttributeAst,
8
8
  ModelAttributeAst,
9
9
  } from '../syntax/ast/attributes';
10
- export type { NamespaceMemberAst } from '../syntax/ast/declarations';
10
+ export type {
11
+ AttributeAst,
12
+ BlockMemberAst,
13
+ DeclarationAst,
14
+ GenericBlockMemberAst,
15
+ NamespaceMemberAst,
16
+ } from '../syntax/ast/declarations';
11
17
  export {
12
18
  CompositeTypeDeclarationAst,
13
19
  DocumentAst,
@@ -35,12 +41,26 @@ export {
35
41
  export { IdentifierAst } from '../syntax/ast/identifier';
36
42
  export { QualifiedNameAst } from '../syntax/ast/qualified-name';
37
43
  export { TypeAnnotationAst } from '../syntax/ast/type-annotation';
38
- export type { AstNode } from '../syntax/ast-helpers';
39
- export { filterChildren, findChildToken, findFirstChild, printSyntax } from '../syntax/ast-helpers';
44
+ export type { AstNode, BracedBlock } from '../syntax/ast-helpers';
45
+ export {
46
+ any,
47
+ filterChildren,
48
+ findChildToken,
49
+ findFirstChild,
50
+ printSyntax,
51
+ } from '../syntax/ast-helpers';
40
52
  export type { GreenElement, GreenNode, GreenToken } from '../syntax/green';
41
53
  export { greenNode, greenToken } from '../syntax/green';
42
54
  export { GreenNodeBuilder } from '../syntax/green-builder';
55
+ // Navigation helpers
56
+ export type { Direction } from '../syntax/navigation';
57
+ export {
58
+ isTrivia,
59
+ isTriviaKind,
60
+ nonTriviaSibling,
61
+ skipTriviaToken,
62
+ } from '../syntax/navigation';
43
63
  // Red layer
44
- export type { SyntaxElement, SyntaxToken } from '../syntax/red';
45
- export { createSyntaxTree, SyntaxNode } from '../syntax/red';
64
+ export type { SyntaxElement } from '../syntax/red';
65
+ export { createSyntaxTree, SyntaxNode, SyntaxToken, TokenAtOffset } from '../syntax/red';
46
66
  export type { SyntaxKind } from '../syntax/syntax-kind';
@@ -0,0 +1,40 @@
1
+ import type {
2
+ ContractSourceContext,
3
+ ContractSourceDiagnostic,
4
+ ContractSourceProvider,
5
+ PslContractSourceProvider,
6
+ } from '@prisma-next/config/config-types';
7
+ import type { SourceFile } from './source-file';
8
+ import type { SymbolTable } from './symbol-table';
9
+ import type { DocumentAst } from './syntax/ast/declarations';
10
+
11
+ /**
12
+ * Lets editor tooling that already parses incrementally (e.g. the language
13
+ * server) hand cached artifacts to the interpreter instead of forcing a
14
+ * disk re-parse.
15
+ */
16
+ export interface PslInterpretInput {
17
+ readonly document: DocumentAst;
18
+ readonly sourceFile: SourceFile;
19
+ readonly symbolTable: SymbolTable;
20
+ readonly sourceId: string;
21
+ }
22
+
23
+ /**
24
+ * Declared here — the authoring layer that owns `DocumentAst` / `SourceFile` /
25
+ * `SymbolTable` — because `@prisma-next/config` (core) cannot name authoring
26
+ * types.
27
+ */
28
+ export interface PslInterpretCapable extends PslContractSourceProvider {
29
+ interpret(
30
+ input: PslInterpretInput,
31
+ context: ContractSourceContext,
32
+ ): readonly ContractSourceDiagnostic[];
33
+ }
34
+
35
+ /** The single seam that narrows a contract source to the interpret capability. */
36
+ export function hasPslInterpreter(source: ContractSourceProvider): source is PslInterpretCapable {
37
+ return (
38
+ source.sourceFormat === 'psl' && 'interpret' in source && typeof source.interpret === 'function'
39
+ );
40
+ }
package/src/parse.ts CHANGED
@@ -399,14 +399,24 @@ function parseParenArgs(cursor: Cursor): void {
399
399
  }
400
400
  }
401
401
 
402
- export function parseAttributeArg(cursor: Cursor): GreenNode {
402
+ export function parseAttributeArg(cursor: Cursor): void {
403
+ const kind = cursor.peekKind();
404
+ if (
405
+ kind !== 'Ident' &&
406
+ kind !== 'StringLiteral' &&
407
+ kind !== 'NumberLiteral' &&
408
+ kind !== 'LBracket' &&
409
+ kind !== 'LBrace'
410
+ ) {
411
+ return;
412
+ }
403
413
  cursor.startNode('AttributeArg');
404
414
  if (cursor.peekKind() === 'Ident' && cursor.peekKind(1) === 'Colon') {
405
415
  parseIdentifier(cursor);
406
416
  cursor.bump();
407
417
  }
408
418
  parseArgValue(cursor);
409
- return cursor.finishNode();
419
+ cursor.finishNode();
410
420
  }
411
421
 
412
422
  function parseArgValue(cursor: Cursor): void {
@@ -435,8 +445,16 @@ export function parseAttribute(cursor: Cursor): GreenNode {
435
445
  return cursor.finishNode();
436
446
  }
437
447
 
438
- /** A type annotation: `QualifiedName (argList)? ([])? (?)?`, e.g. `pgvector.Vector(1536)[]?`. */
439
- export function parseTypeAnnotation(cursor: Cursor): GreenNode {
448
+ /**
449
+ * A type annotation: `QualifiedName (argList)? ([])? (?)?`, e.g.
450
+ * `pgvector.Vector(1536)[]?`. When the field has no type, no node is emitted —
451
+ * a missing type is the absence of a `TypeAnnotation`, not a zero-width one.
452
+ */
453
+ export function parseTypeAnnotation(cursor: Cursor): void {
454
+ const kind = cursor.peekKind();
455
+ if (kind !== 'Ident' && kind !== 'LBracket' && kind !== 'Question') {
456
+ return;
457
+ }
440
458
  cursor.startNode('TypeAnnotation');
441
459
  if (cursor.peekKind() === 'Ident') {
442
460
  parseQualifiedName(cursor);
@@ -453,7 +471,7 @@ export function parseTypeAnnotation(cursor: Cursor): GreenNode {
453
471
  if (cursor.peekKind() === 'Question') {
454
472
  cursor.bump();
455
473
  }
456
- return cursor.finishNode();
474
+ cursor.finishNode();
457
475
  }
458
476
 
459
477
  type MemberParser = (cursor: Cursor) => void;
package/src/resolve.ts CHANGED
@@ -15,6 +15,7 @@ export interface ResolvedAttributeArg {
15
15
  readonly kind: 'positional' | 'named';
16
16
  readonly name?: string;
17
17
  readonly value: string;
18
+ readonly expression?: ExpressionAst;
18
19
  readonly span: PslSpan;
19
20
  }
20
21
 
@@ -69,10 +70,12 @@ function readResolvedArgList(
69
70
  const args: ResolvedAttributeArg[] = [];
70
71
  for (const arg of argList.args()) {
71
72
  const name = arg.name()?.name();
73
+ const expression = arg.value();
72
74
  args.push({
73
75
  kind: name !== undefined ? 'named' : 'positional',
74
76
  ...(name !== undefined ? { name } : {}),
75
- value: renderExpression(arg.value()),
77
+ value: renderExpression(expression),
78
+ ...(expression !== undefined ? { expression } : {}),
76
79
  span: nodePslSpan(arg.syntax, sourceFile),
77
80
  });
78
81
  }
@@ -1,3 +1,4 @@
1
+ const CARRIAGE_RETURN = 13;
1
2
  const LINE_FEED = 10;
2
3
 
3
4
  export interface Position {
@@ -41,6 +42,30 @@ export class SourceFile {
41
42
  return this.#lineStarts;
42
43
  }
43
44
 
45
+ lineStartOffset(line: number): number {
46
+ if (line <= 0) {
47
+ return 0;
48
+ }
49
+ return this.#lineStarts[line] ?? this.#text.length;
50
+ }
51
+
52
+ lineEndOffset(line: number): number {
53
+ if (line < 0) {
54
+ return 0;
55
+ }
56
+
57
+ const nextLineStart = this.#lineStarts[line + 1];
58
+ if (nextLineStart === undefined) {
59
+ return this.#text.length;
60
+ }
61
+
62
+ const lineFeedOffset = nextLineStart - 1;
63
+ const carriageReturnOffset = lineFeedOffset - 1;
64
+ return this.#text.charCodeAt(carriageReturnOffset) === CARRIAGE_RETURN
65
+ ? carriageReturnOffset
66
+ : lineFeedOffset;
67
+ }
68
+
44
69
  positionAt(offset: number): Position {
45
70
  const clamped = clamp(offset, 0, this.#text.length);
46
71
  const line = this.#lineIndexAt(clamped);
@@ -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 type { SyntaxNode } from '../red';
3
+ import type { SyntaxNode, SyntaxToken } from '../red';
5
4
  import { AttributeArgAst } from './expressions';
6
5
  import { QualifiedNameAst } from './qualified-name';
7
6
 
@@ -12,11 +11,11 @@ export class AttributeArgListAst implements AstNode {
12
11
  this.syntax = syntax;
13
12
  }
14
13
 
15
- lparen(): Token | undefined {
14
+ lparen(): SyntaxToken | undefined {
16
15
  return findChildToken(this.syntax, 'LParen');
17
16
  }
18
17
 
19
- rparen(): Token | undefined {
18
+ rparen(): SyntaxToken | undefined {
20
19
  return findChildToken(this.syntax, 'RParen');
21
20
  }
22
21
 
@@ -36,7 +35,7 @@ export class FieldAttributeAst implements AstNode {
36
35
  this.syntax = syntax;
37
36
  }
38
37
 
39
- at(): Token | undefined {
38
+ at(): SyntaxToken | undefined {
40
39
  return findChildToken(this.syntax, 'At');
41
40
  }
42
41
 
@@ -60,7 +59,7 @@ export class ModelAttributeAst implements AstNode {
60
59
  this.syntax = syntax;
61
60
  }
62
61
 
63
- doubleAt(): Token | undefined {
62
+ doubleAt(): SyntaxToken | undefined {
64
63
  return findChildToken(this.syntax, 'DoubleAt');
65
64
  }
66
65
 
@@ -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).