agency-lang 0.0.15 → 0.0.17
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/dist/lib/backends/agencyGenerator.js +24 -6
- package/dist/lib/backends/agencyGenerator.test.d.ts +1 -0
- package/dist/lib/backends/agencyGenerator.test.js +205 -0
- package/dist/lib/backends/baseGenerator.d.ts +1 -0
- package/dist/lib/backends/baseGenerator.js +12 -2
- package/dist/lib/backends/graphGenerator.js +7 -5
- package/dist/lib/backends/typescriptGenerator.d.ts +3 -3
- package/dist/lib/backends/typescriptGenerator.js +17 -11
- package/dist/lib/parsers/access.d.ts +3 -5
- package/dist/lib/parsers/access.js +54 -13
- package/dist/lib/parsers/access.test.js +40 -500
- package/dist/lib/parsers/assignment.js +2 -2
- package/dist/lib/parsers/assignment.test.d.ts +1 -0
- package/dist/lib/parsers/assignment.test.js +279 -0
- package/dist/lib/parsers/dataStructures.js +3 -3
- package/dist/lib/parsers/function.d.ts +3 -1
- package/dist/lib/parsers/function.js +6 -4
- package/dist/lib/parsers/function.test.js +653 -8
- package/dist/lib/parsers/functionCall.js +3 -2
- package/dist/lib/parsers/functionCall.test.js +310 -0
- package/dist/lib/parsers/literals.d.ts +2 -1
- package/dist/lib/parsers/literals.js +15 -5
- package/dist/lib/parsers/literals.test.js +189 -16
- package/dist/lib/parsers/matchBlock.js +2 -2
- package/dist/lib/parsers/parserUtils.test.d.ts +1 -0
- package/dist/lib/parsers/parserUtils.test.js +46 -0
- package/dist/lib/parsers/returnStatement.js +2 -2
- package/dist/lib/parsers/returnStatement.test.d.ts +1 -0
- package/dist/lib/parsers/returnStatement.test.js +268 -0
- package/dist/lib/parsers/specialVar.test.d.ts +1 -0
- package/dist/lib/parsers/specialVar.test.js +219 -0
- package/dist/lib/types/access.d.ts +5 -5
- package/dist/lib/types/access.js +6 -1
- package/dist/lib/types/dataStructures.d.ts +3 -3
- package/dist/lib/types/function.d.ts +10 -4
- package/dist/lib/types/literals.d.ts +5 -1
- package/dist/lib/types/matchBlock.d.ts +2 -2
- package/dist/lib/types/returnStatement.d.ts +2 -2
- package/dist/lib/types/whileLoop.d.ts +2 -2
- package/dist/lib/types.d.ts +3 -3
- package/package.json +2 -2
|
@@ -66,12 +66,13 @@ export class AgencyGenerator extends BaseGenerator {
|
|
|
66
66
|
return literal.value;
|
|
67
67
|
case "string":
|
|
68
68
|
// Escape backslashes and quotes
|
|
69
|
-
const escaped = literal.value
|
|
70
|
-
.replace(/\\/g, "\\\\")
|
|
71
|
-
.replace(/"/g, '\\"');
|
|
69
|
+
const escaped = literal.value;
|
|
72
70
|
return `"${escaped}"`;
|
|
73
71
|
case "variableName":
|
|
74
72
|
return literal.value;
|
|
73
|
+
case "multiLineString":
|
|
74
|
+
const escapedMultiLine = literal.value;
|
|
75
|
+
return `"""${escapedMultiLine}"""`;
|
|
75
76
|
case "prompt":
|
|
76
77
|
return this.generatePromptLiteral(literal);
|
|
77
78
|
default:
|
|
@@ -100,12 +101,28 @@ export class AgencyGenerator extends BaseGenerator {
|
|
|
100
101
|
processFunctionDefinition(node) {
|
|
101
102
|
const { functionName, body, parameters } = node;
|
|
102
103
|
// Build parameter list
|
|
103
|
-
const params = parameters
|
|
104
|
+
const params = parameters
|
|
105
|
+
.map((p) => {
|
|
106
|
+
if (p.typeHint) {
|
|
107
|
+
const typeStr = variableTypeToString(p.typeHint, this.typeAliases);
|
|
108
|
+
return `${p.name}: ${typeStr}`;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
return p.name;
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
.join(", ");
|
|
104
115
|
// Start function definition
|
|
105
116
|
let result = this.indentStr(`def ${functionName}(${params}) {\n`);
|
|
106
117
|
// Process body with increased indentation
|
|
107
118
|
this.increaseIndent();
|
|
108
|
-
|
|
119
|
+
if (node.docString) {
|
|
120
|
+
const docLines = [`"""`, node.docString.value, `"""`]
|
|
121
|
+
.map((line) => this.indentStr(line))
|
|
122
|
+
.join("\n");
|
|
123
|
+
result += `${docLines}\n`;
|
|
124
|
+
}
|
|
125
|
+
this.functionScopedVariables = [...parameters.map((p) => p.name)];
|
|
109
126
|
const lines = [];
|
|
110
127
|
for (const stmt of body) {
|
|
111
128
|
lines.push(this.processNode(stmt));
|
|
@@ -154,7 +171,8 @@ export class AgencyGenerator extends BaseGenerator {
|
|
|
154
171
|
}
|
|
155
172
|
}
|
|
156
173
|
processDotProperty(node) {
|
|
157
|
-
|
|
174
|
+
let objectCode = this.processNode(node.object);
|
|
175
|
+
objectCode = objectCode.trim();
|
|
158
176
|
return `${objectCode}.${node.propertyName}`;
|
|
159
177
|
}
|
|
160
178
|
processIndexAccess(node) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { AgencyGenerator } from "./agencyGenerator.js";
|
|
3
|
+
import { parseAgency } from "../parser.js";
|
|
4
|
+
describe("AgencyGenerator - Function Parameter Type Hints", () => {
|
|
5
|
+
describe("processFunctionDefinition", () => {
|
|
6
|
+
const testCases = [
|
|
7
|
+
{
|
|
8
|
+
description: "single parameter with type hint",
|
|
9
|
+
input: "def add(x: number) { x }",
|
|
10
|
+
expectedOutput: "def add(x: number) {\nx\n}",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
description: "multiple parameters with type hints",
|
|
14
|
+
input: "def add(x: number, y: number) { x }",
|
|
15
|
+
expectedOutput: "def add(x: number, y: number) {\nx\n}",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
description: "mixed typed and untyped parameters",
|
|
19
|
+
input: "def mixed(x: number, y) { x }",
|
|
20
|
+
expectedOutput: "def mixed(x: number, y) {\nx\n}",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
description: "array type hint",
|
|
24
|
+
input: "def process(items: number[]) { items }",
|
|
25
|
+
expectedOutput: "def process(items: number[]) {\nitems\n}",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
description: "union type hint",
|
|
29
|
+
input: "def flexible(value: string | number) { value }",
|
|
30
|
+
expectedOutput: "def flexible(value: string | number) {\nvalue\n}",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
description: "type hint with docstring",
|
|
34
|
+
input: 'def add(x: number, y: number) {\n """Adds two numbers"""\n x\n}',
|
|
35
|
+
expectedOutput: 'def add(x: number, y: number) {\n """\n Adds two numbers\n """\nx\n}',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
description: "multiple array types",
|
|
39
|
+
input: "def multi(arr: string[], count: number) { arr }",
|
|
40
|
+
expectedOutput: "def multi(arr: string[], count: number) {\narr\n}",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
description: "nested array type",
|
|
44
|
+
input: "def nested(matrix: number[][]) { matrix }",
|
|
45
|
+
expectedOutput: "def nested(matrix: number[][]) {\nmatrix\n}",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
description: "custom type name",
|
|
49
|
+
input: "def handle(data: CustomType) { data }",
|
|
50
|
+
expectedOutput: "def handle(data: CustomType) {\ndata\n}",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
description: "untyped parameters (backward compatibility)",
|
|
54
|
+
input: "def old(x, y) { x }",
|
|
55
|
+
expectedOutput: "def old(x, y) {\nx\n}",
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
testCases.forEach(({ description, input, expectedOutput }) => {
|
|
59
|
+
it(`should correctly generate ${description}`, () => {
|
|
60
|
+
const parseResult = parseAgency(input);
|
|
61
|
+
expect(parseResult.success).toBe(true);
|
|
62
|
+
if (!parseResult.success)
|
|
63
|
+
return;
|
|
64
|
+
const generator = new AgencyGenerator();
|
|
65
|
+
const result = generator.generate(parseResult.result);
|
|
66
|
+
// Normalize whitespace for comparison
|
|
67
|
+
const normalizedOutput = result.output.trim();
|
|
68
|
+
const normalizedExpected = expectedOutput.trim();
|
|
69
|
+
expect(normalizedOutput).toBe(normalizedExpected);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe("Round-trip parsing", () => {
|
|
74
|
+
const testCases = [
|
|
75
|
+
{
|
|
76
|
+
description: "function with typed parameters",
|
|
77
|
+
input: "def add(x: number, y: number) { x }",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
description: "function with mixed parameters",
|
|
81
|
+
input: "def mixed(a: string, b) { a }",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
description: "function with array type",
|
|
85
|
+
input: "def process(items: number[]) { items }",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
description: "function with union type",
|
|
89
|
+
input: "def flex(val: string | number) { val }",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
description: "function with complex types",
|
|
93
|
+
input: "def complex(arr: string[], count: number, flag: boolean) { arr }",
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
testCases.forEach(({ description, input }) => {
|
|
97
|
+
it(`should preserve ${description} in round-trip`, () => {
|
|
98
|
+
// First parse
|
|
99
|
+
const firstParse = parseAgency(input);
|
|
100
|
+
expect(firstParse.success).toBe(true);
|
|
101
|
+
if (!firstParse.success)
|
|
102
|
+
return;
|
|
103
|
+
// Generate agency code
|
|
104
|
+
const generator = new AgencyGenerator();
|
|
105
|
+
const generated = generator.generate(firstParse.result);
|
|
106
|
+
// Second parse
|
|
107
|
+
const secondParse = parseAgency(generated.output);
|
|
108
|
+
expect(secondParse.success).toBe(true);
|
|
109
|
+
if (!secondParse.success)
|
|
110
|
+
return;
|
|
111
|
+
// Compare the function nodes - they should be identical
|
|
112
|
+
const firstFunc = firstParse.result.nodes[0];
|
|
113
|
+
const secondFunc = secondParse.result.nodes[0];
|
|
114
|
+
expect(secondFunc.type).toBe("function");
|
|
115
|
+
expect(secondFunc.functionName).toBe(firstFunc.functionName);
|
|
116
|
+
expect(secondFunc.parameters).toEqual(firstFunc.parameters);
|
|
117
|
+
expect(secondFunc.body).toEqual(firstFunc.body);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe("Type preservation", () => {
|
|
122
|
+
it("should preserve primitive types", () => {
|
|
123
|
+
const input = "def test(n: number, s: string, b: boolean) { n }";
|
|
124
|
+
const parseResult = parseAgency(input);
|
|
125
|
+
expect(parseResult.success).toBe(true);
|
|
126
|
+
if (!parseResult.success)
|
|
127
|
+
return;
|
|
128
|
+
const generator = new AgencyGenerator();
|
|
129
|
+
const result = generator.generate(parseResult.result);
|
|
130
|
+
expect(result.output).toContain("n: number");
|
|
131
|
+
expect(result.output).toContain("s: string");
|
|
132
|
+
expect(result.output).toContain("b: boolean");
|
|
133
|
+
});
|
|
134
|
+
it("should preserve array types", () => {
|
|
135
|
+
const input = "def test(nums: number[], strs: string[]) { nums }";
|
|
136
|
+
const parseResult = parseAgency(input);
|
|
137
|
+
expect(parseResult.success).toBe(true);
|
|
138
|
+
if (!parseResult.success)
|
|
139
|
+
return;
|
|
140
|
+
const generator = new AgencyGenerator();
|
|
141
|
+
const result = generator.generate(parseResult.result);
|
|
142
|
+
expect(result.output).toContain("nums: number[]");
|
|
143
|
+
expect(result.output).toContain("strs: string[]");
|
|
144
|
+
});
|
|
145
|
+
it("should preserve union types", () => {
|
|
146
|
+
const input = "def test(val: string | number | boolean) { val }";
|
|
147
|
+
const parseResult = parseAgency(input);
|
|
148
|
+
expect(parseResult.success).toBe(true);
|
|
149
|
+
if (!parseResult.success)
|
|
150
|
+
return;
|
|
151
|
+
const generator = new AgencyGenerator();
|
|
152
|
+
const result = generator.generate(parseResult.result);
|
|
153
|
+
expect(result.output).toContain("val: string | number | boolean");
|
|
154
|
+
});
|
|
155
|
+
it("should preserve nested array types", () => {
|
|
156
|
+
const input = "def test(matrix: number[][]) { matrix }";
|
|
157
|
+
const parseResult = parseAgency(input);
|
|
158
|
+
expect(parseResult.success).toBe(true);
|
|
159
|
+
if (!parseResult.success)
|
|
160
|
+
return;
|
|
161
|
+
const generator = new AgencyGenerator();
|
|
162
|
+
const result = generator.generate(parseResult.result);
|
|
163
|
+
expect(result.output).toContain("matrix: number[][]");
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
describe("Mixed typed and untyped parameters", () => {
|
|
167
|
+
it("should handle first parameter typed, second untyped", () => {
|
|
168
|
+
const input = "def test(x: number, y) { x }";
|
|
169
|
+
const parseResult = parseAgency(input);
|
|
170
|
+
expect(parseResult.success).toBe(true);
|
|
171
|
+
if (!parseResult.success)
|
|
172
|
+
return;
|
|
173
|
+
const generator = new AgencyGenerator();
|
|
174
|
+
const result = generator.generate(parseResult.result);
|
|
175
|
+
expect(result.output).toContain("x: number");
|
|
176
|
+
expect(result.output).toContain(", y)");
|
|
177
|
+
expect(result.output).not.toContain("y:");
|
|
178
|
+
});
|
|
179
|
+
it("should handle first parameter untyped, second typed", () => {
|
|
180
|
+
const input = "def test(x, y: string) { x }";
|
|
181
|
+
const parseResult = parseAgency(input);
|
|
182
|
+
expect(parseResult.success).toBe(true);
|
|
183
|
+
if (!parseResult.success)
|
|
184
|
+
return;
|
|
185
|
+
const generator = new AgencyGenerator();
|
|
186
|
+
const result = generator.generate(parseResult.result);
|
|
187
|
+
expect(result.output).toContain("y: string");
|
|
188
|
+
expect(result.output).toMatch(/test\(x,/);
|
|
189
|
+
expect(result.output).not.toContain("x:");
|
|
190
|
+
});
|
|
191
|
+
it("should handle alternating typed and untyped parameters", () => {
|
|
192
|
+
const input = "def test(a, b: number, c, d: string) { a }";
|
|
193
|
+
const parseResult = parseAgency(input);
|
|
194
|
+
expect(parseResult.success).toBe(true);
|
|
195
|
+
if (!parseResult.success)
|
|
196
|
+
return;
|
|
197
|
+
const generator = new AgencyGenerator();
|
|
198
|
+
const result = generator.generate(parseResult.result);
|
|
199
|
+
expect(result.output).toContain("b: number");
|
|
200
|
+
expect(result.output).toContain("d: string");
|
|
201
|
+
expect(result.output).not.toContain("a:");
|
|
202
|
+
expect(result.output).not.toContain("c:");
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|
|
@@ -15,6 +15,7 @@ export declare class BaseGenerator {
|
|
|
15
15
|
protected generatedStatements: string[];
|
|
16
16
|
protected generatedTypeAliases: string[];
|
|
17
17
|
protected functionScopedVariables: string[];
|
|
18
|
+
protected globalScopedVariables: string[];
|
|
18
19
|
protected toolsUsed: string[];
|
|
19
20
|
protected typeAliases: Record<string, VariableType>;
|
|
20
21
|
protected functionsUsed: Set<string>;
|
|
@@ -4,6 +4,7 @@ export class BaseGenerator {
|
|
|
4
4
|
generatedStatements = [];
|
|
5
5
|
generatedTypeAliases = [];
|
|
6
6
|
functionScopedVariables = [];
|
|
7
|
+
globalScopedVariables = [];
|
|
7
8
|
// collect tools for a prompt
|
|
8
9
|
toolsUsed = [];
|
|
9
10
|
typeAliases = {};
|
|
@@ -39,7 +40,13 @@ export class BaseGenerator {
|
|
|
39
40
|
this.collectFunctionSignature(node);
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
|
-
// Pass 5:
|
|
43
|
+
// Pass 5: Collect global scoped variables
|
|
44
|
+
for (const node of program.nodes) {
|
|
45
|
+
if (node.type === "assignment") {
|
|
46
|
+
this.globalScopedVariables.push(node.variableName);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Pass 6: Process all nodes and generate code
|
|
43
50
|
for (const node of program.nodes) {
|
|
44
51
|
const result = this.processNode(node);
|
|
45
52
|
this.generatedStatements.push(result);
|
|
@@ -69,7 +76,7 @@ export class BaseGenerator {
|
|
|
69
76
|
return "";
|
|
70
77
|
}
|
|
71
78
|
collectFunctionSignature(node) {
|
|
72
|
-
this.functionSignatures[node.functionName] = node.parameters;
|
|
79
|
+
this.functionSignatures[node.functionName] = node.parameters.map((param) => param.name);
|
|
73
80
|
}
|
|
74
81
|
processGraphNodeName(node) { }
|
|
75
82
|
processNode(node) {
|
|
@@ -91,6 +98,7 @@ export class BaseGenerator {
|
|
|
91
98
|
case "matchBlock":
|
|
92
99
|
return this.processMatchBlock(node);
|
|
93
100
|
case "number":
|
|
101
|
+
case "multiLineString":
|
|
94
102
|
case "string":
|
|
95
103
|
case "variableName":
|
|
96
104
|
case "prompt":
|
|
@@ -113,6 +121,8 @@ export class BaseGenerator {
|
|
|
113
121
|
return this.processWhileLoop(node);
|
|
114
122
|
case "specialVar":
|
|
115
123
|
return this.processSpecialVar(node);
|
|
124
|
+
case "indexAccess":
|
|
125
|
+
return this.processIndexAccess(node);
|
|
116
126
|
default:
|
|
117
127
|
throw new Error(`Unhandled Agency node type: ${node.type}`);
|
|
118
128
|
}
|
|
@@ -257,12 +257,14 @@ export class GraphGenerator extends TypeScriptGenerator {
|
|
|
257
257
|
if (arg.type === "functionCall") {
|
|
258
258
|
this.functionsUsed.add(arg.functionName);
|
|
259
259
|
return this.generateFunctionCallExpression(arg);
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
260
|
+
/* } else if (arg.type === "accessExpression") {
|
|
261
|
+
return this.processAccessExpression(arg);
|
|
262
|
+
} else if (arg.type === "indexAccess") {
|
|
263
|
+
return this.processIndexAccess(arg);
|
|
264
|
+
*/ }
|
|
264
265
|
else {
|
|
265
|
-
return this.
|
|
266
|
+
return this.processNode(arg);
|
|
267
|
+
// return this.generateLiteral(arg);
|
|
266
268
|
}
|
|
267
269
|
});
|
|
268
270
|
const argsString = parts.join(", ");
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { AgencyComment, AgencyProgram, Assignment, Literal, PromptLiteral, PromptSegment, TypeAlias, TypeHint, TypeHintMap } from "../types.js";
|
|
2
|
+
import { SpecialVar } from "../types/specialVar.js";
|
|
2
3
|
import { AccessExpression, DotFunctionCall, DotProperty, IndexAccess } from "../types/access.js";
|
|
3
4
|
import { AgencyArray, AgencyObject } from "../types/dataStructures.js";
|
|
4
5
|
import { FunctionCall, FunctionDefinition } from "../types/function.js";
|
|
6
|
+
import { ImportStatement } from "../types/importStatement.js";
|
|
5
7
|
import { MatchBlock } from "../types/matchBlock.js";
|
|
6
|
-
import { BaseGenerator } from "./baseGenerator.js";
|
|
7
8
|
import { ReturnStatement } from "../types/returnStatement.js";
|
|
8
9
|
import { UsesTool } from "../types/tools.js";
|
|
9
|
-
import { ImportStatement } from "../types/importStatement.js";
|
|
10
10
|
import { WhileLoop } from "../types/whileLoop.js";
|
|
11
|
-
import {
|
|
11
|
+
import { BaseGenerator } from "./baseGenerator.js";
|
|
12
12
|
export declare class TypeScriptGenerator extends BaseGenerator {
|
|
13
13
|
constructor();
|
|
14
14
|
protected generateBuiltins(): string;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import * as renderSpecialVar from "../templates/backends/graphGenerator/specialVar.js";
|
|
2
|
+
import * as builtinTools from "../templates/backends/typescriptGenerator/builtinTools.js";
|
|
3
|
+
import * as renderFunctionDefinition from "../templates/backends/typescriptGenerator/functionDefinition.js";
|
|
1
4
|
import * as renderImports from "../templates/backends/typescriptGenerator/imports.js";
|
|
2
5
|
import * as promptFunction from "../templates/backends/typescriptGenerator/promptFunction.js";
|
|
3
6
|
import * as renderTool from "../templates/backends/typescriptGenerator/tool.js";
|
|
4
7
|
import * as renderToolCall from "../templates/backends/typescriptGenerator/toolCall.js";
|
|
5
|
-
import * as renderFunctionDefinition from "../templates/backends/typescriptGenerator/functionDefinition.js";
|
|
6
|
-
import * as renderSpecialVar from "../templates/backends/graphGenerator/specialVar.js";
|
|
7
8
|
import { escape, zip } from "../utils.js";
|
|
8
9
|
import { BaseGenerator } from "./baseGenerator.js";
|
|
9
10
|
import { generateBuiltinHelpers, mapFunctionName, } from "./typescriptGenerator/builtins.js";
|
|
10
11
|
import { variableTypeToString } from "./typescriptGenerator/typeToString.js";
|
|
11
12
|
import { DEFAULT_SCHEMA, mapTypeToZodSchema, } from "./typescriptGenerator/typeToZodSchema.js";
|
|
12
|
-
import * as builtinTools from "../templates/backends/typescriptGenerator/builtinTools.js";
|
|
13
13
|
export class TypeScriptGenerator extends BaseGenerator {
|
|
14
14
|
constructor() {
|
|
15
15
|
super();
|
|
@@ -135,7 +135,8 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
135
135
|
.filter((s) => s.type === "interpolation")
|
|
136
136
|
.map((s) => s.variableName);
|
|
137
137
|
for (const varName of interpolatedVars) {
|
|
138
|
-
if (!this.functionScopedVariables.includes(varName)
|
|
138
|
+
if (!this.functionScopedVariables.includes(varName) &&
|
|
139
|
+
!this.globalScopedVariables.includes(varName)) {
|
|
139
140
|
throw new Error(`Variable '${varName}' used in prompt interpolation but not defined. ` +
|
|
140
141
|
`Referenced in assignment to '${variableName}'.`);
|
|
141
142
|
}
|
|
@@ -157,12 +158,12 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
157
158
|
}
|
|
158
159
|
const properties = {};
|
|
159
160
|
parameters.forEach((param) => {
|
|
160
|
-
const typeHint =
|
|
161
|
+
const typeHint = param.typeHint || {
|
|
161
162
|
type: "primitiveType",
|
|
162
163
|
value: "string",
|
|
163
164
|
};
|
|
164
165
|
const tsType = mapTypeToZodSchema(typeHint, this.typeAliases);
|
|
165
|
-
properties[param] = tsType;
|
|
166
|
+
properties[param.name] = tsType;
|
|
166
167
|
});
|
|
167
168
|
let schema = "";
|
|
168
169
|
for (const [key, value] of Object.entries(properties)) {
|
|
@@ -183,13 +184,13 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
183
184
|
*/
|
|
184
185
|
processFunctionDefinition(node) {
|
|
185
186
|
const { functionName, body, parameters } = node;
|
|
186
|
-
this.functionScopedVariables = [...parameters];
|
|
187
|
+
this.functionScopedVariables = [...parameters.map((p) => p.name)];
|
|
187
188
|
const bodyCode = [];
|
|
188
189
|
for (const stmt of body) {
|
|
189
190
|
bodyCode.push(this.processNode(stmt));
|
|
190
191
|
}
|
|
191
192
|
this.functionScopedVariables = [];
|
|
192
|
-
const args = parameters.join(", ") || "";
|
|
193
|
+
const args = parameters.map((p) => p.name).join(", ") || "";
|
|
193
194
|
return renderFunctionDefinition.default({
|
|
194
195
|
functionName,
|
|
195
196
|
args: "{" + args + "}",
|
|
@@ -219,12 +220,15 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
219
220
|
if (arg.type === "functionCall") {
|
|
220
221
|
this.functionsUsed.add(arg.functionName);
|
|
221
222
|
return this.generateFunctionCallExpression(arg);
|
|
222
|
-
|
|
223
|
-
else if (arg.type === "accessExpression") {
|
|
223
|
+
/* } else if (arg.type === "accessExpression") {
|
|
224
224
|
return this.processAccessExpression(arg);
|
|
225
|
+
} else if (arg.type === "indexAccess") {
|
|
226
|
+
return this.processIndexAccess(arg);
|
|
227
|
+
*/
|
|
225
228
|
}
|
|
226
229
|
else {
|
|
227
|
-
return this.generateLiteral(arg);
|
|
230
|
+
// return this.generateLiteral(arg);
|
|
231
|
+
return this.processNode(arg);
|
|
228
232
|
}
|
|
229
233
|
});
|
|
230
234
|
let argsString = "";
|
|
@@ -250,6 +254,8 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
250
254
|
return literal.value;
|
|
251
255
|
case "string":
|
|
252
256
|
return `"${escape(literal.value)}"`;
|
|
257
|
+
case "multiLineString":
|
|
258
|
+
return `\`${escape(literal.value)}\``;
|
|
253
259
|
case "variableName":
|
|
254
260
|
return literal.value;
|
|
255
261
|
case "prompt":
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export declare
|
|
1
|
+
import { ParserResult } from "tarsec";
|
|
2
|
+
import { AccessExpression, IndexAccess } from "../types/access.js";
|
|
3
|
+
export declare function accessExpressionParser(input: string): ParserResult<AccessExpression>;
|
|
4
4
|
export declare const indexAccessParser: (input: string) => ParserResult<IndexAccess>;
|
|
5
|
-
export declare const dotFunctionCallParser: (input: string) => ParserResult<DotFunctionCall>;
|
|
6
|
-
export declare const accessExpressionParser: Parser<AccessExpression>;
|
|
@@ -1,19 +1,60 @@
|
|
|
1
|
-
import { capture, char,
|
|
1
|
+
import { capture, char, failure, or, sepBy1, seqC, set, success, } from "tarsec";
|
|
2
|
+
import { accessExpression, } from "../types/access.js";
|
|
2
3
|
import { agencyArrayParser } from "./dataStructures.js";
|
|
3
4
|
import { functionCallParser } from "./functionCall.js";
|
|
4
|
-
import { literalParser } from "./literals.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import { literalParser, variableNameParser } from "./literals.js";
|
|
6
|
+
function createAccessExpression(arr) {
|
|
7
|
+
const expression = _createAccessExpression(arr);
|
|
8
|
+
return expression;
|
|
9
|
+
}
|
|
10
|
+
function _createAccessExpression(arr) {
|
|
11
|
+
if (arr.length < 1) {
|
|
12
|
+
throw new Error(`Not enough items to create access expression: ${JSON.stringify(arr)}`);
|
|
13
|
+
}
|
|
14
|
+
if (arr.length === 1) {
|
|
15
|
+
return arr[0];
|
|
16
|
+
}
|
|
17
|
+
if (arr.length > 1) {
|
|
18
|
+
const head = arr.slice(0, -1);
|
|
19
|
+
const last = arr.at(-1);
|
|
20
|
+
switch (last?.type) {
|
|
21
|
+
case "variableName":
|
|
22
|
+
return accessExpression({
|
|
23
|
+
type: "dotProperty",
|
|
24
|
+
object: _createAccessExpression(head),
|
|
25
|
+
propertyName: last.value,
|
|
26
|
+
});
|
|
27
|
+
case "functionCall":
|
|
28
|
+
return accessExpression({
|
|
29
|
+
type: "dotFunctionCall",
|
|
30
|
+
object: _createAccessExpression(head),
|
|
31
|
+
functionCall: last,
|
|
32
|
+
});
|
|
33
|
+
case "indexAccess":
|
|
34
|
+
return accessExpression({
|
|
35
|
+
type: "indexAccess",
|
|
36
|
+
array: _createAccessExpression([...head, last.array]),
|
|
37
|
+
index: last.index,
|
|
38
|
+
});
|
|
39
|
+
default:
|
|
40
|
+
throw new Error(`unknown type ${last && last.type} in createAccessExpression`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`we should NEVER get here: ${JSON.stringify(arr)} `);
|
|
44
|
+
}
|
|
45
|
+
export function accessExpressionParser(input) {
|
|
46
|
+
const parser = sepBy1(char("."), or(indexAccessParser, functionCallParser, variableNameParser));
|
|
47
|
+
const result = parser(input);
|
|
48
|
+
if (result.success === false) {
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
if (result.result.length < 2) {
|
|
52
|
+
return failure("Didn't find property access or function call", input);
|
|
53
|
+
}
|
|
54
|
+
const access = createAccessExpression(result.result);
|
|
55
|
+
return success(access, result.rest);
|
|
56
|
+
}
|
|
11
57
|
export const indexAccessParser = (input) => {
|
|
12
58
|
const parser = seqC(set("type", "indexAccess"), capture(or(agencyArrayParser, functionCallParser, literalParser), "array"), char("["), capture(or(functionCallParser, literalParser), "index"), char("]"));
|
|
13
59
|
return parser(input);
|
|
14
60
|
};
|
|
15
|
-
export const dotFunctionCallParser = (input) => {
|
|
16
|
-
const parser = seqC(set("type", "dotFunctionCall"), capture(or(functionCallParser, literalParser), "object"), char("."), capture(functionCallParser, "functionCall"));
|
|
17
|
-
return parser(input);
|
|
18
|
-
};
|
|
19
|
-
export const accessExpressionParser = seqC(set("type", "accessExpression"), capture(or(dotFunctionCallParser, dotPropertyParser, indexAccessParser), "expression"), optionalSemicolon);
|