agency-lang 0.0.15 → 0.0.16
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 +20 -3
- package/dist/lib/backends/agencyGenerator.test.d.ts +1 -0
- package/dist/lib/backends/agencyGenerator.test.js +205 -0
- package/dist/lib/backends/baseGenerator.js +3 -1
- package/dist/lib/backends/graphGenerator.js +7 -5
- package/dist/lib/backends/typescriptGenerator.d.ts +3 -3
- package/dist/lib/backends/typescriptGenerator.js +14 -12
- 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.js +11 -2
- package/dist/lib/parsers/literals.test.js +7 -7
- 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/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 +3 -3
|
@@ -100,12 +100,28 @@ export class AgencyGenerator extends BaseGenerator {
|
|
|
100
100
|
processFunctionDefinition(node) {
|
|
101
101
|
const { functionName, body, parameters } = node;
|
|
102
102
|
// Build parameter list
|
|
103
|
-
const params = parameters
|
|
103
|
+
const params = parameters
|
|
104
|
+
.map((p) => {
|
|
105
|
+
if (p.typeHint) {
|
|
106
|
+
const typeStr = variableTypeToString(p.typeHint, this.typeAliases);
|
|
107
|
+
return `${p.name}: ${typeStr}`;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
return p.name;
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
.join(", ");
|
|
104
114
|
// Start function definition
|
|
105
115
|
let result = this.indentStr(`def ${functionName}(${params}) {\n`);
|
|
106
116
|
// Process body with increased indentation
|
|
107
117
|
this.increaseIndent();
|
|
108
|
-
|
|
118
|
+
if (node.docString) {
|
|
119
|
+
const docLines = [`"""`, node.docString.value, `"""`]
|
|
120
|
+
.map((line) => this.indentStr(line))
|
|
121
|
+
.join("\n");
|
|
122
|
+
result += `${docLines}\n`;
|
|
123
|
+
}
|
|
124
|
+
this.functionScopedVariables = [...parameters.map((p) => p.name)];
|
|
109
125
|
const lines = [];
|
|
110
126
|
for (const stmt of body) {
|
|
111
127
|
lines.push(this.processNode(stmt));
|
|
@@ -154,7 +170,8 @@ export class AgencyGenerator extends BaseGenerator {
|
|
|
154
170
|
}
|
|
155
171
|
}
|
|
156
172
|
processDotProperty(node) {
|
|
157
|
-
|
|
173
|
+
let objectCode = this.processNode(node.object);
|
|
174
|
+
objectCode = objectCode.trim();
|
|
158
175
|
return `${objectCode}.${node.propertyName}`;
|
|
159
176
|
}
|
|
160
177
|
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
|
+
});
|
|
@@ -69,7 +69,7 @@ export class BaseGenerator {
|
|
|
69
69
|
return "";
|
|
70
70
|
}
|
|
71
71
|
collectFunctionSignature(node) {
|
|
72
|
-
this.functionSignatures[node.functionName] = node.parameters;
|
|
72
|
+
this.functionSignatures[node.functionName] = node.parameters.map((param) => param.name);
|
|
73
73
|
}
|
|
74
74
|
processGraphNodeName(node) { }
|
|
75
75
|
processNode(node) {
|
|
@@ -113,6 +113,8 @@ export class BaseGenerator {
|
|
|
113
113
|
return this.processWhileLoop(node);
|
|
114
114
|
case "specialVar":
|
|
115
115
|
return this.processSpecialVar(node);
|
|
116
|
+
case "indexAccess":
|
|
117
|
+
return this.processIndexAccess(node);
|
|
116
118
|
default:
|
|
117
119
|
throw new Error(`Unhandled Agency node type: ${node.type}`);
|
|
118
120
|
}
|
|
@@ -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();
|
|
@@ -157,12 +157,12 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
157
157
|
}
|
|
158
158
|
const properties = {};
|
|
159
159
|
parameters.forEach((param) => {
|
|
160
|
-
const typeHint =
|
|
160
|
+
const typeHint = param.typeHint || {
|
|
161
161
|
type: "primitiveType",
|
|
162
162
|
value: "string",
|
|
163
163
|
};
|
|
164
164
|
const tsType = mapTypeToZodSchema(typeHint, this.typeAliases);
|
|
165
|
-
properties[param] = tsType;
|
|
165
|
+
properties[param.name] = tsType;
|
|
166
166
|
});
|
|
167
167
|
let schema = "";
|
|
168
168
|
for (const [key, value] of Object.entries(properties)) {
|
|
@@ -183,13 +183,13 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
183
183
|
*/
|
|
184
184
|
processFunctionDefinition(node) {
|
|
185
185
|
const { functionName, body, parameters } = node;
|
|
186
|
-
this.functionScopedVariables = [...parameters];
|
|
186
|
+
this.functionScopedVariables = [...parameters.map((p) => p.name)];
|
|
187
187
|
const bodyCode = [];
|
|
188
188
|
for (const stmt of body) {
|
|
189
189
|
bodyCode.push(this.processNode(stmt));
|
|
190
190
|
}
|
|
191
191
|
this.functionScopedVariables = [];
|
|
192
|
-
const args = parameters.join(", ") || "";
|
|
192
|
+
const args = parameters.map((p) => p.name).join(", ") || "";
|
|
193
193
|
return renderFunctionDefinition.default({
|
|
194
194
|
functionName,
|
|
195
195
|
args: "{" + args + "}",
|
|
@@ -219,12 +219,14 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
219
219
|
if (arg.type === "functionCall") {
|
|
220
220
|
this.functionsUsed.add(arg.functionName);
|
|
221
221
|
return this.generateFunctionCallExpression(arg);
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
222
|
+
/* } else if (arg.type === "accessExpression") {
|
|
223
|
+
return this.processAccessExpression(arg);
|
|
224
|
+
} else if (arg.type === "indexAccess") {
|
|
225
|
+
return this.processIndexAccess(arg);
|
|
226
|
+
*/ }
|
|
226
227
|
else {
|
|
227
|
-
return this.generateLiteral(arg);
|
|
228
|
+
// return this.generateLiteral(arg);
|
|
229
|
+
return this.processNode(arg);
|
|
228
230
|
}
|
|
229
231
|
});
|
|
230
232
|
let argsString = "";
|
|
@@ -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);
|