@prisma-next/psl-parser 0.13.0 → 0.14.0-dev.10

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 (54) hide show
  1. package/README.md +28 -10
  2. package/dist/declarations-D9h_ihD3.mjs +820 -0
  3. package/dist/declarations-D9h_ihD3.mjs.map +1 -0
  4. package/dist/format.d.mts +19 -0
  5. package/dist/format.d.mts.map +1 -0
  6. package/dist/format.mjs +470 -0
  7. package/dist/format.mjs.map +1 -0
  8. package/dist/index.d.mts +136 -3
  9. package/dist/index.d.mts.map +1 -1
  10. package/dist/index.mjs +472 -2
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/parse-BjZ1LPe6.d.mts +349 -0
  13. package/dist/parse-BjZ1LPe6.d.mts.map +1 -0
  14. package/dist/parse-DhEV6av6.mjs +605 -0
  15. package/dist/parse-DhEV6av6.mjs.map +1 -0
  16. package/dist/syntax.d.mts +3 -272
  17. package/dist/syntax.d.mts.map +1 -1
  18. package/dist/syntax.mjs +3 -708
  19. package/dist/tokenizer-1hAHZzmp.mjs +228 -0
  20. package/dist/tokenizer-1hAHZzmp.mjs.map +1 -0
  21. package/dist/tokenizer.mjs +1 -190
  22. package/package.json +6 -6
  23. package/src/block-reconstruction.ts +139 -0
  24. package/src/exports/format.ts +3 -0
  25. package/src/exports/index.ts +40 -5
  26. package/src/exports/syntax.ts +9 -4
  27. package/src/extension-block.ts +107 -0
  28. package/src/format/emit.ts +603 -0
  29. package/src/format/error.ts +13 -0
  30. package/src/format/format.ts +13 -0
  31. package/src/format/options.ts +28 -0
  32. package/src/parse.ts +751 -0
  33. package/src/resolve.ts +120 -0
  34. package/src/source-file.ts +89 -0
  35. package/src/symbol-table.ts +446 -0
  36. package/src/syntax/ast/attributes.ts +5 -24
  37. package/src/syntax/ast/declarations.ts +14 -67
  38. package/src/syntax/ast/expressions.ts +187 -19
  39. package/src/syntax/ast/identifier.ts +4 -0
  40. package/src/syntax/ast/qualified-name.ts +87 -0
  41. package/src/syntax/ast/type-annotation.ts +11 -41
  42. package/src/syntax/ast-helpers.ts +12 -0
  43. package/src/syntax/syntax-kind.ts +8 -4
  44. package/src/tokenizer.ts +47 -7
  45. package/dist/parser-Cw_zV0M5.mjs +0 -1176
  46. package/dist/parser-Cw_zV0M5.mjs.map +0 -1
  47. package/dist/parser-Dfi3Wfdq.d.mts +0 -7
  48. package/dist/parser-Dfi3Wfdq.d.mts.map +0 -1
  49. package/dist/parser.d.mts +0 -2
  50. package/dist/parser.mjs +0 -2
  51. package/dist/syntax.mjs.map +0 -1
  52. package/dist/tokenizer.mjs.map +0 -1
  53. package/src/exports/parser.ts +0 -1
  54. package/src/parser.ts +0 -1713
@@ -0,0 +1,228 @@
1
+ //#region src/tokenizer.ts
2
+ var Tokenizer = class {
3
+ #source;
4
+ #pos;
5
+ #buffer;
6
+ constructor(source) {
7
+ this.#source = source;
8
+ this.#pos = 0;
9
+ this.#buffer = [];
10
+ }
11
+ next() {
12
+ const next = this.#buffer.shift();
13
+ if (next) return next;
14
+ return this.#scanNext();
15
+ }
16
+ peek(offset = 0) {
17
+ if (offset > this.#buffer.length) {
18
+ const last = this.#buffer.at(-1);
19
+ if (last?.kind === "Eof") return last;
20
+ }
21
+ const token = this.#buffer[offset];
22
+ if (token) return token;
23
+ while (this.#buffer.length <= offset) {
24
+ const token = this.#scanNext();
25
+ if (token.kind === "Eof") return token;
26
+ this.#buffer.push(token);
27
+ }
28
+ return this.#buffer[offset];
29
+ }
30
+ #scanNext() {
31
+ const token = scan(this.#source, this.#pos);
32
+ this.#pos += token.text.length;
33
+ return token;
34
+ }
35
+ };
36
+ function scan(source, pos) {
37
+ if (pos >= source.length) return {
38
+ kind: "Eof",
39
+ text: ""
40
+ };
41
+ return scanNewline(source, pos) ?? scanWhitespace(source, pos) ?? scanComment(source, pos) ?? scanAt(source, pos) ?? scanKeywordNumber(source, pos) ?? scanIdent(source, pos) ?? scanNumber(source, pos) ?? scanString(source, pos) ?? scanPunctuation(source, pos) ?? {
42
+ kind: "Invalid",
43
+ text: readChar(source, pos)
44
+ };
45
+ }
46
+ function scanNewline(source, pos) {
47
+ const ch = source.charAt(pos);
48
+ if (ch !== "\r" && ch !== "\n") return void 0;
49
+ if (ch === "\r" && source.charAt(pos + 1) === "\n") return {
50
+ kind: "Newline",
51
+ text: "\r\n"
52
+ };
53
+ return {
54
+ kind: "Newline",
55
+ text: ch
56
+ };
57
+ }
58
+ function scanWhitespace(source, pos) {
59
+ const ch = source.charAt(pos);
60
+ if (ch !== " " && ch !== " ") return void 0;
61
+ let end = pos + 1;
62
+ while (end < source.length) {
63
+ const c = source.charAt(end);
64
+ if (c !== " " && c !== " ") break;
65
+ end++;
66
+ }
67
+ return {
68
+ kind: "Whitespace",
69
+ text: source.slice(pos, end)
70
+ };
71
+ }
72
+ function scanComment(source, pos) {
73
+ if (source.charAt(pos) !== "/" || source.charAt(pos + 1) !== "/") return void 0;
74
+ let end = pos + 2;
75
+ while (end < source.length) {
76
+ const c = source.charAt(end);
77
+ if (c === "\n" || c === "\r") break;
78
+ end++;
79
+ }
80
+ return {
81
+ kind: "Comment",
82
+ text: source.slice(pos, end)
83
+ };
84
+ }
85
+ function scanAt(source, pos) {
86
+ if (source.charAt(pos) !== "@") return void 0;
87
+ if (source.charAt(pos + 1) === "@") return {
88
+ kind: "DoubleAt",
89
+ text: "@@"
90
+ };
91
+ return {
92
+ kind: "At",
93
+ text: "@"
94
+ };
95
+ }
96
+ function scanIdent(source, pos) {
97
+ const ch = readChar(source, pos);
98
+ if (!isIdentStart(ch)) return void 0;
99
+ let end = pos + ch.length;
100
+ while (end < source.length) {
101
+ const c = readChar(source, end);
102
+ if (isIdentPart(c)) end += c.length;
103
+ else break;
104
+ }
105
+ return {
106
+ kind: "Ident",
107
+ text: source.slice(pos, end)
108
+ };
109
+ }
110
+ const KEYWORD_NUMBERS = [
111
+ "-Infinity",
112
+ "Infinity",
113
+ "NaN"
114
+ ];
115
+ function scanKeywordNumber(source, pos) {
116
+ for (const word of KEYWORD_NUMBERS) {
117
+ if (!source.startsWith(word, pos)) continue;
118
+ const after = readChar(source, pos + word.length);
119
+ if (after !== "" && isIdentPart(after)) return void 0;
120
+ return {
121
+ kind: "NumberLiteral",
122
+ text: word
123
+ };
124
+ }
125
+ }
126
+ function scanNumber(source, pos) {
127
+ let end = pos;
128
+ if (source.charAt(end) === "-") {
129
+ if (end + 1 >= source.length || !isDigit(source.charAt(end + 1))) return void 0;
130
+ end++;
131
+ } else if (!isDigit(source.charAt(end))) return;
132
+ end++;
133
+ while (end < source.length && isDigit(source.charAt(end))) end++;
134
+ if (source.charAt(end) === "." && end + 1 < source.length && isDigit(source.charAt(end + 1))) {
135
+ end++;
136
+ while (end < source.length && isDigit(source.charAt(end))) end++;
137
+ }
138
+ return {
139
+ kind: "NumberLiteral",
140
+ text: source.slice(pos, end)
141
+ };
142
+ }
143
+ function scanString(source, pos) {
144
+ const quote = source.charAt(pos);
145
+ if (quote !== "\"" && quote !== "'") return void 0;
146
+ let end = pos + 1;
147
+ while (end < source.length) {
148
+ const c = source.charAt(end);
149
+ if (c === "\\" && end + 1 < source.length) {
150
+ end += 2;
151
+ continue;
152
+ }
153
+ if (c === quote) {
154
+ end++;
155
+ return {
156
+ kind: "StringLiteral",
157
+ text: source.slice(pos, end)
158
+ };
159
+ }
160
+ if (c === "\n" || c === "\r") return {
161
+ kind: "StringLiteral",
162
+ text: source.slice(pos, end)
163
+ };
164
+ end++;
165
+ }
166
+ return {
167
+ kind: "StringLiteral",
168
+ text: source.slice(pos, end)
169
+ };
170
+ }
171
+ /**
172
+ * `scanString` emits the same `StringLiteral` kind for well-formed and
173
+ * unterminated strings, so callers ask here to tell them apart.
174
+ *
175
+ * The closing quote is unescaped iff an **even** number of backslashes precede
176
+ * it (each `\\` pair cancels; an odd run leaves the final `\` escaping the
177
+ * quote), so counting the trailing backslash run suffices — no full re-scan:
178
+ *
179
+ * - `"ok"` → 0 backslashes (even) → terminated
180
+ * - `'a\'` → 1 backslash (odd) → the quote is escaped → unterminated
181
+ * - `"a\\"` → 2 backslashes (even) → escaped `\`, real `"` → terminated
182
+ */
183
+ function isTerminatedStringLiteral(text) {
184
+ const quote = text.charAt(0);
185
+ if (quote !== "\"" && quote !== "'") return false;
186
+ if (text.length < 2 || text.charAt(text.length - 1) !== quote) return false;
187
+ let backslashes = 0;
188
+ for (let i = text.length - 2; i >= 1 && text.charAt(i) === "\\"; i--) backslashes++;
189
+ return backslashes % 2 === 0;
190
+ }
191
+ function scanPunctuation(source, pos) {
192
+ const kind = PUNCTUATION[source.charAt(pos)];
193
+ if (kind === void 0) return void 0;
194
+ return {
195
+ kind,
196
+ text: source.charAt(pos)
197
+ };
198
+ }
199
+ function readChar(source, pos) {
200
+ const cp = source.codePointAt(pos);
201
+ return cp !== void 0 ? String.fromCodePoint(cp) : "";
202
+ }
203
+ function isIdentStart(ch) {
204
+ return /\p{L}/u.test(ch) || ch === "_";
205
+ }
206
+ function isIdentPart(ch) {
207
+ return isIdentStart(ch) || isDigit(ch) || ch === "-";
208
+ }
209
+ function isDigit(ch) {
210
+ return ch >= "0" && ch <= "9";
211
+ }
212
+ const PUNCTUATION = {
213
+ "{": "LBrace",
214
+ "}": "RBrace",
215
+ "(": "LParen",
216
+ ")": "RParen",
217
+ "[": "LBracket",
218
+ "]": "RBracket",
219
+ "=": "Equals",
220
+ "?": "Question",
221
+ ".": "Dot",
222
+ ",": "Comma",
223
+ ":": "Colon"
224
+ };
225
+ //#endregion
226
+ export { isTerminatedStringLiteral as n, Tokenizer as t };
227
+
228
+ //# sourceMappingURL=tokenizer-1hAHZzmp.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenizer-1hAHZzmp.mjs","names":["#source","#buffer","#pos","#scanNext"],"sources":["../src/tokenizer.ts"],"sourcesContent":["export type TokenKind =\n | 'Ident'\n | 'StringLiteral'\n | 'NumberLiteral'\n | 'At'\n | 'DoubleAt'\n | 'LBrace'\n | 'RBrace'\n | 'LParen'\n | 'RParen'\n | 'LBracket'\n | 'RBracket'\n | 'Equals'\n | 'Question'\n | 'Dot'\n | 'Comma'\n | 'Colon'\n | 'Whitespace'\n | 'Newline'\n | 'Comment'\n | 'Invalid'\n | 'Eof';\n\nexport interface Token {\n readonly kind: TokenKind;\n readonly text: string;\n}\n\nexport class Tokenizer {\n readonly #source: string;\n #pos: number;\n readonly #buffer: Token[];\n\n constructor(source: string) {\n this.#source = source;\n this.#pos = 0;\n this.#buffer = [];\n }\n\n next(): Token {\n const next = this.#buffer.shift();\n if (next) {\n return next;\n }\n return this.#scanNext();\n }\n\n peek(offset = 0): Token {\n if (offset > this.#buffer.length) {\n const last = this.#buffer.at(-1);\n if (last?.kind === 'Eof') {\n return last;\n }\n }\n\n const token = this.#buffer[offset];\n if (token) {\n return token;\n }\n\n while (this.#buffer.length <= offset) {\n const token = this.#scanNext();\n if (token.kind === 'Eof') {\n return token;\n }\n this.#buffer.push(token);\n }\n\n return this.#buffer[offset] as Token;\n }\n\n #scanNext(): Token {\n const token = scan(this.#source, this.#pos);\n this.#pos += token.text.length;\n return token;\n }\n}\n\nfunction scan(source: string, pos: number): Token {\n if (pos >= source.length) {\n return { kind: 'Eof', text: '' };\n }\n\n return (\n scanNewline(source, pos) ??\n scanWhitespace(source, pos) ??\n scanComment(source, pos) ??\n scanAt(source, pos) ??\n scanKeywordNumber(source, pos) ??\n scanIdent(source, pos) ??\n scanNumber(source, pos) ??\n scanString(source, pos) ??\n scanPunctuation(source, pos) ?? {\n kind: 'Invalid' as const,\n text: readChar(source, pos),\n }\n );\n}\n\nfunction scanNewline(source: string, pos: number): Token | undefined {\n const ch = source.charAt(pos);\n if (ch !== '\\r' && ch !== '\\n') return undefined;\n if (ch === '\\r' && source.charAt(pos + 1) === '\\n') {\n return { kind: 'Newline', text: '\\r\\n' };\n }\n return { kind: 'Newline', text: ch };\n}\n\nfunction scanWhitespace(source: string, pos: number): Token | undefined {\n const ch = source.charAt(pos);\n if (ch !== ' ' && ch !== '\\t') return undefined;\n let end = pos + 1;\n while (end < source.length) {\n const c = source.charAt(end);\n if (c !== ' ' && c !== '\\t') break;\n end++;\n }\n return { kind: 'Whitespace', text: source.slice(pos, end) };\n}\n\nfunction scanComment(source: string, pos: number): Token | undefined {\n if (source.charAt(pos) !== '/' || source.charAt(pos + 1) !== '/') return undefined;\n let end = pos + 2;\n while (end < source.length) {\n const c = source.charAt(end);\n if (c === '\\n' || c === '\\r') break;\n end++;\n }\n return { kind: 'Comment', text: source.slice(pos, end) };\n}\n\nfunction scanAt(source: string, pos: number): Token | undefined {\n if (source.charAt(pos) !== '@') return undefined;\n if (source.charAt(pos + 1) === '@') {\n return { kind: 'DoubleAt', text: '@@' };\n }\n return { kind: 'At', text: '@' };\n}\n\nfunction scanIdent(source: string, pos: number): Token | undefined {\n const ch = readChar(source, pos);\n if (!isIdentStart(ch)) return undefined;\n let end = pos + ch.length;\n while (end < source.length) {\n const c = readChar(source, end);\n if (isIdentPart(c)) {\n end += c.length;\n } else {\n break;\n }\n }\n return { kind: 'Ident', text: source.slice(pos, end) };\n}\n\nconst KEYWORD_NUMBERS = ['-Infinity', 'Infinity', 'NaN'] as const;\n\n// `NaN`, `Infinity`, and `-Infinity` are numeric literals; they must match\n// before `scanIdent` and are word-bounded, so `Infinityx` stays an `Ident`.\nfunction scanKeywordNumber(source: string, pos: number): Token | undefined {\n for (const word of KEYWORD_NUMBERS) {\n if (!source.startsWith(word, pos)) continue;\n const after = readChar(source, pos + word.length);\n if (after !== '' && isIdentPart(after)) return undefined;\n return { kind: 'NumberLiteral', text: word };\n }\n return undefined;\n}\n\nfunction scanNumber(source: string, pos: number): Token | undefined {\n let end = pos;\n if (source.charAt(end) === '-') {\n if (end + 1 >= source.length || !isDigit(source.charAt(end + 1))) return undefined;\n end++;\n } else if (!isDigit(source.charAt(end))) {\n return undefined;\n }\n end++;\n while (end < source.length && isDigit(source.charAt(end))) {\n end++;\n }\n if (source.charAt(end) === '.' && end + 1 < source.length && isDigit(source.charAt(end + 1))) {\n end++;\n while (end < source.length && isDigit(source.charAt(end))) {\n end++;\n }\n }\n return { kind: 'NumberLiteral', text: source.slice(pos, end) };\n}\n\nfunction scanString(source: string, pos: number): Token | undefined {\n const quote = source.charAt(pos);\n if (quote !== '\"' && quote !== \"'\") return undefined;\n let end = pos + 1;\n while (end < source.length) {\n const c = source.charAt(end);\n if (c === '\\\\' && end + 1 < source.length) {\n end += 2;\n continue;\n }\n if (c === quote) {\n end++;\n return { kind: 'StringLiteral', text: source.slice(pos, end) };\n }\n if (c === '\\n' || c === '\\r') {\n // Unterminated string: stop before the newline.\n return { kind: 'StringLiteral', text: source.slice(pos, end) };\n }\n end++;\n }\n return { kind: 'StringLiteral', text: source.slice(pos, end) };\n}\n\n/**\n * `scanString` emits the same `StringLiteral` kind for well-formed and\n * unterminated strings, so callers ask here to tell them apart.\n *\n * The closing quote is unescaped iff an **even** number of backslashes precede\n * it (each `\\\\` pair cancels; an odd run leaves the final `\\` escaping the\n * quote), so counting the trailing backslash run suffices — no full re-scan:\n *\n * - `\"ok\"` → 0 backslashes (even) → terminated\n * - `'a\\'` → 1 backslash (odd) → the quote is escaped → unterminated\n * - `\"a\\\\\"` → 2 backslashes (even) → escaped `\\`, real `\"` → terminated\n */\nexport function isTerminatedStringLiteral(text: string): boolean {\n const quote = text.charAt(0);\n if (quote !== '\"' && quote !== \"'\") return false;\n if (text.length < 2 || text.charAt(text.length - 1) !== quote) {\n return false;\n }\n let backslashes = 0;\n for (let i = text.length - 2; i >= 1 && text.charAt(i) === '\\\\'; i--) {\n backslashes++;\n }\n return backslashes % 2 === 0;\n}\n\nfunction scanPunctuation(source: string, pos: number): Token | undefined {\n const kind = PUNCTUATION[source.charAt(pos)];\n if (kind === undefined) return undefined;\n return { kind, text: source.charAt(pos) };\n}\n\nfunction readChar(source: string, pos: number): string {\n const cp = source.codePointAt(pos);\n return cp !== undefined ? String.fromCodePoint(cp) : '';\n}\n\nfunction isIdentStart(ch: string): boolean {\n return /\\p{L}/u.test(ch) || ch === '_';\n}\n\nfunction isIdentPart(ch: string): boolean {\n return isIdentStart(ch) || isDigit(ch) || ch === '-';\n}\n\nfunction isDigit(ch: string): boolean {\n return ch >= '0' && ch <= '9';\n}\n\nconst PUNCTUATION: Record<string, TokenKind> = {\n '{': 'LBrace',\n '}': 'RBrace',\n '(': 'LParen',\n ')': 'RParen',\n '[': 'LBracket',\n ']': 'RBracket',\n '=': 'Equals',\n '?': 'Question',\n '.': 'Dot',\n ',': 'Comma',\n ':': 'Colon',\n};\n"],"mappings":";AA4BA,IAAa,YAAb,MAAuB;CACrB;CACA;CACA;CAEA,YAAY,QAAgB;EAC1B,KAAKA,UAAU;EACf,KAAKE,OAAO;EACZ,KAAKD,UAAU,CAAC;CAClB;CAEA,OAAc;EACZ,MAAM,OAAO,KAAKA,QAAQ,MAAM;EAChC,IAAI,MACF,OAAO;EAET,OAAO,KAAKE,UAAU;CACxB;CAEA,KAAK,SAAS,GAAU;EACtB,IAAI,SAAS,KAAKF,QAAQ,QAAQ;GAChC,MAAM,OAAO,KAAKA,QAAQ,GAAG,EAAE;GAC/B,IAAI,MAAM,SAAS,OACjB,OAAO;EAEX;EAEA,MAAM,QAAQ,KAAKA,QAAQ;EAC3B,IAAI,OACF,OAAO;EAGT,OAAO,KAAKA,QAAQ,UAAU,QAAQ;GACpC,MAAM,QAAQ,KAAKE,UAAU;GAC7B,IAAI,MAAM,SAAS,OACjB,OAAO;GAET,KAAKF,QAAQ,KAAK,KAAK;EACzB;EAEA,OAAO,KAAKA,QAAQ;CACtB;CAEA,YAAmB;EACjB,MAAM,QAAQ,KAAK,KAAKD,SAAS,KAAKE,IAAI;EAC1C,KAAKA,QAAQ,MAAM,KAAK;EACxB,OAAO;CACT;AACF;AAEA,SAAS,KAAK,QAAgB,KAAoB;CAChD,IAAI,OAAO,OAAO,QAChB,OAAO;EAAE,MAAM;EAAO,MAAM;CAAG;CAGjC,OACE,YAAY,QAAQ,GAAG,KACvB,eAAe,QAAQ,GAAG,KAC1B,YAAY,QAAQ,GAAG,KACvB,OAAO,QAAQ,GAAG,KAClB,kBAAkB,QAAQ,GAAG,KAC7B,UAAU,QAAQ,GAAG,KACrB,WAAW,QAAQ,GAAG,KACtB,WAAW,QAAQ,GAAG,KACtB,gBAAgB,QAAQ,GAAG,KAAK;EAC9B,MAAM;EACN,MAAM,SAAS,QAAQ,GAAG;CAC5B;AAEJ;AAEA,SAAS,YAAY,QAAgB,KAAgC;CACnE,MAAM,KAAK,OAAO,OAAO,GAAG;CAC5B,IAAI,OAAO,QAAQ,OAAO,MAAM,OAAO,KAAA;CACvC,IAAI,OAAO,QAAQ,OAAO,OAAO,MAAM,CAAC,MAAM,MAC5C,OAAO;EAAE,MAAM;EAAW,MAAM;CAAO;CAEzC,OAAO;EAAE,MAAM;EAAW,MAAM;CAAG;AACrC;AAEA,SAAS,eAAe,QAAgB,KAAgC;CACtE,MAAM,KAAK,OAAO,OAAO,GAAG;CAC5B,IAAI,OAAO,OAAO,OAAO,KAAM,OAAO,KAAA;CACtC,IAAI,MAAM,MAAM;CAChB,OAAO,MAAM,OAAO,QAAQ;EAC1B,MAAM,IAAI,OAAO,OAAO,GAAG;EAC3B,IAAI,MAAM,OAAO,MAAM,KAAM;EAC7B;CACF;CACA,OAAO;EAAE,MAAM;EAAc,MAAM,OAAO,MAAM,KAAK,GAAG;CAAE;AAC5D;AAEA,SAAS,YAAY,QAAgB,KAAgC;CACnE,IAAI,OAAO,OAAO,GAAG,MAAM,OAAO,OAAO,OAAO,MAAM,CAAC,MAAM,KAAK,OAAO,KAAA;CACzE,IAAI,MAAM,MAAM;CAChB,OAAO,MAAM,OAAO,QAAQ;EAC1B,MAAM,IAAI,OAAO,OAAO,GAAG;EAC3B,IAAI,MAAM,QAAQ,MAAM,MAAM;EAC9B;CACF;CACA,OAAO;EAAE,MAAM;EAAW,MAAM,OAAO,MAAM,KAAK,GAAG;CAAE;AACzD;AAEA,SAAS,OAAO,QAAgB,KAAgC;CAC9D,IAAI,OAAO,OAAO,GAAG,MAAM,KAAK,OAAO,KAAA;CACvC,IAAI,OAAO,OAAO,MAAM,CAAC,MAAM,KAC7B,OAAO;EAAE,MAAM;EAAY,MAAM;CAAK;CAExC,OAAO;EAAE,MAAM;EAAM,MAAM;CAAI;AACjC;AAEA,SAAS,UAAU,QAAgB,KAAgC;CACjE,MAAM,KAAK,SAAS,QAAQ,GAAG;CAC/B,IAAI,CAAC,aAAa,EAAE,GAAG,OAAO,KAAA;CAC9B,IAAI,MAAM,MAAM,GAAG;CACnB,OAAO,MAAM,OAAO,QAAQ;EAC1B,MAAM,IAAI,SAAS,QAAQ,GAAG;EAC9B,IAAI,YAAY,CAAC,GACf,OAAO,EAAE;OAET;CAEJ;CACA,OAAO;EAAE,MAAM;EAAS,MAAM,OAAO,MAAM,KAAK,GAAG;CAAE;AACvD;AAEA,MAAM,kBAAkB;CAAC;CAAa;CAAY;AAAK;AAIvD,SAAS,kBAAkB,QAAgB,KAAgC;CACzE,KAAK,MAAM,QAAQ,iBAAiB;EAClC,IAAI,CAAC,OAAO,WAAW,MAAM,GAAG,GAAG;EACnC,MAAM,QAAQ,SAAS,QAAQ,MAAM,KAAK,MAAM;EAChD,IAAI,UAAU,MAAM,YAAY,KAAK,GAAG,OAAO,KAAA;EAC/C,OAAO;GAAE,MAAM;GAAiB,MAAM;EAAK;CAC7C;AAEF;AAEA,SAAS,WAAW,QAAgB,KAAgC;CAClE,IAAI,MAAM;CACV,IAAI,OAAO,OAAO,GAAG,MAAM,KAAK;EAC9B,IAAI,MAAM,KAAK,OAAO,UAAU,CAAC,QAAQ,OAAO,OAAO,MAAM,CAAC,CAAC,GAAG,OAAO,KAAA;EACzE;CACF,OAAO,IAAI,CAAC,QAAQ,OAAO,OAAO,GAAG,CAAC,GACpC;CAEF;CACA,OAAO,MAAM,OAAO,UAAU,QAAQ,OAAO,OAAO,GAAG,CAAC,GACtD;CAEF,IAAI,OAAO,OAAO,GAAG,MAAM,OAAO,MAAM,IAAI,OAAO,UAAU,QAAQ,OAAO,OAAO,MAAM,CAAC,CAAC,GAAG;EAC5F;EACA,OAAO,MAAM,OAAO,UAAU,QAAQ,OAAO,OAAO,GAAG,CAAC,GACtD;CAEJ;CACA,OAAO;EAAE,MAAM;EAAiB,MAAM,OAAO,MAAM,KAAK,GAAG;CAAE;AAC/D;AAEA,SAAS,WAAW,QAAgB,KAAgC;CAClE,MAAM,QAAQ,OAAO,OAAO,GAAG;CAC/B,IAAI,UAAU,QAAO,UAAU,KAAK,OAAO,KAAA;CAC3C,IAAI,MAAM,MAAM;CAChB,OAAO,MAAM,OAAO,QAAQ;EAC1B,MAAM,IAAI,OAAO,OAAO,GAAG;EAC3B,IAAI,MAAM,QAAQ,MAAM,IAAI,OAAO,QAAQ;GACzC,OAAO;GACP;EACF;EACA,IAAI,MAAM,OAAO;GACf;GACA,OAAO;IAAE,MAAM;IAAiB,MAAM,OAAO,MAAM,KAAK,GAAG;GAAE;EAC/D;EACA,IAAI,MAAM,QAAQ,MAAM,MAEtB,OAAO;GAAE,MAAM;GAAiB,MAAM,OAAO,MAAM,KAAK,GAAG;EAAE;EAE/D;CACF;CACA,OAAO;EAAE,MAAM;EAAiB,MAAM,OAAO,MAAM,KAAK,GAAG;CAAE;AAC/D;;;;;;;;;;;;;AAcA,SAAgB,0BAA0B,MAAuB;CAC/D,MAAM,QAAQ,KAAK,OAAO,CAAC;CAC3B,IAAI,UAAU,QAAO,UAAU,KAAK,OAAO;CAC3C,IAAI,KAAK,SAAS,KAAK,KAAK,OAAO,KAAK,SAAS,CAAC,MAAM,OACtD,OAAO;CAET,IAAI,cAAc;CAClB,KAAK,IAAI,IAAI,KAAK,SAAS,GAAG,KAAK,KAAK,KAAK,OAAO,CAAC,MAAM,MAAM,KAC/D;CAEF,OAAO,cAAc,MAAM;AAC7B;AAEA,SAAS,gBAAgB,QAAgB,KAAgC;CACvE,MAAM,OAAO,YAAY,OAAO,OAAO,GAAG;CAC1C,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;CAC/B,OAAO;EAAE;EAAM,MAAM,OAAO,OAAO,GAAG;CAAE;AAC1C;AAEA,SAAS,SAAS,QAAgB,KAAqB;CACrD,MAAM,KAAK,OAAO,YAAY,GAAG;CACjC,OAAO,OAAO,KAAA,IAAY,OAAO,cAAc,EAAE,IAAI;AACvD;AAEA,SAAS,aAAa,IAAqB;CACzC,OAAO,SAAS,KAAK,EAAE,KAAK,OAAO;AACrC;AAEA,SAAS,YAAY,IAAqB;CACxC,OAAO,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,OAAO;AACnD;AAEA,SAAS,QAAQ,IAAqB;CACpC,OAAO,MAAM,OAAO,MAAM;AAC5B;AAEA,MAAM,cAAyC;CAC7C,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;AACP"}
@@ -1,191 +1,2 @@
1
- //#region src/tokenizer.ts
2
- var Tokenizer = class {
3
- #source;
4
- #pos;
5
- #buffer;
6
- constructor(source) {
7
- this.#source = source;
8
- this.#pos = 0;
9
- this.#buffer = [];
10
- }
11
- next() {
12
- const next = this.#buffer.shift();
13
- if (next) return next;
14
- return this.#scanNext();
15
- }
16
- peek(offset = 0) {
17
- if (offset > this.#buffer.length) {
18
- const last = this.#buffer.at(-1);
19
- if (last?.kind === "Eof") return last;
20
- }
21
- const token = this.#buffer[offset];
22
- if (token) return token;
23
- while (this.#buffer.length <= offset) {
24
- const token = this.#scanNext();
25
- if (token.kind === "Eof") return token;
26
- this.#buffer.push(token);
27
- }
28
- return this.#buffer[offset];
29
- }
30
- #scanNext() {
31
- const token = scan(this.#source, this.#pos);
32
- this.#pos += token.text.length;
33
- return token;
34
- }
35
- };
36
- function scan(source, pos) {
37
- if (pos >= source.length) return {
38
- kind: "Eof",
39
- text: ""
40
- };
41
- return scanNewline(source, pos) ?? scanWhitespace(source, pos) ?? scanComment(source, pos) ?? scanAt(source, pos) ?? scanIdent(source, pos) ?? scanNumber(source, pos) ?? scanString(source, pos) ?? scanPunctuation(source, pos) ?? {
42
- kind: "Invalid",
43
- text: readChar(source, pos)
44
- };
45
- }
46
- function scanNewline(source, pos) {
47
- const ch = source.charAt(pos);
48
- if (ch !== "\r" && ch !== "\n") return void 0;
49
- if (ch === "\r" && source.charAt(pos + 1) === "\n") return {
50
- kind: "Newline",
51
- text: "\r\n"
52
- };
53
- return {
54
- kind: "Newline",
55
- text: ch
56
- };
57
- }
58
- function scanWhitespace(source, pos) {
59
- const ch = source.charAt(pos);
60
- if (ch !== " " && ch !== " ") return void 0;
61
- let end = pos + 1;
62
- while (end < source.length) {
63
- const c = source.charAt(end);
64
- if (c !== " " && c !== " ") break;
65
- end++;
66
- }
67
- return {
68
- kind: "Whitespace",
69
- text: source.slice(pos, end)
70
- };
71
- }
72
- function scanComment(source, pos) {
73
- if (source.charAt(pos) !== "/" || source.charAt(pos + 1) !== "/") return void 0;
74
- let end = pos + 2;
75
- while (end < source.length) {
76
- const c = source.charAt(end);
77
- if (c === "\n" || c === "\r") break;
78
- end++;
79
- }
80
- return {
81
- kind: "Comment",
82
- text: source.slice(pos, end)
83
- };
84
- }
85
- function scanAt(source, pos) {
86
- if (source.charAt(pos) !== "@") return void 0;
87
- if (source.charAt(pos + 1) === "@") return {
88
- kind: "DoubleAt",
89
- text: "@@"
90
- };
91
- return {
92
- kind: "At",
93
- text: "@"
94
- };
95
- }
96
- function scanIdent(source, pos) {
97
- const ch = readChar(source, pos);
98
- if (!isIdentStart(ch)) return void 0;
99
- let end = pos + ch.length;
100
- while (end < source.length) {
101
- const c = readChar(source, end);
102
- if (isIdentPart(c)) end += c.length;
103
- else break;
104
- }
105
- return {
106
- kind: "Ident",
107
- text: source.slice(pos, end)
108
- };
109
- }
110
- function scanNumber(source, pos) {
111
- let end = pos;
112
- if (source.charAt(end) === "-") {
113
- if (end + 1 >= source.length || !isDigit(source.charAt(end + 1))) return void 0;
114
- end++;
115
- } else if (!isDigit(source.charAt(end))) return;
116
- end++;
117
- while (end < source.length && isDigit(source.charAt(end))) end++;
118
- if (source.charAt(end) === "." && end + 1 < source.length && isDigit(source.charAt(end + 1))) {
119
- end++;
120
- while (end < source.length && isDigit(source.charAt(end))) end++;
121
- }
122
- return {
123
- kind: "NumberLiteral",
124
- text: source.slice(pos, end)
125
- };
126
- }
127
- function scanString(source, pos) {
128
- if (source.charAt(pos) !== "\"") return void 0;
129
- let end = pos + 1;
130
- while (end < source.length) {
131
- const c = source.charAt(end);
132
- if (c === "\\" && end + 1 < source.length) {
133
- end += 2;
134
- continue;
135
- }
136
- if (c === "\"") {
137
- end++;
138
- return {
139
- kind: "StringLiteral",
140
- text: source.slice(pos, end)
141
- };
142
- }
143
- if (c === "\n" || c === "\r") return {
144
- kind: "StringLiteral",
145
- text: source.slice(pos, end)
146
- };
147
- end++;
148
- }
149
- return {
150
- kind: "StringLiteral",
151
- text: source.slice(pos, end)
152
- };
153
- }
154
- function scanPunctuation(source, pos) {
155
- const kind = PUNCTUATION[source.charAt(pos)];
156
- if (kind === void 0) return void 0;
157
- return {
158
- kind,
159
- text: source.charAt(pos)
160
- };
161
- }
162
- function readChar(source, pos) {
163
- const cp = source.codePointAt(pos);
164
- return cp !== void 0 ? String.fromCodePoint(cp) : "";
165
- }
166
- function isIdentStart(ch) {
167
- return /\p{L}/u.test(ch) || ch === "_";
168
- }
169
- function isIdentPart(ch) {
170
- return isIdentStart(ch) || isDigit(ch) || ch === "-";
171
- }
172
- function isDigit(ch) {
173
- return ch >= "0" && ch <= "9";
174
- }
175
- const PUNCTUATION = {
176
- "{": "LBrace",
177
- "}": "RBrace",
178
- "(": "LParen",
179
- ")": "RParen",
180
- "[": "LBracket",
181
- "]": "RBracket",
182
- "=": "Equals",
183
- "?": "Question",
184
- ".": "Dot",
185
- ",": "Comma",
186
- ":": "Colon"
187
- };
188
- //#endregion
1
+ import { t as Tokenizer } from "./tokenizer-1hAHZzmp.mjs";
189
2
  export { Tokenizer };
190
-
191
- //# sourceMappingURL=tokenizer.mjs.map
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@prisma-next/psl-parser",
3
- "version": "0.13.0",
3
+ "version": "0.14.0-dev.10",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "Reusable parser for Prisma Schema Language (PSL)",
8
8
  "dependencies": {
9
- "@prisma-next/framework-components": "0.13.0",
10
- "@prisma-next/utils": "0.13.0"
9
+ "@prisma-next/framework-components": "0.14.0-dev.10",
10
+ "@prisma-next/utils": "0.14.0-dev.10"
11
11
  },
12
12
  "devDependencies": {
13
- "@prisma-next/tsconfig": "0.13.0",
14
- "@prisma-next/tsdown": "0.13.0",
13
+ "@prisma-next/tsconfig": "0.14.0-dev.10",
14
+ "@prisma-next/tsdown": "0.14.0-dev.10",
15
15
  "tsdown": "0.22.1",
16
16
  "typescript": "5.9.3",
17
17
  "vitest": "4.1.8"
@@ -31,7 +31,7 @@
31
31
  "types": "./dist/index.d.mts",
32
32
  "exports": {
33
33
  ".": "./dist/index.mjs",
34
- "./parser": "./dist/parser.mjs",
34
+ "./format": "./dist/format.mjs",
35
35
  "./syntax": "./dist/syntax.mjs",
36
36
  "./tokenizer": "./dist/tokenizer.mjs",
37
37
  "./package.json": "./package.json"
@@ -0,0 +1,139 @@
1
+ import type { AuthoringPslBlockDescriptor } from '@prisma-next/framework-components/authoring';
2
+ import type {
3
+ PslBlockParam,
4
+ PslExtensionBlock,
5
+ PslExtensionBlockAttribute,
6
+ PslExtensionBlockParamValue,
7
+ PslSpan,
8
+ } from '@prisma-next/framework-components/psl-ast';
9
+ import type { ParseDiagnostic } from './parse';
10
+ import { nodePslSpan } from './resolve';
11
+ import type { SourceFile } from './source-file';
12
+ import type { GenericBlockDeclarationAst, KeyValuePairAst } from './syntax/ast/declarations';
13
+ import { ArrayLiteralAst, type ExpressionAst } from './syntax/ast/expressions';
14
+ import { printSyntax } from './syntax/ast-helpers';
15
+
16
+ /**
17
+ * Descriptor-free and unknown parameters become `value` stubs so validation can
18
+ * report them via key-set comparison. Duplicate member names are first-wins.
19
+ */
20
+ export function reconstructExtensionBlock(
21
+ node: GenericBlockDeclarationAst,
22
+ descriptor: AuthoringPslBlockDescriptor | undefined,
23
+ sourceFile: SourceFile,
24
+ diagnostics: ParseDiagnostic[],
25
+ ): PslExtensionBlock {
26
+ const keyword = node.keyword()?.text ?? '';
27
+ const blockName = node.name()?.name() ?? '';
28
+
29
+ const blockAttributes: PslExtensionBlockAttribute[] = [];
30
+ for (const attribute of node.attributes()) {
31
+ const name = attribute.name()?.path().join('.') ?? '';
32
+ const args = Array.from(attribute.argList()?.args() ?? [], (arg) => {
33
+ const value = arg.value();
34
+ return {
35
+ kind: 'positional' as const,
36
+ value: value === undefined ? '' : printSyntax(value.syntax).trim(),
37
+ span: nodePslSpan(arg.syntax, sourceFile),
38
+ };
39
+ });
40
+ blockAttributes.push({
41
+ name,
42
+ args,
43
+ span: nodePslSpan(attribute.syntax, sourceFile),
44
+ });
45
+ }
46
+
47
+ const parameters: Record<string, PslExtensionBlockParamValue> = {};
48
+ for (const entry of node.entries()) {
49
+ const key = entry.key()?.name();
50
+ if (key === undefined) continue;
51
+ const span = nodePslSpan(entry.syntax, sourceFile);
52
+ if (Object.hasOwn(parameters, key)) {
53
+ diagnostics.push({
54
+ code: 'PSL_EXTENSION_DUPLICATE_PARAMETER',
55
+ message: `Duplicate parameter "${key}" in "${keyword}" block "${blockName}"; first occurrence wins`,
56
+ range: {
57
+ start: sourceFile.positionAt(entry.syntax.offset),
58
+ end: sourceFile.positionAt(entry.syntax.offset + entry.syntax.green.textLength),
59
+ },
60
+ });
61
+ continue;
62
+ }
63
+ parameters[key] = reconstructParamValue(
64
+ entry,
65
+ descriptor?.parameters[key],
66
+ span,
67
+ sourceFile,
68
+ diagnostics,
69
+ );
70
+ }
71
+
72
+ return {
73
+ kind: descriptor?.discriminator ?? keyword,
74
+ name: blockName,
75
+ parameters,
76
+ blockAttributes,
77
+ span: nodePslSpan(node.syntax, sourceFile),
78
+ };
79
+ }
80
+
81
+ function reconstructParamValue(
82
+ entry: KeyValuePairAst,
83
+ param: PslBlockParam | undefined,
84
+ span: PslSpan,
85
+ sourceFile: SourceFile,
86
+ diagnostics: ParseDiagnostic[],
87
+ ): PslExtensionBlockParamValue {
88
+ const value = entry.value();
89
+ if (value === undefined) {
90
+ return { kind: 'bare', span };
91
+ }
92
+ return reconstructFromExpression(value, param, span, sourceFile, diagnostics);
93
+ }
94
+
95
+ function reconstructFromExpression(
96
+ value: ExpressionAst,
97
+ param: PslBlockParam | undefined,
98
+ span: PslSpan,
99
+ sourceFile: SourceFile,
100
+ diagnostics?: ParseDiagnostic[],
101
+ ): PslExtensionBlockParamValue {
102
+ const raw = printSyntax(value.syntax).trim();
103
+ if (param?.kind === 'list') {
104
+ const array = ArrayLiteralAst.cast(value.syntax);
105
+ if (!array) {
106
+ diagnostics?.push({
107
+ code: 'PSL_EXTENSION_INVALID_VALUE',
108
+ message: `List parameter expects an array literal, got ${raw}`,
109
+ range: {
110
+ start: sourceFile.positionAt(value.syntax.offset),
111
+ end: sourceFile.positionAt(value.syntax.offset + value.syntax.green.textLength),
112
+ },
113
+ });
114
+ return { kind: 'value', raw, span };
115
+ }
116
+
117
+ const items: PslExtensionBlockParamValue[] = [];
118
+ for (const element of array.elements()) {
119
+ items.push(
120
+ reconstructFromExpression(
121
+ element,
122
+ param.of,
123
+ nodePslSpan(element.syntax, sourceFile),
124
+ sourceFile,
125
+ diagnostics,
126
+ ),
127
+ );
128
+ }
129
+ return { kind: 'list', items, span };
130
+ }
131
+ switch (param?.kind) {
132
+ case 'ref':
133
+ return { kind: 'ref', identifier: raw, span };
134
+ case 'option':
135
+ return { kind: 'option', token: raw, span };
136
+ default:
137
+ return { kind: 'value', raw, span };
138
+ }
139
+ }
@@ -0,0 +1,3 @@
1
+ export { PslFormatError } from '../format/error';
2
+ export { format } from '../format/format';
3
+ export type { FormatOptions } from '../format/options';
@@ -1,6 +1,4 @@
1
1
  export type {
2
- ParsePslDocumentInput,
3
- ParsePslDocumentResult,
4
2
  PslAttribute,
5
3
  PslAttributeArgument,
6
4
  PslAttributeNamedArgument,
@@ -13,8 +11,15 @@ export type {
13
11
  PslDiagnostic,
14
12
  PslDiagnosticCode,
15
13
  PslDocumentAst,
16
- PslEnum,
17
- PslEnumValue,
14
+ PslExtensionBlock,
15
+ PslExtensionBlockAttribute,
16
+ PslExtensionBlockAttributeArg,
17
+ PslExtensionBlockParamBare,
18
+ PslExtensionBlockParamList,
19
+ PslExtensionBlockParamOption,
20
+ PslExtensionBlockParamRef,
21
+ PslExtensionBlockParamScalarValue,
22
+ PslExtensionBlockParamValue,
18
23
  PslField,
19
24
  PslFieldAttribute,
20
25
  PslModel,
@@ -26,5 +31,35 @@ export type {
26
31
  PslTypeConstructorCall,
27
32
  PslTypesBlock,
28
33
  } from '@prisma-next/framework-components/psl-ast';
34
+ export {
35
+ flatPslModels,
36
+ namespacePslExtensionBlocks,
37
+ } from '@prisma-next/framework-components/psl-ast';
29
38
  export { getPositionalArgument, parseQuotedStringLiteral } from '../attribute-helpers';
30
- export { parsePslDocument } from '../parser';
39
+ export { findBlockDescriptor, validateExtensionBlockFromSymbol } from '../extension-block';
40
+ export {
41
+ keywordPslSpan,
42
+ nodePslSpan,
43
+ rangeToPslSpan,
44
+ readResolvedAttribute,
45
+ readResolvedAttributes,
46
+ readResolvedConstructorCall,
47
+ } from '../resolve';
48
+ export type {
49
+ BlockSymbol,
50
+ BuildSymbolTableOptions,
51
+ CompositeTypeSymbol,
52
+ FieldSymbol,
53
+ ModelSymbol,
54
+ NamespaceSymbol,
55
+ ResolvedAttribute,
56
+ ResolvedAttributeArg,
57
+ ResolvedNamedTypeBinding,
58
+ ResolvedTypeConstructorCall,
59
+ ScalarSymbol,
60
+ SymbolTable,
61
+ SymbolTableResult,
62
+ TopLevelScope,
63
+ TypeAliasSymbol,
64
+ } from '../symbol-table';
65
+ export { buildSymbolTable } from '../symbol-table';