toad-compiler 0.1.0

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.
Files changed (114) hide show
  1. package/LICENSE +21 -0
  2. package/dist/analyze.d.ts +13 -0
  3. package/dist/analyze.d.ts.map +1 -0
  4. package/dist/analyze.js +20 -0
  5. package/dist/analyze.js.map +1 -0
  6. package/dist/analyze.test.d.ts +2 -0
  7. package/dist/analyze.test.d.ts.map +1 -0
  8. package/dist/analyze.test.js +45 -0
  9. package/dist/analyze.test.js.map +1 -0
  10. package/dist/ast.d.ts +63 -0
  11. package/dist/ast.d.ts.map +1 -0
  12. package/dist/ast.js +6 -0
  13. package/dist/ast.js.map +1 -0
  14. package/dist/bin.d.ts +3 -0
  15. package/dist/bin.d.ts.map +1 -0
  16. package/dist/bin.js +7 -0
  17. package/dist/bin.js.map +1 -0
  18. package/dist/cli.d.ts +11 -0
  19. package/dist/cli.d.ts.map +1 -0
  20. package/dist/cli.js +93 -0
  21. package/dist/cli.js.map +1 -0
  22. package/dist/cli.test.d.ts +2 -0
  23. package/dist/cli.test.d.ts.map +1 -0
  24. package/dist/cli.test.js +54 -0
  25. package/dist/cli.test.js.map +1 -0
  26. package/dist/codegen.d.ts +9 -0
  27. package/dist/codegen.d.ts.map +1 -0
  28. package/dist/codegen.js +152 -0
  29. package/dist/codegen.js.map +1 -0
  30. package/dist/codegen.test.d.ts +2 -0
  31. package/dist/codegen.test.d.ts.map +1 -0
  32. package/dist/codegen.test.js +76 -0
  33. package/dist/codegen.test.js.map +1 -0
  34. package/dist/controlflow.test.d.ts +2 -0
  35. package/dist/controlflow.test.d.ts.map +1 -0
  36. package/dist/controlflow.test.js +75 -0
  37. package/dist/controlflow.test.js.map +1 -0
  38. package/dist/diagnostics.d.ts +24 -0
  39. package/dist/diagnostics.d.ts.map +1 -0
  40. package/dist/diagnostics.js +26 -0
  41. package/dist/diagnostics.js.map +1 -0
  42. package/dist/each.test.d.ts +2 -0
  43. package/dist/each.test.d.ts.map +1 -0
  44. package/dist/each.test.js +82 -0
  45. package/dist/each.test.js.map +1 -0
  46. package/dist/env.test.d.ts +2 -0
  47. package/dist/env.test.d.ts.map +1 -0
  48. package/dist/env.test.js +27 -0
  49. package/dist/env.test.js.map +1 -0
  50. package/dist/example.test.d.ts +2 -0
  51. package/dist/example.test.d.ts.map +1 -0
  52. package/dist/example.test.js +19 -0
  53. package/dist/example.test.js.map +1 -0
  54. package/dist/if.test.d.ts +2 -0
  55. package/dist/if.test.d.ts.map +1 -0
  56. package/dist/if.test.js +72 -0
  57. package/dist/if.test.js.map +1 -0
  58. package/dist/index.d.ts +17 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +22 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/index.test.d.ts +2 -0
  63. package/dist/index.test.d.ts.map +1 -0
  64. package/dist/index.test.js +19 -0
  65. package/dist/index.test.js.map +1 -0
  66. package/dist/interpolate.d.ts +18 -0
  67. package/dist/interpolate.d.ts.map +1 -0
  68. package/dist/interpolate.js +237 -0
  69. package/dist/interpolate.js.map +1 -0
  70. package/dist/interpolate.test.d.ts +2 -0
  71. package/dist/interpolate.test.d.ts.map +1 -0
  72. package/dist/interpolate.test.js +35 -0
  73. package/dist/interpolate.test.js.map +1 -0
  74. package/dist/lifecycle.test.d.ts +2 -0
  75. package/dist/lifecycle.test.d.ts.map +1 -0
  76. package/dist/lifecycle.test.js +26 -0
  77. package/dist/lifecycle.test.js.map +1 -0
  78. package/dist/objecttypes.test.d.ts +2 -0
  79. package/dist/objecttypes.test.d.ts.map +1 -0
  80. package/dist/objecttypes.test.js +83 -0
  81. package/dist/objecttypes.test.js.map +1 -0
  82. package/dist/preprocess.d.ts +20 -0
  83. package/dist/preprocess.d.ts.map +1 -0
  84. package/dist/preprocess.js +98 -0
  85. package/dist/preprocess.js.map +1 -0
  86. package/dist/preprocess.test.d.ts +2 -0
  87. package/dist/preprocess.test.d.ts.map +1 -0
  88. package/dist/preprocess.test.js +58 -0
  89. package/dist/preprocess.test.js.map +1 -0
  90. package/dist/system.test.d.ts +2 -0
  91. package/dist/system.test.d.ts.map +1 -0
  92. package/dist/system.test.js +35 -0
  93. package/dist/system.test.js.map +1 -0
  94. package/dist/toon.d.ts +16 -0
  95. package/dist/toon.d.ts.map +1 -0
  96. package/dist/toon.js +32 -0
  97. package/dist/toon.js.map +1 -0
  98. package/dist/toon.test.d.ts +2 -0
  99. package/dist/toon.test.d.ts.map +1 -0
  100. package/dist/toon.test.js +50 -0
  101. package/dist/toon.test.js.map +1 -0
  102. package/dist/uses.test.d.ts +2 -0
  103. package/dist/uses.test.d.ts.map +1 -0
  104. package/dist/uses.test.js +32 -0
  105. package/dist/uses.test.js.map +1 -0
  106. package/dist/validate.d.ts +14 -0
  107. package/dist/validate.d.ts.map +1 -0
  108. package/dist/validate.js +351 -0
  109. package/dist/validate.js.map +1 -0
  110. package/dist/validate.test.d.ts +2 -0
  111. package/dist/validate.test.d.ts.map +1 -0
  112. package/dist/validate.test.js +64 -0
  113. package/dist/validate.test.js.map +1 -0
  114. package/package.json +59 -0
@@ -0,0 +1,76 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { analyze } from "./analyze.js";
3
+ import { generate } from "./codegen.js";
4
+ const SOURCE = [
5
+ "agent: researcher",
6
+ "model: claude-opus-4-7",
7
+ "description: Research a topic and return a sourced summary.",
8
+ "inputs[1]{name,type}:",
9
+ " topic,string",
10
+ "tools[2]: web_search,fetch_page",
11
+ "prompt: |",
12
+ " You are a research analyst. Research: {inputs.topic}",
13
+ " Use web_search to find sources, then fetch_page to read them.",
14
+ " Return a cited summary.",
15
+ "outputs[2]{name,type}:",
16
+ " summary,string",
17
+ " sources,string[]",
18
+ ].join("\n");
19
+ // The canonical contract from _bmad-output/architecture.md §5.
20
+ const EXPECTED = `// Generated by toac from researcher.agent — do not edit.
21
+ import { createAgent, type Agent } from "toad-runtime";
22
+ import { z } from "zod";
23
+ import { web_search, fetch_page } from "./researcher.tools";
24
+
25
+ export interface ResearcherInput {
26
+ topic: string;
27
+ }
28
+
29
+ export interface ResearcherOutput {
30
+ summary: string;
31
+ sources: string[];
32
+ }
33
+
34
+ const inputSchema = z.object({
35
+ topic: z.string(),
36
+ });
37
+
38
+ const outputSchema = z.object({
39
+ summary: z.string(),
40
+ sources: z.array(z.string()),
41
+ });
42
+
43
+ export const researcher: Agent<ResearcherInput, ResearcherOutput> = createAgent({
44
+ name: "researcher",
45
+ model: "claude-opus-4-7",
46
+ description: "Research a topic and return a sourced summary.",
47
+ tools: { web_search, fetch_page },
48
+ inputSchema,
49
+ outputSchema,
50
+ prompt: (inputs: ResearcherInput) =>
51
+ \`You are a research analyst. Research: \${inputs.topic}
52
+ Use web_search to find sources, then fetch_page to read them.
53
+ Return a cited summary.\`,
54
+ });
55
+
56
+ export default researcher;
57
+ `;
58
+ describe("generate — codegen contract", () => {
59
+ it("emits the researcher.ts contract (architecture.md §5)", () => {
60
+ const { ast, diagnostics } = analyze(SOURCE, "researcher.agent");
61
+ expect(diagnostics).toEqual([]);
62
+ expect(generate(ast)).toBe(EXPECTED);
63
+ });
64
+ it("is deterministic", () => {
65
+ const { ast } = analyze(SOURCE, "researcher.agent");
66
+ expect(generate(ast)).toBe(generate(ast));
67
+ });
68
+ it("omits zod/tools and returns string output for a minimal agent", () => {
69
+ const { ast } = analyze("agent: ping\nmodel: m\nprompt: hi", "ping.agent");
70
+ const code = generate(ast);
71
+ expect(code).toContain("Agent<PingInput, string>");
72
+ expect(code).not.toContain('from "zod"');
73
+ expect(code).not.toContain("tools:");
74
+ });
75
+ });
76
+ //# sourceMappingURL=codegen.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen.test.js","sourceRoot":"","sources":["../src/codegen.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,MAAM,GAAG;IACb,mBAAmB;IACnB,wBAAwB;IACxB,6DAA6D;IAC7D,uBAAuB;IACvB,gBAAgB;IAChB,iCAAiC;IACjC,WAAW;IACX,wDAAwD;IACxD,iEAAiE;IACjE,2BAA2B;IAC3B,wBAAwB;IACxB,kBAAkB;IAClB,oBAAoB;CACrB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,+DAA+D;AAC/D,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqChB,CAAC;AAEF,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;QACjE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,CAAC,QAAQ,CAAC,GAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAC1B,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;QACpD,MAAM,CAAC,QAAQ,CAAC,GAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAI,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,mCAAmC,EAAE,YAAY,CAAC,CAAC;QAC3E,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAI,CAAC,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;QACnD,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=controlflow.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controlflow.test.d.ts","sourceRoot":"","sources":["../src/controlflow.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,75 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { analyze } from "./analyze.js";
3
+ import { generate } from "./codegen.js";
4
+ import { parsePromptTemplate } from "./interpolate.js";
5
+ function agent(promptBody, rows) {
6
+ return [
7
+ "agent: a",
8
+ "model: m",
9
+ `inputs[${rows.length}]{name,type}:`,
10
+ ...rows.map((r) => ` ${r}`),
11
+ "prompt: |",
12
+ ...promptBody.map((l) => ` ${l}`),
13
+ ].join("\n");
14
+ }
15
+ describe("{#each} index + {:else}", () => {
16
+ it("parses an index variable", () => {
17
+ const { segments } = parsePromptTemplate("{#each inputs.xs as x, i}\n{i}. {x}\n{/each}");
18
+ expect(segments[0]).toMatchObject({
19
+ kind: "each",
20
+ item: { kind: "name", name: "x" },
21
+ index: "i",
22
+ });
23
+ });
24
+ it("compiles the index to .map((it, i) =>", () => {
25
+ const src = agent(["{#each inputs.items as it, i}", "{i}. {it}", "{/each}"], ["items,string[]"]);
26
+ const { ast, diagnostics } = analyze(src, "a.agent");
27
+ expect(diagnostics).toEqual([]);
28
+ expect(generate(ast)).toContain("inputs.items.map((it, i) => `");
29
+ });
30
+ it("compiles an each-{:else} to a length guard", () => {
31
+ const src = agent(["{#each inputs.items as it}", "- {it}", "{:else}", "(none)", "{/each}"], ["items,string[]"]);
32
+ const { ast, diagnostics } = analyze(src, "a.agent");
33
+ expect(diagnostics).toEqual([]);
34
+ const code = generate(ast);
35
+ expect(code).toContain("inputs.items.length > 0 ?");
36
+ expect(code).toContain("(none)");
37
+ });
38
+ });
39
+ describe("{#if} {:else if} chains", () => {
40
+ it("parses else-if into nested ifs", () => {
41
+ const { segments, errors } = parsePromptTemplate("{#if inputs.a}\nA\n{:else if inputs.b}\nB\n{:else}\nC\n{/if}");
42
+ expect(errors).toEqual([]);
43
+ expect(segments[0]).toMatchObject({
44
+ kind: "if",
45
+ cond: ["inputs", "a"],
46
+ then: [{ kind: "text", value: "A\n" }],
47
+ else: [
48
+ {
49
+ kind: "if",
50
+ cond: ["inputs", "b"],
51
+ then: [{ kind: "text", value: "B\n" }],
52
+ else: [{ kind: "text", value: "C\n" }],
53
+ },
54
+ ],
55
+ });
56
+ });
57
+ it("compiles else-if to nested ternaries", () => {
58
+ const src = agent([
59
+ "{#if inputs.a}",
60
+ "alpha",
61
+ "{:else if inputs.b}",
62
+ "beta",
63
+ "{:else}",
64
+ "gamma",
65
+ "{/if}",
66
+ ], ["a,boolean", "b,boolean"]);
67
+ const { ast, diagnostics } = analyze(src, "a.agent");
68
+ expect(diagnostics).toEqual([]);
69
+ const code = generate(ast);
70
+ expect(code).toContain("inputs.a ?");
71
+ expect(code).toContain("inputs.b ?");
72
+ expect(code).toContain("gamma");
73
+ });
74
+ });
75
+ //# sourceMappingURL=controlflow.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controlflow.test.js","sourceRoot":"","sources":["../src/controlflow.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Diagnostics shared across the compiler pipeline. User errors are collected and
3
+ * reported here — never thrown (see `_bmad-output/architecture.md`, NFR4).
4
+ *
5
+ * Code ranges: TOA0xx general · TOA1xx parse/decode · TOA2xx validation ·
6
+ * TOA3xx interpolation.
7
+ */
8
+ export interface Diagnostic {
9
+ severity: "error" | "warning";
10
+ /** Stable code, e.g. "TOA101". */
11
+ code: string;
12
+ message: string;
13
+ file: string;
14
+ line?: number;
15
+ col?: number;
16
+ }
17
+ /** Build an error-severity diagnostic, attaching location only when known. */
18
+ export declare function errorDiagnostic(code: string, message: string, file: string, loc?: {
19
+ line?: number;
20
+ col?: number;
21
+ }): Diagnostic;
22
+ /** Format a diagnostic as `file:line:col severity CODE: message`. */
23
+ export declare function formatDiagnostic(d: Diagnostic): string;
24
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACpC,UAAU,CASZ;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAMtD"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Diagnostics shared across the compiler pipeline. User errors are collected and
3
+ * reported here — never thrown (see `_bmad-output/architecture.md`, NFR4).
4
+ *
5
+ * Code ranges: TOA0xx general · TOA1xx parse/decode · TOA2xx validation ·
6
+ * TOA3xx interpolation.
7
+ */
8
+ /** Build an error-severity diagnostic, attaching location only when known. */
9
+ export function errorDiagnostic(code, message, file, loc) {
10
+ const diagnostic = { severity: "error", code, message, file };
11
+ if (loc?.line !== undefined) {
12
+ diagnostic.line = loc.line;
13
+ }
14
+ if (loc?.col !== undefined) {
15
+ diagnostic.col = loc.col;
16
+ }
17
+ return diagnostic;
18
+ }
19
+ /** Format a diagnostic as `file:line:col severity CODE: message`. */
20
+ export function formatDiagnostic(d) {
21
+ const loc = d.line !== undefined
22
+ ? `:${d.line}${d.col !== undefined ? `:${d.col}` : ""}`
23
+ : "";
24
+ return `${d.file}${loc} ${d.severity} ${d.code}: ${d.message}`;
25
+ }
26
+ //# sourceMappingURL=diagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAYH,8EAA8E;AAC9E,MAAM,UAAU,eAAe,CAC7B,IAAY,EACZ,OAAe,EACf,IAAY,EACZ,GAAqC;IAErC,MAAM,UAAU,GAAe,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC1E,IAAI,GAAG,EAAE,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IAC7B,CAAC;IACD,IAAI,GAAG,EAAE,GAAG,KAAK,SAAS,EAAE,CAAC;QAC3B,UAAU,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IAC3B,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,gBAAgB,CAAC,CAAa;IAC5C,MAAM,GAAG,GACP,CAAC,CAAC,IAAI,KAAK,SAAS;QAClB,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;QACvD,CAAC,CAAC,EAAE,CAAC;IACT,OAAO,GAAG,CAAC,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;AACjE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=each.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"each.test.d.ts","sourceRoot":"","sources":["../src/each.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,82 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { analyze } from "./analyze.js";
3
+ import { generate } from "./codegen.js";
4
+ import { parsePromptTemplate } from "./interpolate.js";
5
+ describe("{#each} parsing", () => {
6
+ it("parses an each block into a nested segment", () => {
7
+ const { segments, errors } = parsePromptTemplate("Sources:\n{#each inputs.sources as s}\n- {s}\n{/each}\nDone.");
8
+ expect(errors).toEqual([]);
9
+ expect(segments).toEqual([
10
+ { kind: "text", value: "Sources:\n" },
11
+ {
12
+ kind: "each",
13
+ source: ["inputs", "sources"],
14
+ item: { kind: "name", name: "s" },
15
+ body: [
16
+ { kind: "text", value: "- " },
17
+ { kind: "interp", path: ["s"] },
18
+ { kind: "text", value: "\n" },
19
+ ],
20
+ },
21
+ { kind: "text", value: "Done." },
22
+ ]);
23
+ });
24
+ it("reports an unclosed each", () => {
25
+ const { errors } = parsePromptTemplate("{#each inputs.xs as x}\n- {x}");
26
+ expect(errors.some((e) => /unclosed/.test(e))).toBe(true);
27
+ });
28
+ it("reports an invalid each header", () => {
29
+ const { errors } = parsePromptTemplate("{#each nope}\n{/each}");
30
+ expect(errors.some((e) => /invalid \{#each\}/.test(e))).toBe(true);
31
+ });
32
+ });
33
+ function digest(promptBody) {
34
+ return [
35
+ "agent: digest",
36
+ "model: m",
37
+ "inputs[1]{name,type}:",
38
+ " sources,string[]",
39
+ "prompt: |",
40
+ ...promptBody.map((l) => ` ${l}`),
41
+ ].join("\n");
42
+ }
43
+ describe("{#each} validation + codegen", () => {
44
+ it("compiles an each over an array input to .map().join()", () => {
45
+ const src = digest([
46
+ "Sources:",
47
+ "{#each inputs.sources as s}",
48
+ "- {s}",
49
+ "{/each}",
50
+ ]);
51
+ const { ast, diagnostics } = analyze(src, "digest.agent");
52
+ expect(diagnostics).toEqual([]);
53
+ const code = generate(ast);
54
+ expect(code).toContain("inputs.sources.map((s) => `");
55
+ expect(code).toContain('.join("")');
56
+ });
57
+ it("rejects {#each} over a non-array input (TOA303)", () => {
58
+ const src = [
59
+ "agent: d",
60
+ "model: m",
61
+ "inputs[1]{name,type}:",
62
+ " topic,string",
63
+ "prompt: |",
64
+ " {#each inputs.topic as t}",
65
+ " - {t}",
66
+ " {/each}",
67
+ ].join("\n");
68
+ const { diagnostics } = analyze(src, "d.agent");
69
+ expect(diagnostics.map((d) => d.code)).toContain("TOA303");
70
+ });
71
+ it("rejects a loop variable used outside its block (TOA301)", () => {
72
+ const src = digest([
73
+ "{#each inputs.sources as s}",
74
+ "- {s}",
75
+ "{/each}",
76
+ "tail {s}",
77
+ ]);
78
+ const { diagnostics } = analyze(src, "digest.agent");
79
+ expect(diagnostics.map((d) => d.code)).toContain("TOA301");
80
+ });
81
+ });
82
+ //# sourceMappingURL=each.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"each.test.js","sourceRoot":"","sources":["../src/each.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=env.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.test.d.ts","sourceRoot":"","sources":["../src/env.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,27 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { analyze } from "./analyze.js";
3
+ import { generate } from "./codegen.js";
4
+ function agent(promptBody) {
5
+ return [
6
+ "agent: a",
7
+ "model: m",
8
+ "prompt: |",
9
+ ...promptBody.map((l) => ` ${l}`),
10
+ ].join("\n");
11
+ }
12
+ describe("{env.X} interpolation", () => {
13
+ it("compiles {env.VAR} to process.env.VAR with a default", () => {
14
+ const { ast, diagnostics } = analyze(agent(["Base: {env.API_BASE}"]), "a.agent");
15
+ expect(diagnostics).toEqual([]);
16
+ expect(generate(ast)).toContain('process.env.API_BASE ?? ""');
17
+ });
18
+ it("does not require env vars to be declared", () => {
19
+ const { diagnostics } = analyze(agent(["{env.ANYTHING}"]), "a.agent");
20
+ expect(diagnostics).toEqual([]);
21
+ });
22
+ it("rejects a bare {env} (TOA301)", () => {
23
+ const { diagnostics } = analyze(agent(["{env}"]), "a.agent");
24
+ expect(diagnostics.map((d) => d.code)).toContain("TOA301");
25
+ });
26
+ });
27
+ //# sourceMappingURL=env.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.test.js","sourceRoot":"","sources":["../src/env.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,SAAS,KAAK,CAAC,UAAoB;IACjC,OAAO;QACL,UAAU;QACV,UAAU;QACV,WAAW;QACX,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;KACnC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,OAAO,CAClC,KAAK,CAAC,CAAC,sBAAsB,CAAC,CAAC,EAC/B,SAAS,CACV,CAAC;QACF,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,CAAC,QAAQ,CAAC,GAAI,CAAC,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,gBAAgB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QACtE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QAC7D,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=example.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example.test.d.ts","sourceRoot":"","sources":["../src/example.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { describe, expect, it } from "vitest";
5
+ import { compile } from "./index.js";
6
+ const here = dirname(fileURLToPath(import.meta.url));
7
+ const exampleDir = join(here, "..", "..", "..", "examples", "researcher");
8
+ describe("examples/researcher", () => {
9
+ it("the committed researcher.ts matches toac output for researcher.agent", async () => {
10
+ const [source, committed] = await Promise.all([
11
+ readFile(join(exampleDir, "researcher.agent"), "utf8"),
12
+ readFile(join(exampleDir, "researcher.ts"), "utf8"),
13
+ ]);
14
+ const { code, diagnostics } = compile(source, "researcher.agent");
15
+ expect(diagnostics).toEqual([]);
16
+ expect(code).toBe(committed);
17
+ });
18
+ });
19
+ //# sourceMappingURL=example.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example.test.js","sourceRoot":"","sources":["../src/example.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;AAE1E,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC5C,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC,EAAE,MAAM,CAAC;YACtD,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC;SACpD,CAAC,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;QAClE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=if.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"if.test.d.ts","sourceRoot":"","sources":["../src/if.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,72 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { analyze } from "./analyze.js";
3
+ import { generate } from "./codegen.js";
4
+ import { parsePromptTemplate } from "./interpolate.js";
5
+ describe("{#if} parsing", () => {
6
+ it("parses if/else into a segment", () => {
7
+ const { segments, errors } = parsePromptTemplate("{#if inputs.verbose}\ndetailed\n{:else}\nbrief\n{/if}");
8
+ expect(errors).toEqual([]);
9
+ expect(segments).toEqual([
10
+ {
11
+ kind: "if",
12
+ cond: ["inputs", "verbose"],
13
+ negate: false,
14
+ then: [{ kind: "text", value: "detailed\n" }],
15
+ else: [{ kind: "text", value: "brief\n" }],
16
+ },
17
+ ]);
18
+ });
19
+ it("parses negation and an absent else", () => {
20
+ const { segments, errors } = parsePromptTemplate("{#if !inputs.x}\nhi\n{/if}");
21
+ expect(errors).toEqual([]);
22
+ expect(segments[0]).toMatchObject({
23
+ kind: "if",
24
+ cond: ["inputs", "x"],
25
+ negate: true,
26
+ else: [],
27
+ });
28
+ });
29
+ it("reports an unclosed if", () => {
30
+ const { errors } = parsePromptTemplate("{#if inputs.x}\nhi");
31
+ expect(errors.some((e) => /unclosed/.test(e))).toBe(true);
32
+ });
33
+ });
34
+ function agent(promptBody, inputRow = " verbose,boolean") {
35
+ return [
36
+ "agent: a",
37
+ "model: m",
38
+ "inputs[1]{name,type}:",
39
+ inputRow,
40
+ "prompt: |",
41
+ ...promptBody.map((l) => ` ${l}`),
42
+ ].join("\n");
43
+ }
44
+ describe("{#if} validation + codegen", () => {
45
+ it("compiles an if over a boolean input to a ternary", () => {
46
+ const src = agent([
47
+ "{#if inputs.verbose}",
48
+ "be detailed",
49
+ "{:else}",
50
+ "be brief",
51
+ "{/if}",
52
+ ]);
53
+ const { ast, diagnostics } = analyze(src, "a.agent");
54
+ expect(diagnostics).toEqual([]);
55
+ const code = generate(ast);
56
+ expect(code).toContain("inputs.verbose ?");
57
+ expect(code).toContain("be detailed");
58
+ expect(code).toContain("be brief");
59
+ });
60
+ it("compiles a negated if", () => {
61
+ const src = agent(["{#if !inputs.verbose}", "short", "{/if}"]);
62
+ const { ast, diagnostics } = analyze(src, "a.agent");
63
+ expect(diagnostics).toEqual([]);
64
+ expect(generate(ast)).toContain("!inputs.verbose ?");
65
+ });
66
+ it("rejects {#if} over a non-boolean input (TOA305)", () => {
67
+ const src = agent(["{#if inputs.topic}", "x", "{/if}"], " topic,string");
68
+ const { diagnostics } = analyze(src, "a.agent");
69
+ expect(diagnostics.map((d) => d.code)).toContain("TOA305");
70
+ });
71
+ });
72
+ //# sourceMappingURL=if.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"if.test.js","sourceRoot":"","sources":["../src/if.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { Diagnostic } from "./diagnostics.js";
2
+ export declare const COMPILER_VERSION = "0.1.0";
3
+ export type { Diagnostic } from "./diagnostics.js";
4
+ export { errorDiagnostic, formatDiagnostic } from "./diagnostics.js";
5
+ export { decodeToon, type DecodeToonResult } from "./toon.js";
6
+ export { preprocess, type PreprocessResult } from "./preprocess.js";
7
+ export { analyze, type AnalyzeResult } from "./analyze.js";
8
+ export { generate } from "./codegen.js";
9
+ export type { AgentAst, FieldDecl, PromptSegment, ToaType, ToaTypeBase, } from "./ast.js";
10
+ export interface CompileResult {
11
+ /** Emitted TypeScript, present only when there are no error diagnostics. */
12
+ code?: string;
13
+ diagnostics: Diagnostic[];
14
+ }
15
+ /** Compile `.agent` source to a TypeScript module, or return diagnostics. */
16
+ export declare function compile(source: string, file?: string): CompileResult;
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,eAAO,MAAM,gBAAgB,UAAU,CAAC;AAExC,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,YAAY,EACV,QAAQ,EACR,SAAS,EACT,aAAa,EACb,OAAO,EACP,WAAW,GACZ,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,6EAA6E;AAC7E,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,SAAY,GAAG,aAAa,CAMvE"}
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `toad-compiler` — the `toac` compiler: `.agent` (a TOON superset) -> typed `.ts`.
3
+ * Pipeline: preprocess -> TOON decode -> validate -> codegen.
4
+ * See `_bmad-output/architecture.md`.
5
+ */
6
+ import { analyze } from "./analyze.js";
7
+ import { generate } from "./codegen.js";
8
+ export const COMPILER_VERSION = "0.1.0";
9
+ export { errorDiagnostic, formatDiagnostic } from "./diagnostics.js";
10
+ export { decodeToon } from "./toon.js";
11
+ export { preprocess } from "./preprocess.js";
12
+ export { analyze } from "./analyze.js";
13
+ export { generate } from "./codegen.js";
14
+ /** Compile `.agent` source to a TypeScript module, or return diagnostics. */
15
+ export function compile(source, file = "<input>") {
16
+ const { ast, diagnostics } = analyze(source, file);
17
+ if (ast === undefined) {
18
+ return { diagnostics };
19
+ }
20
+ return { code: generate(ast), diagnostics };
21
+ }
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGxC,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC;AAGxC,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAyB,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAyB,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAsB,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAexC,6EAA6E;AAC7E,MAAM,UAAU,OAAO,CAAC,MAAc,EAAE,IAAI,GAAG,SAAS;IACtD,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACnD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,OAAO,EAAE,WAAW,EAAE,CAAC;IACzB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { compile, COMPILER_VERSION } from "./index.js";
3
+ describe("toad-compiler", () => {
4
+ it("exposes a version", () => {
5
+ expect(COMPILER_VERSION).toBe("0.1.0");
6
+ });
7
+ it("returns diagnostics (and no code) for an invalid agent", () => {
8
+ const result = compile("agent: demo");
9
+ expect(result.code).toBeUndefined();
10
+ expect(result.diagnostics.length).toBeGreaterThan(0);
11
+ });
12
+ it("emits code for a valid agent", () => {
13
+ const result = compile("agent: demo\nmodel: m\nprompt: hi");
14
+ expect(result.diagnostics).toEqual([]);
15
+ expect(result.code).toContain("createAgent({");
16
+ expect(result.code).toContain("export default demo;");
17
+ });
18
+ });
19
+ //# sourceMappingURL=index.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEvD,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,MAAM,GAAG,OAAO,CAAC,mCAAmC,CAAC,CAAC;QAC5D,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { PromptSegment } from "./ast.js";
2
+ export interface ParseTemplateResult {
3
+ segments: PromptSegment[];
4
+ errors: string[];
5
+ }
6
+ /**
7
+ * Parse a prompt string into segments. Supports:
8
+ * - `{ a.b }` / `{ env.X }` interpolation
9
+ * - `{#each xs as x, i}…{:else}…{/each}` loop (index + empty fallback)
10
+ * - `{#if a}…{:else if b}…{:else}…{/if}` conditional chain
11
+ * - `{{` / `}}` literal braces
12
+ *
13
+ * `{:else if}` desugars to a nested `{#if}` in the else branch, so the `if`
14
+ * segment stays a simple then/else. Block directives are control lines (the
15
+ * newline immediately after each is consumed). Validation happens in `validate`.
16
+ */
17
+ export declare function parsePromptTemplate(text: string): ParseTemplateResult;
18
+ //# sourceMappingURL=interpolate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interpolate.d.ts","sourceRoot":"","sources":["../src/interpolate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,aAAa,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAgCD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,CA6OrE"}