agency-lang 0.0.56 → 0.0.58
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/README.md +1 -1
- package/dist/lib/backends/agencyGenerator.js +0 -4
- package/dist/lib/backends/baseGenerator.d.ts +2 -19
- package/dist/lib/backends/baseGenerator.js +6 -27
- package/dist/lib/backends/graphGenerator.js +0 -10
- package/dist/lib/backends/typescriptGenerator.js +18 -18
- package/dist/lib/cli/commands.js +5 -35
- package/dist/lib/cli/evaluate.d.ts +2 -1
- package/dist/lib/cli/evaluate.js +8 -2
- package/dist/lib/cli/test.d.ts +3 -2
- package/dist/lib/cli/test.js +36 -32
- package/dist/lib/cli/util.d.ts +16 -5
- package/dist/lib/cli/util.js +23 -2
- package/dist/lib/preprocessors/typescriptPreprocessor.d.ts +6 -0
- package/dist/lib/preprocessors/typescriptPreprocessor.js +67 -0
- package/dist/lib/typeChecker.js +12 -6
- package/dist/lib/types/literals.d.ts +3 -0
- package/dist/lib/types.d.ts +24 -6
- package/dist/lib/types.js +11 -3
- package/dist/lib/utils/node.d.ts +3 -2
- package/dist/lib/utils/node.js +33 -25
- package/dist/scripts/agency.js +117 -77
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ agency infile.agency
|
|
|
25
25
|
Note that the generated files use several other libraries that you will need to install as well:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
pnpm i nanoid openai piemachine statelog-client zod
|
|
28
|
+
pnpm i nanoid openai piemachine statelog-client zod smoltalk
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
## troubleshooting
|
|
@@ -186,14 +186,12 @@ export class AgencyGenerator extends BaseGenerator {
|
|
|
186
186
|
.join("\n");
|
|
187
187
|
result += `${docLines}\n`;
|
|
188
188
|
}
|
|
189
|
-
this.functionScopedVariables = [...parameters.map((p) => p.name)];
|
|
190
189
|
const lines = [];
|
|
191
190
|
for (const stmt of body) {
|
|
192
191
|
lines.push(this.processNode(stmt));
|
|
193
192
|
}
|
|
194
193
|
const bodyCode = lines.join("").trimEnd() + "\n";
|
|
195
194
|
result += bodyCode;
|
|
196
|
-
this.functionScopedVariables = [];
|
|
197
195
|
this.decreaseIndent();
|
|
198
196
|
// Close function
|
|
199
197
|
result += this.indentStr(`}`);
|
|
@@ -365,14 +363,12 @@ export class AgencyGenerator extends BaseGenerator {
|
|
|
365
363
|
: "";
|
|
366
364
|
let result = this.indentStr(`node ${nodeName}(${params})${returnTypeStr} {\n`);
|
|
367
365
|
this.increaseIndent();
|
|
368
|
-
this.functionScopedVariables = parameters.map((p) => p.name);
|
|
369
366
|
const lines = [];
|
|
370
367
|
for (const stmt of body) {
|
|
371
368
|
lines.push(this.processNode(stmt));
|
|
372
369
|
}
|
|
373
370
|
const bodyCode = lines.join("").trimEnd() + "\n";
|
|
374
371
|
result += bodyCode;
|
|
375
|
-
this.functionScopedVariables = [];
|
|
376
372
|
this.decreaseIndent();
|
|
377
373
|
result += this.indentStr(`}`);
|
|
378
374
|
return result;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SpecialVar } from "../types/specialVar.js";
|
|
2
|
-
import { AgencyComment, AgencyNode, AgencyProgram, Assignment, Literal, NewLine, PromptLiteral, TypeAlias, TypeHint, TypeHintMap, VariableType } from "../types.js";
|
|
2
|
+
import { AgencyComment, AgencyNode, AgencyProgram, Assignment, Literal, NewLine, PromptLiteral, Scope, ScopeType, TypeAlias, TypeHint, TypeHintMap, VariableType } from "../types.js";
|
|
3
3
|
import { AwaitStatement } from "../types/await.js";
|
|
4
4
|
import { TimeBlock } from "../types/timeBlock.js";
|
|
5
5
|
import { AccessExpression, DotFunctionCall, DotProperty, IndexAccess } from "../types/access.js";
|
|
@@ -15,26 +15,11 @@ import { WhileLoop } from "../types/whileLoop.js";
|
|
|
15
15
|
import { AgencyConfig } from "../config.js";
|
|
16
16
|
import { MessageThread } from "../types/messageThread.js";
|
|
17
17
|
import { Skill } from "../types/skill.js";
|
|
18
|
-
type Scope = GlobalScope | FunctionScope | NodeScope;
|
|
19
|
-
type GlobalScope = {
|
|
20
|
-
type: "global";
|
|
21
|
-
};
|
|
22
|
-
type FunctionScope = {
|
|
23
|
-
type: "function";
|
|
24
|
-
functionName: string;
|
|
25
|
-
};
|
|
26
|
-
type NodeScope = {
|
|
27
|
-
type: "node";
|
|
28
|
-
nodeName: string;
|
|
29
|
-
};
|
|
30
18
|
export declare class BaseGenerator {
|
|
31
19
|
protected typeHints: TypeHintMap;
|
|
32
20
|
protected graphNodes: GraphNodeDefinition[];
|
|
33
21
|
protected generatedStatements: string[];
|
|
34
22
|
protected generatedTypeAliases: string[];
|
|
35
|
-
protected functionScopedVariables: string[];
|
|
36
|
-
protected globalScopedVariables: string[];
|
|
37
|
-
protected functionParameters: string[];
|
|
38
23
|
protected typeAliases: Record<string, VariableType>;
|
|
39
24
|
protected functionsUsed: Set<string>;
|
|
40
25
|
protected importStatements: string[];
|
|
@@ -94,9 +79,7 @@ export declare class BaseGenerator {
|
|
|
94
79
|
protected startScope(scope: Scope): void;
|
|
95
80
|
protected endScope(): void;
|
|
96
81
|
protected getCurrentScope(): Scope;
|
|
97
|
-
protected
|
|
98
|
-
protected generateScopedVariableName(variableName: string): string;
|
|
82
|
+
protected scopetoString(scope: ScopeType): string;
|
|
99
83
|
protected isImportedTool(functionName: string): boolean;
|
|
100
84
|
protected isAgencyFunction(functionName: string): boolean;
|
|
101
85
|
}
|
|
102
|
-
export {};
|
|
@@ -4,9 +4,6 @@ export class BaseGenerator {
|
|
|
4
4
|
graphNodes = [];
|
|
5
5
|
generatedStatements = [];
|
|
6
6
|
generatedTypeAliases = [];
|
|
7
|
-
functionScopedVariables = [];
|
|
8
|
-
globalScopedVariables = [];
|
|
9
|
-
functionParameters = [];
|
|
10
7
|
typeAliases = {};
|
|
11
8
|
// collect functions used to see what builtin helpers to include
|
|
12
9
|
functionsUsed = new Set();
|
|
@@ -64,12 +61,6 @@ export class BaseGenerator {
|
|
|
64
61
|
this.collectFunctionSignature(node);
|
|
65
62
|
}
|
|
66
63
|
}
|
|
67
|
-
// Pass 6: Collect global scoped variables
|
|
68
|
-
for (const node of program.nodes) {
|
|
69
|
-
if (node.type === "assignment") {
|
|
70
|
-
this.globalScopedVariables.push(node.variableName);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
64
|
/* For each function, mark whether it is async or not.
|
|
74
65
|
A function has to be run synchronously if
|
|
75
66
|
- it or any of its child functions could throw an interrupt
|
|
@@ -294,31 +285,19 @@ export class BaseGenerator {
|
|
|
294
285
|
getCurrentScope() {
|
|
295
286
|
return this.currentScope[this.currentScope.length - 1];
|
|
296
287
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
which is used when retrieving the value of a variable. */
|
|
300
|
-
getScopeVar() {
|
|
301
|
-
const currentScope = this.getCurrentScope();
|
|
302
|
-
switch (currentScope.type) {
|
|
288
|
+
scopetoString(scope) {
|
|
289
|
+
switch (scope) {
|
|
303
290
|
case "global":
|
|
304
291
|
return "__stateStack.globals";
|
|
305
292
|
case "function":
|
|
306
293
|
case "node":
|
|
307
294
|
return "__stack.locals";
|
|
295
|
+
case "args":
|
|
296
|
+
return "__stack.args";
|
|
297
|
+
default:
|
|
298
|
+
throw new Error(`Unknown scope type: ${scope}`);
|
|
308
299
|
}
|
|
309
300
|
}
|
|
310
|
-
generateScopedVariableName(variableName) {
|
|
311
|
-
if (this.functionParameters.includes(variableName)) {
|
|
312
|
-
return `__stack.args.${variableName}`;
|
|
313
|
-
}
|
|
314
|
-
if (this.functionScopedVariables.includes(variableName)) {
|
|
315
|
-
return `__stack.locals.${variableName}`;
|
|
316
|
-
}
|
|
317
|
-
else if (this.globalScopedVariables.includes(variableName)) {
|
|
318
|
-
return `__stateStack.globals.${variableName}`;
|
|
319
|
-
}
|
|
320
|
-
return variableName;
|
|
321
|
-
}
|
|
322
301
|
isImportedTool(functionName) {
|
|
323
302
|
return this.importedTools
|
|
324
303
|
.map((node) => node.importedTools)
|
|
@@ -22,11 +22,6 @@ export class GraphGenerator extends TypeScriptGenerator {
|
|
|
22
22
|
}
|
|
23
23
|
configDefaults() {
|
|
24
24
|
return {
|
|
25
|
-
log: {
|
|
26
|
-
host: "https://statelog.adit.io",
|
|
27
|
-
projectId: "agency-lang",
|
|
28
|
-
debugMode: false,
|
|
29
|
-
},
|
|
30
25
|
client: {
|
|
31
26
|
logLevel: "warn",
|
|
32
27
|
defaultModel: "gpt-4o-mini",
|
|
@@ -61,18 +56,13 @@ export class GraphGenerator extends TypeScriptGenerator {
|
|
|
61
56
|
} */
|
|
62
57
|
this.adjacentNodes[nodeName] = [];
|
|
63
58
|
this.currentAdjacentNodes = [];
|
|
64
|
-
this.functionParameters = [];
|
|
65
59
|
this.isInsideGraphNode = true;
|
|
66
|
-
for (const param of parameters) {
|
|
67
|
-
this.functionParameters.push(param.name);
|
|
68
|
-
}
|
|
69
60
|
for (const stmt of body) {
|
|
70
61
|
if (stmt.type === "functionCall" && this.isGraphNode(stmt.functionName)) {
|
|
71
62
|
throw new Error(`Call to graph node '${stmt.functionName}' inside graph node '${nodeName}' was not returned. All calls to graph nodes must be returned, eg (return ${stmt.functionName}(...)).`);
|
|
72
63
|
}
|
|
73
64
|
}
|
|
74
65
|
const bodyCode = this.processBodyAsParts(body);
|
|
75
|
-
this.functionParameters = [];
|
|
76
66
|
this.adjacentNodes[nodeName] = [...this.currentAdjacentNodes];
|
|
77
67
|
this.isInsideGraphNode = false;
|
|
78
68
|
this.endScope();
|
|
@@ -132,13 +132,7 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
132
132
|
}
|
|
133
133
|
processAssignment(node) {
|
|
134
134
|
const { variableName, typeHint, value } = node;
|
|
135
|
-
const
|
|
136
|
-
if (_currentScope.type === "global") {
|
|
137
|
-
this.globalScopedVariables.push(variableName);
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
this.functionScopedVariables.push(variableName);
|
|
141
|
-
}
|
|
135
|
+
const scopeVar = this.scopetoString(node.scope);
|
|
142
136
|
const typeAnnotation = "";
|
|
143
137
|
if (value.type === "prompt") {
|
|
144
138
|
return this.processPromptLiteral(variableName, typeHint, value);
|
|
@@ -147,7 +141,7 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
147
141
|
// Direct assignment for other literal types
|
|
148
142
|
const code = this.processNode(value);
|
|
149
143
|
return renderFunctionCallAssignment.default({
|
|
150
|
-
variableName: `${
|
|
144
|
+
variableName: `${scopeVar}.${variableName}`,
|
|
151
145
|
functionCode: code.trim(),
|
|
152
146
|
nodeContext: this.getCurrentScope().type === "node",
|
|
153
147
|
globalScope: this.getCurrentScope().type === "global",
|
|
@@ -159,14 +153,13 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
159
153
|
return code;
|
|
160
154
|
}
|
|
161
155
|
else if (value.type === "messageThread") {
|
|
162
|
-
const varName = `${
|
|
156
|
+
const varName = `${scopeVar}.${variableName}`;
|
|
163
157
|
return this.processMessageThread(value, varName);
|
|
164
158
|
}
|
|
165
159
|
else {
|
|
166
160
|
// Direct assignment for other literal types
|
|
167
161
|
const code = this.processNode(value);
|
|
168
|
-
return (`${
|
|
169
|
-
"\n");
|
|
162
|
+
return (`${scopeVar}.${variableName}${typeAnnotation} = ${code.trim()};` + "\n");
|
|
170
163
|
}
|
|
171
164
|
}
|
|
172
165
|
/*
|
|
@@ -240,11 +233,7 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
240
233
|
this.startScope({ type: "function", functionName: node.functionName });
|
|
241
234
|
const { functionName, body, parameters } = node;
|
|
242
235
|
const args = parameters.map((p) => p.name);
|
|
243
|
-
this.functionScopedVariables = [...parameters.map((p) => p.name)];
|
|
244
|
-
this.functionParameters = args;
|
|
245
236
|
const bodyCode = this.processBodyAsParts(body);
|
|
246
|
-
this.functionScopedVariables = [];
|
|
247
|
-
this.functionParameters = [];
|
|
248
237
|
this.endScope();
|
|
249
238
|
const argsStr = args.map((arg) => `"${arg}"`).join(", ") || "";
|
|
250
239
|
return renderFunctionDefinition.default({
|
|
@@ -318,7 +307,7 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
318
307
|
case "multiLineString":
|
|
319
308
|
return this.generateStringLiteral(literal.segments);
|
|
320
309
|
case "variableName":
|
|
321
|
-
return this.
|
|
310
|
+
return `${this.scopetoString(literal.scope)}.${literal.value}`;
|
|
322
311
|
case "prompt":
|
|
323
312
|
return this.processPromptLiteral(DEFAULT_PROMPT_NAME, this.getScopeReturnType(), literal);
|
|
324
313
|
}
|
|
@@ -393,7 +382,11 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
393
382
|
}
|
|
394
383
|
else {
|
|
395
384
|
// Interpolation segment
|
|
396
|
-
stringParts.push("${" +
|
|
385
|
+
stringParts.push("${" +
|
|
386
|
+
this.scopetoString(segment.scope) +
|
|
387
|
+
"." +
|
|
388
|
+
segment.variableName +
|
|
389
|
+
"}");
|
|
397
390
|
}
|
|
398
391
|
}
|
|
399
392
|
return "`" + stringParts.join("") + "`";
|
|
@@ -461,7 +454,14 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
461
454
|
const metadataObj = `{
|
|
462
455
|
messages: __self.messages_${this.currentMessageThreadNodeId.at(-1)}.getMessages(),
|
|
463
456
|
}`;
|
|
464
|
-
const scopedFunctionArgs = functionArgs.map((arg) =>
|
|
457
|
+
const scopedFunctionArgs = functionArgs.map((arg) => {
|
|
458
|
+
// Find the scope for this interpolated variable from the prompt segments
|
|
459
|
+
const interpSegment = prompt.segments.find((s) => s.type === "interpolation" && s.variableName === arg);
|
|
460
|
+
const scope = interpSegment?.type === "interpolation"
|
|
461
|
+
? interpSegment.scope
|
|
462
|
+
: undefined;
|
|
463
|
+
return `${this.scopetoString(scope)}.${arg}`;
|
|
464
|
+
});
|
|
465
465
|
return promptFunction.default({
|
|
466
466
|
variableName,
|
|
467
467
|
argsStr,
|
package/dist/lib/cli/commands.js
CHANGED
|
@@ -6,6 +6,7 @@ import { spawn } from "child_process";
|
|
|
6
6
|
import * as fs from "fs";
|
|
7
7
|
import * as path from "path";
|
|
8
8
|
import { parseAgency } from "../parser.js";
|
|
9
|
+
import { findRecursively } from "./util.js";
|
|
9
10
|
// Load configuration from agency.json
|
|
10
11
|
export function loadConfig(configPath, verbose = false) {
|
|
11
12
|
let config = {};
|
|
@@ -94,31 +95,13 @@ export function getImports(program) {
|
|
|
94
95
|
return [...toolAndNodeImports, ...importStatements];
|
|
95
96
|
}
|
|
96
97
|
const compiledFiles = new Set();
|
|
97
|
-
const dirSearched = new Set();
|
|
98
98
|
export function compile(config, inputFile, _outputFile) {
|
|
99
99
|
// Check if the input is a directory
|
|
100
100
|
const stats = fs.statSync(inputFile);
|
|
101
101
|
const verbose = config.verbose ?? false;
|
|
102
102
|
if (stats.isDirectory()) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const files = fs.readdirSync(inputFile);
|
|
106
|
-
const agencyFiles = files.filter((file) => file.endsWith(".agency"));
|
|
107
|
-
for (const file of agencyFiles) {
|
|
108
|
-
const fullPath = path.join(inputFile, file);
|
|
109
|
-
compile(config, fullPath, undefined);
|
|
110
|
-
}
|
|
111
|
-
// Find all subdirectories and compile their .agency files
|
|
112
|
-
const subdirs = files.filter((file) => {
|
|
113
|
-
const fullPath = path.join(inputFile, file);
|
|
114
|
-
return fs.statSync(fullPath).isDirectory();
|
|
115
|
-
});
|
|
116
|
-
for (const subdir of subdirs) {
|
|
117
|
-
const fullSubdirPath = path.join(inputFile, subdir);
|
|
118
|
-
const resolvedSubdirPath = path.resolve(fullSubdirPath);
|
|
119
|
-
if (!dirSearched.has(resolvedSubdirPath)) {
|
|
120
|
-
compile(config, fullSubdirPath, undefined);
|
|
121
|
-
}
|
|
103
|
+
for (const { path } of findRecursively(inputFile)) {
|
|
104
|
+
compile(config, path, undefined);
|
|
122
105
|
}
|
|
123
106
|
return null;
|
|
124
107
|
}
|
|
@@ -188,21 +171,8 @@ export async function format(contents, config) {
|
|
|
188
171
|
export function formatFile(inputPath, inPlace, config) {
|
|
189
172
|
const stats = fs.statSync(inputPath);
|
|
190
173
|
if (stats.isDirectory()) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const agencyFiles = files.filter((file) => file.endsWith(".agency"));
|
|
194
|
-
for (const file of agencyFiles) {
|
|
195
|
-
const fullPath = path.join(inputPath, file);
|
|
196
|
-
formatFile(fullPath, inPlace, config);
|
|
197
|
-
}
|
|
198
|
-
// Recursively format subdirectories
|
|
199
|
-
const subdirs = files.filter((file) => {
|
|
200
|
-
const fullPath = path.join(inputPath, file);
|
|
201
|
-
return fs.statSync(fullPath).isDirectory();
|
|
202
|
-
});
|
|
203
|
-
for (const subdir of subdirs) {
|
|
204
|
-
const fullSubdirPath = path.join(inputPath, subdir);
|
|
205
|
-
formatFile(fullSubdirPath, inPlace, config);
|
|
174
|
+
for (const { path } of findRecursively(inputPath)) {
|
|
175
|
+
formatFile(path, inPlace, config);
|
|
206
176
|
}
|
|
207
177
|
return;
|
|
208
178
|
}
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { AgencyConfig } from "../config.js";
|
|
2
|
+
export declare function evaluate(config: AgencyConfig, target?: string, argsFilePath?: string, resultsFilePath?: string): Promise<void>;
|
package/dist/lib/cli/evaluate.js
CHANGED
|
@@ -81,7 +81,7 @@ async function createArgsFileInteractively(filename, selectedNode) {
|
|
|
81
81
|
console.log(`Args file saved to ${filename}`);
|
|
82
82
|
return argsFile;
|
|
83
83
|
}
|
|
84
|
-
export async function evaluate(target, argsFilePath, resultsFilePath) {
|
|
84
|
+
export async function evaluate(config, target, argsFilePath, resultsFilePath) {
|
|
85
85
|
// A. Resolve target
|
|
86
86
|
let { filename, nodeName } = target
|
|
87
87
|
? parseTarget(target)
|
|
@@ -162,7 +162,13 @@ export async function evaluate(target, argsFilePath, resultsFilePath) {
|
|
|
162
162
|
const argsString = hasArgs
|
|
163
163
|
? argsRecordToString(c.args, selectedNode.parameters)
|
|
164
164
|
: "";
|
|
165
|
-
const json = executeNode(
|
|
165
|
+
const json = executeNode({
|
|
166
|
+
config,
|
|
167
|
+
agencyFile: filename,
|
|
168
|
+
nodeName,
|
|
169
|
+
hasArgs,
|
|
170
|
+
argsString,
|
|
171
|
+
});
|
|
166
172
|
console.log("\nOutput:");
|
|
167
173
|
console.log(JSON.stringify(json.data, null, 2));
|
|
168
174
|
const ratingResponse = await prompts({
|
package/dist/lib/cli/test.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function
|
|
1
|
+
import { AgencyConfig } from "../config.js";
|
|
2
|
+
export declare function fixtures(config: AgencyConfig, target?: string): Promise<void>;
|
|
3
|
+
export declare function test(config: AgencyConfig, testFile: string): Promise<void>;
|
package/dist/lib/cli/test.js
CHANGED
|
@@ -2,8 +2,9 @@ import { parseAgency } from "../parser.js";
|
|
|
2
2
|
import { getNodesOfType } from "../utils/node.js";
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import prompts from "prompts";
|
|
5
|
-
import { executeJudge, executeNode, parseTarget, pickANode, promptForArgs, promptForTarget, } from "./util.js";
|
|
5
|
+
import { executeJudge, executeNode, findRecursively, parseTarget, pickANode, promptForArgs, promptForTarget, } from "./util.js";
|
|
6
6
|
import { color } from "termcolors";
|
|
7
|
+
import path from "path";
|
|
7
8
|
function readFile(filename) {
|
|
8
9
|
console.log("Trying to read file", filename, "...");
|
|
9
10
|
const data = fs.readFileSync(filename);
|
|
@@ -17,7 +18,7 @@ function writeTestCase(agencyFilename, nodeName, input, expectedOutput, evaluati
|
|
|
17
18
|
tests = JSON.parse(fs.readFileSync(testFilePath, "utf-8"));
|
|
18
19
|
}
|
|
19
20
|
else {
|
|
20
|
-
tests = { sourceFile: agencyFilename, tests: [] };
|
|
21
|
+
tests = { sourceFile: path.basename(agencyFilename), tests: [] };
|
|
21
22
|
}
|
|
22
23
|
const testCase = {
|
|
23
24
|
nodeName,
|
|
@@ -32,7 +33,7 @@ function writeTestCase(agencyFilename, nodeName, input, expectedOutput, evaluati
|
|
|
32
33
|
fs.writeFileSync(testFilePath, JSON.stringify(tests, null, 2));
|
|
33
34
|
return testFilePath;
|
|
34
35
|
}
|
|
35
|
-
export async function fixtures(target) {
|
|
36
|
+
export async function fixtures(config, target) {
|
|
36
37
|
let { filename, nodeName } = target
|
|
37
38
|
? parseTarget(target)
|
|
38
39
|
: await promptForTarget();
|
|
@@ -56,7 +57,13 @@ export async function fixtures(target) {
|
|
|
56
57
|
const selectedNode = nodes.find((n) => n.nodeName === nodeName);
|
|
57
58
|
let { hasArgs, argsString } = await promptForArgs(selectedNode);
|
|
58
59
|
console.log("Running program from entrypoint", nodeName);
|
|
59
|
-
let json = executeNode(
|
|
60
|
+
let json = executeNode({
|
|
61
|
+
config,
|
|
62
|
+
agencyFile: filename,
|
|
63
|
+
nodeName,
|
|
64
|
+
hasArgs,
|
|
65
|
+
argsString,
|
|
66
|
+
});
|
|
60
67
|
// Handle interrupt discovery
|
|
61
68
|
const interruptHandlers = [];
|
|
62
69
|
while (json.data &&
|
|
@@ -105,7 +112,14 @@ export async function fixtures(target) {
|
|
|
105
112
|
}
|
|
106
113
|
interruptHandlers.push(handler);
|
|
107
114
|
// Continue execution with this handler to see if there are more interrupts
|
|
108
|
-
json = executeNode(
|
|
115
|
+
json = executeNode({
|
|
116
|
+
config,
|
|
117
|
+
agencyFile: filename,
|
|
118
|
+
nodeName,
|
|
119
|
+
hasArgs,
|
|
120
|
+
argsString,
|
|
121
|
+
interruptHandlers,
|
|
122
|
+
});
|
|
109
123
|
}
|
|
110
124
|
console.log("\nFinal Output:");
|
|
111
125
|
console.log(JSON.stringify(json.data, null, 2));
|
|
@@ -166,34 +180,16 @@ export async function fixtures(target) {
|
|
|
166
180
|
const testFilePath = writeTestCase(filename, nodeName, inputStr, expectedOutput, criteria, interruptHandlers.length > 0 ? interruptHandlers : undefined);
|
|
167
181
|
console.log(`Test case saved to ${testFilePath}`);
|
|
168
182
|
}
|
|
169
|
-
export async function test(testFile) {
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
else {
|
|
175
|
-
const testFiles = fs
|
|
176
|
-
.readdirSync(process.cwd())
|
|
177
|
-
.filter((file) => file.endsWith(".test.json"))
|
|
178
|
-
.map((file) => ({
|
|
179
|
-
title: file,
|
|
180
|
-
value: file,
|
|
181
|
-
}));
|
|
182
|
-
if (testFiles.length === 0) {
|
|
183
|
-
console.log("No .test.json files found in the current directory.");
|
|
184
|
-
return;
|
|
183
|
+
export async function test(config, testFile) {
|
|
184
|
+
const stats = fs.statSync(testFile);
|
|
185
|
+
if (stats.isDirectory()) {
|
|
186
|
+
for (const { path } of findRecursively(testFile, ".test.json")) {
|
|
187
|
+
await test(config, path);
|
|
185
188
|
}
|
|
186
|
-
|
|
187
|
-
type: "select",
|
|
188
|
-
name: "filename",
|
|
189
|
-
message: "Select a test file to run:",
|
|
190
|
-
choices: testFiles,
|
|
191
|
-
});
|
|
192
|
-
if (!response.filename)
|
|
193
|
-
return;
|
|
194
|
-
selectedFile = response.filename;
|
|
189
|
+
return;
|
|
195
190
|
}
|
|
196
|
-
|
|
191
|
+
console.log(color.yellow(`Running tests for ${testFile}...`));
|
|
192
|
+
const tests = JSON.parse(fs.readFileSync(testFile, "utf-8"));
|
|
197
193
|
let passed = 0;
|
|
198
194
|
const total = tests.tests.length;
|
|
199
195
|
for (let i = 0; i < total; i++) {
|
|
@@ -207,7 +203,15 @@ export async function test(testFile) {
|
|
|
207
203
|
if (testCase.description) {
|
|
208
204
|
console.log(color.cyan("Description:", testCase.description), "\n");
|
|
209
205
|
}
|
|
210
|
-
const
|
|
206
|
+
const relativeSourceFilePath = path.join(path.dirname(testFile), tests.sourceFile);
|
|
207
|
+
const result = executeNode({
|
|
208
|
+
config,
|
|
209
|
+
agencyFile: relativeSourceFilePath,
|
|
210
|
+
nodeName: testCase.nodeName,
|
|
211
|
+
hasArgs,
|
|
212
|
+
argsString: testCase.input,
|
|
213
|
+
interruptHandlers: testCase.interruptHandlers,
|
|
214
|
+
});
|
|
211
215
|
let testPassed = true;
|
|
212
216
|
for (const criterion of testCase.evaluationCriteria) {
|
|
213
217
|
if (criterion.type === "exact") {
|
package/dist/lib/cli/util.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GraphNodeDefinition, VariableType } from "../types.js";
|
|
2
|
+
import { AgencyConfig } from "../config.js";
|
|
2
3
|
export declare function parseTarget(target: string): {
|
|
3
4
|
filename: string;
|
|
4
5
|
nodeName: string;
|
|
@@ -12,11 +13,18 @@ export declare function promptForArgs(selectedNode: GraphNodeDefinition): Promis
|
|
|
12
13
|
hasArgs: boolean;
|
|
13
14
|
argsString: string;
|
|
14
15
|
}>;
|
|
15
|
-
export declare function executeNode(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
export declare function executeNode({ config, agencyFile, nodeName, hasArgs, argsString, interruptHandlers, }: {
|
|
17
|
+
config: AgencyConfig;
|
|
18
|
+
agencyFile: string;
|
|
19
|
+
nodeName: string;
|
|
20
|
+
hasArgs: boolean;
|
|
21
|
+
argsString: string;
|
|
22
|
+
interruptHandlers?: Array<{
|
|
23
|
+
action: "approve" | "reject" | "modify";
|
|
24
|
+
modifiedArgs?: Record<string, any>;
|
|
25
|
+
expectedMessage?: string;
|
|
26
|
+
}>;
|
|
27
|
+
}): {
|
|
20
28
|
data: any;
|
|
21
29
|
[key: string]: any;
|
|
22
30
|
};
|
|
@@ -29,3 +37,6 @@ export declare function executeJudge(actualOutput: string, expectedOutput: strin
|
|
|
29
37
|
score: number;
|
|
30
38
|
reasoning: string;
|
|
31
39
|
};
|
|
40
|
+
export declare function findRecursively(dirName: string, ext?: string, searched?: string[]): Generator<{
|
|
41
|
+
path: string;
|
|
42
|
+
}>;
|
package/dist/lib/cli/util.js
CHANGED
|
@@ -89,9 +89,9 @@ export async function promptForArgs(selectedNode) {
|
|
|
89
89
|
}
|
|
90
90
|
return { hasArgs, argsString };
|
|
91
91
|
}
|
|
92
|
-
export function executeNode(agencyFile, nodeName, hasArgs, argsString, interruptHandlers) {
|
|
92
|
+
export function executeNode({ config, agencyFile, nodeName, hasArgs, argsString, interruptHandlers, }) {
|
|
93
93
|
const outFile = agencyFile.replace(".agency", ".js");
|
|
94
|
-
compile(
|
|
94
|
+
compile(config, agencyFile);
|
|
95
95
|
const evaluateScript = renderEvaluate({
|
|
96
96
|
filename: outFile,
|
|
97
97
|
nodeName,
|
|
@@ -158,3 +158,24 @@ export function executeJudge(actualOutput, expectedOutput, judgePrompt, interrup
|
|
|
158
158
|
const results = readFileSync("__judge_evaluate.json", "utf-8");
|
|
159
159
|
return JSON.parse(results).data;
|
|
160
160
|
}
|
|
161
|
+
export function* findRecursively(dirName, ext = ".agency", searched = []) {
|
|
162
|
+
searched.push(path.resolve(dirName));
|
|
163
|
+
// Find all .agency files in the directory
|
|
164
|
+
const files = fs.readdirSync(dirName);
|
|
165
|
+
const filesToProcess = files.filter((file) => {
|
|
166
|
+
return ((file.endsWith(ext) ||
|
|
167
|
+
fs.statSync(path.join(dirName, file)).isDirectory()) &&
|
|
168
|
+
!file.startsWith("."));
|
|
169
|
+
});
|
|
170
|
+
for (const file of filesToProcess) {
|
|
171
|
+
const fullPath = path.join(dirName, file);
|
|
172
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
173
|
+
if (!searched.includes(path.resolve(fullPath))) {
|
|
174
|
+
yield* findRecursively(fullPath, ext, searched);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
yield { path: fullPath };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -90,4 +90,10 @@ export declare class TypescriptPreprocessor {
|
|
|
90
90
|
* Extract domain from URL string
|
|
91
91
|
*/
|
|
92
92
|
protected _extractDomain(url: string): string | null;
|
|
93
|
+
/**
|
|
94
|
+
* Resolve variable scopes by annotating AST nodes with their scope.
|
|
95
|
+
* After this pass, every VariableNameLiteral, InterpolationSegment, and Assignment
|
|
96
|
+
* will have a `scope` property indicating whether the variable is global, local, or args.
|
|
97
|
+
*/
|
|
98
|
+
protected resolveVariableScopes(): void;
|
|
93
99
|
}
|
|
@@ -25,6 +25,7 @@ export class TypescriptPreprocessor {
|
|
|
25
25
|
this.filterExcludedBuiltinFunctions();
|
|
26
26
|
this.validateFetchDomains();
|
|
27
27
|
this.addNodeIDsToMessageThreads();
|
|
28
|
+
this.resolveVariableScopes();
|
|
28
29
|
return this.program;
|
|
29
30
|
}
|
|
30
31
|
addNodeIDsToMessageThreads() {
|
|
@@ -903,4 +904,70 @@ export class TypescriptPreprocessor {
|
|
|
903
904
|
return null; // Invalid URL
|
|
904
905
|
}
|
|
905
906
|
}
|
|
907
|
+
/**
|
|
908
|
+
* Resolve variable scopes by annotating AST nodes with their scope.
|
|
909
|
+
* After this pass, every VariableNameLiteral, InterpolationSegment, and Assignment
|
|
910
|
+
* will have a `scope` property indicating whether the variable is global, local, or args.
|
|
911
|
+
*/
|
|
912
|
+
resolveVariableScopes() {
|
|
913
|
+
const varNameToScope = {};
|
|
914
|
+
// First, for each variable name, we try to collect its scope.
|
|
915
|
+
for (const { node, scopes } of walkNodes(this.program.nodes)) {
|
|
916
|
+
if (scopes.length === 0) {
|
|
917
|
+
throw new Error(`Top-level nodes should have at least the global scope in their scopes array. Node: ${JSON.stringify({ node })}, scopes: ${JSON.stringify({ scopes })}`);
|
|
918
|
+
}
|
|
919
|
+
if (node.type === "assignment") {
|
|
920
|
+
varNameToScope[node.variableName] = scopes.at(-1)?.type || "global";
|
|
921
|
+
}
|
|
922
|
+
else if (node.type === "function" || node.type === "graphNode") {
|
|
923
|
+
// Parameters are in the function's scope
|
|
924
|
+
for (const param of node.parameters) {
|
|
925
|
+
varNameToScope[param.name] = "args";
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
else if (node.type === "importStatement") {
|
|
929
|
+
// todo imported names need to get parsed better,
|
|
930
|
+
// into an array
|
|
931
|
+
}
|
|
932
|
+
else if (node.type === "importNodeStatement") {
|
|
933
|
+
node.importedNodes.forEach((n) => {
|
|
934
|
+
varNameToScope[n] = "global";
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
else if (node.type === "importToolStatement") {
|
|
938
|
+
node.importedTools.forEach((t) => {
|
|
939
|
+
varNameToScope[t] = "global";
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
const lookupScope = (varName) => {
|
|
944
|
+
if (varName in varNameToScope) {
|
|
945
|
+
return varNameToScope[varName];
|
|
946
|
+
}
|
|
947
|
+
return "global";
|
|
948
|
+
// TODO enable this
|
|
949
|
+
/* throw new Error(
|
|
950
|
+
`Variable "${varName}" is referenced but not defined in any scope.`,
|
|
951
|
+
); */
|
|
952
|
+
};
|
|
953
|
+
// Then, whenever we see a variable being referenced,
|
|
954
|
+
// we try to look up its scope and set it on that variable.
|
|
955
|
+
for (const { node, scopes } of walkNodes(this.program.nodes)) {
|
|
956
|
+
if (node.type === "assignment") {
|
|
957
|
+
node.scope = lookupScope(node.variableName);
|
|
958
|
+
}
|
|
959
|
+
else if (node.type === "variableName") {
|
|
960
|
+
node.scope = lookupScope(node.value);
|
|
961
|
+
}
|
|
962
|
+
else if (node.type === "prompt" ||
|
|
963
|
+
node.type === "string" ||
|
|
964
|
+
node.type === "multiLineString") {
|
|
965
|
+
node.segments.forEach((seg) => {
|
|
966
|
+
if (seg.type === "interpolation") {
|
|
967
|
+
seg.scope = lookupScope(seg.variableName);
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
906
973
|
}
|
package/dist/lib/typeChecker.js
CHANGED
|
@@ -319,22 +319,28 @@ export class TypeChecker {
|
|
|
319
319
|
return true;
|
|
320
320
|
}
|
|
321
321
|
// Same kind matching
|
|
322
|
-
if (resolvedSource.type === "primitiveType" &&
|
|
322
|
+
if (resolvedSource.type === "primitiveType" &&
|
|
323
|
+
resolvedTarget.type === "primitiveType") {
|
|
323
324
|
return resolvedSource.value === resolvedTarget.value;
|
|
324
325
|
}
|
|
325
|
-
if (resolvedSource.type === "stringLiteralType" &&
|
|
326
|
+
if (resolvedSource.type === "stringLiteralType" &&
|
|
327
|
+
resolvedTarget.type === "stringLiteralType") {
|
|
326
328
|
return resolvedSource.value === resolvedTarget.value;
|
|
327
329
|
}
|
|
328
|
-
if (resolvedSource.type === "numberLiteralType" &&
|
|
330
|
+
if (resolvedSource.type === "numberLiteralType" &&
|
|
331
|
+
resolvedTarget.type === "numberLiteralType") {
|
|
329
332
|
return resolvedSource.value === resolvedTarget.value;
|
|
330
333
|
}
|
|
331
|
-
if (resolvedSource.type === "booleanLiteralType" &&
|
|
334
|
+
if (resolvedSource.type === "booleanLiteralType" &&
|
|
335
|
+
resolvedTarget.type === "booleanLiteralType") {
|
|
332
336
|
return resolvedSource.value === resolvedTarget.value;
|
|
333
337
|
}
|
|
334
|
-
if (resolvedSource.type === "arrayType" &&
|
|
338
|
+
if (resolvedSource.type === "arrayType" &&
|
|
339
|
+
resolvedTarget.type === "arrayType") {
|
|
335
340
|
return this.isAssignable(resolvedSource.elementType, resolvedTarget.elementType);
|
|
336
341
|
}
|
|
337
|
-
if (resolvedSource.type === "objectType" &&
|
|
342
|
+
if (resolvedSource.type === "objectType" &&
|
|
343
|
+
resolvedTarget.type === "objectType") {
|
|
338
344
|
// Structural: source must have all properties of target with compatible types
|
|
339
345
|
for (const targetProp of resolvedTarget.properties) {
|
|
340
346
|
const sourceProp = resolvedSource.properties.find((p) => p.key === targetProp.key);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ScopeType } from "../types.js";
|
|
1
2
|
import { AgencyObject } from "./dataStructures.js";
|
|
2
3
|
import { Skill } from "./skill.js";
|
|
3
4
|
import { UsesTool } from "./tools.js";
|
|
@@ -17,6 +18,7 @@ export type MultiLineStringLiteral = {
|
|
|
17
18
|
export type VariableNameLiteral = {
|
|
18
19
|
type: "variableName";
|
|
19
20
|
value: string;
|
|
21
|
+
scope?: ScopeType;
|
|
20
22
|
};
|
|
21
23
|
export type PromptSegment = TextSegment | InterpolationSegment;
|
|
22
24
|
export type TextSegment = {
|
|
@@ -26,6 +28,7 @@ export type TextSegment = {
|
|
|
26
28
|
export type InterpolationSegment = {
|
|
27
29
|
type: "interpolation";
|
|
28
30
|
variableName: string;
|
|
31
|
+
scope?: ScopeType;
|
|
29
32
|
};
|
|
30
33
|
export type PromptLiteral = {
|
|
31
34
|
type: "prompt";
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -2,18 +2,18 @@ import { AccessExpression, DotProperty, IndexAccess } from "./types/access.js";
|
|
|
2
2
|
import { AgencyArray, AgencyObject } from "./types/dataStructures.js";
|
|
3
3
|
import { FunctionCall, FunctionDefinition } from "./types/function.js";
|
|
4
4
|
import { GraphNodeDefinition } from "./types/graphNode.js";
|
|
5
|
+
import { IfElse } from "./types/ifElse.js";
|
|
5
6
|
import { ImportNodeStatement, ImportStatement, ImportToolStatement } from "./types/importStatement.js";
|
|
6
7
|
import { Literal, RawCode } from "./types/literals.js";
|
|
7
8
|
import { MatchBlock } from "./types/matchBlock.js";
|
|
9
|
+
import { MessageThread } from "./types/messageThread.js";
|
|
8
10
|
import { ReturnStatement } from "./types/returnStatement.js";
|
|
11
|
+
import { Skill } from "./types/skill.js";
|
|
9
12
|
import { SpecialVar } from "./types/specialVar.js";
|
|
10
13
|
import { TimeBlock } from "./types/timeBlock.js";
|
|
11
14
|
import { UsesTool } from "./types/tools.js";
|
|
12
15
|
import { TypeAlias, TypeHint, VariableType } from "./types/typeHints.js";
|
|
13
16
|
import { WhileLoop } from "./types/whileLoop.js";
|
|
14
|
-
import { IfElse } from "./types/ifElse.js";
|
|
15
|
-
import { MessageThread } from "./types/messageThread.js";
|
|
16
|
-
import { Skill } from "./types/skill.js";
|
|
17
17
|
export * from "./types/access.js";
|
|
18
18
|
export * from "./types/dataStructures.js";
|
|
19
19
|
export * from "./types/function.js";
|
|
@@ -23,18 +23,36 @@ export * from "./types/importStatement.js";
|
|
|
23
23
|
export * from "./types/literals.js";
|
|
24
24
|
export * from "./types/matchBlock.js";
|
|
25
25
|
export * from "./types/returnStatement.js";
|
|
26
|
+
export * from "./types/specialVar.js";
|
|
27
|
+
export * from "./types/timeBlock.js";
|
|
26
28
|
export * from "./types/tools.js";
|
|
27
29
|
export * from "./types/typeHints.js";
|
|
28
30
|
export * from "./types/whileLoop.js";
|
|
29
|
-
export
|
|
30
|
-
export
|
|
31
|
-
export
|
|
31
|
+
export type Scope = GlobalScope | FunctionScope | NodeScope;
|
|
32
|
+
export type ScopeType = Scope["type"] | "args";
|
|
33
|
+
export type GlobalScope = {
|
|
34
|
+
type: "global";
|
|
35
|
+
};
|
|
36
|
+
export type FunctionScope = {
|
|
37
|
+
type: "function";
|
|
38
|
+
functionName: string;
|
|
39
|
+
args?: boolean;
|
|
40
|
+
};
|
|
41
|
+
export type NodeScope = {
|
|
42
|
+
type: "node";
|
|
43
|
+
nodeName: string;
|
|
44
|
+
args?: boolean;
|
|
45
|
+
};
|
|
32
46
|
export type Assignment = {
|
|
33
47
|
type: "assignment";
|
|
34
48
|
variableName: string;
|
|
35
49
|
typeHint?: VariableType;
|
|
50
|
+
scope?: ScopeType;
|
|
36
51
|
value: AccessExpression | Literal | FunctionCall | AgencyObject | AgencyArray | IndexAccess | TimeBlock | MessageThread;
|
|
37
52
|
};
|
|
53
|
+
export declare function globalScope(): Scope;
|
|
54
|
+
export declare function functionScope(functionName: string, args?: boolean): Scope;
|
|
55
|
+
export declare function nodeScope(nodeName: string, args?: boolean): Scope;
|
|
38
56
|
export type AgencyComment = {
|
|
39
57
|
type: "comment";
|
|
40
58
|
content: string;
|
package/dist/lib/types.js
CHANGED
|
@@ -7,9 +7,17 @@ export * from "./types/importStatement.js";
|
|
|
7
7
|
export * from "./types/literals.js";
|
|
8
8
|
export * from "./types/matchBlock.js";
|
|
9
9
|
export * from "./types/returnStatement.js";
|
|
10
|
+
export * from "./types/specialVar.js";
|
|
11
|
+
export * from "./types/timeBlock.js";
|
|
10
12
|
export * from "./types/tools.js";
|
|
11
13
|
export * from "./types/typeHints.js";
|
|
12
14
|
export * from "./types/whileLoop.js";
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
export function globalScope() {
|
|
16
|
+
return { type: "global" };
|
|
17
|
+
}
|
|
18
|
+
export function functionScope(functionName, args = false) {
|
|
19
|
+
return { type: "function", functionName, args };
|
|
20
|
+
}
|
|
21
|
+
export function nodeScope(nodeName, args = false) {
|
|
22
|
+
return { type: "node", nodeName, args };
|
|
23
|
+
}
|
package/dist/lib/utils/node.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { AgencyNode } from "../types.js";
|
|
1
|
+
import { AgencyNode, Scope } from "../types.js";
|
|
2
2
|
export declare function getAllVariablesInBody(body: AgencyNode[]): Generator<{
|
|
3
3
|
name: string;
|
|
4
4
|
node: AgencyNode;
|
|
5
5
|
}>;
|
|
6
6
|
export declare function getNodesOfType<T extends AgencyNode["type"]>(nodes: AgencyNode[], type: T): AgencyNode[];
|
|
7
|
-
export declare function walkNodes(nodes: AgencyNode[], ancestors?: AgencyNode[]): Generator<{
|
|
7
|
+
export declare function walkNodes(nodes: AgencyNode[], ancestors?: AgencyNode[], scopes?: Scope[]): Generator<{
|
|
8
8
|
node: AgencyNode;
|
|
9
9
|
ancestors: AgencyNode[];
|
|
10
|
+
scopes: Scope[];
|
|
10
11
|
}>;
|
package/dist/lib/utils/node.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { functionScope, globalScope, nodeScope, } from "../types.js";
|
|
1
2
|
export function* getAllVariablesInBody(body) {
|
|
2
3
|
for (const { node } of walkNodes(body)) {
|
|
3
4
|
if (node.type === "assignment") {
|
|
@@ -19,6 +20,7 @@ export function* getAllVariablesInBody(body) {
|
|
|
19
20
|
yield* getAllVariablesInBody(node.body);
|
|
20
21
|
}
|
|
21
22
|
else if (node.type === "ifElse") {
|
|
23
|
+
yield* getAllVariablesInBody([node.condition]);
|
|
22
24
|
yield* getAllVariablesInBody(node.thenBody);
|
|
23
25
|
if (node.elseBody) {
|
|
24
26
|
yield* getAllVariablesInBody(node.elseBody);
|
|
@@ -131,78 +133,84 @@ export function getNodesOfType(nodes, type) {
|
|
|
131
133
|
}
|
|
132
134
|
return result;
|
|
133
135
|
}
|
|
134
|
-
export function* walkNodes(nodes, ancestors = []) {
|
|
136
|
+
export function* walkNodes(nodes, ancestors = [], scopes = []) {
|
|
137
|
+
if (scopes.length === 0) {
|
|
138
|
+
scopes.push(globalScope());
|
|
139
|
+
}
|
|
135
140
|
for (const node of nodes) {
|
|
136
|
-
yield { node, ancestors };
|
|
141
|
+
yield { node, ancestors, scopes };
|
|
137
142
|
if (node.type === "function") {
|
|
138
|
-
yield* walkNodes(node.body, [...ancestors, node]);
|
|
143
|
+
yield* walkNodes(node.body, [...ancestors, node], [...scopes, functionScope(node.functionName)]);
|
|
139
144
|
}
|
|
140
145
|
else if (node.type === "graphNode") {
|
|
141
|
-
yield* walkNodes(node.body, [...ancestors, node]);
|
|
146
|
+
yield* walkNodes(node.body, [...ancestors, node], [...scopes, nodeScope(node.nodeName)]);
|
|
142
147
|
}
|
|
143
148
|
else if (node.type === "ifElse") {
|
|
144
|
-
yield* walkNodes(node.
|
|
149
|
+
yield* walkNodes([node.condition], [...ancestors, node], scopes);
|
|
150
|
+
yield* walkNodes(node.thenBody, [...ancestors, node], scopes);
|
|
145
151
|
if (node.elseBody) {
|
|
146
|
-
yield* walkNodes(node.elseBody, [...ancestors, node]);
|
|
152
|
+
yield* walkNodes(node.elseBody, [...ancestors, node], scopes);
|
|
147
153
|
}
|
|
148
154
|
}
|
|
149
155
|
else if (node.type === "whileLoop") {
|
|
150
|
-
yield* walkNodes(node.
|
|
156
|
+
yield* walkNodes([node.condition], [...ancestors, node], scopes);
|
|
157
|
+
yield* walkNodes(node.body, [...ancestors, node], scopes);
|
|
151
158
|
}
|
|
152
159
|
else if (node.type === "timeBlock") {
|
|
153
|
-
yield* walkNodes(node.body, [...ancestors, node]);
|
|
160
|
+
yield* walkNodes(node.body, [...ancestors, node], scopes);
|
|
154
161
|
}
|
|
155
162
|
else if (node.type === "messageThread") {
|
|
156
|
-
yield* walkNodes(node.body, [...ancestors, node]);
|
|
163
|
+
yield* walkNodes(node.body, [...ancestors, node], scopes);
|
|
157
164
|
}
|
|
158
165
|
else if (node.type === "returnStatement") {
|
|
159
|
-
yield* walkNodes([node.value], [...ancestors, node]);
|
|
166
|
+
yield* walkNodes([node.value], [...ancestors, node], scopes);
|
|
160
167
|
}
|
|
161
168
|
else if (node.type === "assignment") {
|
|
162
|
-
yield* walkNodes([node.value], [...ancestors, node]);
|
|
169
|
+
yield* walkNodes([node.value], [...ancestors, node], scopes);
|
|
163
170
|
}
|
|
164
171
|
else if (node.type === "functionCall") {
|
|
165
|
-
yield* walkNodes(node.arguments, [...ancestors, node]);
|
|
172
|
+
yield* walkNodes(node.arguments, [...ancestors, node], scopes);
|
|
166
173
|
}
|
|
167
174
|
else if (node.type === "matchBlock") {
|
|
175
|
+
yield* walkNodes([node.expression], [...ancestors, node], scopes);
|
|
168
176
|
for (const caseItem of node.cases) {
|
|
169
177
|
if (caseItem.type === "comment")
|
|
170
178
|
continue;
|
|
171
179
|
if (caseItem.caseValue !== "_") {
|
|
172
|
-
yield* walkNodes([caseItem.caseValue], [...ancestors, node]);
|
|
180
|
+
yield* walkNodes([caseItem.caseValue], [...ancestors, node], scopes);
|
|
173
181
|
}
|
|
174
|
-
yield* walkNodes([caseItem.body], [...ancestors, node]);
|
|
182
|
+
yield* walkNodes([caseItem.body], [...ancestors, node], scopes);
|
|
175
183
|
}
|
|
176
184
|
}
|
|
177
185
|
else if (node.type === "accessExpression") {
|
|
178
186
|
const expr = node.expression;
|
|
179
187
|
if (expr.type === "dotProperty") {
|
|
180
|
-
yield* walkNodes([expr.object], [...ancestors, node]);
|
|
188
|
+
yield* walkNodes([expr.object], [...ancestors, node], scopes);
|
|
181
189
|
}
|
|
182
190
|
else if (expr.type === "indexAccess") {
|
|
183
|
-
yield* walkNodes([expr.array], [...ancestors, node]);
|
|
184
|
-
yield* walkNodes([expr.index], [...ancestors, node]);
|
|
191
|
+
yield* walkNodes([expr.array], [...ancestors, node], scopes);
|
|
192
|
+
yield* walkNodes([expr.index], [...ancestors, node], scopes);
|
|
185
193
|
}
|
|
186
194
|
else if (expr.type === "dotFunctionCall") {
|
|
187
|
-
yield* walkNodes([expr.object], [...ancestors, node]);
|
|
188
|
-
yield* walkNodes([expr.functionCall], [...ancestors, node]);
|
|
195
|
+
yield* walkNodes([expr.object], [...ancestors, node], scopes);
|
|
196
|
+
yield* walkNodes([expr.functionCall], [...ancestors, node], scopes);
|
|
189
197
|
}
|
|
190
198
|
}
|
|
191
199
|
else if (node.type === "dotProperty") {
|
|
192
|
-
yield* walkNodes([node.object], [...ancestors, node]);
|
|
200
|
+
yield* walkNodes([node.object], [...ancestors, node], scopes);
|
|
193
201
|
}
|
|
194
202
|
else if (node.type === "indexAccess") {
|
|
195
|
-
yield* walkNodes([node.array], [...ancestors, node]);
|
|
196
|
-
yield* walkNodes([node.index], [...ancestors, node]);
|
|
203
|
+
yield* walkNodes([node.array], [...ancestors, node], scopes);
|
|
204
|
+
yield* walkNodes([node.index], [...ancestors, node], scopes);
|
|
197
205
|
}
|
|
198
206
|
else if (node.type === "agencyArray") {
|
|
199
|
-
yield* walkNodes(node.items, [...ancestors, node]);
|
|
207
|
+
yield* walkNodes(node.items, [...ancestors, node], scopes);
|
|
200
208
|
}
|
|
201
209
|
else if (node.type === "agencyObject") {
|
|
202
|
-
yield* walkNodes(node.entries.map((e) => e.value), [...ancestors, node]);
|
|
210
|
+
yield* walkNodes(node.entries.map((e) => e.value), [...ancestors, node], scopes);
|
|
203
211
|
}
|
|
204
212
|
else if (node.type === "specialVar") {
|
|
205
|
-
yield* walkNodes([node.value], [...ancestors, node]);
|
|
213
|
+
yield* walkNodes([node.value], [...ancestors, node], scopes);
|
|
206
214
|
}
|
|
207
215
|
}
|
|
208
216
|
}
|
package/dist/scripts/agency.js
CHANGED
|
@@ -4,10 +4,11 @@ import { evaluate } from "../lib/cli/evaluate.js";
|
|
|
4
4
|
import { fixtures, test } from "../lib/cli/test.js";
|
|
5
5
|
import { _parseAgency } from "../lib/parser.js";
|
|
6
6
|
import { TypescriptPreprocessor } from "../lib/preprocessors/typescriptPreprocessor.js";
|
|
7
|
-
import {
|
|
7
|
+
import { formatErrors, typeCheck } from "../lib/typeChecker.js";
|
|
8
8
|
import { Command } from "commander";
|
|
9
9
|
import * as fs from "fs";
|
|
10
10
|
import { TarsecError } from "tarsec";
|
|
11
|
+
import process from "process";
|
|
11
12
|
const program = new Command();
|
|
12
13
|
program
|
|
13
14
|
.name("agency")
|
|
@@ -23,93 +24,104 @@ function getConfig() {
|
|
|
23
24
|
}
|
|
24
25
|
return config;
|
|
25
26
|
}
|
|
26
|
-
function sleep(ms) {
|
|
27
|
-
return new Promise((resolve) => setTimeout(resolve, ms * 1000));
|
|
28
|
-
}
|
|
29
27
|
program
|
|
30
28
|
.command("compile")
|
|
31
29
|
.alias("build")
|
|
32
|
-
.description("Compile .agency file or directory to JavaScript")
|
|
33
|
-
.argument("<
|
|
34
|
-
.
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
.description("Compile .agency file(s) or directory(s) to JavaScript")
|
|
31
|
+
.argument("<inputs...>", "Paths to .agency input files or directories")
|
|
32
|
+
.action(async (inputs) => {
|
|
33
|
+
const config = getConfig();
|
|
34
|
+
for (const input of inputs) {
|
|
35
|
+
compile(config, input);
|
|
36
|
+
}
|
|
37
37
|
});
|
|
38
38
|
program
|
|
39
39
|
.command("run")
|
|
40
|
-
.description("Compile and run .agency file")
|
|
41
|
-
.argument("
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
run(
|
|
40
|
+
.description("Compile and run .agency file(s)")
|
|
41
|
+
.argument("[input]", "Paths to .agency input file")
|
|
42
|
+
.action((input) => {
|
|
43
|
+
const config = getConfig();
|
|
44
|
+
run(config, input);
|
|
45
45
|
});
|
|
46
46
|
program
|
|
47
47
|
.command("format")
|
|
48
48
|
.alias("fmt")
|
|
49
|
-
.description("Format .agency file or directory (reads from stdin if no input)")
|
|
50
|
-
.argument("[
|
|
49
|
+
.description("Format .agency file(s) or directory(s) (reads from stdin if no input)")
|
|
50
|
+
.argument("[inputs...]", "Paths to .agency input files or directories")
|
|
51
51
|
.option("-i, --in-place", "Format file(s) in-place")
|
|
52
|
-
.action(async (
|
|
52
|
+
.action(async (inputs, opts) => {
|
|
53
53
|
const config = getConfig();
|
|
54
|
-
if (
|
|
54
|
+
if (inputs.length === 0) {
|
|
55
55
|
const contents = await readStdin();
|
|
56
56
|
const formatted = await format(contents, config);
|
|
57
57
|
console.log(formatted);
|
|
58
58
|
}
|
|
59
59
|
else {
|
|
60
|
-
|
|
60
|
+
for (const input of inputs) {
|
|
61
|
+
formatFile(input, opts.inPlace ?? false, config);
|
|
62
|
+
}
|
|
61
63
|
}
|
|
62
64
|
});
|
|
63
65
|
program
|
|
64
66
|
.command("ast")
|
|
65
67
|
.alias("parse")
|
|
66
|
-
.description("Parse .agency file and show AST (reads from stdin if no input)")
|
|
67
|
-
.argument("[
|
|
68
|
-
.action(async (
|
|
68
|
+
.description("Parse .agency file(s) and show AST (reads from stdin if no input)")
|
|
69
|
+
.argument("[inputs...]", "Paths to .agency input files")
|
|
70
|
+
.action(async (inputs) => {
|
|
69
71
|
const config = getConfig();
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
if (inputs.length === 0) {
|
|
73
|
+
const contents = await readStdin();
|
|
74
|
+
const result = parse(contents, config);
|
|
75
|
+
console.log(JSON.stringify(result, null, 2));
|
|
73
76
|
}
|
|
74
77
|
else {
|
|
75
|
-
|
|
78
|
+
for (const input of inputs) {
|
|
79
|
+
const contents = readFile(input);
|
|
80
|
+
const result = parse(contents, config);
|
|
81
|
+
console.log(JSON.stringify(result, null, 2));
|
|
82
|
+
}
|
|
76
83
|
}
|
|
77
|
-
const result = parse(contents, config);
|
|
78
|
-
console.log(JSON.stringify(result, null, 2));
|
|
79
84
|
});
|
|
80
85
|
program
|
|
81
86
|
.command("graph")
|
|
82
87
|
.alias("mermaid")
|
|
83
|
-
.description("Render Mermaid graph from .agency file (reads from stdin if no input)")
|
|
84
|
-
.argument("[
|
|
85
|
-
.action(async (
|
|
88
|
+
.description("Render Mermaid graph from .agency file(s) (reads from stdin if no input)")
|
|
89
|
+
.argument("[inputs...]", "Paths to .agency input files")
|
|
90
|
+
.action(async (inputs) => {
|
|
86
91
|
const config = getConfig();
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
contents
|
|
92
|
+
if (inputs.length === 0) {
|
|
93
|
+
const contents = await readStdin();
|
|
94
|
+
renderGraph(contents, config);
|
|
90
95
|
}
|
|
91
96
|
else {
|
|
92
|
-
|
|
97
|
+
for (const input of inputs) {
|
|
98
|
+
const contents = readFile(input);
|
|
99
|
+
renderGraph(contents, config);
|
|
100
|
+
}
|
|
93
101
|
}
|
|
94
|
-
renderGraph(contents, config);
|
|
95
102
|
});
|
|
96
103
|
program
|
|
97
104
|
.command("preprocess")
|
|
98
|
-
.description("Parse .agency file and show AST after preprocessing (reads from stdin if no input)")
|
|
99
|
-
.argument("[
|
|
100
|
-
.action(async (
|
|
105
|
+
.description("Parse .agency file(s) and show AST after preprocessing (reads from stdin if no input)")
|
|
106
|
+
.argument("[inputs...]", "Paths to .agency input files")
|
|
107
|
+
.action(async (inputs) => {
|
|
101
108
|
const config = getConfig();
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
109
|
+
const process = (contents) => {
|
|
110
|
+
const parsedProgram = parse(contents, config);
|
|
111
|
+
const preprocessor = new TypescriptPreprocessor(parsedProgram, config);
|
|
112
|
+
preprocessor.preprocess();
|
|
113
|
+
console.log(JSON.stringify(preprocessor.program, null, 2));
|
|
114
|
+
};
|
|
115
|
+
if (inputs.length === 0) {
|
|
116
|
+
const contents = await readStdin();
|
|
117
|
+
process(contents);
|
|
105
118
|
}
|
|
106
119
|
else {
|
|
107
|
-
|
|
120
|
+
for (const input of inputs) {
|
|
121
|
+
const contents = readFile(input);
|
|
122
|
+
process(contents);
|
|
123
|
+
}
|
|
108
124
|
}
|
|
109
|
-
const parsedProgram = parse(contents, config);
|
|
110
|
-
const preprocessor = new TypescriptPreprocessor(parsedProgram, config);
|
|
111
|
-
preprocessor.preprocess();
|
|
112
|
-
console.log(JSON.stringify(preprocessor.program, null, 2));
|
|
113
125
|
});
|
|
114
126
|
program
|
|
115
127
|
.command("evaluate")
|
|
@@ -119,7 +131,7 @@ program
|
|
|
119
131
|
.option("--args <path>", "Path to eval args JSON file")
|
|
120
132
|
.option("--results <path>", "Path to existing results file (to resume)")
|
|
121
133
|
.action(async (target, opts) => {
|
|
122
|
-
await evaluate(target, opts.args, opts.results);
|
|
134
|
+
await evaluate(getConfig(), target, opts.args, opts.results);
|
|
123
135
|
});
|
|
124
136
|
program
|
|
125
137
|
.command("gen-fixtures")
|
|
@@ -127,59 +139,87 @@ program
|
|
|
127
139
|
.description("Generate test fixtures")
|
|
128
140
|
.argument("[target]", "Target in file.agency:nodeName format")
|
|
129
141
|
.action(async (target) => {
|
|
130
|
-
await fixtures(target);
|
|
142
|
+
await fixtures(getConfig(), target);
|
|
131
143
|
});
|
|
132
144
|
program
|
|
133
145
|
.command("test")
|
|
134
146
|
.description("Run tests")
|
|
135
|
-
.argument("[
|
|
147
|
+
.argument("[inputs...]", "Paths to .test.json files or directories")
|
|
136
148
|
.action(async (testFile) => {
|
|
137
|
-
|
|
149
|
+
for (const file of testFile) {
|
|
150
|
+
await test(getConfig(), file);
|
|
151
|
+
}
|
|
138
152
|
});
|
|
139
153
|
program
|
|
140
154
|
.command("diagnostics")
|
|
141
155
|
.description("Run diagnostics for VSCode")
|
|
142
|
-
.argument("[
|
|
143
|
-
.action(async (
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
catch (error) {
|
|
149
|
-
if (error instanceof TarsecError) {
|
|
150
|
-
console.log(JSON.stringify(error.data, null, 2));
|
|
156
|
+
.argument("[inputs...]", "Paths to .agency input files")
|
|
157
|
+
.action(async (inputs) => {
|
|
158
|
+
if (inputs.length === 0) {
|
|
159
|
+
const contents = await readStdin();
|
|
160
|
+
try {
|
|
161
|
+
_parseAgency(contents);
|
|
151
162
|
}
|
|
152
|
-
|
|
153
|
-
|
|
163
|
+
catch (error) {
|
|
164
|
+
if (error instanceof TarsecError) {
|
|
165
|
+
console.log(JSON.stringify(error.data, null, 2));
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
for (const input of inputs) {
|
|
174
|
+
const contents = readFile(input);
|
|
175
|
+
try {
|
|
176
|
+
_parseAgency(contents);
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
if (error instanceof TarsecError) {
|
|
180
|
+
console.log(JSON.stringify(error.data, null, 2));
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
154
186
|
}
|
|
155
187
|
}
|
|
156
188
|
});
|
|
157
189
|
program
|
|
158
190
|
.command("typecheck")
|
|
159
191
|
.alias("tc")
|
|
160
|
-
.description("Type check .agency file (reads from stdin if no input)")
|
|
161
|
-
.argument("[
|
|
192
|
+
.description("Type check .agency file(s) (reads from stdin if no input)")
|
|
193
|
+
.argument("[inputs...]", "Paths to .agency input files")
|
|
162
194
|
.option("--strict", "Enable strict types (untyped variables are errors)")
|
|
163
|
-
.action(async (
|
|
195
|
+
.action(async (inputs, opts) => {
|
|
164
196
|
const config = getConfig();
|
|
197
|
+
let hasErrors = false;
|
|
198
|
+
const runTypeCheck = (contents) => {
|
|
199
|
+
const parsedProgram = parse(contents, config);
|
|
200
|
+
const { errors } = typeCheck(parsedProgram, config);
|
|
201
|
+
if (errors.length > 0) {
|
|
202
|
+
console.error(formatErrors(errors));
|
|
203
|
+
hasErrors = true;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
console.log("No type errors found.");
|
|
207
|
+
}
|
|
208
|
+
};
|
|
165
209
|
if (opts.strict)
|
|
166
210
|
config.strictTypes = true;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
contents
|
|
211
|
+
if (inputs.length === 0) {
|
|
212
|
+
const contents = await readStdin();
|
|
213
|
+
runTypeCheck(contents);
|
|
170
214
|
}
|
|
171
215
|
else {
|
|
172
|
-
|
|
216
|
+
for (const input of inputs) {
|
|
217
|
+
const contents = readFile(input);
|
|
218
|
+
runTypeCheck(contents);
|
|
219
|
+
}
|
|
173
220
|
}
|
|
174
|
-
|
|
175
|
-
const { errors } = typeCheck(parsedProgram, config);
|
|
176
|
-
if (errors.length > 0) {
|
|
177
|
-
console.error(formatErrors(errors));
|
|
221
|
+
if (hasErrors)
|
|
178
222
|
process.exit(1);
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
console.log("No type errors found.");
|
|
182
|
-
}
|
|
183
223
|
});
|
|
184
224
|
// Default: treat unknown args as a file to run
|
|
185
225
|
program.arguments("[file]").action((file) => {
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agency-lang",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.58",
|
|
4
4
|
"description": "The Agency language",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "vitest",
|
|
8
8
|
"test:run": "vitest run",
|
|
9
|
+
"test:agency": "node ./dist/scripts/agency.js test -v tests/agency",
|
|
9
10
|
"build": "rm -rf dist/ && tsc && tsc-alias && cp -r lib/agents dist/lib/agents",
|
|
10
11
|
"start": "node dist/index.js",
|
|
11
12
|
"templates": "typestache ./lib/templates",
|
|
@@ -54,7 +55,7 @@
|
|
|
54
55
|
"piemachine": "^0.0.8",
|
|
55
56
|
"prompts": "^2.4.2",
|
|
56
57
|
"smoltalk": "^0.0.18",
|
|
57
|
-
"statelog-client": "^0.0.
|
|
58
|
+
"statelog-client": "^0.0.38",
|
|
58
59
|
"tarsec": "^0.1.8",
|
|
59
60
|
"typestache": "^0.4.4",
|
|
60
61
|
"zod": "^4.3.5"
|