agency-lang 0.0.16 → 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 +4 -3
- package/dist/lib/backends/baseGenerator.d.ts +1 -0
- package/dist/lib/backends/baseGenerator.js +9 -1
- package/dist/lib/backends/typescriptGenerator.js +9 -5
- package/dist/lib/parsers/literals.d.ts +2 -1
- package/dist/lib/parsers/literals.js +5 -4
- package/dist/lib/parsers/literals.test.js +182 -9
- package/dist/lib/types/literals.d.ts +5 -1
- 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:
|
|
@@ -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);
|
|
@@ -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":
|
|
@@ -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
|
}
|
|
@@ -220,10 +221,11 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
220
221
|
this.functionsUsed.add(arg.functionName);
|
|
221
222
|
return this.generateFunctionCallExpression(arg);
|
|
222
223
|
/* } else if (arg.type === "accessExpression") {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
return this.processAccessExpression(arg);
|
|
225
|
+
} else if (arg.type === "indexAccess") {
|
|
226
|
+
return this.processIndexAccess(arg);
|
|
227
|
+
*/
|
|
228
|
+
}
|
|
227
229
|
else {
|
|
228
230
|
// return this.generateLiteral(arg);
|
|
229
231
|
return this.processNode(arg);
|
|
@@ -252,6 +254,8 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
252
254
|
return literal.value;
|
|
253
255
|
case "string":
|
|
254
256
|
return `"${escape(literal.value)}"`;
|
|
257
|
+
case "multiLineString":
|
|
258
|
+
return `\`${escape(literal.value)}\``;
|
|
255
259
|
case "variableName":
|
|
256
260
|
return literal.value;
|
|
257
261
|
case "prompt":
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { InterpolationSegment, Literal, NumberLiteral, PromptLiteral, StringLiteral, TextSegment, VariableNameLiteral } from "../types.js";
|
|
1
|
+
import { InterpolationSegment, Literal, MultiLineStringLiteral, NumberLiteral, PromptLiteral, StringLiteral, TextSegment, VariableNameLiteral } from "../types.js";
|
|
2
2
|
import { Parser } from "tarsec";
|
|
3
3
|
export declare const textSegmentParser: Parser<TextSegment>;
|
|
4
4
|
export declare const interpolationSegmentParser: Parser<InterpolationSegment>;
|
|
5
5
|
export declare const promptParser: Parser<PromptLiteral>;
|
|
6
6
|
export declare const numberParser: Parser<NumberLiteral>;
|
|
7
7
|
export declare const stringParser: Parser<StringLiteral>;
|
|
8
|
+
export declare const multiLineStringParser: Parser<MultiLineStringLiteral>;
|
|
8
9
|
export declare const variableNameParser: Parser<VariableNameLiteral>;
|
|
9
10
|
export declare const literalParser: Parser<Literal>;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { backtick, varNameChar } from "./utils.js";
|
|
2
|
-
import { capture, char, digit, letter, many, many1Till, many1WithJoin,
|
|
2
|
+
import { capture, char, digit, letter, many, many1Till, many1WithJoin, manyTillOneOf, manyTillStr, manyWithJoin, map, or, seq, seqC, set, str, } from "tarsec";
|
|
3
3
|
export const textSegmentParser = map(many1Till(or(backtick, char("$"))), (text) => ({
|
|
4
4
|
type: "text",
|
|
5
5
|
value: text,
|
|
6
6
|
}));
|
|
7
|
-
export const interpolationSegmentParser = seqC(set("type", "interpolation"), char("$"), char("{"), capture(
|
|
7
|
+
export const interpolationSegmentParser = seqC(set("type", "interpolation"), char("$"), char("{"), capture(manyTillStr("}"), "variableName"), char("}"));
|
|
8
8
|
export const promptParser = seqC(set("type", "prompt"), backtick, capture(many(or(textSegmentParser, interpolationSegmentParser)), "segments"), backtick);
|
|
9
9
|
export const numberParser = seqC(set("type", "number"), capture(many1WithJoin(or(char("-"), char("."), digit)), "value"));
|
|
10
|
-
export const stringParser = seqC(set("type", "string"), char('"'), capture(
|
|
10
|
+
export const stringParser = seqC(set("type", "string"), char('"'), capture(manyTillOneOf(['"', "\n"]), "value"), char('"'));
|
|
11
|
+
export const multiLineStringParser = seqC(set("type", "multiLineString"), str('"""'), capture(manyTillStr('"""'), "value"), str('"""'));
|
|
11
12
|
export const variableNameParser = seq([
|
|
12
13
|
set("type", "variableName"),
|
|
13
14
|
capture(letter, "init"),
|
|
@@ -18,4 +19,4 @@ export const variableNameParser = seq([
|
|
|
18
19
|
value: `${captures.init}${captures.value}`,
|
|
19
20
|
};
|
|
20
21
|
});
|
|
21
|
-
export const literalParser = or(promptParser, numberParser, stringParser, variableNameParser);
|
|
22
|
+
export const literalParser = or(promptParser, numberParser, multiLineStringParser, stringParser, variableNameParser);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { textSegmentParser, interpolationSegmentParser, promptParser, numberParser, stringParser, variableNameParser, literalParser, } from "./literals.js";
|
|
2
|
+
import { textSegmentParser, interpolationSegmentParser, promptParser, numberParser, stringParser, multiLineStringParser, variableNameParser, literalParser, } from "./literals.js";
|
|
3
3
|
describe("literals parsers", () => {
|
|
4
4
|
describe("textSegmentParser", () => {
|
|
5
5
|
const testCases = [
|
|
@@ -112,7 +112,16 @@ describe("literals parsers", () => {
|
|
|
112
112
|
{ input: "${foo", expected: { success: false } },
|
|
113
113
|
{ input: "$foo}", expected: { success: false } },
|
|
114
114
|
{ input: "{foo}", expected: { success: false } },
|
|
115
|
-
{
|
|
115
|
+
{
|
|
116
|
+
input: "${}",
|
|
117
|
+
expected: {
|
|
118
|
+
success: true,
|
|
119
|
+
result: {
|
|
120
|
+
type: "interpolation",
|
|
121
|
+
variableName: "",
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
116
125
|
{ input: "", expected: { success: false } },
|
|
117
126
|
{ input: "foo", expected: { success: false } },
|
|
118
127
|
];
|
|
@@ -391,13 +400,6 @@ describe("literals parsers", () => {
|
|
|
391
400
|
result: { type: "string", value: "tab\there" },
|
|
392
401
|
},
|
|
393
402
|
},
|
|
394
|
-
{
|
|
395
|
-
input: '"newline\nhere"',
|
|
396
|
-
expected: {
|
|
397
|
-
success: true,
|
|
398
|
-
result: { type: "string", value: "newline\nhere" },
|
|
399
|
-
},
|
|
400
|
-
},
|
|
401
403
|
{
|
|
402
404
|
input: '"special!@#$%^&*()"',
|
|
403
405
|
expected: {
|
|
@@ -425,6 +427,13 @@ describe("literals parsers", () => {
|
|
|
425
427
|
{ input: "'hello'", expected: { success: false } },
|
|
426
428
|
{ input: "", expected: { success: false } },
|
|
427
429
|
{ input: "hello", expected: { success: false } },
|
|
430
|
+
/// use """ for multi-line strings
|
|
431
|
+
{
|
|
432
|
+
input: '"newline\nhere"',
|
|
433
|
+
expected: {
|
|
434
|
+
success: false,
|
|
435
|
+
},
|
|
436
|
+
},
|
|
428
437
|
];
|
|
429
438
|
testCases.forEach(({ input, expected }) => {
|
|
430
439
|
if (expected.success) {
|
|
@@ -444,6 +453,170 @@ describe("literals parsers", () => {
|
|
|
444
453
|
}
|
|
445
454
|
});
|
|
446
455
|
});
|
|
456
|
+
describe("multiLineStringParser", () => {
|
|
457
|
+
const testCases = [
|
|
458
|
+
// Happy path - simple strings
|
|
459
|
+
{
|
|
460
|
+
input: '"""hello"""',
|
|
461
|
+
expected: {
|
|
462
|
+
success: true,
|
|
463
|
+
result: { type: "multiLineString", value: "hello" },
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
input: '"""world"""',
|
|
468
|
+
expected: {
|
|
469
|
+
success: true,
|
|
470
|
+
result: { type: "multiLineString", value: "world" },
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
// Empty multi-line string
|
|
474
|
+
{
|
|
475
|
+
input: '""""""',
|
|
476
|
+
expected: {
|
|
477
|
+
success: true,
|
|
478
|
+
result: { type: "multiLineString", value: "" },
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
// Multi-line strings with actual newlines
|
|
482
|
+
{
|
|
483
|
+
input: '"""line1\nline2"""',
|
|
484
|
+
expected: {
|
|
485
|
+
success: true,
|
|
486
|
+
result: { type: "multiLineString", value: "line1\nline2" },
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
input: '"""line1\nline2\nline3"""',
|
|
491
|
+
expected: {
|
|
492
|
+
success: true,
|
|
493
|
+
result: { type: "multiLineString", value: "line1\nline2\nline3" },
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
input: '"""\nstarts with newline"""',
|
|
498
|
+
expected: {
|
|
499
|
+
success: true,
|
|
500
|
+
result: { type: "multiLineString", value: "\nstarts with newline" },
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
input: '"""ends with newline\n"""',
|
|
505
|
+
expected: {
|
|
506
|
+
success: true,
|
|
507
|
+
result: { type: "multiLineString", value: "ends with newline\n" },
|
|
508
|
+
},
|
|
509
|
+
},
|
|
510
|
+
// Strings with special characters
|
|
511
|
+
{
|
|
512
|
+
input: '"""Hello, World!"""',
|
|
513
|
+
expected: {
|
|
514
|
+
success: true,
|
|
515
|
+
result: { type: "multiLineString", value: "Hello, World!" },
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
input: '"""123"""',
|
|
520
|
+
expected: {
|
|
521
|
+
success: true,
|
|
522
|
+
result: { type: "multiLineString", value: "123" },
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
input: '""" spaces and tabs\t\t"""',
|
|
527
|
+
expected: {
|
|
528
|
+
success: true,
|
|
529
|
+
result: { type: "multiLineString", value: " spaces and tabs\t\t" },
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
input: '"""special!@#$%^&*()"""',
|
|
534
|
+
expected: {
|
|
535
|
+
success: true,
|
|
536
|
+
result: { type: "multiLineString", value: "special!@#$%^&*()" },
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
// Strings containing single and double quotes
|
|
540
|
+
{
|
|
541
|
+
input: '"""single\'quotes\'here"""',
|
|
542
|
+
expected: {
|
|
543
|
+
success: true,
|
|
544
|
+
result: { type: "multiLineString", value: "single'quotes'here" },
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
input: '"""double"quotes"here"""',
|
|
549
|
+
expected: {
|
|
550
|
+
success: true,
|
|
551
|
+
result: { type: "multiLineString", value: 'double"quotes"here' },
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
input: '"""mixed"and\'quotes"""',
|
|
556
|
+
expected: {
|
|
557
|
+
success: true,
|
|
558
|
+
result: { type: "multiLineString", value: `mixed"and'quotes` },
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
// Strings containing backticks and interpolation-like syntax
|
|
562
|
+
{
|
|
563
|
+
input: '"""`backtick`"""',
|
|
564
|
+
expected: {
|
|
565
|
+
success: true,
|
|
566
|
+
result: { type: "multiLineString", value: "`backtick`" },
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
input: '"""${notInterpolation}"""',
|
|
571
|
+
expected: {
|
|
572
|
+
success: true,
|
|
573
|
+
result: { type: "multiLineString", value: "${notInterpolation}" },
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
// Multiple consecutive newlines
|
|
577
|
+
{
|
|
578
|
+
input: '"""line1\n\n\nline2"""',
|
|
579
|
+
expected: {
|
|
580
|
+
success: true,
|
|
581
|
+
result: { type: "multiLineString", value: "line1\n\n\nline2" },
|
|
582
|
+
},
|
|
583
|
+
},
|
|
584
|
+
// Mixed whitespace
|
|
585
|
+
{
|
|
586
|
+
input: '""" \n\t\n """',
|
|
587
|
+
expected: {
|
|
588
|
+
success: true,
|
|
589
|
+
result: { type: "multiLineString", value: " \n\t\n " },
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
// Failure cases
|
|
593
|
+
{ input: '"""hello', expected: { success: false } },
|
|
594
|
+
{ input: 'hello"""', expected: { success: false } },
|
|
595
|
+
{ input: '""hello"""', expected: { success: false } },
|
|
596
|
+
{ input: '"""hello""', expected: { success: false } },
|
|
597
|
+
{ input: '"hello"', expected: { success: false } },
|
|
598
|
+
{ input: "'hello'", expected: { success: false } },
|
|
599
|
+
{ input: "", expected: { success: false } },
|
|
600
|
+
{ input: "hello", expected: { success: false } },
|
|
601
|
+
];
|
|
602
|
+
testCases.forEach(({ input, expected }) => {
|
|
603
|
+
if (expected.success) {
|
|
604
|
+
it(`should parse ${JSON.stringify(input)} successfully`, () => {
|
|
605
|
+
const result = multiLineStringParser(input);
|
|
606
|
+
expect(result.success).toBe(true);
|
|
607
|
+
if (result.success) {
|
|
608
|
+
expect(result.result).toEqual(expected.result);
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
it(`should fail to parse ${JSON.stringify(input)}`, () => {
|
|
614
|
+
const result = multiLineStringParser(input);
|
|
615
|
+
expect(result.success).toBe(false);
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
});
|
|
447
620
|
describe("variableNameParser", () => {
|
|
448
621
|
const testCases = [
|
|
449
622
|
// Happy path
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type Literal = NumberLiteral | StringLiteral | VariableNameLiteral | PromptLiteral;
|
|
1
|
+
export type Literal = NumberLiteral | MultiLineStringLiteral | StringLiteral | VariableNameLiteral | PromptLiteral;
|
|
2
2
|
export type NumberLiteral = {
|
|
3
3
|
type: "number";
|
|
4
4
|
value: string;
|
|
@@ -7,6 +7,10 @@ export type StringLiteral = {
|
|
|
7
7
|
type: "string";
|
|
8
8
|
value: string;
|
|
9
9
|
};
|
|
10
|
+
export type MultiLineStringLiteral = {
|
|
11
|
+
type: "multiLineString";
|
|
12
|
+
value: string;
|
|
13
|
+
};
|
|
10
14
|
export type VariableNameLiteral = {
|
|
11
15
|
type: "variableName";
|
|
12
16
|
value: string;
|
package/package.json
CHANGED