sf-agentpmd 0.1.0 → 0.1.2

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 (100) hide show
  1. package/NOTICE +7 -5
  2. package/dist/commands/agentpmd/analyze.js +4141 -109
  3. package/dist/commands/agentpmd/analyze.js.map +7 -1
  4. package/dist/commands/agentpmd/install-skill.js +42 -31
  5. package/dist/commands/agentpmd/install-skill.js.map +7 -1
  6. package/dist/index.js +4155 -3
  7. package/dist/index.js.map +7 -1
  8. package/oclif.manifest.json +2 -2
  9. package/package.json +7 -8
  10. package/dist/analyzer/action-references.d.ts +0 -21
  11. package/dist/analyzer/action-references.js +0 -130
  12. package/dist/analyzer/action-references.js.map +0 -1
  13. package/dist/analyzer/analyze.d.ts +0 -43
  14. package/dist/analyzer/analyze.js +0 -222
  15. package/dist/analyzer/analyze.js.map +0 -1
  16. package/dist/analyzer/apex-analyze.d.ts +0 -14
  17. package/dist/analyzer/apex-analyze.js +0 -60
  18. package/dist/analyzer/apex-analyze.js.map +0 -1
  19. package/dist/analyzer/apex-complexity.d.ts +0 -27
  20. package/dist/analyzer/apex-complexity.js +0 -133
  21. package/dist/analyzer/apex-complexity.js.map +0 -1
  22. package/dist/analyzer/apex-parse.d.ts +0 -39
  23. package/dist/analyzer/apex-parse.js +0 -32
  24. package/dist/analyzer/apex-parse.js.map +0 -1
  25. package/dist/analyzer/apex-resolve.d.ts +0 -32
  26. package/dist/analyzer/apex-resolve.js +0 -59
  27. package/dist/analyzer/apex-resolve.js.map +0 -1
  28. package/dist/analyzer/complexity.d.ts +0 -30
  29. package/dist/analyzer/complexity.js +0 -126
  30. package/dist/analyzer/complexity.js.map +0 -1
  31. package/dist/analyzer/parse.d.ts +0 -51
  32. package/dist/analyzer/parse.js +0 -143
  33. package/dist/analyzer/parse.js.map +0 -1
  34. package/dist/analyzer/project.d.ts +0 -12
  35. package/dist/analyzer/project.js +0 -51
  36. package/dist/analyzer/project.js.map +0 -1
  37. package/dist/analyzer/types.d.ts +0 -76
  38. package/dist/analyzer/types.js +0 -2
  39. package/dist/analyzer/types.js.map +0 -1
  40. package/dist/commands/agentpmd/analyze.d.ts +0 -20
  41. package/dist/commands/agentpmd/install-skill.d.ts +0 -11
  42. package/dist/index.d.ts +0 -3
  43. package/dist/renderers/csv.d.ts +0 -6
  44. package/dist/renderers/csv.js +0 -78
  45. package/dist/renderers/csv.js.map +0 -1
  46. package/dist/renderers/index.d.ts +0 -8
  47. package/dist/renderers/index.js +0 -25
  48. package/dist/renderers/index.js.map +0 -1
  49. package/dist/renderers/markdown.d.ts +0 -12
  50. package/dist/renderers/markdown.js +0 -233
  51. package/dist/renderers/markdown.js.map +0 -1
  52. package/dist/renderers/options.d.ts +0 -20
  53. package/dist/renderers/options.js +0 -2
  54. package/dist/renderers/options.js.map +0 -1
  55. package/dist/renderers/sarif.d.ts +0 -3
  56. package/dist/renderers/sarif.js +0 -131
  57. package/dist/renderers/sarif.js.map +0 -1
  58. package/dist/renderers/text.d.ts +0 -3
  59. package/dist/renderers/text.js +0 -243
  60. package/dist/renderers/text.js.map +0 -1
  61. package/vendor/agentscript-parser-javascript/dist/cst-node.d.ts +0 -83
  62. package/vendor/agentscript-parser-javascript/dist/cst-node.js +0 -238
  63. package/vendor/agentscript-parser-javascript/dist/errors.d.ts +0 -34
  64. package/vendor/agentscript-parser-javascript/dist/errors.js +0 -74
  65. package/vendor/agentscript-parser-javascript/dist/expressions.d.ts +0 -36
  66. package/vendor/agentscript-parser-javascript/dist/expressions.js +0 -682
  67. package/vendor/agentscript-parser-javascript/dist/highlighter.d.ts +0 -24
  68. package/vendor/agentscript-parser-javascript/dist/highlighter.js +0 -260
  69. package/vendor/agentscript-parser-javascript/dist/index.d.ts +0 -29
  70. package/vendor/agentscript-parser-javascript/dist/index.js +0 -35
  71. package/vendor/agentscript-parser-javascript/dist/lexer.d.ts +0 -60
  72. package/vendor/agentscript-parser-javascript/dist/lexer.js +0 -630
  73. package/vendor/agentscript-parser-javascript/dist/parse-mapping.d.ts +0 -46
  74. package/vendor/agentscript-parser-javascript/dist/parse-mapping.js +0 -549
  75. package/vendor/agentscript-parser-javascript/dist/parse-sequence.d.ts +0 -10
  76. package/vendor/agentscript-parser-javascript/dist/parse-sequence.js +0 -118
  77. package/vendor/agentscript-parser-javascript/dist/parse-statements.d.ts +0 -15
  78. package/vendor/agentscript-parser-javascript/dist/parse-statements.js +0 -519
  79. package/vendor/agentscript-parser-javascript/dist/parse-templates.d.ts +0 -15
  80. package/vendor/agentscript-parser-javascript/dist/parse-templates.js +0 -323
  81. package/vendor/agentscript-parser-javascript/dist/parser.d.ts +0 -65
  82. package/vendor/agentscript-parser-javascript/dist/parser.js +0 -163
  83. package/vendor/agentscript-parser-javascript/dist/recovery.d.ts +0 -51
  84. package/vendor/agentscript-parser-javascript/dist/recovery.js +0 -199
  85. package/vendor/agentscript-parser-javascript/dist/token.d.ts +0 -58
  86. package/vendor/agentscript-parser-javascript/dist/token.js +0 -62
  87. package/vendor/agentscript-parser-javascript/package.json +0 -19
  88. package/vendor/agentscript-types/dist/comment.d.ts +0 -11
  89. package/vendor/agentscript-types/dist/comment.js +0 -10
  90. package/vendor/agentscript-types/dist/cst.d.ts +0 -7
  91. package/vendor/agentscript-types/dist/cst.js +0 -8
  92. package/vendor/agentscript-types/dist/diagnostic.d.ts +0 -34
  93. package/vendor/agentscript-types/dist/diagnostic.js +0 -23
  94. package/vendor/agentscript-types/dist/index.d.ts +0 -9
  95. package/vendor/agentscript-types/dist/index.js +0 -10
  96. package/vendor/agentscript-types/dist/position.d.ts +0 -11
  97. package/vendor/agentscript-types/dist/position.js +0 -16
  98. package/vendor/agentscript-types/dist/syntax-node.d.ts +0 -39
  99. package/vendor/agentscript-types/dist/syntax-node.js +0 -8
  100. package/vendor/agentscript-types/package.json +0 -15
@@ -1,630 +0,0 @@
1
- /*
2
- * Copyright (c) 2026, Salesforce, Inc.
3
- * All rights reserved.
4
- * SPDX-License-Identifier: Apache-2.0
5
- * For full license text, see the LICENSE file in the repo root or https://www.apache.org/licenses/LICENSE-2.0
6
- */
7
- /**
8
- * Indentation-aware lexer for AgentScript.
9
- *
10
- * Produces a flat Token[] array including synthetic INDENT, DEDENT, and NEWLINE
11
- * tokens. Keywords are emitted as ID — the parser checks token text.
12
- *
13
- * Indentation: space = 1 unit, tab = 3 units (matching scanner.c).
14
- */
15
- import invariant from 'tiny-invariant';
16
- import { TokenKind } from './token.js';
17
- // ---------------------------------------------------------------------------
18
- // Character classification via charCode (replaces regex hot-path checks)
19
- // ---------------------------------------------------------------------------
20
- const CH_TAB = 9; // \t
21
- const CH_LF = 10; // \n
22
- const CH_CR = 13; // \r
23
- const CH_SPACE = 32;
24
- const CH_BANG = 33; // !
25
- const CH_DQUOTE = 34; // "
26
- const CH_HASH = 35; // #
27
- const CH_DASH = 45; // -
28
- const CH_DOT = 46; // .
29
- const CH_0 = 48;
30
- const CH_9 = 57;
31
- const CH_LT = 60; // <
32
- const CH_EQ = 61; // =
33
- const CH_GT = 62; // >
34
- const CH_A = 65;
35
- const CH_Z = 90;
36
- const CH_BACKSLASH = 92; // \
37
- const CH_UNDERSCORE = 95; // _
38
- const CH_a = 97;
39
- const CH_z = 122;
40
- const CH_LBRACE = 123; // {
41
- const CH_NUL = 0;
42
- function isIdStart(c) {
43
- return ((c >= CH_A && c <= CH_Z) || (c >= CH_a && c <= CH_z) || c === CH_UNDERSCORE);
44
- }
45
- function isIdCont(c) {
46
- return isIdStart(c) || (c >= CH_0 && c <= CH_9);
47
- }
48
- function isDigit(c) {
49
- return c >= CH_0 && c <= CH_9;
50
- }
51
- function isHorizontalWs(c) {
52
- return c === CH_SPACE || c === CH_TAB;
53
- }
54
- // ---------------------------------------------------------------------------
55
- // charCode-indexed single-char token lookup (replaces Record<string, TokenKind>)
56
- // ---------------------------------------------------------------------------
57
- const SINGLE_CHAR_TOKENS = new Array(128).fill(0);
58
- SINGLE_CHAR_TOKENS[43] = TokenKind.PLUS; // +
59
- SINGLE_CHAR_TOKENS[CH_DASH] = TokenKind.MINUS; // -
60
- SINGLE_CHAR_TOKENS[42] = TokenKind.STAR; // *
61
- SINGLE_CHAR_TOKENS[47] = TokenKind.SLASH; // /
62
- // % is not a valid operator in AgentScript (tree-sitter parity)
63
- SINGLE_CHAR_TOKENS[CH_DOT] = TokenKind.DOT; // .
64
- SINGLE_CHAR_TOKENS[44] = TokenKind.COMMA; // ,
65
- SINGLE_CHAR_TOKENS[58] = TokenKind.COLON; // :
66
- SINGLE_CHAR_TOKENS[61] = TokenKind.EQ; // =
67
- SINGLE_CHAR_TOKENS[60] = TokenKind.LT; // <
68
- SINGLE_CHAR_TOKENS[CH_GT] = TokenKind.GT; // >
69
- SINGLE_CHAR_TOKENS[124] = TokenKind.PIPE; // |
70
- SINGLE_CHAR_TOKENS[64] = TokenKind.AT; // @
71
- SINGLE_CHAR_TOKENS[40] = TokenKind.LPAREN; // (
72
- SINGLE_CHAR_TOKENS[41] = TokenKind.RPAREN; // )
73
- SINGLE_CHAR_TOKENS[91] = TokenKind.LBRACKET; // [
74
- SINGLE_CHAR_TOKENS[93] = TokenKind.RBRACKET; // ]
75
- SINGLE_CHAR_TOKENS[CH_LBRACE] = TokenKind.LBRACE; // {
76
- SINGLE_CHAR_TOKENS[125] = TokenKind.RBRACE; // }
77
- export class Lexer {
78
- source;
79
- offset = 0;
80
- row = 0;
81
- col = 0;
82
- tokens = [];
83
- indentStack = [0];
84
- /** True when the current line started with `|` (template line). */
85
- onTemplateLine = false;
86
- /** Indent level of the line containing `|`. Content deeper than this is template content. */
87
- templateBaseIndent = -1;
88
- /** Nested brace depth inside a template expression (for `{` inside `{!...}`). -1 means not inside a template expression. */
89
- templateExprBraceDepth = -1;
90
- get inTemplateExpr() {
91
- return this.templateExprBraceDepth >= 0;
92
- }
93
- /** Parenthesis depth — suppresses INDENT/DEDENT/NEWLINE when > 0 to support multi-line call expressions. */
94
- bracketDepth = 0;
95
- constructor(source) {
96
- this.source = source;
97
- }
98
- tokenize() {
99
- // Pre-allocate token array backing store for large inputs
100
- this.tokens = [];
101
- const estimate = (this.source.length / 8) | 0;
102
- if (estimate > 64) {
103
- this.tokens.length = estimate;
104
- this.tokens.length = 0;
105
- }
106
- this.offset = 0;
107
- this.row = 0;
108
- this.col = 0;
109
- this.indentStack = [0];
110
- this.bracketDepth = 0;
111
- while (this.hasMore) {
112
- this.tokenizeLine();
113
- }
114
- // Emit remaining DEDENTs
115
- while (this.indentStack.length > 1) {
116
- this.indentStack.pop();
117
- this.emitVirtual(TokenKind.DEDENT);
118
- }
119
- this.emitVirtual(TokenKind.EOF);
120
- return this.tokens;
121
- }
122
- tokenizeLine() {
123
- // Note: onTemplateLine persists across continuation lines and is only
124
- // reset when indentation decreases (DEDENT) in emitIndentation().
125
- // Measure leading indentation
126
- const indentLength = this.consumeIndentation();
127
- if (this.consumeNewline()) {
128
- return;
129
- }
130
- const c = this.peekCharCode();
131
- // Comment-only line: tree-sitter's scanner skips past comment-only lines
132
- // when deciding INDENT/DEDENT. If this comment is at deeper indent than
133
- // current but no real content follows at that depth, emit NEWLINE instead.
134
- // Similarly, if a comment is at shallower indent but the next real content
135
- // is back at the current block's depth, suppress the DEDENT.
136
- // When on a template line, only treat `#` as a comment if it's at or below
137
- // the template's base indent (outside the template content area).
138
- if (c === CH_HASH &&
139
- (!this.onTemplateLine || indentLength <= this.templateBaseIndent)) {
140
- const currentIndent = this.indentStack[this.indentStack.length - 1];
141
- if (indentLength > currentIndent) {
142
- const nextContentIndent = this.peekNextContentIndent();
143
- if (nextContentIndent < indentLength) {
144
- // No real content at this depth — suppress INDENT
145
- this.emitIndentation(currentIndent);
146
- return this.tokenizeComment();
147
- }
148
- }
149
- else if (indentLength < currentIndent) {
150
- const nextContentIndent = this.peekNextContentIndent();
151
- if (nextContentIndent > indentLength) {
152
- // Next real content is at a deeper indent than the comment.
153
- // Emit indentation based on where the next content is, not
154
- // where the comment sits, so we only close the blocks that
155
- // actually end (not the ones the comment just happens to be
156
- // outside of).
157
- this.emitIndentation(nextContentIndent);
158
- return this.tokenizeComment();
159
- }
160
- }
161
- else {
162
- // Comment at same indent as current block. If next real content
163
- // is deeper (i.e. the comment sits between a key and its indented
164
- // body), suppress NEWLINE so the body's INDENT fires on the next
165
- // iteration. Example:
166
- // a:
167
- // # comment ← same indent as current block
168
- // body: "x" ← should INDENT into a's body
169
- const nextContentIndent = this.peekNextContentIndent();
170
- if (nextContentIndent > indentLength) {
171
- return this.tokenizeComment();
172
- }
173
- }
174
- this.emitIndentation(indentLength);
175
- return this.tokenizeComment();
176
- }
177
- // Normal line — emit indentation tokens.
178
- // Template continuation lines (deeper than templateBaseIndent) should
179
- // not cause INDENT/DEDENT when their indent varies relative to other
180
- // template lines. The initial INDENT into the template block is needed
181
- // (it establishes the template_content grammar rule), but subsequent
182
- // indent variation within the template corrupts the indent stack and
183
- // breaks downstream blocks. Detect "already inside template block" by
184
- // checking if the indent stack top is already deeper than templateBaseIndent.
185
- const currentIndent = this.indentStack[this.indentStack.length - 1];
186
- if (this.onTemplateLine &&
187
- indentLength > this.templateBaseIndent &&
188
- currentIndent > this.templateBaseIndent &&
189
- indentLength !== currentIndent) {
190
- this.emitIndentation(currentIndent);
191
- }
192
- else {
193
- this.emitIndentation(indentLength);
194
- }
195
- // Check for "- " sequence element at start of content
196
- if (this.bracketDepth === 0 && c === CH_DASH) {
197
- const nc = this.peekCharCode(1); // NaN at EOF — won't match any CH_*
198
- const atEOF = this.offset + 1 >= this.source.length;
199
- if (nc === CH_SPACE || this.atNewline(1) || atEOF) {
200
- this.emit(TokenKind.DASH_SPACE, nc === CH_SPACE ? '- ' : '-');
201
- }
202
- }
203
- // Tokenize the rest of the line
204
- while (this.hasMore) {
205
- const c = this.peekCharCode();
206
- // Newline ends the line
207
- if (this.consumeNewline()) {
208
- return;
209
- }
210
- if (c === CH_CR) {
211
- // Not followed with line feed (otherwise this.consumeLine() would be true)
212
- invariant(!this.atNewline());
213
- this.advance();
214
- continue;
215
- }
216
- // Skip horizontal whitespace
217
- if (isHorizontalWs(c)) {
218
- this.advance();
219
- continue;
220
- }
221
- // Line continuation
222
- if (c === CH_BACKSLASH) {
223
- if (this.atNewline(1)) {
224
- this.advance(); // skip backslash
225
- invariant(this.consumeNewline());
226
- // Skip leading whitespace on continuation line
227
- while (isHorizontalWs(this.peekCharCode())) {
228
- this.advance();
229
- }
230
- continue;
231
- }
232
- }
233
- // Comment (but not on template content lines where # is literal text)
234
- if (c === CH_HASH && !this.onTemplateLine) {
235
- return this.tokenizeComment();
236
- }
237
- this.tokenizeToken();
238
- }
239
- }
240
- emitIndentation(indentLength) {
241
- if (this.bracketDepth > 0)
242
- return;
243
- const currentIndent = this.indentStack[this.indentStack.length - 1];
244
- if (indentLength > currentIndent) {
245
- this.indentStack.push(indentLength);
246
- this.emitVirtual(TokenKind.INDENT);
247
- }
248
- else if (indentLength < currentIndent) {
249
- // Emit DEDENTs and a NEWLINE; leave template context only when
250
- // indentation drops to or below the template's base indent level.
251
- if (indentLength <= this.templateBaseIndent) {
252
- this.onTemplateLine = false;
253
- this.templateExprBraceDepth = -1;
254
- }
255
- while (this.indentStack.length > 1 &&
256
- this.indentStack[this.indentStack.length - 1] > indentLength) {
257
- this.indentStack.pop();
258
- this.emitVirtual(TokenKind.DEDENT);
259
- }
260
- this.emitVirtual(TokenKind.NEWLINE);
261
- }
262
- else {
263
- // Same indent — emit NEWLINE (line separator) unless we're at the start.
264
- // Leave template context only when at or below the template's base indent.
265
- if (indentLength <= this.templateBaseIndent) {
266
- this.onTemplateLine = false;
267
- this.templateExprBraceDepth = -1;
268
- }
269
- if (this.tokens.length > 0) {
270
- this.emitVirtual(TokenKind.NEWLINE);
271
- }
272
- }
273
- }
274
- tokenizeToken() {
275
- const c = this.peekCharCode();
276
- // Datetimes or numbers
277
- if (isDigit(c)) {
278
- // Datetime literal: YYYY-MM-DD...
279
- // Must check before numbers since datetimes start with digits
280
- if (this.tryDatetime()) {
281
- return;
282
- }
283
- this.tokenizeNumber();
284
- return;
285
- }
286
- // Identifier
287
- if (isIdStart(c)) {
288
- this.tokenizeId();
289
- return;
290
- }
291
- // String (double-quoted always, single-quoted only if not a contraction)
292
- // Inside template lines (after |), quotes are literal characters — don't
293
- // start string tokenization unless we're inside a {!...} expression.
294
- if (!this.onTemplateLine || this.inTemplateExpr) {
295
- if (c === CH_DQUOTE) {
296
- this.tokenizeString();
297
- return;
298
- }
299
- }
300
- // Template expression start {!
301
- if (c === CH_LBRACE && this.peekCharCode(1) === CH_BANG) {
302
- this.templateExprBraceDepth = 0;
303
- this.emit(TokenKind.TEMPLATE_EXPR_START, '{!');
304
- return;
305
- }
306
- // Tokens beginning with .
307
- if (c === CH_DOT) {
308
- if (this.peekCharCode(1) === CH_DOT && this.peekCharCode(2) === CH_DOT) {
309
- this.emit(TokenKind.ELLIPSIS, '...');
310
- return;
311
- }
312
- // Leading-dot number (e.g., .5, .123) — but not after identifiers
313
- // (which would be member access like @variable.5)
314
- if (isDigit(this.peekCharCode(1))) {
315
- const prev = this.tokens[this.tokens.length - 1];
316
- const isMemberAccess = prev !== undefined &&
317
- (prev.kind === TokenKind.ID ||
318
- prev.kind === TokenKind.NUMBER ||
319
- prev.kind === TokenKind.RPAREN ||
320
- prev.kind === TokenKind.RBRACKET);
321
- if (!isMemberAccess) {
322
- this.tokenizeNumber();
323
- return;
324
- }
325
- }
326
- }
327
- if (c === CH_DASH) {
328
- if (this.peekCharCode(1) === CH_GT) {
329
- return this.emit(TokenKind.ARROW, '->');
330
- }
331
- }
332
- // == != <= >=
333
- const nc = this.peekCharCode(1);
334
- if (nc === CH_EQ) {
335
- // next is '='
336
- if (c === CH_EQ) {
337
- return this.emit(TokenKind.EQEQ, '==');
338
- }
339
- if (c === CH_BANG) {
340
- return this.emit(TokenKind.NEQ, '!=');
341
- }
342
- if (c === CH_LT) {
343
- return this.emit(TokenKind.LTE, '<=');
344
- }
345
- if (c === CH_GT) {
346
- return this.emit(TokenKind.GTE, '>=');
347
- }
348
- }
349
- // Single-char tokens (charCode-indexed lookup)
350
- const kind = c < 128 ? SINGLE_CHAR_TOKENS[c] : 0;
351
- if (kind) {
352
- this.emitSpan(kind, 1);
353
- switch (kind) {
354
- // Track template lines: `|` starts a template context for this line
355
- // and continuation lines indented deeper than this level.
356
- case TokenKind.PIPE:
357
- this.onTemplateLine = true;
358
- this.templateBaseIndent =
359
- this.indentStack[this.indentStack.length - 1];
360
- break;
361
- // Track parenthesis depth to suppress structural tokens inside
362
- // multi-line call expressions. Skip when inside a template line —
363
- // parens in template content are literal text and must not suppress
364
- // INDENT/DEDENT emission (unmatched parens would eat the rest of
365
- // the file).
366
- case TokenKind.LPAREN:
367
- if (!this.onTemplateLine)
368
- this.bracketDepth++;
369
- break;
370
- case TokenKind.RPAREN:
371
- if (!this.onTemplateLine)
372
- this.bracketDepth--;
373
- break;
374
- // Track brace depth inside {!...} template expressions so that nested
375
- // braces (e.g. JSON objects) don't prematurely close the expression.
376
- case TokenKind.LBRACE:
377
- if (this.inTemplateExpr) {
378
- this.templateExprBraceDepth++;
379
- }
380
- break;
381
- case TokenKind.RBRACE:
382
- if (this.inTemplateExpr) {
383
- this.templateExprBraceDepth--;
384
- }
385
- break;
386
- }
387
- return;
388
- }
389
- // Unknown character — emit error token
390
- this.emitSpan(TokenKind.ERROR_TOKEN, 1);
391
- }
392
- tokenizeId() {
393
- let i = 0;
394
- for (;; i++) {
395
- const c = this.peekCharCode(i);
396
- if (!isIdCont(c))
397
- break;
398
- }
399
- this.emitSpan(TokenKind.ID, i);
400
- }
401
- tokenizeNumber() {
402
- let tokenLength = 0;
403
- // Leading dot (e.g., .5) — consume the `.` first
404
- const leadingDot = this.peekCharCode(tokenLength) === CH_DOT;
405
- if (leadingDot) {
406
- tokenLength++;
407
- }
408
- // Integer part — inline advance (digits never contain newlines)
409
- while (isDigit(this.peekCharCode(tokenLength))) {
410
- tokenLength++;
411
- }
412
- // Decimal part — only consume `.` if followed by a digit (and no leading dot)
413
- if (!leadingDot && this.peekCharCode(tokenLength) === CH_DOT) {
414
- tokenLength++;
415
- }
416
- while (isDigit(this.peekCharCode(tokenLength))) {
417
- tokenLength++;
418
- }
419
- this.emitSpan(TokenKind.NUMBER, tokenLength);
420
- }
421
- tryDatetime() {
422
- // ISO 8601: YYYY-MM-DD optionally followed by time
423
- // Need at least YYYY-MM-DD = 10 chars
424
- const remaining = this.source.length - this.offset;
425
- if (remaining < 10)
426
- return false;
427
- // Fast reject: most numbers aren't datetimes. Check the fixed '-' positions
428
- // before allocating a slice or running the regex.
429
- if (this.source.charCodeAt(this.offset + 4) !== CH_DASH ||
430
- this.source.charCodeAt(this.offset + 7) !== CH_DASH) {
431
- return false;
432
- }
433
- const slice = this.source.slice(this.offset, this.offset + 30);
434
- const match = slice.match(/^\d{4}-\d{2}-\d{2}(T\d{1,2}(:\d{2})?(:\d{2})?(\.\d+)?Z?)?/);
435
- if (!match)
436
- return false;
437
- // Only treat as datetime if it has the full YYYY-MM-DD pattern
438
- // and the character after isn't an identifier character
439
- const matchText = match[0];
440
- if (matchText.length < 10)
441
- return false; // Must have at least YYYY-MM-DD
442
- this.emit(TokenKind.DATETIME, matchText);
443
- return true;
444
- }
445
- tokenizeString() {
446
- const start = this.position;
447
- const startOffset = this.offset;
448
- const quoteCode = this.peekCharCode(); // " or '
449
- // Opening quote
450
- this.advance();
451
- while (this.hasMore) {
452
- const c = this.peekCharCode();
453
- if (c === quoteCode) {
454
- this.advance(); // closing quote
455
- const text = this.source.slice(startOffset, this.offset);
456
- this.tokens.push(this.makeToken(TokenKind.STRING, text, start, this.position, startOffset));
457
- return;
458
- }
459
- if (c === CH_BACKSLASH) {
460
- this.advance(2);
461
- continue;
462
- }
463
- if (this.atNewline()) {
464
- // Unclosed string — stop at newline for error recovery
465
- break;
466
- }
467
- if (c === CH_CR) {
468
- // Bare \r inside string — treat as content
469
- invariant(!this.atNewline());
470
- this.advance();
471
- continue;
472
- }
473
- if (c === CH_NUL) {
474
- // Null byte — tree-sitter rejects these in string content
475
- break;
476
- }
477
- this.advance();
478
- }
479
- // Unclosed string
480
- const text = this.source.slice(startOffset, this.offset);
481
- this.tokens.push(this.makeToken(TokenKind.STRING, text, start, this.position, startOffset));
482
- }
483
- tokenizeComment() {
484
- const start = this.position;
485
- const startOffset = this.offset;
486
- // Consume # and everything until end of line or EOF
487
- while (this.hasMore && !this.atNewline()) {
488
- this.advance();
489
- }
490
- const text = this.source.slice(startOffset, this.offset);
491
- this.tokens.push(this.makeToken(TokenKind.COMMENT, text, start, this.position, startOffset));
492
- this.consumeNewline();
493
- }
494
- consumeIndentation() {
495
- let indentLength = 0;
496
- while (this.hasMore) {
497
- const c = this.peekCharCode();
498
- if (c === CH_SPACE) {
499
- indentLength += 1;
500
- this.advance();
501
- }
502
- else if (c === CH_TAB) {
503
- indentLength += 3;
504
- this.advance();
505
- }
506
- else if (c === CH_CR) {
507
- // Bare \r (not followed by \n) resets indent to 0,
508
- // matching tree-sitter scanner.c behavior (line 140-142)
509
- indentLength = 0;
510
- this.advance();
511
- }
512
- else {
513
- break;
514
- }
515
- }
516
- return indentLength;
517
- }
518
- /**
519
- * Scan ahead (without advancing) past comment/blank lines to find the indent
520
- * of the next line with real (non-comment) content. Returns -1 if only
521
- * comments, blanks, or EOF remain. Matches tree-sitter scanner behavior which
522
- * skips past comment-only lines when computing INDENT/DEDENT.
523
- */
524
- peekNextContentIndent() {
525
- const startPosition = this.position;
526
- const startOffset = this.offset;
527
- // Skip past the current comment line
528
- while (this.hasMore) {
529
- if (this.consumeNewline())
530
- break;
531
- this.advance();
532
- }
533
- // Scan subsequent lines
534
- while (this.hasMore) {
535
- // Measure indent
536
- const lineIndent = this.consumeIndentation();
537
- // Blank line — skip
538
- if (this.consumeNewline())
539
- continue;
540
- // Comment line — skip
541
- const c = this.peekCharCode();
542
- if (c === CH_HASH) {
543
- while (this.hasMore) {
544
- if (this.consumeNewline())
545
- break;
546
- this.advance();
547
- }
548
- continue;
549
- }
550
- this.offset = startOffset;
551
- this.row = startPosition.row;
552
- this.col = startPosition.column;
553
- // Real content — return its indent
554
- return lineIndent;
555
- }
556
- this.offset = startOffset;
557
- this.row = startPosition.row;
558
- this.col = startPosition.column;
559
- return -1;
560
- }
561
- // --- Utility methods ---
562
- peekCharCode(additiveOffset = 0) {
563
- return this.source.charCodeAt(this.offset + additiveOffset);
564
- }
565
- get hasMore() {
566
- return this.offset < this.source.length && this.offset >= 0;
567
- }
568
- /**
569
- * Attempt to advance n characters.
570
- * @returns how many characters were advanced.
571
- */
572
- advance(n = 1) {
573
- n = Math.max(0, Math.min(n, this.source.length - this.offset));
574
- this.col += n;
575
- for (let i = 0; i < n; i++) {
576
- if (this.peekCharCode(i) === CH_LF) {
577
- this.row++;
578
- this.col = n - i - 1;
579
- }
580
- }
581
- this.offset += n;
582
- return n;
583
- }
584
- /**
585
- * Attempt to consume a newline.
586
- * @returns whether a newline was consumed.
587
- */
588
- consumeNewline() {
589
- const newChars = this.atNewline();
590
- if (newChars > 0) {
591
- invariant(this.advance(newChars));
592
- return true;
593
- }
594
- return false;
595
- }
596
- /**
597
- * Checks if the current position is at a newline.
598
- * @param additiveOffset
599
- * @returns 0 if not at a newline, 1 if at an LF newline, 2 if at a CR LF newline.
600
- */
601
- atNewline(additiveOffset = 0) {
602
- const firstChar = this.peekCharCode(additiveOffset);
603
- if (firstChar === CH_LF)
604
- return 1;
605
- if (firstChar === CH_CR && this.peekCharCode(additiveOffset + 1) === CH_LF)
606
- return 2;
607
- return 0;
608
- }
609
- get position() {
610
- return { row: this.row, column: this.col };
611
- }
612
- emitSpan(kind, length) {
613
- const text = this.source.slice(this.offset, this.offset + length);
614
- return this.emit(kind, text);
615
- }
616
- emit(kind, text) {
617
- const startPosition = this.position;
618
- const startOffset = this.offset;
619
- invariant(text === this.source.slice(startOffset, startOffset + text.length), `expected '${text}' but got ${this.source.slice(startOffset, startOffset + text.length)} at offset ${startOffset}`);
620
- this.advance(text.length);
621
- this.tokens.push(this.makeToken(kind, text, startPosition, this.position, startOffset));
622
- }
623
- emitVirtual(kind) {
624
- return this.emit(kind, '');
625
- }
626
- makeToken(kind, text, start, end, startOffset) {
627
- return { kind, text, start, end, startOffset };
628
- }
629
- }
630
- //# sourceMappingURL=lexer.js.map
@@ -1,46 +0,0 @@
1
- import { CSTNode } from './cst-node.js';
2
- import type { ParserContext } from './parser.js';
3
- /** Callback type for parseSequence to break circular dependency. */
4
- export type ParseSequenceFn = (ctx: ParserContext) => CSTNode;
5
- /**
6
- * Parse a mapping-or-expression at the top level.
7
- * If the current position starts a mapping, delegates to parseMapping;
8
- * otherwise parses an expression (possibly an assignment).
9
- */
10
- export declare function parseMappingOrExpression(ctx: ParserContext, parseSequence: ParseSequenceFn): CSTNode | null;
11
- /**
12
- * Lookahead to determine if the current position starts a mapping (key-value
13
- * pairs) rather than an expression.
14
- *
15
- * Keys are at most a few tokens (1-3 words, possibly with hyphens/dots), so
16
- * we only need a small lookahead window. The limit exists as a safety cap —
17
- * it should never be reached on valid input.
18
- */
19
- export declare function isMappingStart(ctx: ParserContext): boolean;
20
- /**
21
- * Parse a mapping (sequence of key-value pairs).
22
- */
23
- export declare function parseMapping(ctx: ParserContext, parseSequence: ParseSequenceFn): CSTNode;
24
- /**
25
- * Parse a single mapping item (statement, template, comment, or key:value element).
26
- */
27
- export declare function parseMappingItem(ctx: ParserContext, parseSequence: ParseSequenceFn): CSTNode | null;
28
- /**
29
- * Check if the current position starts a colinear mapping element (key: value
30
- * on same line after "- ").
31
- */
32
- export declare function isColinearMappingElement(ctx: ParserContext): boolean;
33
- /**
34
- * Parse a colinear mapping element (key: value on the same line as "- ").
35
- */
36
- export declare function parseColinearMappingElement(ctx: ParserContext): CSTNode;
37
- /**
38
- * Try to parse a colinear value (template, variable declaration, or expression).
39
- * Returns the parsed value node and optional error prefix, or null if nothing
40
- * can be parsed.
41
- */
42
- export declare function tryParseColinearValue(ctx: ParserContext): {
43
- value: CSTNode;
44
- errorPrefix?: CSTNode;
45
- } | null;
46
- //# sourceMappingURL=parse-mapping.d.ts.map