sf-agentpmd 0.1.0 → 0.1.1
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.
- package/NOTICE +7 -5
- package/dist/commands/agentpmd/analyze.js +4141 -109
- package/dist/commands/agentpmd/analyze.js.map +7 -1
- package/dist/commands/agentpmd/install-skill.js +27 -30
- package/dist/commands/agentpmd/install-skill.js.map +7 -1
- package/dist/index.js +4155 -3
- package/dist/index.js.map +7 -1
- package/oclif.manifest.json +1 -1
- package/package.json +7 -8
- package/dist/analyzer/action-references.d.ts +0 -21
- package/dist/analyzer/action-references.js +0 -130
- package/dist/analyzer/action-references.js.map +0 -1
- package/dist/analyzer/analyze.d.ts +0 -43
- package/dist/analyzer/analyze.js +0 -222
- package/dist/analyzer/analyze.js.map +0 -1
- package/dist/analyzer/apex-analyze.d.ts +0 -14
- package/dist/analyzer/apex-analyze.js +0 -60
- package/dist/analyzer/apex-analyze.js.map +0 -1
- package/dist/analyzer/apex-complexity.d.ts +0 -27
- package/dist/analyzer/apex-complexity.js +0 -133
- package/dist/analyzer/apex-complexity.js.map +0 -1
- package/dist/analyzer/apex-parse.d.ts +0 -39
- package/dist/analyzer/apex-parse.js +0 -32
- package/dist/analyzer/apex-parse.js.map +0 -1
- package/dist/analyzer/apex-resolve.d.ts +0 -32
- package/dist/analyzer/apex-resolve.js +0 -59
- package/dist/analyzer/apex-resolve.js.map +0 -1
- package/dist/analyzer/complexity.d.ts +0 -30
- package/dist/analyzer/complexity.js +0 -126
- package/dist/analyzer/complexity.js.map +0 -1
- package/dist/analyzer/parse.d.ts +0 -51
- package/dist/analyzer/parse.js +0 -143
- package/dist/analyzer/parse.js.map +0 -1
- package/dist/analyzer/project.d.ts +0 -12
- package/dist/analyzer/project.js +0 -51
- package/dist/analyzer/project.js.map +0 -1
- package/dist/analyzer/types.d.ts +0 -76
- package/dist/analyzer/types.js +0 -2
- package/dist/analyzer/types.js.map +0 -1
- package/dist/commands/agentpmd/analyze.d.ts +0 -20
- package/dist/commands/agentpmd/install-skill.d.ts +0 -11
- package/dist/index.d.ts +0 -3
- package/dist/renderers/csv.d.ts +0 -6
- package/dist/renderers/csv.js +0 -78
- package/dist/renderers/csv.js.map +0 -1
- package/dist/renderers/index.d.ts +0 -8
- package/dist/renderers/index.js +0 -25
- package/dist/renderers/index.js.map +0 -1
- package/dist/renderers/markdown.d.ts +0 -12
- package/dist/renderers/markdown.js +0 -233
- package/dist/renderers/markdown.js.map +0 -1
- package/dist/renderers/options.d.ts +0 -20
- package/dist/renderers/options.js +0 -2
- package/dist/renderers/options.js.map +0 -1
- package/dist/renderers/sarif.d.ts +0 -3
- package/dist/renderers/sarif.js +0 -131
- package/dist/renderers/sarif.js.map +0 -1
- package/dist/renderers/text.d.ts +0 -3
- package/dist/renderers/text.js +0 -243
- package/dist/renderers/text.js.map +0 -1
- package/vendor/agentscript-parser-javascript/dist/cst-node.d.ts +0 -83
- package/vendor/agentscript-parser-javascript/dist/cst-node.js +0 -238
- package/vendor/agentscript-parser-javascript/dist/errors.d.ts +0 -34
- package/vendor/agentscript-parser-javascript/dist/errors.js +0 -74
- package/vendor/agentscript-parser-javascript/dist/expressions.d.ts +0 -36
- package/vendor/agentscript-parser-javascript/dist/expressions.js +0 -682
- package/vendor/agentscript-parser-javascript/dist/highlighter.d.ts +0 -24
- package/vendor/agentscript-parser-javascript/dist/highlighter.js +0 -260
- package/vendor/agentscript-parser-javascript/dist/index.d.ts +0 -29
- package/vendor/agentscript-parser-javascript/dist/index.js +0 -35
- package/vendor/agentscript-parser-javascript/dist/lexer.d.ts +0 -60
- package/vendor/agentscript-parser-javascript/dist/lexer.js +0 -630
- package/vendor/agentscript-parser-javascript/dist/parse-mapping.d.ts +0 -46
- package/vendor/agentscript-parser-javascript/dist/parse-mapping.js +0 -549
- package/vendor/agentscript-parser-javascript/dist/parse-sequence.d.ts +0 -10
- package/vendor/agentscript-parser-javascript/dist/parse-sequence.js +0 -118
- package/vendor/agentscript-parser-javascript/dist/parse-statements.d.ts +0 -15
- package/vendor/agentscript-parser-javascript/dist/parse-statements.js +0 -519
- package/vendor/agentscript-parser-javascript/dist/parse-templates.d.ts +0 -15
- package/vendor/agentscript-parser-javascript/dist/parse-templates.js +0 -323
- package/vendor/agentscript-parser-javascript/dist/parser.d.ts +0 -65
- package/vendor/agentscript-parser-javascript/dist/parser.js +0 -163
- package/vendor/agentscript-parser-javascript/dist/recovery.d.ts +0 -51
- package/vendor/agentscript-parser-javascript/dist/recovery.js +0 -199
- package/vendor/agentscript-parser-javascript/dist/token.d.ts +0 -58
- package/vendor/agentscript-parser-javascript/dist/token.js +0 -62
- package/vendor/agentscript-parser-javascript/package.json +0 -19
- package/vendor/agentscript-types/dist/comment.d.ts +0 -11
- package/vendor/agentscript-types/dist/comment.js +0 -10
- package/vendor/agentscript-types/dist/cst.d.ts +0 -7
- package/vendor/agentscript-types/dist/cst.js +0 -8
- package/vendor/agentscript-types/dist/diagnostic.d.ts +0 -34
- package/vendor/agentscript-types/dist/diagnostic.js +0 -23
- package/vendor/agentscript-types/dist/index.d.ts +0 -9
- package/vendor/agentscript-types/dist/index.js +0 -10
- package/vendor/agentscript-types/dist/position.d.ts +0 -11
- package/vendor/agentscript-types/dist/position.js +0 -16
- package/vendor/agentscript-types/dist/syntax-node.d.ts +0 -39
- package/vendor/agentscript-types/dist/syntax-node.js +0 -8
- package/vendor/agentscript-types/package.json +0 -15
|
@@ -1,323 +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
|
-
* Template-parsing functions extracted from Parser class.
|
|
9
|
-
*
|
|
10
|
-
* Each function takes a ParserContext as its first parameter, following
|
|
11
|
-
* the same free-function pattern as parse-statements.ts and expressions.ts.
|
|
12
|
-
*
|
|
13
|
-
* Template indentation state (templateOuterIndent) is computed locally in
|
|
14
|
-
* parseTemplate and passed as an explicit parameter to templateContinues.
|
|
15
|
-
*/
|
|
16
|
-
import { TokenKind } from './token.js';
|
|
17
|
-
import { CSTNode } from './cst-node.js';
|
|
18
|
-
import { makeEmptyError, makeMissing, synchronize, isAtEnd, } from './recovery.js';
|
|
19
|
-
import { parseExpression, wrapExpression } from './expressions.js';
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
// Exported template parsers
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
/**
|
|
24
|
-
* Parse a template starting with `|`.
|
|
25
|
-
* Consumes tokens from the lexer stream, treating everything as template content
|
|
26
|
-
* except `{!...}` breaks which are parsed as template expressions.
|
|
27
|
-
*/
|
|
28
|
-
export function parseTemplate(ctx) {
|
|
29
|
-
const startTok = ctx.peek();
|
|
30
|
-
const node = ctx.startNode('template');
|
|
31
|
-
// Compute the indent level of the line containing `|`.
|
|
32
|
-
// Tree-sitter uses *array_back(&scanner->indents) — the top of the indent
|
|
33
|
-
// stack, which equals the line indent. We scan backward in the source to
|
|
34
|
-
// measure the leading whitespace on this line.
|
|
35
|
-
const pipeOffset = ctx.peekOffset();
|
|
36
|
-
let lineStart = pipeOffset;
|
|
37
|
-
while (lineStart > 0 &&
|
|
38
|
-
ctx.source.charCodeAt(lineStart - 1) !== 10 /* \n */) {
|
|
39
|
-
lineStart--;
|
|
40
|
-
}
|
|
41
|
-
let templateOuterIndent = 0;
|
|
42
|
-
for (let i = lineStart; i < pipeOffset; i++) {
|
|
43
|
-
const ch = ctx.source.charCodeAt(i);
|
|
44
|
-
if (ch === 32 /* space */)
|
|
45
|
-
templateOuterIndent += 1;
|
|
46
|
-
else if (ch === 9 /* tab */)
|
|
47
|
-
templateOuterIndent += 3;
|
|
48
|
-
else
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
// Consume the | token and track position right after it
|
|
52
|
-
const pipeToken = ctx.consume();
|
|
53
|
-
ctx.addAnonymousChild(node, pipeToken);
|
|
54
|
-
// If there are tokens on the same line after |, pass afterPipeOffset
|
|
55
|
-
// so whitespace between | and {! is captured as template_content.
|
|
56
|
-
// If the line is empty after |, don't pass it (avoids phantom content).
|
|
57
|
-
const hasContentOnSameLine = !isAtEnd(ctx) &&
|
|
58
|
-
ctx.peekKind() !== TokenKind.NEWLINE &&
|
|
59
|
-
ctx.peekKind() !== TokenKind.INDENT &&
|
|
60
|
-
ctx.peekKind() !== TokenKind.DEDENT;
|
|
61
|
-
if (hasContentOnSameLine) {
|
|
62
|
-
const afterPipeOffset = pipeToken.startOffset + 1;
|
|
63
|
-
gatherTemplateContentLine(ctx, node, afterPipeOffset);
|
|
64
|
-
}
|
|
65
|
-
// Consume NEWLINE if present
|
|
66
|
-
if (ctx.peekKind() === TokenKind.NEWLINE) {
|
|
67
|
-
ctx.consume();
|
|
68
|
-
}
|
|
69
|
-
// If there's an INDENT, the template continues on indented lines.
|
|
70
|
-
// Templates consume ALL indented content until we fully return to the
|
|
71
|
-
// base indent. We track indent depth: each INDENT increments, each
|
|
72
|
-
// DEDENT decrements. When depth reaches 0, a final DEDENT exits.
|
|
73
|
-
// Mid-template DEDENTs (under-indented continuation lines) are consumed
|
|
74
|
-
// as content.
|
|
75
|
-
if (ctx.peekKind() === TokenKind.INDENT) {
|
|
76
|
-
ctx.consume(); // outer INDENT
|
|
77
|
-
let indentDepth = 1;
|
|
78
|
-
while (!isAtEnd(ctx)) {
|
|
79
|
-
const tok = ctx.peek();
|
|
80
|
-
if (tok.kind === TokenKind.DEDENT) {
|
|
81
|
-
indentDepth--;
|
|
82
|
-
ctx.consume();
|
|
83
|
-
if (indentDepth <= 0) {
|
|
84
|
-
// Check if template continues with under-indented content.
|
|
85
|
-
// If the next meaningful token is content (not EOF/DEDENT),
|
|
86
|
-
// the template has under-indented continuation lines.
|
|
87
|
-
if (templateContinues(ctx, templateOuterIndent)) {
|
|
88
|
-
// Re-enter: consume content at the new (lower) indent
|
|
89
|
-
indentDepth = 0; // will re-increment on next INDENT
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
else if (tok.kind === TokenKind.INDENT) {
|
|
96
|
-
indentDepth++;
|
|
97
|
-
ctx.consume();
|
|
98
|
-
}
|
|
99
|
-
else if (tok.kind === TokenKind.NEWLINE) {
|
|
100
|
-
ctx.consume();
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
// When at the template's base indent depth, check if the next
|
|
104
|
-
// token should continue the template (e.g. comments at the base
|
|
105
|
-
// level should not be absorbed as template content).
|
|
106
|
-
if (indentDepth <= 0 && !templateContinues(ctx, templateOuterIndent)) {
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
// For continuation lines, start the content from the end of the
|
|
110
|
-
// last template child so that newlines + indentation between a
|
|
111
|
-
// template_expression and the next template_content are preserved
|
|
112
|
-
// in the source text. (mergeTemplateContent handles this for
|
|
113
|
-
// consecutive template_content nodes, but not across expressions.)
|
|
114
|
-
const lastChild = node.children.length > 0
|
|
115
|
-
? node.children[node.children.length - 1]
|
|
116
|
-
: null;
|
|
117
|
-
const gapOffset = lastChild && lastChild.endOffset < ctx.peekOffset()
|
|
118
|
-
? lastChild.endOffset
|
|
119
|
-
: undefined;
|
|
120
|
-
const gapPos = gapOffset !== undefined ? lastChild.endPosition : undefined;
|
|
121
|
-
gatherTemplateContentLine(ctx, node, gapOffset, gapPos);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// Merge consecutive template_content children into single nodes.
|
|
126
|
-
// Tree-sitter produces one template_content per contiguous text span;
|
|
127
|
-
// our line-by-line parsing creates one per line.
|
|
128
|
-
mergeTemplateContent(ctx, node);
|
|
129
|
-
ctx.finishNode(node, startTok);
|
|
130
|
-
return node;
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Parse a template in colinear position (after a colon on the same line).
|
|
134
|
-
* Currently identical to parseTemplate; kept as a separate entry point
|
|
135
|
-
* for semantic clarity and potential future divergence.
|
|
136
|
-
*/
|
|
137
|
-
export function parseTemplateAsColinear(ctx) {
|
|
138
|
-
return parseTemplate(ctx);
|
|
139
|
-
}
|
|
140
|
-
// ---------------------------------------------------------------------------
|
|
141
|
-
// Internal helpers
|
|
142
|
-
// ---------------------------------------------------------------------------
|
|
143
|
-
/**
|
|
144
|
-
* Check if the template continues with under-indented content.
|
|
145
|
-
* After a DEDENT brings us to depth 0, if the next meaningful token
|
|
146
|
-
* is content (not EOF, not DEDENT, not a mapping key pattern), the
|
|
147
|
-
* template has continuation lines.
|
|
148
|
-
*/
|
|
149
|
-
function templateContinues(ctx, templateOuterIndent) {
|
|
150
|
-
let i = 0;
|
|
151
|
-
while (ctx.peekAt(i).kind === TokenKind.NEWLINE)
|
|
152
|
-
i++;
|
|
153
|
-
const tok = ctx.peekAt(i);
|
|
154
|
-
// If we see content (ID, etc.) that's NOT a mapping key pattern, continue
|
|
155
|
-
if (tok.kind === TokenKind.EOF || tok.kind === TokenKind.DEDENT)
|
|
156
|
-
return false;
|
|
157
|
-
// Content deeper than the template's base indent is always template content,
|
|
158
|
-
// regardless of keywords. Matches tree-sitter scanner behavior where
|
|
159
|
-
// indent_length > out_of_template_indent_length keeps content in the template.
|
|
160
|
-
if (tok.start.column > templateOuterIndent)
|
|
161
|
-
return true;
|
|
162
|
-
// Another pipe starts a new template — don't absorb it
|
|
163
|
-
if (tok.kind === TokenKind.PIPE)
|
|
164
|
-
return false;
|
|
165
|
-
// If it looks like a mapping key (ID followed by COLON), template is done
|
|
166
|
-
if (tok.kind === TokenKind.ID || tok.kind === TokenKind.STRING) {
|
|
167
|
-
const after = ctx.peekAt(i + 1);
|
|
168
|
-
if (after.kind === TokenKind.COLON)
|
|
169
|
-
return false;
|
|
170
|
-
// Two-word key check
|
|
171
|
-
if (after.kind === TokenKind.ID) {
|
|
172
|
-
const afterAfter = ctx.peekAt(i + 2);
|
|
173
|
-
if (afterAfter.kind === TokenKind.COLON)
|
|
174
|
-
return false;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
// Statement keywords terminate template continuation — they're
|
|
178
|
-
// sibling statements, not template content
|
|
179
|
-
if (tok.kind === TokenKind.ID) {
|
|
180
|
-
switch (tok.text) {
|
|
181
|
-
case 'if':
|
|
182
|
-
case 'elif':
|
|
183
|
-
case 'else':
|
|
184
|
-
case 'run':
|
|
185
|
-
case 'set':
|
|
186
|
-
case 'transition':
|
|
187
|
-
return false;
|
|
188
|
-
case 'with':
|
|
189
|
-
// "with" not followed by colon is a statement
|
|
190
|
-
if (ctx.peekAt(i + 1).kind !== TokenKind.COLON)
|
|
191
|
-
return false;
|
|
192
|
-
break;
|
|
193
|
-
case 'available':
|
|
194
|
-
if (ctx.peekAt(i + 1).kind === TokenKind.ID &&
|
|
195
|
-
ctx.peekAt(i + 1).text === 'when')
|
|
196
|
-
return false;
|
|
197
|
-
break;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
// If it looks like a dash (sequence), template is done
|
|
201
|
-
if (tok.kind === TokenKind.DASH_SPACE)
|
|
202
|
-
return false;
|
|
203
|
-
// Comments at the template's base indent level are not template content
|
|
204
|
-
if (tok.kind === TokenKind.COMMENT)
|
|
205
|
-
return false;
|
|
206
|
-
// Otherwise, assume it's template continuation
|
|
207
|
-
return true;
|
|
208
|
-
}
|
|
209
|
-
/** Merge consecutive template_content children into single nodes. */
|
|
210
|
-
function mergeTemplateContent(ctx, template) {
|
|
211
|
-
const merged = [];
|
|
212
|
-
let i = 0;
|
|
213
|
-
while (i < template.children.length) {
|
|
214
|
-
const child = template.children[i];
|
|
215
|
-
if (child.type === 'template_content') {
|
|
216
|
-
// Find the run of consecutive template_content nodes
|
|
217
|
-
let end = i + 1;
|
|
218
|
-
while (end < template.children.length &&
|
|
219
|
-
template.children[end].type === 'template_content') {
|
|
220
|
-
end++;
|
|
221
|
-
}
|
|
222
|
-
if (end > i + 1) {
|
|
223
|
-
// Merge into one node
|
|
224
|
-
const first = template.children[i];
|
|
225
|
-
const last = template.children[end - 1];
|
|
226
|
-
const mergedNode = new CSTNode('template_content', ctx.source, first.startOffset, last.endOffset, first.startPosition, last.endPosition);
|
|
227
|
-
mergedNode.parent = template;
|
|
228
|
-
merged.push(mergedNode);
|
|
229
|
-
i = end;
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
merged.push(child);
|
|
233
|
-
i++;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
merged.push(child);
|
|
238
|
-
i++;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
template.children = merged;
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Gather tokens on the current line as template content.
|
|
245
|
-
* Recognizes {! ... } as template expression breaks.
|
|
246
|
-
* Everything else (including inter-token whitespace) becomes template_content.
|
|
247
|
-
* Uses source-level offsets so whitespace between tokens is preserved.
|
|
248
|
-
*/
|
|
249
|
-
function gatherTemplateContentLine(ctx, parent, initialOffset, initialPos) {
|
|
250
|
-
// Track the source range of content before/after template expressions.
|
|
251
|
-
// If initialOffset is provided, use it (captures whitespace after |).
|
|
252
|
-
// Otherwise start from the current token position.
|
|
253
|
-
let contentStartOffset = initialOffset ?? ctx.peekOffset();
|
|
254
|
-
let contentStartPos = initialPos ?? ctx.peek().start;
|
|
255
|
-
let lastConsumedEndOffset = contentStartOffset;
|
|
256
|
-
let lastConsumedEndPos = contentStartPos;
|
|
257
|
-
while (!isAtEnd(ctx)) {
|
|
258
|
-
const tok = ctx.peek();
|
|
259
|
-
if (tok.kind === TokenKind.NEWLINE ||
|
|
260
|
-
tok.kind === TokenKind.DEDENT ||
|
|
261
|
-
tok.kind === TokenKind.INDENT ||
|
|
262
|
-
tok.kind === TokenKind.EOF) {
|
|
263
|
-
break;
|
|
264
|
-
}
|
|
265
|
-
// Template expression start
|
|
266
|
-
if (tok.kind === TokenKind.TEMPLATE_EXPR_START) {
|
|
267
|
-
// Flush accumulated content up to the {!
|
|
268
|
-
const exprOffset = ctx.peekOffset();
|
|
269
|
-
if (exprOffset > contentStartOffset) {
|
|
270
|
-
parent.appendChild(new CSTNode('template_content', ctx.source, contentStartOffset, exprOffset, contentStartPos, tok.start));
|
|
271
|
-
}
|
|
272
|
-
// Parse template expression
|
|
273
|
-
const exprNode = parseTemplateExpression(ctx);
|
|
274
|
-
parent.appendChild(exprNode);
|
|
275
|
-
// Content after } continues from the end of the expression node
|
|
276
|
-
// (not from the next token — that would skip whitespace between } and next content)
|
|
277
|
-
contentStartOffset = exprNode.endOffset;
|
|
278
|
-
contentStartPos = exprNode.endPosition;
|
|
279
|
-
lastConsumedEndOffset = exprNode.endOffset;
|
|
280
|
-
lastConsumedEndPos = exprNode.endPosition;
|
|
281
|
-
continue;
|
|
282
|
-
}
|
|
283
|
-
// Track end of this token for accurate content span
|
|
284
|
-
const tokOffset = ctx.peekOffset();
|
|
285
|
-
lastConsumedEndOffset = tokOffset + tok.text.length;
|
|
286
|
-
lastConsumedEndPos = tok.end;
|
|
287
|
-
ctx.consume();
|
|
288
|
-
}
|
|
289
|
-
// Flush remaining content — use end of last consumed token (not next token offset,
|
|
290
|
-
// which would include blank lines between this content and the next line)
|
|
291
|
-
if (lastConsumedEndOffset > contentStartOffset) {
|
|
292
|
-
parent.appendChild(new CSTNode('template_content', ctx.source, contentStartOffset, lastConsumedEndOffset, contentStartPos, lastConsumedEndPos));
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
function parseTemplateExpression(ctx) {
|
|
296
|
-
const startTok = ctx.peek();
|
|
297
|
-
const node = ctx.startNode('template_expression');
|
|
298
|
-
ctx.addAnonymousChild(node, ctx.consume()); // {!
|
|
299
|
-
const expr = parseExpression(ctx, 0);
|
|
300
|
-
if (expr) {
|
|
301
|
-
node.appendChild(wrapExpression(ctx, expr), 'expression');
|
|
302
|
-
}
|
|
303
|
-
else {
|
|
304
|
-
// Empty template expression {!} → ERROR for missing expression
|
|
305
|
-
node.appendChild(makeEmptyError(ctx));
|
|
306
|
-
}
|
|
307
|
-
// Consume any extra tokens before } (e.g., unclosed {!@var.name world)
|
|
308
|
-
if (ctx.peekKind() !== TokenKind.RBRACE && !ctx.isAtSyncPoint()) {
|
|
309
|
-
const err = synchronize(ctx);
|
|
310
|
-
if (err)
|
|
311
|
-
node.appendChild(err);
|
|
312
|
-
}
|
|
313
|
-
if (ctx.peekKind() === TokenKind.RBRACE) {
|
|
314
|
-
ctx.addAnonymousChild(node, ctx.consume()); // }
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
// Unclosed template expression → MISSING }
|
|
318
|
-
node.appendChild(makeMissing(ctx, '}'));
|
|
319
|
-
}
|
|
320
|
-
ctx.finishNode(node, startTok);
|
|
321
|
-
return node;
|
|
322
|
-
}
|
|
323
|
-
//# sourceMappingURL=parse-templates.js.map
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Recursive descent parser for AgentScript.
|
|
3
|
-
*
|
|
4
|
-
* Core invariant: NEWLINE and DEDENT are unconditional synchronization points.
|
|
5
|
-
* Every parse function that encounters an unexpected token calls synchronize()
|
|
6
|
-
* which skips to the next NEWLINE/DEDENT/EOF.
|
|
7
|
-
*/
|
|
8
|
-
import { TokenKind, type Token } from './token.js';
|
|
9
|
-
import { CSTNode } from './cst-node.js';
|
|
10
|
-
/**
|
|
11
|
-
* Token consumption — peek, advance, and query the token stream.
|
|
12
|
-
*/
|
|
13
|
-
export interface TokenStream {
|
|
14
|
-
source: string;
|
|
15
|
-
peek(): Token;
|
|
16
|
-
peekAt(offset: number): Token;
|
|
17
|
-
peekAtIndex(idx: number): Token;
|
|
18
|
-
peekKind(): TokenKind;
|
|
19
|
-
consume(): Token;
|
|
20
|
-
consumeKind<K extends TokenKind>(kind: K): Token<K>;
|
|
21
|
-
currentOffset(): number;
|
|
22
|
-
peekOffset(): number;
|
|
23
|
-
isAtSyncPoint(): boolean;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* CST node construction — create, populate, and finalize nodes.
|
|
27
|
-
*/
|
|
28
|
-
export interface NodeBuilder {
|
|
29
|
-
consumeNamed(type: string): CSTNode;
|
|
30
|
-
startNode(type: string): CSTNode;
|
|
31
|
-
startNodeAt(type: string, existingChild: CSTNode): CSTNode;
|
|
32
|
-
finishNode(node: CSTNode, startTok: Token): void;
|
|
33
|
-
addAnonymousChild(parent: CSTNode, token: Token): void;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Combined interface used by expression parser to access parser state.
|
|
37
|
-
* Avoids circular dependency between parser.ts and expressions.ts.
|
|
38
|
-
*/
|
|
39
|
-
export interface ParserContext extends TokenStream, NodeBuilder {
|
|
40
|
-
}
|
|
41
|
-
export declare class Parser implements ParserContext {
|
|
42
|
-
source: string;
|
|
43
|
-
private tokens;
|
|
44
|
-
private pos;
|
|
45
|
-
private _eof;
|
|
46
|
-
constructor(source: string);
|
|
47
|
-
parse(): CSTNode;
|
|
48
|
-
peek(): Token;
|
|
49
|
-
peekAt(offset: number): Token;
|
|
50
|
-
peekAtIndex(idx: number): Token;
|
|
51
|
-
peekKind(): TokenKind;
|
|
52
|
-
consume(): Token;
|
|
53
|
-
consumeKind<K extends TokenKind>(kind: K): Token<K>;
|
|
54
|
-
consumeNamed(type: string): CSTNode;
|
|
55
|
-
currentOffset(): number;
|
|
56
|
-
peekOffset(): number;
|
|
57
|
-
isAtSyncPoint(): boolean;
|
|
58
|
-
startNode(type: string): CSTNode;
|
|
59
|
-
startNodeAt(type: string, existingChild: CSTNode): CSTNode;
|
|
60
|
-
finishNode(_node: CSTNode, _startTok: Token): void;
|
|
61
|
-
addAnonymousChild(parent: CSTNode, token: Token): void;
|
|
62
|
-
private parseSourceFile;
|
|
63
|
-
private eofToken;
|
|
64
|
-
}
|
|
65
|
-
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -1,163 +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
|
-
* Recursive descent parser for AgentScript.
|
|
9
|
-
*
|
|
10
|
-
* Core invariant: NEWLINE and DEDENT are unconditional synchronization points.
|
|
11
|
-
* Every parse function that encounters an unexpected token calls synchronize()
|
|
12
|
-
* which skips to the next NEWLINE/DEDENT/EOF.
|
|
13
|
-
*/
|
|
14
|
-
import { isTokenKind, TokenKind } from './token.js';
|
|
15
|
-
import { Lexer } from './lexer.js';
|
|
16
|
-
import { CSTNode } from './cst-node.js';
|
|
17
|
-
import { isSyncPoint } from './errors.js';
|
|
18
|
-
import { synchronize, skipNewlines, consumeCommentsAndSkipNewlines, isAtEnd, } from './recovery.js';
|
|
19
|
-
import { parseMappingOrExpression } from './parse-mapping.js';
|
|
20
|
-
import { parseSequence } from './parse-sequence.js';
|
|
21
|
-
import invariant from 'tiny-invariant';
|
|
22
|
-
export class Parser {
|
|
23
|
-
source;
|
|
24
|
-
tokens;
|
|
25
|
-
pos = 0;
|
|
26
|
-
_eof;
|
|
27
|
-
constructor(source) {
|
|
28
|
-
this.source = source;
|
|
29
|
-
const lexer = new Lexer(source);
|
|
30
|
-
this.tokens = lexer.tokenize();
|
|
31
|
-
}
|
|
32
|
-
parse() {
|
|
33
|
-
const root = this.parseSourceFile();
|
|
34
|
-
return root;
|
|
35
|
-
}
|
|
36
|
-
// --- ParserContext implementation ---
|
|
37
|
-
peek() {
|
|
38
|
-
return this.peekAt(0);
|
|
39
|
-
}
|
|
40
|
-
peekAt(offset) {
|
|
41
|
-
// n.b. (Allen): Because this is called so frequently, these invariants cause significant runtime overhead.
|
|
42
|
-
// invariant(this.pos + offset >= 0, 'peekAt too small');
|
|
43
|
-
// invariant(this.pos + offset <= this.tokens.length, 'peekAt too large');
|
|
44
|
-
return this.peekAtIndex(this.pos + offset);
|
|
45
|
-
}
|
|
46
|
-
peekAtIndex(idx) {
|
|
47
|
-
return this.tokens[idx] ?? this.eofToken();
|
|
48
|
-
}
|
|
49
|
-
peekKind() {
|
|
50
|
-
return this.peek().kind;
|
|
51
|
-
}
|
|
52
|
-
consume() {
|
|
53
|
-
const tok = this.peek();
|
|
54
|
-
this.pos++;
|
|
55
|
-
return tok;
|
|
56
|
-
}
|
|
57
|
-
consumeKind(kind) {
|
|
58
|
-
const tok = this.peek();
|
|
59
|
-
invariant(isTokenKind(tok, kind), `Expected token kind ${kind} but got ${tok.kind}`);
|
|
60
|
-
this.pos++;
|
|
61
|
-
return tok;
|
|
62
|
-
}
|
|
63
|
-
consumeNamed(type) {
|
|
64
|
-
const tok = this.consume();
|
|
65
|
-
const offset = tok.startOffset;
|
|
66
|
-
return new CSTNode(type, this.source, offset, offset + tok.text.length, tok.start, tok.end);
|
|
67
|
-
}
|
|
68
|
-
currentOffset() {
|
|
69
|
-
const idx = this.pos > 0 ? this.pos - 1 : 0;
|
|
70
|
-
return this.peekAtIndex(idx).startOffset;
|
|
71
|
-
}
|
|
72
|
-
peekOffset() {
|
|
73
|
-
return this.peek().startOffset;
|
|
74
|
-
}
|
|
75
|
-
isAtSyncPoint() {
|
|
76
|
-
return isSyncPoint(this.peekKind());
|
|
77
|
-
}
|
|
78
|
-
startNode(type) {
|
|
79
|
-
const tok = this.peek();
|
|
80
|
-
const offset = tok.startOffset;
|
|
81
|
-
return new CSTNode(type, this.source, offset, offset, tok.start, tok.end);
|
|
82
|
-
}
|
|
83
|
-
startNodeAt(type, existingChild) {
|
|
84
|
-
return new CSTNode(type, this.source, existingChild.startOffset, existingChild.endOffset, existingChild.startPosition, existingChild.endPosition);
|
|
85
|
-
}
|
|
86
|
-
finishNode(_node, _startTok) {
|
|
87
|
-
// No-op: appendChild() tracks end position incrementally.
|
|
88
|
-
}
|
|
89
|
-
addAnonymousChild(parent, token) {
|
|
90
|
-
const offset = token.startOffset;
|
|
91
|
-
const child = new CSTNode(token.text, this.source, offset, offset + token.text.length, token.start, token.end, false);
|
|
92
|
-
parent.appendChild(child);
|
|
93
|
-
}
|
|
94
|
-
// --- Top-level parsing ---
|
|
95
|
-
parseSourceFile() {
|
|
96
|
-
const node = this.startNode('source_file');
|
|
97
|
-
// Skip leading newlines and indentation (handles template literals with leading whitespace)
|
|
98
|
-
skipNewlines(this);
|
|
99
|
-
if (this.peekKind() === TokenKind.INDENT) {
|
|
100
|
-
this.consume();
|
|
101
|
-
}
|
|
102
|
-
// Consume leading comments at source_file level (tree-sitter treats them as extras)
|
|
103
|
-
consumeCommentsAndSkipNewlines(this, node);
|
|
104
|
-
// Determine what kind of source file this is
|
|
105
|
-
if (this.peekKind() === TokenKind.DASH_SPACE) {
|
|
106
|
-
// Sequence
|
|
107
|
-
node.appendChild(parseSequence(this));
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
// Mapping or expression
|
|
111
|
-
// n.b. (Allen): Originally we didn't permit expressions at the top level, but
|
|
112
|
-
// we did that to make testing easier in tree-sitter so I suppose
|
|
113
|
-
// we can just make this a feature of the language.
|
|
114
|
-
const content = parseMappingOrExpression(this, _ctx => parseSequence(_ctx));
|
|
115
|
-
if (content)
|
|
116
|
-
node.appendChild(content);
|
|
117
|
-
}
|
|
118
|
-
// Consume trailing comments at source_file level
|
|
119
|
-
consumeCommentsAndSkipNewlines(this, node);
|
|
120
|
-
// Catch-all: if there are unconsumed tokens, wrap them in ERROR nodes.
|
|
121
|
-
// This ensures every byte of source is represented in the CST.
|
|
122
|
-
while (!isAtEnd(this)) {
|
|
123
|
-
if (this.peekKind() === TokenKind.NEWLINE ||
|
|
124
|
-
this.peekKind() === TokenKind.DEDENT) {
|
|
125
|
-
this.consume();
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
if (this.peekKind() === TokenKind.COMMENT) {
|
|
129
|
-
node.appendChild(this.consumeNamed('comment'));
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
const err = synchronize(this);
|
|
133
|
-
if (err) {
|
|
134
|
-
node.appendChild(err);
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
// Consume one token to guarantee progress
|
|
138
|
-
this.consume();
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
// Root node must span entire source (matches tree-sitter invariant)
|
|
142
|
-
node.startOffset = 0;
|
|
143
|
-
node.startPosition = { row: 0, column: 0 };
|
|
144
|
-
node.endOffset = this.source.length;
|
|
145
|
-
node.endPosition = this.eofToken().end;
|
|
146
|
-
return node;
|
|
147
|
-
}
|
|
148
|
-
eofToken() {
|
|
149
|
-
if (!this._eof) {
|
|
150
|
-
const lastToken = this.tokens[this.tokens.length - 1];
|
|
151
|
-
const pos = lastToken ? lastToken.end : { row: 0, column: 0 };
|
|
152
|
-
this._eof = {
|
|
153
|
-
kind: TokenKind.EOF,
|
|
154
|
-
text: '',
|
|
155
|
-
start: pos,
|
|
156
|
-
end: pos,
|
|
157
|
-
startOffset: this.source.length,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
return this._eof;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
//# sourceMappingURL=parser.js.map
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Recovery and utility functions extracted from parser.ts.
|
|
3
|
-
*
|
|
4
|
-
* All functions take a ParserContext as their first argument,
|
|
5
|
-
* following the free-function pattern established by expressions.ts.
|
|
6
|
-
*/
|
|
7
|
-
import { TokenKind } from './token.js';
|
|
8
|
-
import { CSTNode } from './cst-node.js';
|
|
9
|
-
import type { ParserContext } from './parser.js';
|
|
10
|
-
/** Create an empty ERROR node at the current position. */
|
|
11
|
-
export declare function makeEmptyError(ctx: ParserContext): CSTNode;
|
|
12
|
-
/** Insert a missing target: `target: (expression (atom (ERROR)))` */
|
|
13
|
-
export declare function addMissingTarget(ctx: ParserContext, node: CSTNode): void;
|
|
14
|
-
/** Create a MISSING node — an expected token/node that wasn't found in source. */
|
|
15
|
-
export declare function makeMissing(ctx: ParserContext, type: string): CSTNode;
|
|
16
|
-
/**
|
|
17
|
-
* Parse a standalone else/elif/for (without a preceding if, or unsupported).
|
|
18
|
-
* Wraps the entire block in an ERROR node, preserving parsed statements inside.
|
|
19
|
-
*
|
|
20
|
-
* @param parseProcedure - callback to parse procedure bodies, avoiding circular
|
|
21
|
-
* dependency with parse-statements.ts
|
|
22
|
-
*/
|
|
23
|
-
export declare function parseOrphanBlock(ctx: ParserContext, parseProcedure: (ctx: ParserContext) => CSTNode): CSTNode;
|
|
24
|
-
/**
|
|
25
|
-
* Consume any leftover tokens in an indented block (before DEDENT) as ERROR
|
|
26
|
-
* nodes. Prevents cascading failures when parseBlockValue() only partially
|
|
27
|
-
* consumes the block content (e.g., unquoted multi-word text).
|
|
28
|
-
*/
|
|
29
|
-
export declare function recoverToBlockEnd(ctx: ParserContext, parent: CSTNode): void;
|
|
30
|
-
/**
|
|
31
|
-
* Synchronize: skip tokens until a stopping condition is met.
|
|
32
|
-
* Returns an ERROR node wrapping the skipped content, or null if
|
|
33
|
-
* nothing was consumed.
|
|
34
|
-
*
|
|
35
|
-
* @param extraStop - optional predicate for additional stop conditions
|
|
36
|
-
* beyond the default sync points (NEWLINE/DEDENT/EOF)
|
|
37
|
-
*/
|
|
38
|
-
export declare function synchronizeUntil(ctx: ParserContext, extraStop?: (kind: TokenKind, row: number) => boolean): CSTNode | null;
|
|
39
|
-
/** Skip tokens on the given row until a sync point, INDENT, or COLON. */
|
|
40
|
-
export declare function synchronizeRowUntilColon(ctx: ParserContext, row: number): CSTNode | null;
|
|
41
|
-
/** Skip tokens on the given row until a sync point or INDENT. */
|
|
42
|
-
export declare function synchronizeRow(ctx: ParserContext, row: number): CSTNode | null;
|
|
43
|
-
/** Skip tokens until the next sync point (NEWLINE/DEDENT/EOF). */
|
|
44
|
-
export declare function synchronize(ctx: ParserContext): CSTNode | null;
|
|
45
|
-
export declare function skipNewlines(ctx: ParserContext): void;
|
|
46
|
-
/** Consume comment and newline tokens and attach to parent node. */
|
|
47
|
-
export declare function consumeCommentsAndSkipNewlines(ctx: ParserContext, parent: CSTNode): void;
|
|
48
|
-
export declare function isAtEnd(ctx: ParserContext): boolean;
|
|
49
|
-
/** Check if from current position, there are only comments, newlines, and then EOF/DEDENT. */
|
|
50
|
-
export declare function isTrailingCommentOnly(ctx: ParserContext): boolean;
|
|
51
|
-
//# sourceMappingURL=recovery.d.ts.map
|