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,682 +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
- * Pratt expression parser for AgentScript.
9
- *
10
- * Precedence levels (matching grammar.js):
11
- * 0: ternary (X if C else Y) — right-associative
12
- * 1: or
13
- * 2: and
14
- * 3: not (prefix)
15
- * 4: ==, !=, <, >, <=, >=, is, is not, = (comparison)
16
- * 5: +, - (binary)
17
- * 6: *, /
18
- * 7: +, - (unary prefix)
19
- * 8: call, member, subscript (postfix)
20
- * 9: parenthesized (atomic)
21
- */
22
- import { TokenKind } from './token.js';
23
- import { CSTNode } from './cst-node.js';
24
- // Hoisted constants — avoid per-call allocation
25
- // Must match ESCAPE_TABLE in @agentscript/language (packages/language/src/core/string-escapes.ts)
26
- const VALID_ESCAPES = new Set(['"', "'", '\\', 'n', 'r', 't', '0']);
27
- const KEY_STOP_KEYWORDS = new Set([
28
- 'if',
29
- 'elif',
30
- 'else',
31
- 'run',
32
- 'set',
33
- 'with',
34
- 'to',
35
- 'transition',
36
- 'available',
37
- 'and',
38
- 'or',
39
- 'not',
40
- 'is',
41
- 'True',
42
- 'False',
43
- 'None',
44
- 'mutable',
45
- 'linked',
46
- 'empty',
47
- ]);
48
- /** Create a MISSING id wrapped in atom → expression at the current parser position. */
49
- function makeMissingArgument(ctx) {
50
- const offset = ctx.peekOffset();
51
- const pos = ctx.peek().start;
52
- const missingId = new CSTNode('id', ctx.source, offset, offset, pos, pos, true, false, true);
53
- const atom = new CSTNode('atom', ctx.source, offset, offset, pos, pos);
54
- atom.appendChild(missingId);
55
- const expr = new CSTNode('expression', ctx.source, offset, offset, pos, pos);
56
- expr.appendChild(atom);
57
- return expr;
58
- }
59
- /** Create an empty ERROR node at the current parser position. */
60
- function makeEmptyError(ctx) {
61
- const tok = ctx.peek();
62
- const offset = ctx.peekOffset();
63
- return new CSTNode('ERROR', ctx.source, offset, offset, tok.start, tok.start, true, true);
64
- }
65
- export function parseExpression(ctx, minPrec = 0) {
66
- let left = parsePrefix(ctx);
67
- if (!left)
68
- return null;
69
- while (true) {
70
- // Fast path: sync points (NEWLINE/DEDENT/EOF) are never infix operators.
71
- // This avoids the infixPrecedence() lookup in the most common case for mappings.
72
- const nextKind = ctx.peekKind();
73
- if (nextKind === TokenKind.NEWLINE ||
74
- nextKind === TokenKind.DEDENT ||
75
- nextKind === TokenKind.EOF)
76
- break;
77
- const prec = infixPrecedence(ctx);
78
- if (prec < minPrec)
79
- break;
80
- const result = parseInfix(ctx, left, prec);
81
- if (!result)
82
- break;
83
- left = result;
84
- }
85
- return left;
86
- }
87
- function parsePrefix(ctx) {
88
- const tok = ctx.peek();
89
- // not (precedence 3)
90
- if (tok.kind === TokenKind.ID && tok.text === 'not') {
91
- return parseUnary(ctx, 'not', 3);
92
- }
93
- // Unary + / - (precedence 7)
94
- if (tok.kind === TokenKind.PLUS || tok.kind === TokenKind.MINUS) {
95
- const op = tok.text;
96
- return parseUnary(ctx, op, 7);
97
- }
98
- // Spread *expr (precedence 7)
99
- if (tok.kind === TokenKind.STAR) {
100
- return parseSpread(ctx);
101
- }
102
- // Parenthesized expression
103
- if (tok.kind === TokenKind.LPAREN) {
104
- return parseParenthesized(ctx);
105
- }
106
- // Atom
107
- return parseAtom(ctx);
108
- }
109
- function parseUnary(ctx, _op, prec) {
110
- const startTok = ctx.peek();
111
- const node = ctx.startNode('unary_expression');
112
- ctx.addAnonymousChild(node, ctx.consume()); // operator
113
- const operand = parseExpression(ctx, prec + 1);
114
- if (operand) {
115
- node.appendChild(wrapExpression(ctx, operand));
116
- }
117
- ctx.finishNode(node, startTok);
118
- return node;
119
- }
120
- function parseSpread(ctx) {
121
- const startTok = ctx.peek();
122
- const node = ctx.startNode('spread_expression');
123
- ctx.addAnonymousChild(node, ctx.consume()); // *
124
- // Bind at precedence 8 (same as postfix call/member/subscript) so
125
- // *@variables.x parses as *(variables.x), not (*variables).x
126
- const operand = parseExpression(ctx, 8);
127
- if (operand) {
128
- node.appendChild(wrapExpression(ctx, operand), 'expression');
129
- }
130
- ctx.finishNode(node, startTok);
131
- return node;
132
- }
133
- function parseParenthesized(ctx) {
134
- const startTok = ctx.peek();
135
- const node = ctx.startNode('parenthesized_expression');
136
- ctx.addAnonymousChild(node, ctx.consume()); // (
137
- const expr = parseExpression(ctx, 0);
138
- if (expr) {
139
- node.appendChild(wrapExpression(ctx, expr), 'expression');
140
- }
141
- else if (ctx.peekKind() === TokenKind.RPAREN) {
142
- // Empty parens () → insert MISSING id
143
- node.appendChild(makeMissingArgument(ctx), 'expression');
144
- }
145
- if (ctx.peekKind() === TokenKind.RPAREN) {
146
- ctx.addAnonymousChild(node, ctx.consume()); // )
147
- }
148
- else {
149
- // Unclosed paren → add ERROR node
150
- node.appendChild(makeEmptyError(ctx));
151
- }
152
- ctx.finishNode(node, startTok);
153
- return node;
154
- }
155
- function parseAtom(ctx) {
156
- const tok = ctx.peek();
157
- // Boolean / None constants
158
- if (tok.kind === TokenKind.ID &&
159
- (tok.text === 'True' || tok.text === 'False' || tok.text === 'None')) {
160
- const node = ctx.startNode('atom');
161
- ctx.addAnonymousChild(node, ctx.consume());
162
- ctx.finishNode(node, tok);
163
- return node;
164
- }
165
- // empty keyword
166
- if (tok.kind === TokenKind.ID && tok.text === 'empty') {
167
- const node = ctx.startNode('empty_keyword');
168
- ctx.addAnonymousChild(node, ctx.consume());
169
- ctx.finishNode(node, tok);
170
- return node;
171
- }
172
- // @id
173
- if (tok.kind === TokenKind.AT) {
174
- return parseAtId(ctx);
175
- }
176
- // id
177
- if (tok.kind === TokenKind.ID) {
178
- return ctx.consumeNamed('id');
179
- }
180
- // number
181
- if (tok.kind === TokenKind.NUMBER) {
182
- return ctx.consumeNamed('number');
183
- }
184
- // datetime
185
- if (tok.kind === TokenKind.DATETIME) {
186
- return ctx.consumeNamed('datetime_literal');
187
- }
188
- // string
189
- if (tok.kind === TokenKind.STRING) {
190
- return parseString(ctx);
191
- }
192
- // ellipsis
193
- if (tok.kind === TokenKind.ELLIPSIS) {
194
- return ctx.consumeNamed('ellipsis');
195
- }
196
- // list [...]
197
- if (tok.kind === TokenKind.LBRACKET) {
198
- return parseList(ctx);
199
- }
200
- // dictionary {...}
201
- if (tok.kind === TokenKind.LBRACE) {
202
- return parseDictionary(ctx);
203
- }
204
- return null;
205
- }
206
- function parseAtId(ctx) {
207
- const startTok = ctx.peek();
208
- const node = ctx.startNode('at_id');
209
- ctx.addAnonymousChild(node, ctx.consume()); // @
210
- if (ctx.peekKind() === TokenKind.ID) {
211
- node.appendChild(ctx.consumeNamed('id'));
212
- }
213
- else {
214
- // @ with no identifier → ERROR
215
- node.appendChild(makeEmptyError(ctx));
216
- }
217
- ctx.finishNode(node, startTok);
218
- return node;
219
- }
220
- export function parseString(ctx) {
221
- const tok = ctx.peek();
222
- const startTok = tok;
223
- const node = ctx.startNode('string');
224
- // The lexer gives us the whole string as one token.
225
- // We need to break it into children: opening quote, string_content/escape_sequence, closing quote.
226
- const text = tok.text;
227
- const tokenOffset = ctx.peekOffset();
228
- ctx.consume(); // consume the full string token
229
- const baseRow = startTok.start.row;
230
- const baseCol = startTok.start.column;
231
- // Opening quote
232
- node.appendChild(new CSTNode('"', ctx.source, tokenOffset, tokenOffset + 1, { row: baseRow, column: baseCol }, { row: baseRow, column: baseCol + 1 }, false));
233
- // Parse content between quotes
234
- let i = 1; // skip opening "
235
- const quoteChar = text[0]; // " or '
236
- const hasClosingQuote = text.length > 1 && text[text.length - 1] === quoteChar;
237
- const contentEnd = hasClosingQuote ? text.length - 1 : text.length;
238
- let contentStart = i;
239
- while (i < contentEnd) {
240
- if (text[i] === '\\' &&
241
- i + 1 < contentEnd &&
242
- VALID_ESCAPES.has(text[i + 1])) {
243
- // Emit any accumulated content before the escape
244
- if (i > contentStart) {
245
- node.appendChild(new CSTNode('string_content', ctx.source, tokenOffset + contentStart, tokenOffset + i, { row: baseRow, column: baseCol + contentStart }, { row: baseRow, column: baseCol + i }));
246
- }
247
- // Emit escape sequence
248
- const escLen = 2;
249
- node.appendChild(new CSTNode('escape_sequence', ctx.source, tokenOffset + i, tokenOffset + i + escLen, { row: baseRow, column: baseCol + i }, { row: baseRow, column: baseCol + i + escLen }));
250
- i += escLen;
251
- contentStart = i;
252
- }
253
- else if (text[i] === '\\' &&
254
- i + 1 < contentEnd &&
255
- !VALID_ESCAPES.has(text[i + 1])) {
256
- // Invalid escape sequence — emit accumulated content, then ERROR
257
- if (i > contentStart) {
258
- node.appendChild(new CSTNode('string_content', ctx.source, tokenOffset + contentStart, tokenOffset + i, { row: baseRow, column: baseCol + contentStart }, { row: baseRow, column: baseCol + i }));
259
- }
260
- // Find the extent of the invalid escape: \x followed by remaining word chars
261
- const escStart = i;
262
- i += 2; // skip \ and the invalid char
263
- while (i < contentEnd && /[a-zA-Z0-9_]/.test(text[i])) {
264
- i++;
265
- }
266
- const errNode = new CSTNode('ERROR', ctx.source, tokenOffset + escStart, tokenOffset + i, { row: baseRow, column: baseCol + escStart }, { row: baseRow, column: baseCol + i }, true, true);
267
- node.appendChild(errNode);
268
- contentStart = i;
269
- }
270
- else {
271
- i++;
272
- }
273
- }
274
- // Emit remaining content
275
- if (i > contentStart) {
276
- node.appendChild(new CSTNode('string_content', ctx.source, tokenOffset + contentStart, tokenOffset + i, { row: baseRow, column: baseCol + contentStart }, { row: baseRow, column: baseCol + i }));
277
- }
278
- // Closing quote
279
- if (hasClosingQuote) {
280
- node.appendChild(new CSTNode(quoteChar, ctx.source, tokenOffset + text.length - 1, tokenOffset + text.length, { row: baseRow, column: baseCol + text.length - 1 }, { row: baseRow, column: baseCol + text.length }, false));
281
- }
282
- else {
283
- // Unclosed string → MISSING closing quote
284
- // Position at end of string content (where the quote should have been),
285
- // not at the next token which may be on the next line.
286
- const missingOffset = tokenOffset + text.length;
287
- const missingPos = { row: baseRow, column: baseCol + text.length };
288
- node.appendChild(new CSTNode(quoteChar, ctx.source, missingOffset, missingOffset, missingPos, missingPos, false, false, true));
289
- }
290
- ctx.finishNode(node, startTok);
291
- return node;
292
- }
293
- function parseList(ctx) {
294
- const startTok = ctx.peek();
295
- const node = ctx.startNode('list');
296
- ctx.addAnonymousChild(node, ctx.consume()); // [
297
- // Lists can span multiple lines — skip whitespace tokens inside [...]
298
- let _listIndentDepth = 0;
299
- while (ctx.peekKind() !== TokenKind.RBRACKET &&
300
- ctx.peekKind() !== TokenKind.EOF) {
301
- if (ctx.peekKind() === TokenKind.NEWLINE) {
302
- ctx.consume();
303
- continue;
304
- }
305
- if (ctx.peekKind() === TokenKind.INDENT) {
306
- _listIndentDepth++;
307
- ctx.consume();
308
- continue;
309
- }
310
- if (ctx.peekKind() === TokenKind.DEDENT) {
311
- _listIndentDepth--;
312
- ctx.consume();
313
- continue;
314
- }
315
- const expr = parseExpression(ctx, 0);
316
- if (expr) {
317
- node.appendChild(wrapExpression(ctx, expr));
318
- }
319
- else {
320
- break;
321
- }
322
- if (ctx.peekKind() === TokenKind.COMMA) {
323
- ctx.addAnonymousChild(node, ctx.consume());
324
- }
325
- else {
326
- break;
327
- }
328
- }
329
- // Skip whitespace tokens to find the closing ]
330
- while (ctx.peekKind() === TokenKind.NEWLINE ||
331
- ctx.peekKind() === TokenKind.INDENT ||
332
- ctx.peekKind() === TokenKind.DEDENT) {
333
- ctx.consume();
334
- }
335
- if (ctx.peekKind() === TokenKind.RBRACKET) {
336
- ctx.addAnonymousChild(node, ctx.consume());
337
- }
338
- else {
339
- node.appendChild(makeEmptyError(ctx));
340
- }
341
- ctx.finishNode(node, startTok);
342
- return node;
343
- }
344
- function parseDictionary(ctx) {
345
- const startTok = ctx.peek();
346
- const node = ctx.startNode('dictionary');
347
- ctx.addAnonymousChild(node, ctx.consume()); // {
348
- // Dictionaries can span multiple lines
349
- while (ctx.peekKind() !== TokenKind.RBRACE &&
350
- ctx.peekKind() !== TokenKind.EOF) {
351
- if (ctx.peekKind() === TokenKind.NEWLINE ||
352
- ctx.peekKind() === TokenKind.INDENT ||
353
- ctx.peekKind() === TokenKind.DEDENT) {
354
- ctx.consume();
355
- continue;
356
- }
357
- const pair = parseDictionaryPair(ctx);
358
- if (pair) {
359
- node.appendChild(pair);
360
- }
361
- else {
362
- break;
363
- }
364
- if (ctx.peekKind() === TokenKind.COMMA) {
365
- ctx.addAnonymousChild(node, ctx.consume());
366
- }
367
- else {
368
- break;
369
- }
370
- }
371
- if (ctx.peekKind() === TokenKind.RBRACE) {
372
- ctx.addAnonymousChild(node, ctx.consume());
373
- }
374
- else {
375
- node.appendChild(makeEmptyError(ctx));
376
- }
377
- ctx.finishNode(node, startTok);
378
- return node;
379
- }
380
- function parseDictionaryPair(ctx) {
381
- const startTok = ctx.peek();
382
- if (!isKeyStart(ctx))
383
- return null;
384
- const node = ctx.startNode('dictionary_pair');
385
- const key = parseKey(ctx);
386
- if (key)
387
- node.appendChild(key, 'key');
388
- if (ctx.peekKind() === TokenKind.COLON) {
389
- ctx.addAnonymousChild(node, ctx.consume());
390
- }
391
- const value = parseExpression(ctx, 0);
392
- if (value)
393
- node.appendChild(wrapExpression(ctx, value), 'value');
394
- ctx.finishNode(node, startTok);
395
- return node;
396
- }
397
- // --- Infix parsing ---
398
- // Precedence lookup tables (O(1) instead of 20+ if-statements)
399
- const INFIX_PREC_BY_KIND = new Map([
400
- [TokenKind.LPAREN, 8],
401
- [TokenKind.DOT, 8],
402
- [TokenKind.LBRACKET, 8],
403
- [TokenKind.EQEQ, 4],
404
- [TokenKind.NEQ, 4],
405
- [TokenKind.LT, 4],
406
- [TokenKind.GT, 4],
407
- [TokenKind.LTE, 4],
408
- [TokenKind.GTE, 4],
409
- [TokenKind.PLUS, 5],
410
- [TokenKind.MINUS, 5],
411
- [TokenKind.STAR, 6],
412
- [TokenKind.SLASH, 6],
413
- ]);
414
- const INFIX_KEYWORD_PREC = new Map([
415
- ['if', 0],
416
- ['or', 1],
417
- ['and', 2],
418
- ['is', 4],
419
- ]);
420
- function infixPrecedence(ctx) {
421
- const tok = ctx.peek();
422
- if (tok.kind === TokenKind.ID)
423
- return INFIX_KEYWORD_PREC.get(tok.text) ?? -2;
424
- return INFIX_PREC_BY_KIND.get(tok.kind) ?? -2;
425
- }
426
- function parseInfix(ctx, left, prec) {
427
- const tok = ctx.peek();
428
- // Call expression: expr(args)
429
- if (tok.kind === TokenKind.LPAREN && prec === 8) {
430
- return parseCall(ctx, left);
431
- }
432
- // Member expression: expr.id
433
- if (tok.kind === TokenKind.DOT && prec === 8) {
434
- return parseMember(ctx, left);
435
- }
436
- // Subscript expression: expr[expr]
437
- if (tok.kind === TokenKind.LBRACKET && prec === 8) {
438
- return parseSubscript(ctx, left);
439
- }
440
- // Ternary: consequence if condition else alternative
441
- if (tok.kind === TokenKind.ID && tok.text === 'if') {
442
- return parseTernary(ctx, left);
443
- }
444
- // "is not" compound operator
445
- if (tok.kind === TokenKind.ID && tok.text === 'is') {
446
- return parseIsExpression(ctx, left);
447
- }
448
- // Binary / comparison
449
- return parseBinaryOrComparison(ctx, left, prec);
450
- }
451
- function parseCall(ctx, func) {
452
- const startTok = ctx.peek();
453
- const node = ctx.startNodeAt('call_expression', func);
454
- node.appendChild(wrapExpression(ctx, func), 'function');
455
- ctx.addAnonymousChild(node, ctx.consume()); // (
456
- while (ctx.peekKind() !== TokenKind.RPAREN && !ctx.isAtSyncPoint()) {
457
- const arg = parseExpression(ctx, 0);
458
- if (arg) {
459
- node.appendChild(wrapExpression(ctx, arg), 'argument');
460
- }
461
- else {
462
- break;
463
- }
464
- if (ctx.peekKind() === TokenKind.COMMA) {
465
- ctx.addAnonymousChild(node, ctx.consume());
466
- // Trailing comma: if `)` follows, insert MISSING id argument
467
- if (ctx.peekKind() === TokenKind.RPAREN) {
468
- node.appendChild(makeMissingArgument(ctx), 'argument');
469
- break;
470
- }
471
- }
472
- else {
473
- break;
474
- }
475
- }
476
- if (ctx.peekKind() === TokenKind.RPAREN) {
477
- ctx.addAnonymousChild(node, ctx.consume());
478
- }
479
- else {
480
- node.appendChild(makeEmptyError(ctx));
481
- }
482
- ctx.finishNode(node, startTok);
483
- return node;
484
- }
485
- function parseMember(ctx, object) {
486
- const startTok = ctx.peek();
487
- const node = ctx.startNodeAt('member_expression', object);
488
- node.appendChild(wrapExpression(ctx, object));
489
- ctx.addAnonymousChild(node, ctx.consume()); // .
490
- if (ctx.peekKind() === TokenKind.ID) {
491
- node.appendChild(ctx.consumeNamed('id'));
492
- }
493
- else if (ctx.peekKind() === TokenKind.NUMBER) {
494
- // Error 19: member access with number like @var.123
495
- const numNode = ctx.consumeNamed('number');
496
- const errNode = new CSTNode('ERROR', ctx.source, numNode.startOffset, numNode.endOffset, numNode.startPosition, numNode.endPosition, true, true);
497
- errNode.appendChild(numNode);
498
- node.appendChild(errNode);
499
- }
500
- else {
501
- // Trailing dot with nothing after → ERROR
502
- node.appendChild(makeEmptyError(ctx));
503
- }
504
- ctx.finishNode(node, startTok);
505
- return node;
506
- }
507
- function parseSubscript(ctx, object) {
508
- const startTok = ctx.peek();
509
- const node = ctx.startNodeAt('subscript_expression', object);
510
- node.appendChild(wrapExpression(ctx, object));
511
- ctx.addAnonymousChild(node, ctx.consume()); // [
512
- const index = parseExpression(ctx, 0);
513
- if (index) {
514
- node.appendChild(wrapExpression(ctx, index));
515
- }
516
- if (ctx.peekKind() === TokenKind.RBRACKET) {
517
- ctx.addAnonymousChild(node, ctx.consume());
518
- }
519
- else {
520
- node.appendChild(makeEmptyError(ctx));
521
- }
522
- ctx.finishNode(node, startTok);
523
- return node;
524
- }
525
- function parseTernary(ctx, consequence) {
526
- const startTok = ctx.peek();
527
- const node = ctx.startNodeAt('ternary_expression', consequence);
528
- node.appendChild(wrapExpression(ctx, consequence), 'consequence');
529
- ctx.addAnonymousChild(node, ctx.consume()); // if
530
- const condition = parseExpression(ctx, 1); // above 'or'
531
- if (condition) {
532
- node.appendChild(wrapExpression(ctx, condition), 'condition');
533
- }
534
- if (ctx.peekKind() === TokenKind.ID && ctx.peek().text === 'else') {
535
- ctx.addAnonymousChild(node, ctx.consume()); // else
536
- const alt = parseExpression(ctx, 0); // right-associative: parse at 0
537
- if (alt) {
538
- node.appendChild(wrapExpression(ctx, alt), 'alternative');
539
- }
540
- }
541
- else {
542
- // Incomplete ternary: "a if condition" without else → ERROR
543
- node.appendChild(makeEmptyError(ctx));
544
- }
545
- ctx.finishNode(node, startTok);
546
- return node;
547
- }
548
- function parseIsExpression(ctx, left) {
549
- const startTok = ctx.peek();
550
- // Check for "is not"
551
- const isNot = ctx.peekAt(1).kind === TokenKind.ID && ctx.peekAt(1).text === 'not';
552
- const nodeType = 'comparison_expression';
553
- const node = ctx.startNodeAt(nodeType, left);
554
- node.appendChild(wrapExpression(ctx, left));
555
- ctx.addAnonymousChild(node, ctx.consume()); // is
556
- if (isNot) {
557
- ctx.addAnonymousChild(node, ctx.consume()); // not
558
- }
559
- const right = parseExpression(ctx, 5); // above binary +/-
560
- if (right) {
561
- node.appendChild(wrapExpression(ctx, right));
562
- }
563
- ctx.finishNode(node, startTok);
564
- return node;
565
- }
566
- function parseBinaryOrComparison(ctx, left, prec) {
567
- const tok = ctx.peek();
568
- const startTok = tok;
569
- // Determine if this is a comparison or binary expression
570
- const isComparison = tok.kind === TokenKind.EQEQ ||
571
- tok.kind === TokenKind.NEQ ||
572
- tok.kind === TokenKind.LT ||
573
- tok.kind === TokenKind.GT ||
574
- tok.kind === TokenKind.LTE ||
575
- tok.kind === TokenKind.GTE ||
576
- tok.kind === TokenKind.EQ;
577
- const nodeType = isComparison ? 'comparison_expression' : 'binary_expression';
578
- const node = ctx.startNodeAt(nodeType, left);
579
- node.appendChild(wrapExpression(ctx, left));
580
- ctx.addAnonymousChild(node, ctx.consume()); // operator
581
- const right = parseExpression(ctx, prec + 1); // left-associative
582
- if (right) {
583
- node.appendChild(wrapExpression(ctx, right));
584
- }
585
- else {
586
- // Incomplete binary/comparison: `3 +` or `@var ==` with no right operand
587
- node.appendChild(makeEmptyError(ctx));
588
- }
589
- ctx.finishNode(node, startTok);
590
- return node;
591
- }
592
- // --- Helpers ---
593
- /**
594
- * Types that are already wrapped or structural — should NOT get an expression wrapper.
595
- * Everything else produced by expression parsing gets wrapped.
596
- */
597
- const SKIP_WRAP_TYPES = new Set(['expression', 'ERROR']);
598
- /**
599
- * Leaf/literal types that need an intermediate `atom` wrapper before the `expression` wrapper.
600
- */
601
- export const ATOM_TYPES = new Set([
602
- 'id',
603
- 'number',
604
- 'string',
605
- 'datetime_literal',
606
- 'at_id',
607
- 'list',
608
- 'dictionary',
609
- 'ellipsis',
610
- ]);
611
- /**
612
- * Wrap an expression in an `expression` supertype node if it isn't already one.
613
- * Tree-sitter wraps most expression children in an (expression ...) wrapper.
614
- */
615
- export function wrapExpression(ctx, inner) {
616
- if (SKIP_WRAP_TYPES.has(inner.type)) {
617
- return inner;
618
- }
619
- // For atoms, wrap in atom first then expression
620
- let wrapped = inner;
621
- if (ATOM_TYPES.has(inner.type)) {
622
- const atom = new CSTNode('atom', ctx.source, inner.startOffset, inner.endOffset, inner.startPosition, inner.endPosition);
623
- atom.appendChild(inner);
624
- wrapped = atom;
625
- }
626
- // Now wrap in expression
627
- const expr = new CSTNode('expression', ctx.source, wrapped.startOffset, wrapped.endOffset, wrapped.startPosition, wrapped.endPosition);
628
- expr.appendChild(wrapped);
629
- return expr;
630
- }
631
- export function isKeyStart(ctx) {
632
- const tok = ctx.peek();
633
- return isKeyTokenStart(tok.kind);
634
- }
635
- /** Can this token kind begin a key? (ID, STRING, or NUMBER for digit-prefixed keys like `3var`) */
636
- export function isKeyTokenStart(kind) {
637
- return (kind === TokenKind.ID ||
638
- kind === TokenKind.STRING ||
639
- kind === TokenKind.NUMBER);
640
- }
641
- /** Can this token kind appear within a multi-part key? (key-start tokens plus MINUS/DOT for `my-var`, `a.b`) */
642
- export function isKeyTokenContinuation(kind) {
643
- return (isKeyTokenStart(kind) || kind === TokenKind.MINUS || kind === TokenKind.DOT);
644
- }
645
- export function parseKey(ctx) {
646
- if (!isKeyStart(ctx))
647
- return null;
648
- const startTok = ctx.peek();
649
- const node = ctx.startNode('key');
650
- // First name — may be a number (digit-starting key like "3var")
651
- if (ctx.peekKind() === TokenKind.NUMBER) {
652
- // Digit-starting key: wrap number in ERROR
653
- const numNode = ctx.consumeNamed('number');
654
- const errNode = new CSTNode('ERROR', ctx.source, numNode.startOffset, numNode.endOffset, numNode.startPosition, numNode.endPosition, true, true);
655
- node.appendChild(errNode);
656
- // Consume the ID part if present
657
- if (ctx.peekKind() === TokenKind.ID) {
658
- node.appendChild(ctx.consumeNamed('id'));
659
- }
660
- }
661
- else if (ctx.peekKind() === TokenKind.STRING) {
662
- node.appendChild(parseString(ctx));
663
- }
664
- else {
665
- node.appendChild(ctx.consumeNamed('id'));
666
- }
667
- // Optional second name (two-word keys like "topic greeting")
668
- // The second word must be on the same line with exactly immediate adjacency
669
- // (the grammar uses token.immediate(' '))
670
- if (ctx.peekKind() === TokenKind.ID &&
671
- !ctx.isAtSyncPoint() &&
672
- ctx.peek().start.row === startTok.start.row) {
673
- // Check if this could be a keyword that starts a value/statement
674
- const nextText = ctx.peek().text;
675
- if (!KEY_STOP_KEYWORDS.has(nextText)) {
676
- node.appendChild(ctx.consumeNamed('id'));
677
- }
678
- }
679
- ctx.finishNode(node, startTok);
680
- return node;
681
- }
682
- //# sourceMappingURL=expressions.js.map
@@ -1,24 +0,0 @@
1
- /**
2
- * CST-walk syntax highlighter for AgentScript.
3
- *
4
- * Produces QueryCapture[] matching the tree-sitter highlights.scm rules.
5
- * Replaces tree-sitter's Query engine with a direct CST walk.
6
- */
7
- import type { CSTNode } from './cst-node.js';
8
- export interface HighlightCapture {
9
- name: string;
10
- text: string;
11
- startRow: number;
12
- startCol: number;
13
- endRow: number;
14
- endCol: number;
15
- }
16
- /**
17
- * Walk the CST and produce highlight captures matching highlights.scm.
18
- *
19
- * Tree-sitter query priority: later patterns override earlier ones.
20
- * We replicate this by first assigning generic captures, then overriding
21
- * with contextual ones (e.g., id → variable, then key > id → property).
22
- */
23
- export declare function highlight(root: CSTNode): HighlightCapture[];
24
- //# sourceMappingURL=highlighter.d.ts.map